From 7694a89d381c14bbefb54ce2589d7e291b79dfe4 Mon Sep 17 00:00:00 2001 From: Platin21 Date: Sat, 31 Oct 2020 22:05:40 +0100 Subject: [PATCH 001/170] Fix for local/lib linking from brew for macOS --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 149c1522d..154be362f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -431,19 +431,19 @@ i32 linker_stage(lbGenerator *gen) { " -e _main " #endif , linker, object_files, LIT(output_base), LIT(output_ext), - lib_str, #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -L/usr/local/lib", #else "-lc -lm", #endif + lib_str, LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); if (exit_code != 0) { return exit_code; } - + #if defined(GB_SYSTEM_OSX) if (build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe @@ -2221,7 +2221,7 @@ int main(int arg_count, char const **arg_ptr) { , linker, LIT(output_base), LIT(output_base), LIT(output_ext), lib_str, #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", #else "-lc -lm", #endif From f3108493fbfc89a80643dba969828b7a01acac4e Mon Sep 17 00:00:00 2001 From: Platin21 Date: Sat, 31 Oct 2020 22:12:50 +0100 Subject: [PATCH 002/170] Combines all link-able types to a single if and adds .o for linking --- src/main.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 154be362f..7e043b8d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -341,12 +341,15 @@ i32 linker_stage(lbGenerator *gen) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a"))) { + } else if (string_ends_with(lib, str_lit(".a")) || + string_ends_with(lib, str_lit(".o")) || + string_ends_with(lib, str_lit(".dylib"))) + { + // For: + // object + // dynamic lib // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else if (string_ends_with(lib, str_lit(".dylib"))) { - // dynamic lib - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); @@ -2136,13 +2139,16 @@ int main(int arg_count, char const **arg_ptr) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a"))) { - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else if (string_ends_with(lib, str_lit(".dylib"))) { - // dynamic lib - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else { + } else if (string_ends_with(lib, str_lit(".a")) || + string_ends_with(lib, str_lit(".o")) || + string_ends_with(lib, str_lit(".dylib"))) + { + // For: + // object + // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); + } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } From 8158239d76e3407f7405e47b222ffb2f1c5aea31 Mon Sep 17 00:00:00 2001 From: Platin21 Date: Sat, 31 Oct 2020 22:15:16 +0100 Subject: [PATCH 003/170] Sets llvm api back to use the generic sdk link --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 7e043b8d4..c50be6136 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -435,7 +435,7 @@ i32 linker_stage(lbGenerator *gen) { #endif , linker, object_files, LIT(output_base), LIT(output_ext), #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -L/usr/local/lib", + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", #else "-lc -lm", #endif From fc7c0ca3b0413a90fabd5d41a43bff227e61477f Mon Sep 17 00:00:00 2001 From: Platin21 Date: Sat, 31 Oct 2020 22:29:02 +0100 Subject: [PATCH 004/170] Changed tab width was on 2 now 4 --- src/main.cpp | 56 +++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c50be6136..b30395941 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -341,16 +341,13 @@ i32 linker_stage(lbGenerator *gen) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a")) || - string_ends_with(lib, str_lit(".o")) || - string_ends_with(lib, str_lit(".dylib"))) - { - // For: - // object - // dynamic lib - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else { + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { + // For: + // object + // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); + } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } @@ -434,12 +431,12 @@ i32 linker_stage(lbGenerator *gen) { " -e _main " #endif , linker, object_files, LIT(output_base), LIT(output_ext), - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", - #else - "-lc -lm", - #endif - lib_str, + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif + lib_str, LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); @@ -2139,16 +2136,13 @@ int main(int arg_count, char const **arg_ptr) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a")) || - string_ends_with(lib, str_lit(".o")) || - string_ends_with(lib, str_lit(".dylib"))) - { - // For: - // object - // dynamic lib - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else { + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { + // For: + // object + // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); + } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } @@ -2226,11 +2220,11 @@ int main(int arg_count, char const **arg_ptr) { #endif , linker, LIT(output_base), LIT(output_base), LIT(output_ext), lib_str, - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", - #else - "-lc -lm", - #endif + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); From f29f7351e983d29037df9c016ab3633ffc6ebae2 Mon Sep 17 00:00:00 2001 From: Platin21 Date: Sat, 31 Oct 2020 22:30:03 +0100 Subject: [PATCH 005/170] Revert "Changed tab width was on 2 now 4" This reverts commit fc7c0ca3b0413a90fabd5d41a43bff227e61477f. --- src/main.cpp | 56 +++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b30395941..c50be6136 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -341,13 +341,16 @@ i32 linker_stage(lbGenerator *gen) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { - // For: - // object - // dynamic lib - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else { + } else if (string_ends_with(lib, str_lit(".a")) || + string_ends_with(lib, str_lit(".o")) || + string_ends_with(lib, str_lit(".dylib"))) + { + // For: + // object + // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); + } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } @@ -431,12 +434,12 @@ i32 linker_stage(lbGenerator *gen) { " -e _main " #endif , linker, object_files, LIT(output_base), LIT(output_ext), - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", - #else - "-lc -lm", - #endif - lib_str, + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif + lib_str, LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); @@ -2136,13 +2139,16 @@ int main(int arg_count, char const **arg_ptr) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { - // For: - // object - // dynamic lib - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else { + } else if (string_ends_with(lib, str_lit(".a")) || + string_ends_with(lib, str_lit(".o")) || + string_ends_with(lib, str_lit(".dylib"))) + { + // For: + // object + // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); + } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } @@ -2220,11 +2226,11 @@ int main(int arg_count, char const **arg_ptr) { #endif , linker, LIT(output_base), LIT(output_base), LIT(output_ext), lib_str, - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", - #else - "-lc -lm", - #endif + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); From 8c46582667bc211df6e61aa676e17e128f89d568 Mon Sep 17 00:00:00 2001 From: Platin21 Date: Sat, 31 Oct 2020 22:37:11 +0100 Subject: [PATCH 006/170] Fixes text layout now via GitHub --- src/main.cpp | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c50be6136..a7b951b67 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -341,13 +341,10 @@ i32 linker_stage(lbGenerator *gen) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a")) || - string_ends_with(lib, str_lit(".o")) || - string_ends_with(lib, str_lit(".dylib"))) - { - // For: - // object - // dynamic lib + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { + // For: + // object + // dynamic lib // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else { @@ -434,12 +431,12 @@ i32 linker_stage(lbGenerator *gen) { " -e _main " #endif , linker, object_files, LIT(output_base), LIT(output_ext), - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", - #else - "-lc -lm", - #endif - lib_str, + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif + lib_str, LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); @@ -2139,13 +2136,11 @@ int main(int arg_count, char const **arg_ptr) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a")) || - string_ends_with(lib, str_lit(".o")) || - string_ends_with(lib, str_lit(".dylib"))) - { - // For: - // object - // dynamic lib + + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { + // For: + // object + // dynamic lib // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else { @@ -2226,11 +2221,11 @@ int main(int arg_count, char const **arg_ptr) { #endif , linker, LIT(output_base), LIT(output_base), LIT(output_ext), lib_str, - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", - #else - "-lc -lm", - #endif + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); From 44cfa3484f915dd5a212270e64588316ad73ede0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20M=2E=20Monacci?= Date: Tue, 3 Nov 2020 07:40:17 -0300 Subject: [PATCH 007/170] Fix typo Fix typo (proprosal -> proposal) --- PROPOSAL-PROCESS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROPOSAL-PROCESS.md b/PROPOSAL-PROCESS.md index d82fc02dd..fa31c47d9 100644 --- a/PROPOSAL-PROCESS.md +++ b/PROPOSAL-PROCESS.md @@ -30,7 +30,7 @@ The proposal process is the process for reviewing a proposal and reaching a deci * Accept proposal * Decline proposal -After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project. +After the proposal is accepted or declined, implementation of the proposal proceeds in the same way as any other contribution to the project. ## Design Documents From 968aa2f6880f63bd87484f563deb701a279371ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20M=2E=20Monacci?= Date: Tue, 3 Nov 2020 10:51:56 -0300 Subject: [PATCH 008/170] Fix some typos Fix some typos --- examples/demo/demo.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 231ed6b91..d4f00475a 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -75,7 +75,7 @@ the_basics :: proc() { // Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal // literals 0x. A leading zero does not produce an octal constant (unlike C). - // In Odin, if a number constant is possible to be represented by a type without + // In Odin, if a numeric constant can be represented by a type without // precision loss, it will automatically convert to that type. x: int = 1.0; // A float literal but it can be represented by an integer without precision loss @@ -85,7 +85,7 @@ the_basics :: proc() { y = 1; // `1` is an untyped integer literal which can implicitly convert to `int` z: f64; // `z` is typed of type `f64` (64-bit floating point number) - z = 1; // `1` is an untyped integer literals which can be implicity conver to `f64` + z = 1; // `1` is an untyped integer literal which can be implicitly converted to `f64` // No need for any suffixes or decimal places like in other languages // CONSTANTS JUST WORK!!! @@ -150,7 +150,7 @@ control_flow :: proc() { i += 1; } - // If the condition is omitted, this produces an infinite loop: + // If the condition is omitted, an infinite loop is produced: for { break; } From c1d3c3f9268bfbdf94e180ce0b032f76cd3c64b8 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Thu, 5 Nov 2020 00:51:50 +0000 Subject: [PATCH 009/170] Fix slice.last() There was a typo that prevented it from being used. --- core/slice/slice.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 764eb6334..3076fe641 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -216,7 +216,7 @@ split_last :: proc(array: $T/[]$E) -> (rest: T, last: E) { first :: proc(array: $T/[]$E) -> E { return array[0]; } -last :: proc(array: $T/[]$E) -> ^E { +last :: proc(array: $T/[]$E) -> E { return array[len(array)-1]; } From 9ac6d45bd6d491c066121725277b5b13f6519339 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 6 Nov 2020 00:38:03 +0000 Subject: [PATCH 010/170] Add more procedures to `package slice` --- core/slice/slice.odin | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 3076fe641..d6172e27c 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -252,3 +252,44 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) { as_ptr :: proc(array: $T/[]$E) -> ^E { return raw_data(array); } + + +mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> []V { + r := make([]V, len(s), allocator); + for v, i in s { + r[i] = f(v); + } + return r; +} + +reduce :: proc(s: $S/[]$U, initializer: $V, f: proc(V, U) -> V) -> V { + r := initializer; + for v in s { + r = f(r, v); + } + return r; +} + +filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -> S { + r := make([dynamic]S, 0, 0, allocator); + for v in s { + if f(v) { + append(&r, v); + } + } + return r[:]; +} + + + +dot_product :: proc(a, b: $S/[]$T) -> T + where intrinsics.type_is_numeric(T) { + if len(a) != len(b) { + panic("slice.dot_product: slices of unequal length"); + } + r: T; + #no_bounds_check for _, i in a { + r += a[i] * b[i]; + } + return r; +} From 7c5247f5fb78263604abb301a67b6bb8a7de2f2a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 6 Nov 2020 00:39:09 +0000 Subject: [PATCH 011/170] Add package sys/cpu - implements processor feature detection --- core/sys/cpu/cpu.odin | 35 ++++++++++++++++++++ core/sys/cpu/cpu_x86.odin | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 core/sys/cpu/cpu.odin create mode 100644 core/sys/cpu/cpu_x86.odin diff --git a/core/sys/cpu/cpu.odin b/core/sys/cpu/cpu.odin new file mode 100644 index 000000000..b6f770aed --- /dev/null +++ b/core/sys/cpu/cpu.odin @@ -0,0 +1,35 @@ +package sys_cpu + +#assert(ODIN_USE_LLVM_API); + +Cache_Line_Pad :: struct {_: [_cache_line_size]byte}; + +initialized: bool; + +x86: struct { + _: Cache_Line_Pad, + has_aes: bool, // AES hardware implementation (AES NI) + has_adx: bool, // Multi-precision add-carry instruction extensions + has_avx: bool, // Advanced vector extension + has_avx2: bool, // Advanced vector extension 2 + has_bmi1: bool, // Bit manipulation instruction set 1 + has_bmi2: bool, // Bit manipulation instruction set 2 + has_erms: bool, // Enhanced REP for MOVSB and STOSB + has_fma: bool, // Fused-multiply-add instructions + has_os_xsave: bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. + has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM + has_popcnt: bool, // Hamming weight instruction POPCNT. + has_rdrand: bool, // RDRAND instruction (on-chip random number generator) + has_rdseed: bool, // RDSEED instruction (on-chip random number generator) + has_sse2: bool, // Streaming SIMD extension 2 (always available on amd64) + has_sse3: bool, // Streaming SIMD extension 3 + has_ssse3: bool, // Supplemental streaming SIMD extension 3 + has_sse41: bool, // Streaming SIMD extension 4 and 4.1 + has_sse42: bool, // Streaming SIMD extension 4 and 4.2 + _: Cache_Line_Pad, +}; + + +init :: proc() { + _init(); +} diff --git a/core/sys/cpu/cpu_x86.odin b/core/sys/cpu/cpu_x86.odin new file mode 100644 index 000000000..8f3560a87 --- /dev/null +++ b/core/sys/cpu/cpu_x86.odin @@ -0,0 +1,67 @@ +//+build 386, amd64 +package sys_cpu + +_cache_line_size :: 64; + +cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) { + return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} { + "cpuid", + "={ax},={bx},={cx},={dx},{ax},{cx}", + }(ax, cx)); +} + +xgetbv :: proc() -> (eax, edx: u32) { + return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} { + "xgetbv", + "={ax},={dx},{cx}", + }(0)); +} + +_init :: proc() { + is_set :: proc(hwc: u32, value: u32) -> bool { + return hwc&value != 0; + } + + initialized = true; + + max_id, _, _, _ := cpuid(0, 0); + + if max_id < 1 { + return; + } + + _, _, ecx1, edx1 := cpuid(1, 0); + + x86.has_sse2 = is_set(26, edx1); + + x86.has_sse3 = is_set(0, ecx1); + x86.has_pclmulqdq = is_set(1, ecx1); + x86.has_ssse3 = is_set(9, ecx1); + x86.has_fma = is_set(12, ecx1); + x86.has_sse41 = is_set(19, ecx1); + x86.has_sse42 = is_set(20, ecx1); + x86.has_popcnt = is_set(23, ecx1); + x86.has_aes = is_set(25, ecx1); + x86.has_os_xsave = is_set(27, ecx1); + x86.has_rdrand = is_set(30, ecx1); + + os_supports_avx := false; + if x86.has_os_xsave { + eax, _ := xgetbv(); + os_supports_avx = is_set(1, eax) && is_set(2, eax); + } + + x86.has_avx = is_set(28, ecx1) && os_supports_avx; + + if max_id < 7 { + return; + } + + _, ebx7, _, _ := cpuid(7, 0); + x86.has_bmi1 = is_set(3, ebx7); + x86.has_avx2 = is_set(5, ebx7) && os_supports_avx; + x86.has_bmi2 = is_set(8, ebx7); + x86.has_erms = is_set(9, ebx7); + x86.has_rdseed = is_set(18, ebx7); + x86.has_adx = is_set(19, ebx7); +} From b5a619e9758e53f774b022f779649df84d561c89 Mon Sep 17 00:00:00 2001 From: F0x1fy Date: Thu, 5 Nov 2020 22:14:56 -0700 Subject: [PATCH 012/170] Moved internal_windows to internal_any to allow for manual linking related to issue odin-lang/Odin#527. --- core/runtime/{internal_windows.odin => internal_any.odin} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/runtime/{internal_windows.odin => internal_any.odin} (100%) diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_any.odin similarity index 100% rename from core/runtime/internal_windows.odin rename to core/runtime/internal_any.odin From 94277fe41c60e32b8aa9ed5931dbfbbb8e6a4d82 Mon Sep 17 00:00:00 2001 From: F0x1fy Date: Fri, 6 Nov 2020 11:11:26 -0700 Subject: [PATCH 013/170] As per GingerBill's request, copied the files over to a linux-specific file instead of renaming to . --- ...{internal_any.odin => internal_linux.odin} | 0 core/runtime/internal_windows.odin | 135 ++++++++++++++++++ 2 files changed, 135 insertions(+) rename core/runtime/{internal_any.odin => internal_linux.odin} (100%) create mode 100644 core/runtime/internal_windows.odin diff --git a/core/runtime/internal_any.odin b/core/runtime/internal_linux.odin similarity index 100% rename from core/runtime/internal_any.odin rename to core/runtime/internal_linux.odin diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_windows.odin new file mode 100644 index 000000000..10d2e2249 --- /dev/null +++ b/core/runtime/internal_windows.odin @@ -0,0 +1,135 @@ +package runtime + +@(link_name="__umodti3") +umodti3 :: proc "c" (a, b: u128) -> u128 { + r: u128 = ---; + _ = udivmod128(a, b, &r); + return r; +} + + +@(link_name="__udivmodti4") +udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { + return udivmod128(a, b, rem); +} + +@(link_name="__udivti3") +udivti3 :: proc "c" (a, b: u128) -> u128 { + return udivmodti4(a, b, nil); +} + + +@(link_name="__modti3") +modti3 :: proc "c" (a, b: i128) -> i128 { + s_a := a >> (128 - 1); + s_b := b >> (128 - 1); + an := (a ~ s_a) - s_a; + bn := (b ~ s_b) - s_b; + + r: u128 = ---; + _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r); + return (transmute(i128)r ~ s_a) - s_a; +} + + +@(link_name="__divmodti4") +divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { + u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem); + return transmute(i128)u; +} + +@(link_name="__divti3") +divti3 :: proc "c" (a, b: i128) -> i128 { + u := udivmodti4(transmute(u128)a, transmute(u128)b, nil); + return transmute(i128)u; +} + + +@(link_name="__fixdfti") +fixdfti :: proc(a: u64) -> i128 { + significandBits :: 52; + typeWidth :: (size_of(u64)*8); + exponentBits :: (typeWidth - significandBits - 1); + maxExponent :: ((1 << exponentBits) - 1); + exponentBias :: (maxExponent >> 1); + + implicitBit :: (u64(1) << significandBits); + significandMask :: (implicitBit - 1); + signBit :: (u64(1) << (significandBits + exponentBits)); + absMask :: (signBit - 1); + exponentMask :: (absMask ~ significandMask); + + // Break a into sign, exponent, significand + aRep := a; + aAbs := aRep & absMask; + sign := i128(-1 if aRep & signBit != 0 else 1); + exponent := u64((aAbs >> significandBits) - exponentBias); + significand := u64((aAbs & significandMask) | implicitBit); + + // If exponent is negative, the result is zero. + if exponent < 0 { + return 0; + } + + // If the value is too large for the integer type, saturate. + if exponent >= size_of(i128) * 8 { + return max(i128) if sign == 1 else min(i128); + } + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if exponent < significandBits { + return sign * i128(significand >> (significandBits - exponent)); + } else { + return sign * (i128(significand) << (exponent - significandBits)); + } + +} + +@(default_calling_convention = "none") +foreign { + @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 --- +} + + +@(link_name="__floattidf") +floattidf :: proc(a: i128) -> f64 { + DBL_MANT_DIG :: 53; + if a == 0 { + return 0.0; + } + a := a; + N :: size_of(i128) * 8; + s := a >> (N-1); + a = (a ~ s) - s; + sd: = N - _clz_i128(a); // number of significant digits + e := u32(sd - 1); // exponent + if sd > DBL_MANT_DIG { + switch sd { + case DBL_MANT_DIG + 1: + a <<= 1; + case DBL_MANT_DIG + 2: + // okay + case: + a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | + i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0); + }; + + a |= i128((a & 4) != 0); + a += 1; + a >>= 2; + + if a & (1 << DBL_MANT_DIG) != 0 { + a >>= 1; + e += 1; + } + } else { + a <<= u128(DBL_MANT_DIG - sd); + } + fb: [2]u32; + fb[1] = (u32(s) & 0x80000000) | // sign + ((e + 1023) << 20) | // exponent + ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high + fb[1] = u32(a); // mantissa-low + return transmute(f64)fb; +} From 39044b5bb52025b1cff4b21f922e6ef3e744ea1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20M=2E=20Monacci?= Date: Fri, 6 Nov 2020 15:46:32 -0300 Subject: [PATCH 014/170] Fix small typo Fix small typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab92d6e46..23a969271 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ main :: proc() { #### [Getting Started](https://odin-lang.org/docs/install) -Instructions for downloading and install the Odin compiler and libraries. +Instructions for downloading and installing the Odin compiler and libraries. ### Learning Odin From 140bb3ebfcfa7579e53e836c3fb75792efbed078 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Fri, 6 Nov 2020 05:43:13 +0000 Subject: [PATCH 015/170] Fix -build-mode:shared type table on Unix Fixes #527. Previously on Linux, '__$startup_runtime', the procedure that initializes the type table for runtime typeids, was NOT called when a shared library was loaded by the dynamic loader. This caused the library to not have its type table populated, which caused an assert to fail if you tried to use runtime typeids - like core:fmt, for example. This commit fixes this by calling ld instead of clang, when building a shared library, so that we can pass "-init '__$startup_runtime'" to it, when building a shared library. Try as I might, I could not get clang to correctly pass through the linker flags that I wanted. --- src/main.cpp | 82 ++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a7b951b67..1ae0e7d97 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -342,9 +342,9 @@ i32 linker_stage(lbGenerator *gen) { lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { - // For: - // object - // dynamic lib + // For: + // object + // dynamic lib // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else { @@ -382,20 +382,33 @@ i32 linker_stage(lbGenerator *gen) { // Unlike the Win32 linker code, the output_ext includes the dot, because // typically executable files on *NIX systems don't have extensions. String output_ext = {}; - char const *link_settings = ""; + gbString link_settings = gb_string_make_reserve(heap_allocator(), 32); char const *linker; if (build_context.build_mode == BuildMode_DynamicLibrary) { + // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time. + // Clang, for some reason, won't let us pass the '-init' flag that lets us do this, + // so use ld instead. + // :UseLDForShared + linker = "ld"; + link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' "); // Shared libraries are .dylib on MacOS and .so on Linux. #if defined(GB_SYSTEM_OSX) output_ext = STR_LIT(".dylib"); - link_settings = "-dylib -dynamic"; + link_settings = gb_string_appendc(link_settings, "-dylib -dynamic "); #else output_ext = STR_LIT(".so"); - link_settings = "-shared"; + link_settings = gb_string_appendc(link_settings, "-shared "); #endif } else { - // TODO: Do I need anything here? - link_settings = ""; + #if defined(GB_SYSTEM_OSX) + linker = "ld"; + #else + // TODO(zangent): Figure out how to make ld work on Linux. + // It probably has to do with including the entire CRT, but + // that's quite a complicated issue to solve while remaining distro-agnostic. + // Clang can figure out linker flags for us, and that's good enough _for now_. + linker = "clang -Wno-unused-command-line-argument"; + #endif } if (build_context.out_filepath.len > 0) { @@ -406,16 +419,6 @@ i32 linker_stage(lbGenerator *gen) { } } - #if defined(GB_SYSTEM_OSX) - linker = "ld"; - #else - // TODO(zangent): Figure out how to make ld work on Linux. - // It probably has to do with including the entire CRT, but - // that's quite a complicated issue to solve while remaining distro-agnostic. - // Clang can figure out linker flags for us, and that's good enough _for now_. - linker = "clang -Wno-unused-command-line-argument"; - #endif - exit_code = system_exec_command_line_app("ld-link", "%s %s -o \"%.*s%.*s\" %s " " %s " @@ -443,7 +446,7 @@ i32 linker_stage(lbGenerator *gen) { if (exit_code != 0) { return exit_code; } - + #if defined(GB_SYSTEM_OSX) if (build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe @@ -2136,11 +2139,11 @@ int main(int arg_count, char const **arg_ptr) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { - // For: - // object - // dynamic lib + // For: + // object + // dynamic lib // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else { @@ -2171,22 +2174,36 @@ int main(int arg_count, char const **arg_ptr) { // Unlike the Win32 linker code, the output_ext includes the dot, because // typically executable files on *NIX systems don't have extensions. String output_ext = {}; - char const *link_settings = ""; + gbString link_settings = gb_string_make_reserve(heap_allocator(), 32); char const *linker; if (build_context.build_mode == BuildMode_DynamicLibrary) { + // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time. + // Clang, for some reason, won't let us pass the '-init' flag that lets us do this, + // so use ld instead. + // :UseLDForShared + linker = "ld"; + link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' "); // Shared libraries are .dylib on MacOS and .so on Linux. #if defined(GB_SYSTEM_OSX) output_ext = STR_LIT(".dylib"); - link_settings = "-dylib -dynamic"; + link_settings = gb_string_appendc(link_settings, "-dylib -dynamic "); #else output_ext = STR_LIT(".so"); - link_settings = "-shared"; + link_settings = gb_string_appendc(link_settings, "-shared "); #endif } else { - // TODO: Do I need anything here? - link_settings = ""; + #if defined(GB_SYSTEM_OSX) + linker = "ld"; + #else + // TODO(zangent): Figure out how to make ld work on Linux. + // It probably has to do with including the entire CRT, but + // that's quite a complicated issue to solve while remaining distro-agnostic. + // Clang can figure out linker flags for us, and that's good enough _for now_. + linker = "clang -Wno-unused-command-line-argument"; + #endif } + if (build_context.out_filepath.len > 0) { //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that isize pos = string_extension_position(build_context.out_filepath); @@ -2195,15 +2212,6 @@ int main(int arg_count, char const **arg_ptr) { } } - #if defined(GB_SYSTEM_OSX) - linker = "ld"; - #else - // TODO(zangent): Figure out how to make ld work on Linux. - // It probably has to do with including the entire CRT, but - // that's quite a complicated issue to solve while remaining distro-agnostic. - // Clang can figure out linker flags for us, and that's good enough _for now_. - linker = "clang -Wno-unused-command-line-argument"; - #endif exit_code = system_exec_command_line_app("ld-link", "%s \"%.*s.o\" -o \"%.*s%.*s\" %s " From 06b2a9a3e7de285ddb7a4add020c8d08c7afa873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 8 Nov 2020 00:01:31 +0100 Subject: [PATCH 016/170] [CI] Try setting python 3.x in nightly upload step --- .github/workflows/nightly.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d43e96b12..d4365d5af 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -96,6 +96,9 @@ jobs: needs: [build_windows, build_macos, build_ubuntu] steps: - uses: actions/checkout@v1 + - uses: actions/setup-python@v2 + with: + python-version: '3.x' - name: Install B2 CLI shell: bash From a77976533cd60f773030d898907b56896959bf72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 8 Nov 2020 00:08:11 +0100 Subject: [PATCH 017/170] [CI] Add manual trigger to nightly --- .github/workflows/nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d4365d5af..4af568604 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,6 +1,7 @@ name: Nightly on: + workflow_dispatch: schedule: - cron: 0 20 * * * From a239fcfa3afbf6faf45a2a23b7f02e4ebd487b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 8 Nov 2020 00:21:28 +0100 Subject: [PATCH 018/170] Update nightly.yml --- .github/workflows/nightly.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 4af568604..9d7b3b49b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -103,7 +103,12 @@ jobs: - name: Install B2 CLI shell: bash - run: sudo pip install --upgrade b2 + run: | + python -m pip install --upgrade pip + sudo pip install --upgrade b2 + + - name: Display Python version + run: python -c "import sys; print(sys.version)" - name: Download Windows artifacts uses: actions/download-artifact@v1 From ef27528ace7e3a23d7a45be761fa1cb0d2af1a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 8 Nov 2020 00:31:25 +0100 Subject: [PATCH 019/170] [CI] Non-sudo install of b2? --- .github/workflows/nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 9d7b3b49b..1648169e1 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -105,7 +105,7 @@ jobs: shell: bash run: | python -m pip install --upgrade pip - sudo pip install --upgrade b2 + pip install --upgrade b2 - name: Display Python version run: python -c "import sys; print(sys.version)" From 817db70bde5ff355d7f02ca5cc6ae1bfd14d9cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 8 Nov 2020 01:23:19 +0100 Subject: [PATCH 020/170] [CI] Remove -march for makefile nightly --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 34e56c950..0be242aeb 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ release: $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin nightly: - $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 -march=native $(LDFLAGS) -o odin + $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin From 19e2f7b7bfd7532fb35456c13361f406d99a4275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 8 Nov 2020 01:46:28 +0100 Subject: [PATCH 021/170] [CI] testing webhook --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 219dcebfd..0513f6659 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,8 @@ jobs: build_linux: runs-on: ubuntu-latest steps: + - name: TESTING + run: exit 1 - uses: actions/checkout@v1 - name: Download LLVM run: sudo apt-get install llvm From 11a4dc8ee3a3f88ae37f444e98442882f8e4e2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 8 Nov 2020 01:47:09 +0100 Subject: [PATCH 022/170] [CI] Done testing webhook --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0513f6659..219dcebfd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,8 +5,6 @@ jobs: build_linux: runs-on: ubuntu-latest steps: - - name: TESTING - run: exit 1 - uses: actions/checkout@v1 - name: Download LLVM run: sudo apt-get install llvm From 44baf56d624240aae3302a38a5083341d8fede3a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 8 Nov 2020 23:54:09 +0000 Subject: [PATCH 023/170] Fix cyclic check in `is_type_polymorphic` --- core/intrinsics/intrinsics.odin | 2 +- core/runtime/procs_windows_amd64.odin | 11 +++++------ src/types.cpp | 14 +++++++++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 52d930fb8..3e20baecd 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -114,7 +114,7 @@ type_is_ordered_numeric :: proc($T: typeid) -> bool --- type_is_indexable :: proc($T: typeid) -> bool --- type_is_sliceable :: proc($T: typeid) -> bool --- type_is_comparable :: proc($T: typeid) -> bool --- -type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp +type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=) type_is_dereferenceable :: proc($T: typeid) -> bool --- type_is_valid_map_key :: proc($T: typeid) -> bool --- diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin index 8593d96f9..511b1866d 100644 --- a/core/runtime/procs_windows_amd64.odin +++ b/core/runtime/procs_windows_amd64.odin @@ -2,15 +2,14 @@ package runtime foreign import kernel32 "system:Kernel32.lib" -windows_trap_array_bounds :: proc "contextless" () -> ! { - DWORD :: u32; - ULONG_PTR :: uint; +@(private) +foreign kernel32 { + RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! --- +} +windows_trap_array_bounds :: proc "contextless" () -> ! { EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C; - foreign kernel32 { - RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! --- - } RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil); } diff --git a/src/types.cpp b/src/types.cpp index fc4544385..f4d375f4f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -323,6 +323,7 @@ String const type_strings[] = { enum TypeFlag : u32 { TypeFlag_Polymorphic = 1<<1, TypeFlag_PolySpecialized = 1<<2, + TypeFlag_InProcessOfCheckingPolymorphic = 1<<3, }; struct Type { @@ -1695,12 +1696,23 @@ TypeTuple *get_record_polymorphic_params(Type *t) { bool is_type_polymorphic(Type *t, bool or_specialized=false) { + if (t->flags & TypeFlag_InProcessOfCheckingPolymorphic) { + return false; + } + switch (t->kind) { case Type_Generic: return true; case Type_Named: - return is_type_polymorphic(t->Named.base, or_specialized); + { + u32 flags = t->flags; + t->flags |= TypeFlag_InProcessOfCheckingPolymorphic; + bool ok = is_type_polymorphic(t->Named.base, or_specialized); + t->flags = flags; + return ok; + } + case Type_Opaque: return is_type_polymorphic(t->Opaque.elem, or_specialized); case Type_Pointer: From c26cb470a22e1985bbb7ec878a402ad8f78ef75e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 9 Nov 2020 10:27:27 +0000 Subject: [PATCH 024/170] Fix LLVM-API type cycle for procedures of named procedures --- src/check_type.cpp | 24 +++++++++++++++++++++--- src/llvm_backend.cpp | 3 ++- src/types.cpp | 1 + 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 93040e493..62b5fd8e1 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2209,6 +2209,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall return new_type; } + if (is_type_proc(original_type)) { + // NOTE(bill): Force a cast to prevent a possible type cycle + return t_rawptr; + } + if (cc == ProcCC_None || cc == ProcCC_PureNone || cc == ProcCC_InlineAsm) { return new_type; } @@ -2332,6 +2337,11 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal return new_type; } + if (is_type_proc(single_type)) { + // NOTE(bill): Force a cast to prevent a possible type cycle + return t_rawptr; + } + if (is_type_simd_vector(single_type)) { return new_type; } @@ -2451,22 +2461,29 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) { return; } - if (type->Proc.abi_types_set) { + if (type->Proc.abi_types_set || type->flags & TypeFlag_InProcessOfCheckingABI) { return; } + u32 flags = type->flags; + type->flags |= TypeFlag_InProcessOfCheckingABI; + type->Proc.abi_compat_params = array_make(allocator, cast(isize)type->Proc.param_count); for (i32 i = 0; i < type->Proc.param_count; i++) { Entity *e = type->Proc.params->Tuple.variables[i]; if (e->kind == Entity_Variable) { Type *original_type = e->type; + if (is_type_named(original_type) && is_type_proc(original_type)) { + continue; + } + Type *new_type = type_to_abi_compat_param_type(allocator, original_type, type->Proc.calling_convention); type->Proc.abi_compat_params[i] = new_type; switch (type->Proc.calling_convention) { case ProcCC_Odin: case ProcCC_Contextless: case ProcCC_Pure: - if (is_type_pointer(new_type) & !is_type_pointer(e->type)) { + if (is_type_pointer(new_type) && !is_type_pointer(e->type) && !is_type_proc(e->type)) { e->flags |= EntityFlag_ImplicitReference; } break; @@ -2474,7 +2491,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) { if (build_context.ODIN_OS == "linux" || build_context.ODIN_OS == "darwin") { - if (is_type_pointer(new_type) & !is_type_pointer(e->type)) { + if (is_type_pointer(new_type) & !is_type_pointer(e->type) && !is_type_proc(e->type)) { e->flags |= EntityFlag_ByVal; } } @@ -2499,6 +2516,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) { type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); type->Proc.abi_types_set = true; + type->flags = flags; } // NOTE(bill): 'operands' is for generating non generic procedure type diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4abc65ab4..c6fee8ab8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1370,7 +1370,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { // Sanity check continue; } - array_add(¶m_types, lb_type(m, v->type)); + LLVMTypeRef t = lb_type(m, v->type); + array_add(¶m_types, t); } } else { array_add(¶m_types, lb_type(m, param)); diff --git a/src/types.cpp b/src/types.cpp index f4d375f4f..acc1c7b2e 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -324,6 +324,7 @@ enum TypeFlag : u32 { TypeFlag_Polymorphic = 1<<1, TypeFlag_PolySpecialized = 1<<2, TypeFlag_InProcessOfCheckingPolymorphic = 1<<3, + TypeFlag_InProcessOfCheckingABI = 1<<4, }; struct Type { From 7909a9f5a599af020cfb151f99fe740d62517def Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 9 Nov 2020 10:36:09 +0000 Subject: [PATCH 025/170] Remove debug code causing bug --- src/check_type.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 62b5fd8e1..2b9bcdb33 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2473,10 +2473,6 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) { Entity *e = type->Proc.params->Tuple.variables[i]; if (e->kind == Entity_Variable) { Type *original_type = e->type; - if (is_type_named(original_type) && is_type_proc(original_type)) { - continue; - } - Type *new_type = type_to_abi_compat_param_type(allocator, original_type, type->Proc.calling_convention); type->Proc.abi_compat_params[i] = new_type; switch (type->Proc.calling_convention) { From 31f4590f4b291bd5a21248d2c27ab8630b3e5fd9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 9 Nov 2020 13:04:36 +0000 Subject: [PATCH 026/170] Fix default parameters on record types --- src/check_expr.cpp | 63 +++++++++++++++++++---- src/check_type.cpp | 123 ++++++++++++++++++++++++++++++++------------- src/entity.cpp | 1 + src/parser.cpp | 4 +- 4 files changed, 144 insertions(+), 47 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 02b54c80a..01e971ac7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7338,6 +7338,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper Entity *e = params->variables[i]; if (e->kind == Entity_Constant) { check_expr_with_type_hint(c, &operands[i], fv->value, e->type); + continue; } } @@ -7371,9 +7372,22 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper TypeTuple *tuple = get_record_polymorphic_params(original_type); isize param_count = tuple->variables.count; + isize minimum_param_count = param_count; + for (minimum_param_count = tuple->variables.count-1; minimum_param_count >= 0; minimum_param_count--) { + Entity *e = tuple->variables[minimum_param_count]; + if (e->kind != Entity_Constant) { + break; + } + if (e->Constant.param_value.kind == ParameterValue_Invalid) { + break; + } + } Array ordered_operands = operands; - if (named_fields) { + if (!named_fields) { + ordered_operands = array_make(c->allocator, param_count); + array_copy(&ordered_operands, operands, 0); + } else { bool *visited = gb_alloc_array(c->allocator, bool, param_count); // LEAK(bill) @@ -7440,26 +7454,55 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper return err; } - if (param_count < ordered_operands.count) { - error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); - err = CallArgumentError_TooManyArguments; - } else if (param_count > ordered_operands.count) { - error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); - err = CallArgumentError_TooFewArguments; + if (minimum_param_count != param_count) { + if (param_count < ordered_operands.count) { + error(call, "Too many polymorphic type arguments, expected a maximum of %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooManyArguments; + } else if (minimum_param_count > ordered_operands.count) { + error(call, "Too few polymorphic type arguments, expected a minimum of %td, got %td", minimum_param_count, ordered_operands.count); + err = CallArgumentError_TooFewArguments; + } + } else { + if (param_count < ordered_operands.count) { + error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooManyArguments; + } else if (param_count > ordered_operands.count) { + error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooFewArguments; + } } if (err != 0) { return err; } + if (minimum_param_count != param_count) { + isize missing_count = 0; + // NOTE(bill): Replace missing operands with the default values (if possible) + for_array(i, ordered_operands) { + Operand *o = &ordered_operands[i]; + if (o->expr == nullptr) { + Entity *e = tuple->variables[i]; + if (e->kind == Entity_Constant) { + missing_count += 1; + o->mode = Addressing_Constant; + o->type = default_type(e->type); + o->expr = unparen_expr(e->Constant.param_value.original_ast_expr); + if (e->Constant.param_value.kind == ParameterValue_Constant) { + o->value = e->Constant.param_value.value; + } + } + } + } + } + i64 score = 0; for (isize i = 0; i < param_count; i++) { + Entity *e = tuple->variables[i]; Operand *o = &ordered_operands[i]; if (o->mode == Addressing_Invalid) { continue; } - Entity *e = tuple->variables[i]; - if (e->kind == Entity_TypeName) { if (o->mode != Addressing_Type) { if (show_error) { @@ -7800,7 +7843,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) { err_str = "used as a value"; break; case Addressing_Type: - err_str = "is not an expression"; + err_str = "is not an expression but a"; break; case Addressing_Builtin: err_str = "must be called"; diff --git a/src/check_type.cpp b/src/check_type.cpp index 2b9bcdb33..ace1ef898 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1,3 +1,4 @@ +ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location); void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) { t = base_type(t); @@ -408,32 +409,50 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< } ast_node(p, Field, param); Ast *type_expr = p->type; + Ast *default_value = unparen_expr(p->default_value); Type *type = nullptr; bool is_type_param = false; bool is_type_polymorphic_type = false; - if (type_expr == nullptr) { + if (type_expr == nullptr && default_value == nullptr) { error(param, "Expected a type for this parameter"); continue; } - if (type_expr->kind == Ast_Ellipsis) { - type_expr = type_expr->Ellipsis.expr; - error(param, "A polymorphic parameter cannot be variadic"); - } - if (type_expr->kind == Ast_TypeidType) { - is_type_param = true; - Type *specialization = nullptr; - if (type_expr->TypeidType.specialization != nullptr) { - Ast *s = type_expr->TypeidType.specialization; - specialization = check_type(ctx, s); + + if (type_expr != nullptr) { + if (type_expr->kind == Ast_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); } - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); - } else { - type = check_type(ctx, type_expr); - if (is_type_polymorphic(type)) { - is_type_polymorphic_type = true; + if (type_expr->kind == Ast_TypeidType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeidType.specialization != nullptr) { + Ast *s = type_expr->TypeidType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else { + type = check_type(ctx, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } } } + ParameterValue param_value = {}; + if (default_value != nullptr) { + Type *out_type = nullptr; + param_value = handle_parameter_value(ctx, type, &out_type, default_value, false); + if (type == nullptr && out_type != nullptr) { + type = out_type; + } + if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) { + error(default_value, "Invalid parameter value"); + param_value = {}; + } + } + + if (type == nullptr) { error(params[i], "Invalid parameter type"); type = t_invalid; @@ -471,7 +490,14 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< Token token = name->Ident.token; if (poly_operands != nullptr) { - Operand operand = (*poly_operands)[entities.count]; + Operand operand = {}; + operand.type = t_invalid; + if (entities.count < poly_operands->count) { + operand = (*poly_operands)[entities.count]; + } else if (param_value.kind != ParameterValue_Invalid) { + operand.mode = Addressing_Constant; + operand.value = param_value.value; + } if (is_type_param) { if (is_type_polymorphic(base_type(operand.type))) { is_polymorphic = true; @@ -486,6 +512,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< } if (e == nullptr) { e = alloc_entity_constant(scope, token, operand.type, operand.value); + e->Constant.param_value = param_value; } } } else { @@ -493,7 +520,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< e = alloc_entity_type_name(scope, token, type); e->TypeName.is_type_alias = true; } else { - e = alloc_entity_constant(scope, token, type, empty_exact_value); + e = alloc_entity_constant(scope, token, type, param_value.value); + e->Constant.param_value = param_value; } } @@ -599,29 +627,45 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraytype; + Ast *default_value = unparen_expr(p->default_value); Type *type = nullptr; bool is_type_param = false; bool is_type_polymorphic_type = false; - if (type_expr == nullptr) { + if (type_expr == nullptr && default_value == nullptr) { error(param, "Expected a type for this parameter"); continue; } - if (type_expr->kind == Ast_Ellipsis) { - type_expr = type_expr->Ellipsis.expr; - error(param, "A polymorphic parameter cannot be variadic"); - } - if (type_expr->kind == Ast_TypeidType) { - is_type_param = true; - Type *specialization = nullptr; - if (type_expr->TypeidType.specialization != nullptr) { - Ast *s = type_expr->TypeidType.specialization; - specialization = check_type(ctx, s); + if (type_expr != nullptr) { + if (type_expr->kind == Ast_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); } - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); - } else { - type = check_type(ctx, type_expr); - if (is_type_polymorphic(type)) { - is_type_polymorphic_type = true; + if (type_expr->kind == Ast_TypeidType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeidType.specialization != nullptr) { + Ast *s = type_expr->TypeidType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else { + type = check_type(ctx, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } + } + } + + ParameterValue param_value = {}; + if (default_value != nullptr) { + Type *out_type = nullptr; + param_value = handle_parameter_value(ctx, type, &out_type, default_value, false); + if (type == nullptr && out_type != nullptr) { + type = out_type; + } + if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) { + error(default_value, "Invalid parameter value"); + param_value = {}; } } @@ -662,7 +706,14 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayIdent.token; if (poly_operands != nullptr) { - Operand operand = (*poly_operands)[entities.count]; + Operand operand = {}; + operand.type = t_invalid; + if (entities.count < poly_operands->count) { + operand = (*poly_operands)[entities.count]; + } else if (param_value.kind != ParameterValue_Invalid) { + operand.mode = Addressing_Constant; + operand.value = param_value.value; + } if (is_type_param) { GB_ASSERT(operand.mode == Addressing_Type || operand.mode == Addressing_Invalid); @@ -675,6 +726,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayConstant.param_value = param_value; } } else { if (is_type_param) { @@ -682,6 +734,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayTypeName.is_type_alias = true; } else { e = alloc_entity_constant(scope, token, type, empty_exact_value); + e->Constant.param_value = param_value; } } diff --git a/src/entity.cpp b/src/entity.cpp index 3d354b9c8..a9d598735 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -120,6 +120,7 @@ struct Entity { union { struct { ExactValue value; + ParameterValue param_value; } Constant; struct { Ast *init_expr; // only used for some variables within procedure bodies diff --git a/src/parser.cpp b/src/parser.cpp index 330d78263..794ff231d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2122,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (allow_token(f, Token_OpenParen)) { isize param_count = 0; - polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true); + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true); if (param_count == 0) { syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter"); polymorphic_params = nullptr; @@ -2203,7 +2203,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (allow_token(f, Token_OpenParen)) { isize param_count = 0; - polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true); + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true); if (param_count == 0) { syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric"); polymorphic_params = nullptr; From eea3a1ecd38bda7b942c621001705a79ac6024e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 9 Nov 2020 13:05:02 +0000 Subject: [PATCH 027/170] Improve sync.Channel to encode the direction into the type --- core/sync/channel.odin | 91 ++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/core/sync/channel.odin b/core/sync/channel.odin index 4259384ce..142c8f602 100644 --- a/core/sync/channel.odin +++ b/core/sync/channel.odin @@ -2,60 +2,89 @@ package sync import "core:mem" import "core:time" -import "core:intrinsics" +import "intrinsics" import "core:math/rand" _, _ :: time, rand; +Channel_Direction :: enum i8 { + Both = 0, + Send = +1, + Recv = -1, +} -Channel :: struct(T: typeid) { +Channel :: struct(T: typeid, Direction := Channel_Direction.Both) { using _internal: ^Raw_Channel, } -channel_init :: proc(ch: ^$C/Channel($T), cap := 0, allocator := context.allocator) { +channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) { context.allocator = allocator; ch._internal = raw_channel_create(size_of(T), align_of(T), cap); return; } -channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T)) { +channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) { context.allocator = allocator; ch._internal = raw_channel_create(size_of(T), align_of(T), cap); return; } -channel_destroy :: proc(ch: $C/Channel($T)) { +channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) { + context.allocator = allocator; + ch._internal = raw_channel_create(size_of(T), align_of(T), cap); + return; +} +channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) { + context.allocator = allocator; + ch._internal = raw_channel_create(size_of(T), align_of(T), cap); + return; +} + +channel_destroy :: proc(ch: $C/Channel($T, $D)) { raw_channel_destroy(ch._internal); } - -channel_len :: proc(ch: $C/Channel($T)) -> int { - return ch._internal.len; +channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) { + res._internal = ch._internal; + return; } -channel_cap :: proc(ch: $C/Channel($T)) -> int { - return ch._internal.cap; + +channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) { + res._internal = ch._internal; + return; } -channel_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) { +channel_len :: proc(ch: $C/Channel($T, $D)) -> int { + return ch._internal.len if ch._internal != nil else 0; +} +channel_cap :: proc(ch: $C/Channel($T, $D)) -> int { + return ch._internal.cap if ch._internal != nil else 0; +} + + +channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both { msg := msg; _ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc); } -channel_try_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) -> bool { +channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both { msg := msg; return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc); } -channel_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T) { +channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both { c := ch._internal; + if c == nil { + panic(message="cannot recv message; channel is nil", loc=loc); + } mutex_lock(&c.mutex); raw_channel_recv_impl(c, &msg, loc); mutex_unlock(&c.mutex); return; } -channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T, ok: bool) { +channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both { c := ch._internal; - if mutex_try_lock(&c.mutex) { + if c != nil && mutex_try_lock(&c.mutex) { if c.len > 0 { raw_channel_recv_impl(c, &msg, loc); ok = true; @@ -64,7 +93,7 @@ channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T } return; } -channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_location) -> (ok: bool) { +channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both { res: T; res, ok = channel_try_recv(ch, loc); if ok && msg != nil { @@ -74,32 +103,32 @@ channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_locatio } -channel_is_nil :: proc(ch: $C/Channel($T)) -> bool { +channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool { return ch._internal == nil; } -channel_is_open :: proc(ch: $C/Channel($T)) -> bool { +channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool { c := ch._internal; return c != nil && !c.closed; } -channel_eq :: proc(a, b: $C/Channel($T)) -> bool { +channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool { return a._internal == b._internal; } -channel_ne :: proc(a, b: $C/Channel($T)) -> bool { +channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool { return a._internal != b._internal; } -channel_can_send :: proc(ch: $C/Channel($T)) -> (ok: bool) { +channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both { return raw_channel_can_send(ch._internal); } -channel_can_recv :: proc(ch: $C/Channel($T)) -> (ok: bool) { +channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both { return raw_channel_can_recv(ch._internal); } -channel_peek :: proc(ch: $C/Channel($T)) -> int { +channel_peek :: proc(ch: $C/Channel($T, $D)) -> int { c := ch._internal; if c == nil { return -1; @@ -111,12 +140,12 @@ channel_peek :: proc(ch: $C/Channel($T)) -> int { } -channel_close :: proc(ch: $C/Channel($T), loc := #caller_location) { +channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) { raw_channel_close(ch._internal, loc); } -channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) { +channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D >= .Both { c := ch._internal; if c == nil { return; @@ -127,12 +156,12 @@ channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) { } return; } -channel_drain :: proc(ch: $C/Channel($T)) { +channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both { raw_channel_drain(ch._internal); } -channel_move :: proc(dst, src: $C/Channel($T)) { +channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both { for msg in channel_iterator(src) { channel_send(dst, msg); } @@ -509,7 +538,7 @@ select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) { return; } -select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) { +select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) { switch len(channels) { case 0: panic("sync: select with no channels"); @@ -560,7 +589,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) { return; } -select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) { +select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) { switch len(channels) { case 0: panic("sync: select with no channels"); @@ -781,7 +810,7 @@ select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_che return; } -select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) { +select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) { switch len(channels) { case 0: index = 0; @@ -820,7 +849,7 @@ select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) return; } -select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) { +select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) { switch len(channels) { case 0: index = 0; From ee3b3fe6a3424d84cda26b1cddc764694ac49e7a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 10 Nov 2020 14:48:57 +0000 Subject: [PATCH 028/170] Fix `typeid_of` bug --- src/check_expr.cpp | 26 +++++++++++++++++++++----- src/llvm_backend.cpp | 30 ++++++++++++------------------ src/llvm_backend.hpp | 2 +- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 01e971ac7..a8e7550a1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4343,7 +4343,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 add_type_info_type(c, t); - t = base_type(t); if (o.mode != Addressing_Type) { error(expr, "Expected a type for 'typeid_of'"); return false; @@ -4351,6 +4350,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Value; operand->type = t_typeid; + operand->value = exact_value_typeid(t); break; } @@ -7843,7 +7843,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) { err_str = "used as a value"; break; case Addressing_Type: - err_str = "is not an expression but a"; + err_str = "is not an expression but a type"; break; case Addressing_Builtin: err_str = "must be called"; @@ -9108,6 +9108,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } is_constant = false; { // Checker values + bool key_is_typeid = is_type_typeid(t->Map.key); + bool value_is_typeid = is_type_typeid(t->Map.value); + for_array(i, cl->elems) { Ast *elem = cl->elems[i]; if (elem->kind != Ast_FieldValue) { @@ -9115,13 +9118,22 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type continue; } ast_node(fv, FieldValue, elem); - check_expr_with_type_hint(c, o, fv->field, t->Map.key); + + if (key_is_typeid) { + check_expr_or_type(c, o, fv->field, t->Map.key); + } else { + check_expr_with_type_hint(c, o, fv->field, t->Map.key); + } check_assignment(c, o, t->Map.key, str_lit("map literal")); if (o->mode == Addressing_Invalid) { continue; } - check_expr_with_type_hint(c, o, fv->value, t->Map.value); + if (value_is_typeid) { + check_expr_or_type(c, o, fv->value, t->Map.value); + } else { + check_expr_with_type_hint(c, o, fv->value, t->Map.value); + } check_assignment(c, o, t->Map.value, str_lit("map literal")); } } @@ -9677,7 +9689,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_map(t)) { Operand key = {}; - check_expr_with_type_hint(c, &key, ie->index, t->Map.key); + if (is_type_typeid(t->Map.key)) { + check_expr_or_type(c, &key, ie->index, t->Map.key); + } else { + check_expr_with_type_hint(c, &key, ie->index, t->Map.key); + } check_assignment(c, &key, t->Map.key, str_lit("map index")); if (key.mode == Addressing_Invalid) { o->mode = Addressing_Invalid; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c6fee8ab8..aab2248d9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -4635,7 +4635,7 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr return -1; } -lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) { +lbValue lb_typeid(lbModule *m, Type *type) { type = default_type(type); u64 id = cast(u64)lb_type_info_index(m->info, type); @@ -4687,6 +4687,7 @@ lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) { u64 data = 0; if (build_context.word_size == 4) { + GB_ASSERT(id <= (1u<<24u)); data |= (id &~ (1u<<24)) << 0u; // index data |= (kind &~ (1u<<5)) << 24u; // kind data |= (named &~ (1u<<1)) << 29u; // kind @@ -4694,6 +4695,7 @@ lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) { data |= (reserved &~ (1u<<1)) << 31u; // kind } else { GB_ASSERT(build_context.word_size == 8); + GB_ASSERT(id <= (1ull<<56u)); data |= (id &~ (1ull<<56)) << 0ul; // index data |= (kind &~ (1ull<<5)) << 56ull; // kind data |= (named &~ (1ull<<1)) << 61ull; // kind @@ -4701,10 +4703,9 @@ lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) { data |= (reserved &~ (1ull<<1)) << 63ull; // kind } - lbValue res = {}; - res.value = LLVMConstInt(lb_type(m, typeid_type), data, false); - res.type = typeid_type; + res.value = LLVMConstInt(lb_type(m, t_typeid), data, false); + res.type = t_typeid; return res; } @@ -4739,7 +4740,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc value = convert_exact_value_for_type(value, type); if (value.kind == ExactValue_Typeid) { - return lb_typeid(m, value.value_typeid, original_type); + return lb_typeid(m, value.value_typeid); } if (value.kind == ExactValue_Invalid) { @@ -5294,7 +5295,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } break; case ExactValue_Typeid: - return lb_typeid(m, value.value_typeid, original_type); + return lb_typeid(m, value.value_typeid); } return lb_const_nil(m, original_type); @@ -5804,10 +5805,10 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { lbValue right = {}; if (be->left->tav.mode == Addressing_Type) { - left = lb_typeid(p->module, be->left->tav.type, t_typeid); + left = lb_typeid(p->module, be->left->tav.type); } if (be->right->tav.mode == Addressing_Type) { - right = lb_typeid(p->module, be->right->tav.type, t_typeid); + right = lb_typeid(p->module, be->right->tav.type); } if (left.value == nullptr) left = lb_build_expr(p, be->left); if (right.value == nullptr) right = lb_build_expr(p, be->right); @@ -7467,16 +7468,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_typeid_of: { Ast *arg = ce->args[0]; TypeAndValue tav = type_and_value_of_expr(arg); - if (tav.mode == Addressing_Type) { - Type *t = default_type(type_of_expr(arg)); - return lb_typeid(p->module, t); - } - Type *t = base_type(tav.type); - GB_ASSERT(are_types_identical(t, t_type_info_ptr)); - - auto args = array_make(heap_allocator(), 1); - args[0] = lb_emit_conv(p, lb_build_expr(p, arg), t_type_info_ptr); - return lb_emit_runtime_call(p, "__typeid_of", args); + GB_ASSERT(tav.mode == Addressing_Type); + Type *t = default_type(type_of_expr(arg)); + return lb_typeid(p->module, t); } case BuiltinProc_len: { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 23b2c7654..4a0816e62 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -301,7 +301,7 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_ini void lb_add_foreign_library_path(lbModule *m, Entity *e); -lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type=t_typeid); +lbValue lb_typeid(lbModule *m, Type *type); lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value); lbValue lb_address_from_load(lbProcedure *p, lbValue value); From 95b94a0f5669d8fb1c38da945bd73505f5c112d3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 10 Nov 2020 15:00:40 +0000 Subject: [PATCH 029/170] Fix sync.Channel code; add `thread.run_with_poly_data` and `run_with_poly_data(2|3|4)` procedures --- core/sync/channel.odin | 24 ++++---- core/thread/thread.odin | 100 +++++++++++++++++++++++++++++--- core/thread/thread_unix.odin | 5 +- core/thread/thread_windows.odin | 8 ++- 4 files changed, 113 insertions(+), 24 deletions(-) diff --git a/core/sync/channel.odin b/core/sync/channel.odin index 142c8f602..7eddbfaf0 100644 --- a/core/sync/channel.odin +++ b/core/sync/channel.odin @@ -145,7 +145,7 @@ channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) { } -channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D >= .Both { +channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both { c := ch._internal; if c == nil { return; @@ -287,18 +287,19 @@ raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := for c.len >= c.cap { condition_wait_for(&c.cond); } - } else if c.len > 0 { + } else if c.len > 0 { // TODO(bill): determine correct behaviour if !block { return false; } condition_wait_for(&c.cond); + } else if c.len == 0 && !block { + return false; } send(c, msg); condition_signal(&c.cond); raw_channel_wait_queue_signal(c.recvq); - return true; } @@ -564,7 +565,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) q.state = &state; raw_channel_wait_queue_insert(&c.recvq, q); } - raw_channel_wait_queue_wait_on(&state); + raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT); for c, i in channels { q := &queues[i]; raw_channel_wait_queue_remove(&c.recvq, q); @@ -618,7 +619,7 @@ select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) q.state = &state; raw_channel_wait_queue_insert(&c.recvq, q); } - raw_channel_wait_queue_wait_on(&state); + raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT); for c, i in channels { q := &queues[i]; raw_channel_wait_queue_remove(&c.recvq, q); @@ -813,13 +814,12 @@ select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_che select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) { switch len(channels) { case 0: - index = 0; + index = -1; return; case 1: - if c := channels[0]; channel_can_recv(c) { + ok: bool; + if msg, ok = channel_try_recv(channels[0]); ok { index = 0; - msg = channel_recv(c); - return; } return; } @@ -850,15 +850,13 @@ select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: i } select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) { + index = -1; switch len(channels) { case 0: - index = 0; return; case 1: - if c := channels[0]; channel_can_send(c) { + if channel_try_send(channels[0], msg) { index = 0; - channel_send(c, msg); - return; } return; } diff --git a/core/thread/thread.odin b/core/thread/thread.odin index 85e0cc3fe..b98f4c07c 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -2,17 +2,26 @@ package thread import "core:runtime" import "core:sync" -import "core:intrinsics" +import "core:mem" +import "intrinsics" + +_ :: intrinsics; Thread_Proc :: #type proc(^Thread); +MAX_USER_ARGUMENTS :: 8; + Thread :: struct { - using specific: Thread_Os_Specific, - procedure: Thread_Proc, - data: rawptr, - user_index: int, + using specific: Thread_Os_Specific, + procedure: Thread_Proc, + data: rawptr, + user_index: int, + user_args: [MAX_USER_ARGUMENTS]rawptr, init_context: Maybe(runtime.Context), + + + creation_allocator: mem.Allocator, } #assert(size_of(Thread{}.user_index) == size_of(uintptr)); @@ -34,17 +43,94 @@ run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) { thread_proc :: proc(t: ^Thread) { fn := cast(proc(rawptr))t.data; - data := rawptr(uintptr(t.user_index)); + assert(t.user_index >= 1); + data := t.user_args[0]; fn(data); destroy(t); } t := create(thread_proc, priority); t.data = rawptr(fn); - t.user_index = int(uintptr(data)); + t.user_index = 1; + t.user_args = data; t.init_context = init_context; start(t); } +run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) + where intrinsics.type_is_pointer(T) || size_of(T) == size_of(rawptr) { + thread_proc :: proc(t: ^Thread) { + fn := cast(proc(rawptr))t.data; + assert(t.user_index >= 1); + data := t.user_args[0]; + fn(data); + destroy(t); + } + t := create(thread_proc, priority); + t.data = rawptr(fn); + t.user_index = 1; + t.user_args[0] = transmute(rawptr)data; + t.init_context = init_context; + start(t); +} + +run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) + where intrinsics.type_is_pointer(T1) || size_of(T1) == size_of(rawptr), + intrinsics.type_is_pointer(T2) || size_of(T2) == size_of(rawptr) { + thread_proc :: proc(t: ^Thread) { + fn := cast(proc(rawptr, rawptr))t.data; + assert(t.user_index >= 2); + fn(t.user_args[0], t.user_args[1]); + destroy(t); + } + t := create(thread_proc, priority); + t.data = rawptr(fn); + t.user_index = 2; + t.user_args[0] = transmute(rawptr)arg1; + t.user_args[1] = transmute(rawptr)arg2; + t.init_context = init_context; + start(t); +} + +run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) + where intrinsics.type_is_pointer(T1) || size_of(T1) == size_of(rawptr), + intrinsics.type_is_pointer(T2) || size_of(T2) == size_of(rawptr), + intrinsics.type_is_pointer(T3) || size_of(T3) == size_of(rawptr) { + thread_proc :: proc(t: ^Thread) { + fn := cast(proc(rawptr, rawptr, rawptr))t.data; + assert(t.user_index >= 3); + fn(t.user_args[0], t.user_args[1], t.user_args[2]); + destroy(t); + } + t := create(thread_proc, priority); + t.data = rawptr(fn); + t.user_index = 3; + t.user_args[0] = transmute(rawptr)arg1; + t.user_args[1] = transmute(rawptr)arg2; + t.user_args[2] = transmute(rawptr)arg3; + t.init_context = init_context; + start(t); +} +run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) + where intrinsics.type_is_pointer(T1) || size_of(T1) == size_of(rawptr), + intrinsics.type_is_pointer(T2) || size_of(T2) == size_of(rawptr), + intrinsics.type_is_pointer(T3) || size_of(T3) == size_of(rawptr) { + thread_proc :: proc(t: ^Thread) { + fn := cast(proc(rawptr, rawptr, rawptr))t.data; + assert(t.user_index >= 3); + fn(t.user_args[0], t.user_args[1], t.user_args[2]); + destroy(t); + } + t := create(thread_proc, priority); + t.data = rawptr(fn); + t.user_index = 3; + t.user_args[0] = transmute(rawptr)arg1; + t.user_args[1] = transmute(rawptr)arg2; + t.user_args[2] = transmute(rawptr)arg3; + t.init_context = init_context; + start(t); +} + + create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread { t := create(fn, priority); diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 027ffe026..d87291c0e 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -85,6 +85,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T if thread == nil { return nil; } + thread.creation_allocator = context.allocator; // Set thread priority. policy: i32; @@ -106,7 +107,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T sync.mutex_init(&thread.start_mutex); sync.condition_init(&thread.start_gate, &thread.start_mutex); if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 { - free(thread); + free(thread, thread.creation_allocator); return nil; } thread.procedure = procedure; @@ -172,7 +173,7 @@ join_multiple :: proc(threads: ..^Thread) { destroy :: proc(t: ^Thread) { join(t); t.unix_thread = {}; - free(t); + free(t, t.creation_allocator); } diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index f94632b35..27a14c7f6 100644 --- a/core/thread/thread_windows.odin +++ b/core/thread/thread_windows.odin @@ -49,10 +49,14 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T thread := new(Thread); + if thread == nil { + return nil; + } + thread.creation_allocator = context.allocator; win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id); if win32_thread == nil { - free(thread); + free(thread, thread.creation_allocator); return nil; } thread.procedure = procedure; @@ -111,7 +115,7 @@ join_multiple :: proc(threads: ..^Thread) { destroy :: proc(thread: ^Thread) { join(thread); - free(thread); + free(thread, thread.creation_allocator); } terminate :: proc(using thread : ^Thread, exit_code: u32) { From 49e140f4db1f9fffa541c4d58efa91b7128c4ff4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 10 Nov 2020 16:47:56 +0000 Subject: [PATCH 030/170] Add utf8.full_rune --- core/unicode/utf8/utf8.odin | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin index f008c3881..50d24d562 100644 --- a/core/unicode/utf8/utf8.odin +++ b/core/unicode/utf8/utf8.odin @@ -350,3 +350,44 @@ rune_size :: proc(r: rune) -> int { } return -1; } + +// full_rune reports if the bytes in b begin with a full utf-8 encoding of a rune or not +// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR) +full_rune :: proc(b: []byte) -> bool { + n := len(b); + if n == 0 { + return false; + } + x := _first[b[0]]; + if n >= int(x & 7) { + return true; + } + accept := accept_ranges[x>>4]; + if n > 1 && (b[1] < accept.lo || accept.hi < b[1]) { + return true; + } else if n > 2 && (b[2] < LOCB || HICB < b[2]) { + return true; + } + return false; +} + +// full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not +// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR) +full_rune_in_string :: proc(s: string) -> bool { + return full_rune(transmute([]byte)s); +} + + +_first := [256]u8{ + 0x00..0x7f = 0xf0, // ascii, size 1 + 0x80..0xc1 = 0xf1, // invalid, size 1 + 0xc2..0xdf = 0x02, // accept 1, size 2 + 0xe0 = 0x13, // accept 1, size 3 + 0xe1..0xec = 0x03, // accept 0, size 3 + 0xed = 0x23, // accept 2, size 3 + 0xee..0xef = 0x03, // accept 0, size 3 + 0xf0 = 0x34, // accept 3, size 4 + 0xf1..0xf3 = 0x04, // accept 0, size 4 + 0xf4 = 0x44, // accept 4, size 4 + 0xf5..0xff = 0xf1, // ascii, size 1 +}; From 301e1d2ff3954453213aefa271263cd2644849f1 Mon Sep 17 00:00:00 2001 From: F0x1fy Date: Tue, 10 Nov 2020 09:50:53 -0700 Subject: [PATCH 031/170] Added -no-entry-point flag and relevant check. --- src/build_settings.cpp | 1 + src/checker.cpp | 2 +- src/main.cpp | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index ed259f7cd..4f06c2913 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -151,6 +151,7 @@ struct BuildContext { bool no_dynamic_literals; bool no_output_files; bool no_crt; + bool no_entry_point; bool use_lld; bool vet; bool cross_compiling; diff --git a/src/checker.cpp b/src/checker.cpp index 5c93e12b6..ac324fffa 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4572,7 +4572,7 @@ void check_parsed_files(Checker *c) { TIME_SECTION("check entry point"); - if (build_context.build_mode == BuildMode_Executable) { + if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point) { Scope *s = c->info.init_scope; GB_ASSERT(s != nullptr); GB_ASSERT(s->flags&ScopeFlag_Init); diff --git a/src/main.cpp b/src/main.cpp index 1ae0e7d97..68d4a03c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -581,6 +581,7 @@ enum BuildFlagKind { BuildFlag_NoBoundsCheck, BuildFlag_NoDynamicLiterals, BuildFlag_NoCRT, + BuildFlag_NoEntryPoint, BuildFlag_UseLLD, BuildFlag_Vet, BuildFlag_UseLLVMApi, @@ -591,6 +592,7 @@ enum BuildFlagKind { BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, + BuildFlag_Compact, BuildFlag_GlobalDefinitions, BuildFlag_GoToDefinitions, @@ -680,6 +682,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None); @@ -1095,6 +1098,10 @@ bool parse_build_flags(Array args) { build_context.no_crt = true; break; + case BuildFlag_NoEntryPoint: + build_context.no_entry_point = true; + break; + case BuildFlag_UseLLD: build_context.use_lld = true; break; From 3bed5fad77e8dcd42d7eab40d9c7d50ce4cc47c4 Mon Sep 17 00:00:00 2001 From: F0x1fy Date: Tue, 10 Nov 2020 09:55:00 -0700 Subject: [PATCH 032/170] Removed unnecessary newline from previous commit. --- src/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 68d4a03c6..5757cdd79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -592,7 +592,6 @@ enum BuildFlagKind { BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, - BuildFlag_Compact, BuildFlag_GlobalDefinitions, BuildFlag_GoToDefinitions, From 6b6f1a5283c90f187e9ffc7feeda2dad6a3a5c8d Mon Sep 17 00:00:00 2001 From: F0x1fy Date: Tue, 10 Nov 2020 09:56:16 -0700 Subject: [PATCH 033/170] For the sake of consistency, fixed the placement of the -no-entry-point flag check. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 5757cdd79..3717a4147 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -681,8 +681,8 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None); From 0eba4b46b5e554f1df01b63084ac4e78abfe3117 Mon Sep 17 00:00:00 2001 From: F0x1fy Date: Tue, 10 Nov 2020 10:16:22 -0700 Subject: [PATCH 034/170] Made sure the entry point is not generated when -no-entry-point is specified. --- src/ir.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2b3bd35df..dc77906e8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12646,7 +12646,7 @@ void ir_gen_tree(irGen *s) { #if defined(GB_SYSTEM_WINDOWS) - if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) { + if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main && !build_context.no_entry_point) { // DllMain :: proc(inst: rawptr, reason: u32, reserved: rawptr) -> i32 String name = str_lit("DllMain"); Type *proc_params = alloc_type_tuple(); @@ -12717,7 +12717,7 @@ void ir_gen_tree(irGen *s) { ir_emit_return(proc, v_one32); } #endif - if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) { + if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) && !build_context.no_entry_point) { // main :: proc(argc: i32, argv: ^^u8) -> i32 String name = str_lit("main"); @@ -12796,7 +12796,7 @@ void ir_gen_tree(irGen *s) { } #if defined(GB_SYSTEM_WINDOWS) - if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt) { + if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt && !build_context.no_entry_point) { s->print_chkstk = true; { From 6c0fa24e5d5ddb0ed68c38ba54d202468cbd4a8a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 10 Nov 2020 19:00:16 +0000 Subject: [PATCH 035/170] Force dependency for @(export) entities --- src/checker.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/checker.cpp b/src/checker.cpp index ac324fffa..fe669ab8c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1842,6 +1842,22 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { add_dependency_to_set(c, e); } + for_array(i, c->info.entities) { + Entity *e = c->info.entities[i]; + switch (e->kind) { + case Entity_Variable: + if (e->Variable.is_export) { + add_dependency_to_set(c, e); + } + break; + case Entity_Procedure: + if (e->Procedure.is_export) { + add_dependency_to_set(c, e); + } + break; + } + } + add_dependency_to_set(c, start); } From e8da2ef65e82f9572944cc26a50065fe190b21cb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 10 Nov 2020 19:00:38 +0000 Subject: [PATCH 036/170] Update package unicode --- core/unicode/letter.odin | 1034 ++----------------------------- core/unicode/tables.odin | 1272 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 1312 insertions(+), 994 deletions(-) create mode 100644 core/unicode/tables.odin diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index 4bf5cf4ea..c31d34dab 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -90,9 +90,10 @@ is_upper :: proc(r: rune) -> bool { return false; } -is_alpha :: proc(r: rune) -> bool { - if r <= MAX_ASCII { - return (u8(r)|32)-'a' < 26; +is_alpha :: is_letter; +is_letter :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pLmask != 0; } if is_upper(r) || is_lower(r) { return true; @@ -150,1000 +151,45 @@ is_combining :: proc(r: rune) -> bool { } -@(static) -alpha_ranges := [?]i32{ - 0x00d8, 0x00f6, - 0x00f8, 0x01f5, - 0x0250, 0x02a8, - 0x038e, 0x03a1, - 0x03a3, 0x03ce, - 0x03d0, 0x03d6, - 0x03e2, 0x03f3, - 0x0490, 0x04c4, - 0x0561, 0x0587, - 0x05d0, 0x05ea, - 0x05f0, 0x05f2, - 0x0621, 0x063a, - 0x0640, 0x064a, - 0x0671, 0x06b7, - 0x06ba, 0x06be, - 0x06c0, 0x06ce, - 0x06d0, 0x06d3, - 0x0905, 0x0939, - 0x0958, 0x0961, - 0x0985, 0x098c, - 0x098f, 0x0990, - 0x0993, 0x09a8, - 0x09aa, 0x09b0, - 0x09b6, 0x09b9, - 0x09dc, 0x09dd, - 0x09df, 0x09e1, - 0x09f0, 0x09f1, - 0x0a05, 0x0a0a, - 0x0a0f, 0x0a10, - 0x0a13, 0x0a28, - 0x0a2a, 0x0a30, - 0x0a32, 0x0a33, - 0x0a35, 0x0a36, - 0x0a38, 0x0a39, - 0x0a59, 0x0a5c, - 0x0a85, 0x0a8b, - 0x0a8f, 0x0a91, - 0x0a93, 0x0aa8, - 0x0aaa, 0x0ab0, - 0x0ab2, 0x0ab3, - 0x0ab5, 0x0ab9, - 0x0b05, 0x0b0c, - 0x0b0f, 0x0b10, - 0x0b13, 0x0b28, - 0x0b2a, 0x0b30, - 0x0b32, 0x0b33, - 0x0b36, 0x0b39, - 0x0b5c, 0x0b5d, - 0x0b5f, 0x0b61, - 0x0b85, 0x0b8a, - 0x0b8e, 0x0b90, - 0x0b92, 0x0b95, - 0x0b99, 0x0b9a, - 0x0b9e, 0x0b9f, - 0x0ba3, 0x0ba4, - 0x0ba8, 0x0baa, - 0x0bae, 0x0bb5, - 0x0bb7, 0x0bb9, - 0x0c05, 0x0c0c, - 0x0c0e, 0x0c10, - 0x0c12, 0x0c28, - 0x0c2a, 0x0c33, - 0x0c35, 0x0c39, - 0x0c60, 0x0c61, - 0x0c85, 0x0c8c, - 0x0c8e, 0x0c90, - 0x0c92, 0x0ca8, - 0x0caa, 0x0cb3, - 0x0cb5, 0x0cb9, - 0x0ce0, 0x0ce1, - 0x0d05, 0x0d0c, - 0x0d0e, 0x0d10, - 0x0d12, 0x0d28, - 0x0d2a, 0x0d39, - 0x0d60, 0x0d61, - 0x0e01, 0x0e30, - 0x0e32, 0x0e33, - 0x0e40, 0x0e46, - 0x0e5a, 0x0e5b, - 0x0e81, 0x0e82, - 0x0e87, 0x0e88, - 0x0e94, 0x0e97, - 0x0e99, 0x0e9f, - 0x0ea1, 0x0ea3, - 0x0eaa, 0x0eab, - 0x0ead, 0x0eae, - 0x0eb2, 0x0eb3, - 0x0ec0, 0x0ec4, - 0x0edc, 0x0edd, - 0x0f18, 0x0f19, - 0x0f40, 0x0f47, - 0x0f49, 0x0f69, - 0x10d0, 0x10f6, - 0x1100, 0x1159, - 0x115f, 0x11a2, - 0x11a8, 0x11f9, - 0x1e00, 0x1e9b, - 0x1f50, 0x1f57, - 0x1f80, 0x1fb4, - 0x1fb6, 0x1fbc, - 0x1fc2, 0x1fc4, - 0x1fc6, 0x1fcc, - 0x1fd0, 0x1fd3, - 0x1fd6, 0x1fdb, - 0x1fe0, 0x1fec, - 0x1ff2, 0x1ff4, - 0x1ff6, 0x1ffc, - 0x210a, 0x2113, - 0x2115, 0x211d, - 0x2120, 0x2122, - 0x212a, 0x2131, - 0x2133, 0x2138, - 0x3041, 0x3094, - 0x30a1, 0x30fa, - 0x3105, 0x312c, - 0x3131, 0x318e, - 0x3192, 0x319f, - 0x3260, 0x327b, - 0x328a, 0x32b0, - 0x32d0, 0x32fe, - 0x3300, 0x3357, - 0x3371, 0x3376, - 0x337b, 0x3394, - 0x3399, 0x339e, - 0x33a9, 0x33ad, - 0x33b0, 0x33c1, - 0x33c3, 0x33c5, - 0x33c7, 0x33d7, - 0x33d9, 0x33dd, - 0x4e00, 0x9fff, - 0xac00, 0xd7a3, - 0xf900, 0xfb06, - 0xfb13, 0xfb17, - 0xfb1f, 0xfb28, - 0xfb2a, 0xfb36, - 0xfb38, 0xfb3c, - 0xfb40, 0xfb41, - 0xfb43, 0xfb44, - 0xfb46, 0xfbb1, - 0xfbd3, 0xfd3d, - 0xfd50, 0xfd8f, - 0xfd92, 0xfdc7, - 0xfdf0, 0xfdf9, - 0xfe70, 0xfe72, - 0xfe76, 0xfefc, - 0xff66, 0xff6f, - 0xff71, 0xff9d, - 0xffa0, 0xffbe, - 0xffc2, 0xffc7, - 0xffca, 0xffcf, - 0xffd2, 0xffd7, - 0xffda, 0xffdc, -}; -@(static) -alpha_singlets := [?]i32{ - 0x00aa, - 0x00b5, - 0x00ba, - 0x03da, - 0x03dc, - 0x03de, - 0x03e0, - 0x06d5, - 0x09b2, - 0x0a5e, - 0x0a8d, - 0x0ae0, - 0x0b9c, - 0x0cde, - 0x0e4f, - 0x0e84, - 0x0e8a, - 0x0e8d, - 0x0ea5, - 0x0ea7, - 0x0eb0, - 0x0ebd, - 0x1fbe, - 0x207f, - 0x20a8, - 0x2102, - 0x2107, - 0x2124, - 0x2126, - 0x2128, - 0xfb3e, - 0xfe74, -}; +is_graphic :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pg != 0; + } + return false; +} -@(static) -space_ranges := [?]i32{ - 0x0009, 0x000d, // tab and newline - 0x0020, 0x0020, // space - 0x0085, 0x0085, // next line - 0x00a0, 0x00a0, - 0x1680, 0x1680, // Ogham space mark - 0x2000, 0x200b, // en dash .. zero-width space - 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) - 0x2028, 0x2029, 0x3000, 0x3000, - 0x202f, 0x202f, // narrow no-break space - 0x205f, 0x205f, // medium mathematical space - 0x3000, 0x3000, // ideographic space - 0xfeff, 0xfeff, -}; +is_print :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pp != 0; + } + return false; +} -@(static) -unicode_spaces := [?]i32{ - 0x0009, // tab - 0x000a, // LF - 0x000d, // CR - 0x0020, // space - 0x0085, // next line - 0x00a0, // unknown - 0x1680, // Ogham space mark - 0x2000, // en dash .. zero-width space - 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) - 0x2028, 0x2029, 0x3000, 0x3000, - 0x202f, // narrow no-break space - 0x205f, // medium mathematical space - 0x3000, // ideographic space - 0xfeff, // unknown -}; +is_control :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pC != 0; + } + return false; +} -@(static) -to_upper_ranges := [?]i32{ - 0x0061, 0x007a, 468, // a-z A-Z - 0x00e0, 0x00f6, 468, - 0x00f8, 0x00fe, 468, - 0x0256, 0x0257, 295, - 0x0258, 0x0259, 298, - 0x028a, 0x028b, 283, - 0x03ad, 0x03af, 463, - 0x03b1, 0x03c1, 468, - 0x03c3, 0x03cb, 468, - 0x03cd, 0x03ce, 437, - 0x0430, 0x044f, 468, - 0x0451, 0x045c, 420, - 0x045e, 0x045f, 420, - 0x0561, 0x0586, 452, - 0x1f00, 0x1f07, 508, - 0x1f10, 0x1f15, 508, - 0x1f20, 0x1f27, 508, - 0x1f30, 0x1f37, 508, - 0x1f40, 0x1f45, 508, - 0x1f60, 0x1f67, 508, - 0x1f70, 0x1f71, 574, - 0x1f72, 0x1f75, 586, - 0x1f76, 0x1f77, 600, - 0x1f78, 0x1f79, 628, - 0x1f7a, 0x1f7b, 612, - 0x1f7c, 0x1f7d, 626, - 0x1f80, 0x1f87, 508, - 0x1f90, 0x1f97, 508, - 0x1fa0, 0x1fa7, 508, - 0x1fb0, 0x1fb1, 508, - 0x1fd0, 0x1fd1, 508, - 0x1fe0, 0x1fe1, 508, - 0x2170, 0x217f, 484, - 0x24d0, 0x24e9, 474, - 0xff41, 0xff5a, 468, -}; +is_number :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pN != 0; + } + return false; +} -@(static) -to_upper_singlets := [?]i32{ - 0x00ff, 621, - 0x0101, 499, - 0x0103, 499, - 0x0105, 499, - 0x0107, 499, - 0x0109, 499, - 0x010b, 499, - 0x010d, 499, - 0x010f, 499, - 0x0111, 499, - 0x0113, 499, - 0x0115, 499, - 0x0117, 499, - 0x0119, 499, - 0x011b, 499, - 0x011d, 499, - 0x011f, 499, - 0x0121, 499, - 0x0123, 499, - 0x0125, 499, - 0x0127, 499, - 0x0129, 499, - 0x012b, 499, - 0x012d, 499, - 0x012f, 499, - 0x0131, 268, // I - 0x0133, 499, - 0x0135, 499, - 0x0137, 499, - 0x013a, 499, - 0x013c, 499, - 0x013e, 499, - 0x0140, 499, - 0x0142, 499, - 0x0144, 499, - 0x0146, 499, - 0x0148, 499, - 0x014b, 499, - 0x014d, 499, - 0x014f, 499, - 0x0151, 499, - 0x0153, 499, - 0x0155, 499, - 0x0157, 499, - 0x0159, 499, - 0x015b, 499, - 0x015d, 499, - 0x015f, 499, - 0x0161, 499, - 0x0163, 499, - 0x0165, 499, - 0x0167, 499, - 0x0169, 499, - 0x016b, 499, - 0x016d, 499, - 0x016f, 499, - 0x0171, 499, - 0x0173, 499, - 0x0175, 499, - 0x0177, 499, - 0x017a, 499, - 0x017c, 499, - 0x017e, 499, - 0x017f, 200, // S - 0x0183, 499, - 0x0185, 499, - 0x0188, 499, - 0x018c, 499, - 0x0192, 499, - 0x0199, 499, - 0x01a1, 499, - 0x01a3, 499, - 0x01a5, 499, - 0x01a8, 499, - 0x01ad, 499, - 0x01b0, 499, - 0x01b4, 499, - 0x01b6, 499, - 0x01b9, 499, - 0x01bd, 499, - 0x01c5, 499, - 0x01c6, 498, - 0x01c8, 499, - 0x01c9, 498, - 0x01cb, 499, - 0x01cc, 498, - 0x01ce, 499, - 0x01d0, 499, - 0x01d2, 499, - 0x01d4, 499, - 0x01d6, 499, - 0x01d8, 499, - 0x01da, 499, - 0x01dc, 499, - 0x01df, 499, - 0x01e1, 499, - 0x01e3, 499, - 0x01e5, 499, - 0x01e7, 499, - 0x01e9, 499, - 0x01eb, 499, - 0x01ed, 499, - 0x01ef, 499, - 0x01f2, 499, - 0x01f3, 498, - 0x01f5, 499, - 0x01fb, 499, - 0x01fd, 499, - 0x01ff, 499, - 0x0201, 499, - 0x0203, 499, - 0x0205, 499, - 0x0207, 499, - 0x0209, 499, - 0x020b, 499, - 0x020d, 499, - 0x020f, 499, - 0x0211, 499, - 0x0213, 499, - 0x0215, 499, - 0x0217, 499, - 0x0253, 290, - 0x0254, 294, - 0x025b, 297, - 0x0260, 295, - 0x0263, 293, - 0x0268, 291, - 0x0269, 289, - 0x026f, 289, - 0x0272, 287, - 0x0283, 282, - 0x0288, 282, - 0x0292, 281, - 0x03ac, 462, - 0x03cc, 436, - 0x03d0, 438, - 0x03d1, 443, - 0x03d5, 453, - 0x03d6, 446, - 0x03e3, 499, - 0x03e5, 499, - 0x03e7, 499, - 0x03e9, 499, - 0x03eb, 499, - 0x03ed, 499, - 0x03ef, 499, - 0x03f0, 414, - 0x03f1, 420, - 0x0461, 499, - 0x0463, 499, - 0x0465, 499, - 0x0467, 499, - 0x0469, 499, - 0x046b, 499, - 0x046d, 499, - 0x046f, 499, - 0x0471, 499, - 0x0473, 499, - 0x0475, 499, - 0x0477, 499, - 0x0479, 499, - 0x047b, 499, - 0x047d, 499, - 0x047f, 499, - 0x0481, 499, - 0x0491, 499, - 0x0493, 499, - 0x0495, 499, - 0x0497, 499, - 0x0499, 499, - 0x049b, 499, - 0x049d, 499, - 0x049f, 499, - 0x04a1, 499, - 0x04a3, 499, - 0x04a5, 499, - 0x04a7, 499, - 0x04a9, 499, - 0x04ab, 499, - 0x04ad, 499, - 0x04af, 499, - 0x04b1, 499, - 0x04b3, 499, - 0x04b5, 499, - 0x04b7, 499, - 0x04b9, 499, - 0x04bb, 499, - 0x04bd, 499, - 0x04bf, 499, - 0x04c2, 499, - 0x04c4, 499, - 0x04c8, 499, - 0x04cc, 499, - 0x04d1, 499, - 0x04d3, 499, - 0x04d5, 499, - 0x04d7, 499, - 0x04d9, 499, - 0x04db, 499, - 0x04dd, 499, - 0x04df, 499, - 0x04e1, 499, - 0x04e3, 499, - 0x04e5, 499, - 0x04e7, 499, - 0x04e9, 499, - 0x04eb, 499, - 0x04ef, 499, - 0x04f1, 499, - 0x04f3, 499, - 0x04f5, 499, - 0x04f9, 499, - 0x1e01, 499, - 0x1e03, 499, - 0x1e05, 499, - 0x1e07, 499, - 0x1e09, 499, - 0x1e0b, 499, - 0x1e0d, 499, - 0x1e0f, 499, - 0x1e11, 499, - 0x1e13, 499, - 0x1e15, 499, - 0x1e17, 499, - 0x1e19, 499, - 0x1e1b, 499, - 0x1e1d, 499, - 0x1e1f, 499, - 0x1e21, 499, - 0x1e23, 499, - 0x1e25, 499, - 0x1e27, 499, - 0x1e29, 499, - 0x1e2b, 499, - 0x1e2d, 499, - 0x1e2f, 499, - 0x1e31, 499, - 0x1e33, 499, - 0x1e35, 499, - 0x1e37, 499, - 0x1e39, 499, - 0x1e3b, 499, - 0x1e3d, 499, - 0x1e3f, 499, - 0x1e41, 499, - 0x1e43, 499, - 0x1e45, 499, - 0x1e47, 499, - 0x1e49, 499, - 0x1e4b, 499, - 0x1e4d, 499, - 0x1e4f, 499, - 0x1e51, 499, - 0x1e53, 499, - 0x1e55, 499, - 0x1e57, 499, - 0x1e59, 499, - 0x1e5b, 499, - 0x1e5d, 499, - 0x1e5f, 499, - 0x1e61, 499, - 0x1e63, 499, - 0x1e65, 499, - 0x1e67, 499, - 0x1e69, 499, - 0x1e6b, 499, - 0x1e6d, 499, - 0x1e6f, 499, - 0x1e71, 499, - 0x1e73, 499, - 0x1e75, 499, - 0x1e77, 499, - 0x1e79, 499, - 0x1e7b, 499, - 0x1e7d, 499, - 0x1e7f, 499, - 0x1e81, 499, - 0x1e83, 499, - 0x1e85, 499, - 0x1e87, 499, - 0x1e89, 499, - 0x1e8b, 499, - 0x1e8d, 499, - 0x1e8f, 499, - 0x1e91, 499, - 0x1e93, 499, - 0x1e95, 499, - 0x1ea1, 499, - 0x1ea3, 499, - 0x1ea5, 499, - 0x1ea7, 499, - 0x1ea9, 499, - 0x1eab, 499, - 0x1ead, 499, - 0x1eaf, 499, - 0x1eb1, 499, - 0x1eb3, 499, - 0x1eb5, 499, - 0x1eb7, 499, - 0x1eb9, 499, - 0x1ebb, 499, - 0x1ebd, 499, - 0x1ebf, 499, - 0x1ec1, 499, - 0x1ec3, 499, - 0x1ec5, 499, - 0x1ec7, 499, - 0x1ec9, 499, - 0x1ecb, 499, - 0x1ecd, 499, - 0x1ecf, 499, - 0x1ed1, 499, - 0x1ed3, 499, - 0x1ed5, 499, - 0x1ed7, 499, - 0x1ed9, 499, - 0x1edb, 499, - 0x1edd, 499, - 0x1edf, 499, - 0x1ee1, 499, - 0x1ee3, 499, - 0x1ee5, 499, - 0x1ee7, 499, - 0x1ee9, 499, - 0x1eeb, 499, - 0x1eed, 499, - 0x1eef, 499, - 0x1ef1, 499, - 0x1ef3, 499, - 0x1ef5, 499, - 0x1ef7, 499, - 0x1ef9, 499, - 0x1f51, 508, - 0x1f53, 508, - 0x1f55, 508, - 0x1f57, 508, - 0x1fb3, 509, - 0x1fc3, 509, - 0x1fe5, 507, - 0x1ff3, 509, -}; +is_punct :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pP != 0; + } + return false; +} -@(static) -to_lower_ranges := [?]i32{ - 0x0041, 0x005a, 532, // A-Z a-z - 0x00c0, 0x00d6, 532, // - - - 0x00d8, 0x00de, 532, // - - - 0x0189, 0x018a, 705, // - - - 0x018e, 0x018f, 702, // - - - 0x01b1, 0x01b2, 717, // - - - 0x0388, 0x038a, 537, // - - - 0x038e, 0x038f, 563, // - - - 0x0391, 0x03a1, 532, // - - - 0x03a3, 0x03ab, 532, // - - - 0x0401, 0x040c, 580, // - - - 0x040e, 0x040f, 580, // - - - 0x0410, 0x042f, 532, // - - - 0x0531, 0x0556, 548, // - - - 0x10a0, 0x10c5, 548, // - - - 0x1f08, 0x1f0f, 492, // - - - 0x1f18, 0x1f1d, 492, // - - - 0x1f28, 0x1f2f, 492, // - - - 0x1f38, 0x1f3f, 492, // - - - 0x1f48, 0x1f4d, 492, // - - - 0x1f68, 0x1f6f, 492, // - - - 0x1f88, 0x1f8f, 492, // - - - 0x1f98, 0x1f9f, 492, // - - - 0x1fa8, 0x1faf, 492, // - - - 0x1fb8, 0x1fb9, 492, // - - - 0x1fba, 0x1fbb, 426, // - - - 0x1fc8, 0x1fcb, 414, // - - - 0x1fd8, 0x1fd9, 492, // - - - 0x1fda, 0x1fdb, 400, // - - - 0x1fe8, 0x1fe9, 492, // - - - 0x1fea, 0x1feb, 388, // - - - 0x1ff8, 0x1ff9, 372, // - - - 0x1ffa, 0x1ffb, 374, // - - - 0x2160, 0x216f, 516, // - - - 0x24b6, 0x24cf, 526, // - - - 0xff21, 0xff3a, 532, // - - -}; - -@(static) -to_lower_singlets := [?]i32{ - 0x0100, 501, - 0x0102, 501, - 0x0104, 501, - 0x0106, 501, - 0x0108, 501, - 0x010a, 501, - 0x010c, 501, - 0x010e, 501, - 0x0110, 501, - 0x0112, 501, - 0x0114, 501, - 0x0116, 501, - 0x0118, 501, - 0x011a, 501, - 0x011c, 501, - 0x011e, 501, - 0x0120, 501, - 0x0122, 501, - 0x0124, 501, - 0x0126, 501, - 0x0128, 501, - 0x012a, 501, - 0x012c, 501, - 0x012e, 501, - 0x0130, 301, // i - 0x0132, 501, - 0x0134, 501, - 0x0136, 501, - 0x0139, 501, - 0x013b, 501, - 0x013d, 501, - 0x013f, 501, - 0x0141, 501, - 0x0143, 501, - 0x0145, 501, - 0x0147, 501, - 0x014a, 501, - 0x014c, 501, - 0x014e, 501, - 0x0150, 501, - 0x0152, 501, - 0x0154, 501, - 0x0156, 501, - 0x0158, 501, - 0x015a, 501, - 0x015c, 501, - 0x015e, 501, - 0x0160, 501, - 0x0162, 501, - 0x0164, 501, - 0x0166, 501, - 0x0168, 501, - 0x016a, 501, - 0x016c, 501, - 0x016e, 501, - 0x0170, 501, - 0x0172, 501, - 0x0174, 501, - 0x0176, 501, - 0x0178, 379, - 0x0179, 501, - 0x017b, 501, - 0x017d, 501, - 0x0181, 710, - 0x0182, 501, - 0x0184, 501, - 0x0186, 706, - 0x0187, 501, - 0x018b, 501, - 0x0190, 703, - 0x0191, 501, - 0x0193, 705, - 0x0194, 707, - 0x0196, 711, - 0x0197, 709, - 0x0198, 501, - 0x019c, 711, - 0x019d, 713, - 0x01a0, 501, - 0x01a2, 501, - 0x01a4, 501, - 0x01a7, 501, - 0x01a9, 718, - 0x01ac, 501, - 0x01ae, 718, - 0x01af, 501, - 0x01b3, 501, - 0x01b5, 501, - 0x01b7, 719, - 0x01b8, 501, - 0x01bc, 501, - 0x01c4, 502, - 0x01c5, 501, - 0x01c7, 502, - 0x01c8, 501, - 0x01ca, 502, - 0x01cb, 501, - 0x01cd, 501, - 0x01cf, 501, - 0x01d1, 501, - 0x01d3, 501, - 0x01d5, 501, - 0x01d7, 501, - 0x01d9, 501, - 0x01db, 501, - 0x01de, 501, - 0x01e0, 501, - 0x01e2, 501, - 0x01e4, 501, - 0x01e6, 501, - 0x01e8, 501, - 0x01ea, 501, - 0x01ec, 501, - 0x01ee, 501, - 0x01f1, 502, - 0x01f2, 501, - 0x01f4, 501, - 0x01fa, 501, - 0x01fc, 501, - 0x01fe, 501, - 0x0200, 501, - 0x0202, 501, - 0x0204, 501, - 0x0206, 501, - 0x0208, 501, - 0x020a, 501, - 0x020c, 501, - 0x020e, 501, - 0x0210, 501, - 0x0212, 501, - 0x0214, 501, - 0x0216, 501, - 0x0386, 538, - 0x038c, 564, - 0x03e2, 501, - 0x03e4, 501, - 0x03e6, 501, - 0x03e8, 501, - 0x03ea, 501, - 0x03ec, 501, - 0x03ee, 501, - 0x0460, 501, - 0x0462, 501, - 0x0464, 501, - 0x0466, 501, - 0x0468, 501, - 0x046a, 501, - 0x046c, 501, - 0x046e, 501, - 0x0470, 501, - 0x0472, 501, - 0x0474, 501, - 0x0476, 501, - 0x0478, 501, - 0x047a, 501, - 0x047c, 501, - 0x047e, 501, - 0x0480, 501, - 0x0490, 501, - 0x0492, 501, - 0x0494, 501, - 0x0496, 501, - 0x0498, 501, - 0x049a, 501, - 0x049c, 501, - 0x049e, 501, - 0x04a0, 501, - 0x04a2, 501, - 0x04a4, 501, - 0x04a6, 501, - 0x04a8, 501, - 0x04aa, 501, - 0x04ac, 501, - 0x04ae, 501, - 0x04b0, 501, - 0x04b2, 501, - 0x04b4, 501, - 0x04b6, 501, - 0x04b8, 501, - 0x04ba, 501, - 0x04bc, 501, - 0x04be, 501, - 0x04c1, 501, - 0x04c3, 501, - 0x04c7, 501, - 0x04cb, 501, - 0x04d0, 501, - 0x04d2, 501, - 0x04d4, 501, - 0x04d6, 501, - 0x04d8, 501, - 0x04da, 501, - 0x04dc, 501, - 0x04de, 501, - 0x04e0, 501, - 0x04e2, 501, - 0x04e4, 501, - 0x04e6, 501, - 0x04e8, 501, - 0x04ea, 501, - 0x04ee, 501, - 0x04f0, 501, - 0x04f2, 501, - 0x04f4, 501, - 0x04f8, 501, - 0x1e00, 501, - 0x1e02, 501, - 0x1e04, 501, - 0x1e06, 501, - 0x1e08, 501, - 0x1e0a, 501, - 0x1e0c, 501, - 0x1e0e, 501, - 0x1e10, 501, - 0x1e12, 501, - 0x1e14, 501, - 0x1e16, 501, - 0x1e18, 501, - 0x1e1a, 501, - 0x1e1c, 501, - 0x1e1e, 501, - 0x1e20, 501, - 0x1e22, 501, - 0x1e24, 501, - 0x1e26, 501, - 0x1e28, 501, - 0x1e2a, 501, - 0x1e2c, 501, - 0x1e2e, 501, - 0x1e30, 501, - 0x1e32, 501, - 0x1e34, 501, - 0x1e36, 501, - 0x1e38, 501, - 0x1e3a, 501, - 0x1e3c, 501, - 0x1e3e, 501, - 0x1e40, 501, - 0x1e42, 501, - 0x1e44, 501, - 0x1e46, 501, - 0x1e48, 501, - 0x1e4a, 501, - 0x1e4c, 501, - 0x1e4e, 501, - 0x1e50, 501, - 0x1e52, 501, - 0x1e54, 501, - 0x1e56, 501, - 0x1e58, 501, - 0x1e5a, 501, - 0x1e5c, 501, - 0x1e5e, 501, - 0x1e60, 501, - 0x1e62, 501, - 0x1e64, 501, - 0x1e66, 501, - 0x1e68, 501, - 0x1e6a, 501, - 0x1e6c, 501, - 0x1e6e, 501, - 0x1e70, 501, - 0x1e72, 501, - 0x1e74, 501, - 0x1e76, 501, - 0x1e78, 501, - 0x1e7a, 501, - 0x1e7c, 501, - 0x1e7e, 501, - 0x1e80, 501, - 0x1e82, 501, - 0x1e84, 501, - 0x1e86, 501, - 0x1e88, 501, - 0x1e8a, 501, - 0x1e8c, 501, - 0x1e8e, 501, - 0x1e90, 501, - 0x1e92, 501, - 0x1e94, 501, - 0x1ea0, 501, - 0x1ea2, 501, - 0x1ea4, 501, - 0x1ea6, 501, - 0x1ea8, 501, - 0x1eaa, 501, - 0x1eac, 501, - 0x1eae, 501, - 0x1eb0, 501, - 0x1eb2, 501, - 0x1eb4, 501, - 0x1eb6, 501, - 0x1eb8, 501, - 0x1eba, 501, - 0x1ebc, 501, - 0x1ebe, 501, - 0x1ec0, 501, - 0x1ec2, 501, - 0x1ec4, 501, - 0x1ec6, 501, - 0x1ec8, 501, - 0x1eca, 501, - 0x1ecc, 501, - 0x1ece, 501, - 0x1ed0, 501, - 0x1ed2, 501, - 0x1ed4, 501, - 0x1ed6, 501, - 0x1ed8, 501, - 0x1eda, 501, - 0x1edc, 501, - 0x1ede, 501, - 0x1ee0, 501, - 0x1ee2, 501, - 0x1ee4, 501, - 0x1ee6, 501, - 0x1ee8, 501, - 0x1eea, 501, - 0x1eec, 501, - 0x1eee, 501, - 0x1ef0, 501, - 0x1ef2, 501, - 0x1ef4, 501, - 0x1ef6, 501, - 0x1ef8, 501, - 0x1f59, 492, - 0x1f5b, 492, - 0x1f5d, 492, - 0x1f5f, 492, - 0x1fbc, 491, - 0x1fcc, 491, - 0x1fec, 493, - 0x1ffc, 491, -}; - -@(static) -to_title_singlets := [?]i32{ - 0x01c4, 501, - 0x01c6, 499, - 0x01c7, 501, - 0x01c9, 499, - 0x01ca, 501, - 0x01cc, 499, - 0x01f1, 501, - 0x01f3, 499, -}; +is_symbol :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pS != 0; + } + return false; +} diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin new file mode 100644 index 000000000..bb858fd04 --- /dev/null +++ b/core/unicode/tables.odin @@ -0,0 +1,1272 @@ +package unicode + +@(private) pC :: 1<<0; // a control character. +@(private) pP :: 1<<1; // a punctuation character. +@(private) pN :: 1<<2; // a numeral. +@(private) pS :: 1<<3; // a symbolic character. +@(private) pZ :: 1<<4; // a spacing character. +@(private) pLu :: 1<<5; // an upper-case letter. +@(private) pLl :: 1<<6; // a lower-case letter. +@(private) pp :: 1<<7; // a printable character according to Go's definition. +@(private) pg :: pp | pZ; // a graphical character according to the Unicode definition. +@(private) pLo :: pLl | pLu; // a letter that is neither upper nor lower case. +@(private) pLmask :: pLo; + +@(static) +char_properties := [MAX_LATIN1+1]u8{ + 0x00 = pC, // '\x00' + 0x01 = pC, // '\x01' + 0x02 = pC, // '\x02' + 0x03 = pC, // '\x03' + 0x04 = pC, // '\x04' + 0x05 = pC, // '\x05' + 0x06 = pC, // '\x06' + 0x07 = pC, // '\a' + 0x08 = pC, // '\b' + 0x09 = pC, // '\t' + 0x0A = pC, // '\n' + 0x0B = pC, // '\v' + 0x0C = pC, // '\f' + 0x0D = pC, // '\r' + 0x0E = pC, // '\x0e' + 0x0F = pC, // '\x0f' + 0x10 = pC, // '\x10' + 0x11 = pC, // '\x11' + 0x12 = pC, // '\x12' + 0x13 = pC, // '\x13' + 0x14 = pC, // '\x14' + 0x15 = pC, // '\x15' + 0x16 = pC, // '\x16' + 0x17 = pC, // '\x17' + 0x18 = pC, // '\x18' + 0x19 = pC, // '\x19' + 0x1A = pC, // '\x1a' + 0x1B = pC, // '\x1b' + 0x1C = pC, // '\x1c' + 0x1D = pC, // '\x1d' + 0x1E = pC, // '\x1e' + 0x1F = pC, // '\x1f' + 0x20 = pZ | pp, // ' ' + 0x21 = pP | pp, // '!' + 0x22 = pP | pp, // '"' + 0x23 = pP | pp, // '#' + 0x24 = pS | pp, // '$' + 0x25 = pP | pp, // '%' + 0x26 = pP | pp, // '&' + 0x27 = pP | pp, // '\'' + 0x28 = pP | pp, // '(' + 0x29 = pP | pp, // ')' + 0x2A = pP | pp, // '*' + 0x2B = pS | pp, // '+' + 0x2C = pP | pp, // ',' + 0x2D = pP | pp, // '-' + 0x2E = pP | pp, // '.' + 0x2F = pP | pp, // '/' + 0x30 = pN | pp, // '0' + 0x31 = pN | pp, // '1' + 0x32 = pN | pp, // '2' + 0x33 = pN | pp, // '3' + 0x34 = pN | pp, // '4' + 0x35 = pN | pp, // '5' + 0x36 = pN | pp, // '6' + 0x37 = pN | pp, // '7' + 0x38 = pN | pp, // '8' + 0x39 = pN | pp, // '9' + 0x3A = pP | pp, // ':' + 0x3B = pP | pp, // ';' + 0x3C = pS | pp, // '<' + 0x3D = pS | pp, // '=' + 0x3E = pS | pp, // '>' + 0x3F = pP | pp, // '?' + 0x40 = pP | pp, // '@' + 0x41 = pLu | pp, // 'A' + 0x42 = pLu | pp, // 'B' + 0x43 = pLu | pp, // 'C' + 0x44 = pLu | pp, // 'D' + 0x45 = pLu | pp, // 'E' + 0x46 = pLu | pp, // 'F' + 0x47 = pLu | pp, // 'G' + 0x48 = pLu | pp, // 'H' + 0x49 = pLu | pp, // 'I' + 0x4A = pLu | pp, // 'J' + 0x4B = pLu | pp, // 'K' + 0x4C = pLu | pp, // 'L' + 0x4D = pLu | pp, // 'M' + 0x4E = pLu | pp, // 'N' + 0x4F = pLu | pp, // 'O' + 0x50 = pLu | pp, // 'P' + 0x51 = pLu | pp, // 'Q' + 0x52 = pLu | pp, // 'R' + 0x53 = pLu | pp, // 'S' + 0x54 = pLu | pp, // 'T' + 0x55 = pLu | pp, // 'U' + 0x56 = pLu | pp, // 'V' + 0x57 = pLu | pp, // 'W' + 0x58 = pLu | pp, // 'X' + 0x59 = pLu | pp, // 'Y' + 0x5A = pLu | pp, // 'Z' + 0x5B = pP | pp, // '[' + 0x5C = pP | pp, // '\\' + 0x5D = pP | pp, // ']' + 0x5E = pS | pp, // '^' + 0x5F = pP | pp, // '_' + 0x60 = pS | pp, // '`' + 0x61 = pLl | pp, // 'a' + 0x62 = pLl | pp, // 'b' + 0x63 = pLl | pp, // 'c' + 0x64 = pLl | pp, // 'd' + 0x65 = pLl | pp, // 'e' + 0x66 = pLl | pp, // 'f' + 0x67 = pLl | pp, // 'g' + 0x68 = pLl | pp, // 'h' + 0x69 = pLl | pp, // 'i' + 0x6A = pLl | pp, // 'j' + 0x6B = pLl | pp, // 'k' + 0x6C = pLl | pp, // 'l' + 0x6D = pLl | pp, // 'm' + 0x6E = pLl | pp, // 'n' + 0x6F = pLl | pp, // 'o' + 0x70 = pLl | pp, // 'p' + 0x71 = pLl | pp, // 'q' + 0x72 = pLl | pp, // 'r' + 0x73 = pLl | pp, // 's' + 0x74 = pLl | pp, // 't' + 0x75 = pLl | pp, // 'u' + 0x76 = pLl | pp, // 'v' + 0x77 = pLl | pp, // 'w' + 0x78 = pLl | pp, // 'x' + 0x79 = pLl | pp, // 'y' + 0x7A = pLl | pp, // 'z' + 0x7B = pP | pp, // '{' + 0x7C = pS | pp, // '|' + 0x7D = pP | pp, // '}' + 0x7E = pS | pp, // '~' + 0x7F = pC, // '\u007f' + 0x80 = pC, // '\u0080' + 0x81 = pC, // '\u0081' + 0x82 = pC, // '\u0082' + 0x83 = pC, // '\u0083' + 0x84 = pC, // '\u0084' + 0x85 = pC, // '\u0085' + 0x86 = pC, // '\u0086' + 0x87 = pC, // '\u0087' + 0x88 = pC, // '\u0088' + 0x89 = pC, // '\u0089' + 0x8A = pC, // '\u008a' + 0x8B = pC, // '\u008b' + 0x8C = pC, // '\u008c' + 0x8D = pC, // '\u008d' + 0x8E = pC, // '\u008e' + 0x8F = pC, // '\u008f' + 0x90 = pC, // '\u0090' + 0x91 = pC, // '\u0091' + 0x92 = pC, // '\u0092' + 0x93 = pC, // '\u0093' + 0x94 = pC, // '\u0094' + 0x95 = pC, // '\u0095' + 0x96 = pC, // '\u0096' + 0x97 = pC, // '\u0097' + 0x98 = pC, // '\u0098' + 0x99 = pC, // '\u0099' + 0x9A = pC, // '\u009a' + 0x9B = pC, // '\u009b' + 0x9C = pC, // '\u009c' + 0x9D = pC, // '\u009d' + 0x9E = pC, // '\u009e' + 0x9F = pC, // '\u009f' + 0xA0 = pZ, // '\u00a0' + 0xA1 = pP | pp, // '¡' + 0xA2 = pS | pp, // '¢' + 0xA3 = pS | pp, // '£' + 0xA4 = pS | pp, // '¤' + 0xA5 = pS | pp, // '¥' + 0xA6 = pS | pp, // '¦' + 0xA7 = pP | pp, // '§' + 0xA8 = pS | pp, // '¨' + 0xA9 = pS | pp, // '©' + 0xAA = pLo | pp, // 'ª' + 0xAB = pP | pp, // '«' + 0xAC = pS | pp, // '¬' + 0xAD = 0, // '\u00ad' + 0xAE = pS | pp, // '®' + 0xAF = pS | pp, // '¯' + 0xB0 = pS | pp, // '°' + 0xB1 = pS | pp, // '±' + 0xB2 = pN | pp, // '²' + 0xB3 = pN | pp, // '³' + 0xB4 = pS | pp, // '´' + 0xB5 = pLl | pp, // 'µ' + 0xB6 = pP | pp, // '¶' + 0xB7 = pP | pp, // '·' + 0xB8 = pS | pp, // '¸' + 0xB9 = pN | pp, // '¹' + 0xBA = pLo | pp, // 'º' + 0xBB = pP | pp, // '»' + 0xBC = pN | pp, // '¼' + 0xBD = pN | pp, // '½' + 0xBE = pN | pp, // '¾' + 0xBF = pP | pp, // '¿' + 0xC0 = pLu | pp, // 'À' + 0xC1 = pLu | pp, // 'Á' + 0xC2 = pLu | pp, // 'Â' + 0xC3 = pLu | pp, // 'Ã' + 0xC4 = pLu | pp, // 'Ä' + 0xC5 = pLu | pp, // 'Å' + 0xC6 = pLu | pp, // 'Æ' + 0xC7 = pLu | pp, // 'Ç' + 0xC8 = pLu | pp, // 'È' + 0xC9 = pLu | pp, // 'É' + 0xCA = pLu | pp, // 'Ê' + 0xCB = pLu | pp, // 'Ë' + 0xCC = pLu | pp, // 'Ì' + 0xCD = pLu | pp, // 'Í' + 0xCE = pLu | pp, // 'Î' + 0xCF = pLu | pp, // 'Ï' + 0xD0 = pLu | pp, // 'Ð' + 0xD1 = pLu | pp, // 'Ñ' + 0xD2 = pLu | pp, // 'Ò' + 0xD3 = pLu | pp, // 'Ó' + 0xD4 = pLu | pp, // 'Ô' + 0xD5 = pLu | pp, // 'Õ' + 0xD6 = pLu | pp, // 'Ö' + 0xD7 = pS | pp, // '×' + 0xD8 = pLu | pp, // 'Ø' + 0xD9 = pLu | pp, // 'Ù' + 0xDA = pLu | pp, // 'Ú' + 0xDB = pLu | pp, // 'Û' + 0xDC = pLu | pp, // 'Ü' + 0xDD = pLu | pp, // 'Ý' + 0xDE = pLu | pp, // 'Þ' + 0xDF = pLl | pp, // 'ß' + 0xE0 = pLl | pp, // 'à' + 0xE1 = pLl | pp, // 'á' + 0xE2 = pLl | pp, // 'â' + 0xE3 = pLl | pp, // 'ã' + 0xE4 = pLl | pp, // 'ä' + 0xE5 = pLl | pp, // 'å' + 0xE6 = pLl | pp, // 'æ' + 0xE7 = pLl | pp, // 'ç' + 0xE8 = pLl | pp, // 'è' + 0xE9 = pLl | pp, // 'é' + 0xEA = pLl | pp, // 'ê' + 0xEB = pLl | pp, // 'ë' + 0xEC = pLl | pp, // 'ì' + 0xED = pLl | pp, // 'í' + 0xEE = pLl | pp, // 'î' + 0xEF = pLl | pp, // 'ï' + 0xF0 = pLl | pp, // 'ð' + 0xF1 = pLl | pp, // 'ñ' + 0xF2 = pLl | pp, // 'ò' + 0xF3 = pLl | pp, // 'ó' + 0xF4 = pLl | pp, // 'ô' + 0xF5 = pLl | pp, // 'õ' + 0xF6 = pLl | pp, // 'ö' + 0xF7 = pS | pp, // '÷' + 0xF8 = pLl | pp, // 'ø' + 0xF9 = pLl | pp, // 'ù' + 0xFA = pLl | pp, // 'ú' + 0xFB = pLl | pp, // 'û' + 0xFC = pLl | pp, // 'ü' + 0xFD = pLl | pp, // 'ý' + 0xFE = pLl | pp, // 'þ' + 0xFF = pLl | pp, // 'ÿ' +}; + + +@(static) +alpha_ranges := [?]i32{ + 0x00d8, 0x00f6, + 0x00f8, 0x01f5, + 0x0250, 0x02a8, + 0x038e, 0x03a1, + 0x03a3, 0x03ce, + 0x03d0, 0x03d6, + 0x03e2, 0x03f3, + 0x0490, 0x04c4, + 0x0561, 0x0587, + 0x05d0, 0x05ea, + 0x05f0, 0x05f2, + 0x0621, 0x063a, + 0x0640, 0x064a, + 0x0671, 0x06b7, + 0x06ba, 0x06be, + 0x06c0, 0x06ce, + 0x06d0, 0x06d3, + 0x0905, 0x0939, + 0x0958, 0x0961, + 0x0985, 0x098c, + 0x098f, 0x0990, + 0x0993, 0x09a8, + 0x09aa, 0x09b0, + 0x09b6, 0x09b9, + 0x09dc, 0x09dd, + 0x09df, 0x09e1, + 0x09f0, 0x09f1, + 0x0a05, 0x0a0a, + 0x0a0f, 0x0a10, + 0x0a13, 0x0a28, + 0x0a2a, 0x0a30, + 0x0a32, 0x0a33, + 0x0a35, 0x0a36, + 0x0a38, 0x0a39, + 0x0a59, 0x0a5c, + 0x0a85, 0x0a8b, + 0x0a8f, 0x0a91, + 0x0a93, 0x0aa8, + 0x0aaa, 0x0ab0, + 0x0ab2, 0x0ab3, + 0x0ab5, 0x0ab9, + 0x0b05, 0x0b0c, + 0x0b0f, 0x0b10, + 0x0b13, 0x0b28, + 0x0b2a, 0x0b30, + 0x0b32, 0x0b33, + 0x0b36, 0x0b39, + 0x0b5c, 0x0b5d, + 0x0b5f, 0x0b61, + 0x0b85, 0x0b8a, + 0x0b8e, 0x0b90, + 0x0b92, 0x0b95, + 0x0b99, 0x0b9a, + 0x0b9e, 0x0b9f, + 0x0ba3, 0x0ba4, + 0x0ba8, 0x0baa, + 0x0bae, 0x0bb5, + 0x0bb7, 0x0bb9, + 0x0c05, 0x0c0c, + 0x0c0e, 0x0c10, + 0x0c12, 0x0c28, + 0x0c2a, 0x0c33, + 0x0c35, 0x0c39, + 0x0c60, 0x0c61, + 0x0c85, 0x0c8c, + 0x0c8e, 0x0c90, + 0x0c92, 0x0ca8, + 0x0caa, 0x0cb3, + 0x0cb5, 0x0cb9, + 0x0ce0, 0x0ce1, + 0x0d05, 0x0d0c, + 0x0d0e, 0x0d10, + 0x0d12, 0x0d28, + 0x0d2a, 0x0d39, + 0x0d60, 0x0d61, + 0x0e01, 0x0e30, + 0x0e32, 0x0e33, + 0x0e40, 0x0e46, + 0x0e5a, 0x0e5b, + 0x0e81, 0x0e82, + 0x0e87, 0x0e88, + 0x0e94, 0x0e97, + 0x0e99, 0x0e9f, + 0x0ea1, 0x0ea3, + 0x0eaa, 0x0eab, + 0x0ead, 0x0eae, + 0x0eb2, 0x0eb3, + 0x0ec0, 0x0ec4, + 0x0edc, 0x0edd, + 0x0f18, 0x0f19, + 0x0f40, 0x0f47, + 0x0f49, 0x0f69, + 0x10d0, 0x10f6, + 0x1100, 0x1159, + 0x115f, 0x11a2, + 0x11a8, 0x11f9, + 0x1e00, 0x1e9b, + 0x1f50, 0x1f57, + 0x1f80, 0x1fb4, + 0x1fb6, 0x1fbc, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fcc, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fdb, + 0x1fe0, 0x1fec, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ffc, + 0x210a, 0x2113, + 0x2115, 0x211d, + 0x2120, 0x2122, + 0x212a, 0x2131, + 0x2133, 0x2138, + 0x3041, 0x3094, + 0x30a1, 0x30fa, + 0x3105, 0x312c, + 0x3131, 0x318e, + 0x3192, 0x319f, + 0x3260, 0x327b, + 0x328a, 0x32b0, + 0x32d0, 0x32fe, + 0x3300, 0x3357, + 0x3371, 0x3376, + 0x337b, 0x3394, + 0x3399, 0x339e, + 0x33a9, 0x33ad, + 0x33b0, 0x33c1, + 0x33c3, 0x33c5, + 0x33c7, 0x33d7, + 0x33d9, 0x33dd, + 0x4e00, 0x9fff, + 0xac00, 0xd7a3, + 0xf900, 0xfb06, + 0xfb13, 0xfb17, + 0xfb1f, 0xfb28, + 0xfb2a, 0xfb36, + 0xfb38, 0xfb3c, + 0xfb40, 0xfb41, + 0xfb43, 0xfb44, + 0xfb46, 0xfbb1, + 0xfbd3, 0xfd3d, + 0xfd50, 0xfd8f, + 0xfd92, 0xfdc7, + 0xfdf0, 0xfdf9, + 0xfe70, 0xfe72, + 0xfe76, 0xfefc, + 0xff66, 0xff6f, + 0xff71, 0xff9d, + 0xffa0, 0xffbe, + 0xffc2, 0xffc7, + 0xffca, 0xffcf, + 0xffd2, 0xffd7, + 0xffda, 0xffdc, +}; + +@(static) +alpha_singlets := [?]i32{ + 0x00aa, + 0x00b5, + 0x00ba, + 0x03da, + 0x03dc, + 0x03de, + 0x03e0, + 0x06d5, + 0x09b2, + 0x0a5e, + 0x0a8d, + 0x0ae0, + 0x0b9c, + 0x0cde, + 0x0e4f, + 0x0e84, + 0x0e8a, + 0x0e8d, + 0x0ea5, + 0x0ea7, + 0x0eb0, + 0x0ebd, + 0x1fbe, + 0x207f, + 0x20a8, + 0x2102, + 0x2107, + 0x2124, + 0x2126, + 0x2128, + 0xfb3e, + 0xfe74, +}; + +@(static) +space_ranges := [?]i32{ + 0x0009, 0x000d, // tab and newline + 0x0020, 0x0020, // space + 0x0085, 0x0085, // next line + 0x00a0, 0x00a0, + 0x1680, 0x1680, // Ogham space mark + 0x2000, 0x200b, // en dash .. zero-width space + 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) + 0x2028, 0x2029, 0x3000, 0x3000, + 0x202f, 0x202f, // narrow no-break space + 0x205f, 0x205f, // medium mathematical space + 0x3000, 0x3000, // ideographic space + 0xfeff, 0xfeff, +}; + +@(static) +unicode_spaces := [?]i32{ + 0x0009, // tab + 0x000a, // LF + 0x000d, // CR + 0x0020, // space + 0x0085, // next line + 0x00a0, // unknown + 0x1680, // Ogham space mark + 0x2000, // en dash .. zero-width space + 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) + 0x2028, 0x2029, 0x3000, 0x3000, + 0x202f, // narrow no-break space + 0x205f, // medium mathematical space + 0x3000, // ideographic space + 0xfeff, // unknown +}; + +@(static) +to_upper_ranges := [?]i32{ + 0x0061, 0x007a, 468, // a-z A-Z + 0x00e0, 0x00f6, 468, + 0x00f8, 0x00fe, 468, + 0x0256, 0x0257, 295, + 0x0258, 0x0259, 298, + 0x028a, 0x028b, 283, + 0x03ad, 0x03af, 463, + 0x03b1, 0x03c1, 468, + 0x03c3, 0x03cb, 468, + 0x03cd, 0x03ce, 437, + 0x0430, 0x044f, 468, + 0x0451, 0x045c, 420, + 0x045e, 0x045f, 420, + 0x0561, 0x0586, 452, + 0x1f00, 0x1f07, 508, + 0x1f10, 0x1f15, 508, + 0x1f20, 0x1f27, 508, + 0x1f30, 0x1f37, 508, + 0x1f40, 0x1f45, 508, + 0x1f60, 0x1f67, 508, + 0x1f70, 0x1f71, 574, + 0x1f72, 0x1f75, 586, + 0x1f76, 0x1f77, 600, + 0x1f78, 0x1f79, 628, + 0x1f7a, 0x1f7b, 612, + 0x1f7c, 0x1f7d, 626, + 0x1f80, 0x1f87, 508, + 0x1f90, 0x1f97, 508, + 0x1fa0, 0x1fa7, 508, + 0x1fb0, 0x1fb1, 508, + 0x1fd0, 0x1fd1, 508, + 0x1fe0, 0x1fe1, 508, + 0x2170, 0x217f, 484, + 0x24d0, 0x24e9, 474, + 0xff41, 0xff5a, 468, +}; + +@(static) +to_upper_singlets := [?]i32{ + 0x00ff, 621, + 0x0101, 499, + 0x0103, 499, + 0x0105, 499, + 0x0107, 499, + 0x0109, 499, + 0x010b, 499, + 0x010d, 499, + 0x010f, 499, + 0x0111, 499, + 0x0113, 499, + 0x0115, 499, + 0x0117, 499, + 0x0119, 499, + 0x011b, 499, + 0x011d, 499, + 0x011f, 499, + 0x0121, 499, + 0x0123, 499, + 0x0125, 499, + 0x0127, 499, + 0x0129, 499, + 0x012b, 499, + 0x012d, 499, + 0x012f, 499, + 0x0131, 268, // I + 0x0133, 499, + 0x0135, 499, + 0x0137, 499, + 0x013a, 499, + 0x013c, 499, + 0x013e, 499, + 0x0140, 499, + 0x0142, 499, + 0x0144, 499, + 0x0146, 499, + 0x0148, 499, + 0x014b, 499, + 0x014d, 499, + 0x014f, 499, + 0x0151, 499, + 0x0153, 499, + 0x0155, 499, + 0x0157, 499, + 0x0159, 499, + 0x015b, 499, + 0x015d, 499, + 0x015f, 499, + 0x0161, 499, + 0x0163, 499, + 0x0165, 499, + 0x0167, 499, + 0x0169, 499, + 0x016b, 499, + 0x016d, 499, + 0x016f, 499, + 0x0171, 499, + 0x0173, 499, + 0x0175, 499, + 0x0177, 499, + 0x017a, 499, + 0x017c, 499, + 0x017e, 499, + 0x017f, 200, // S + 0x0183, 499, + 0x0185, 499, + 0x0188, 499, + 0x018c, 499, + 0x0192, 499, + 0x0199, 499, + 0x01a1, 499, + 0x01a3, 499, + 0x01a5, 499, + 0x01a8, 499, + 0x01ad, 499, + 0x01b0, 499, + 0x01b4, 499, + 0x01b6, 499, + 0x01b9, 499, + 0x01bd, 499, + 0x01c5, 499, + 0x01c6, 498, + 0x01c8, 499, + 0x01c9, 498, + 0x01cb, 499, + 0x01cc, 498, + 0x01ce, 499, + 0x01d0, 499, + 0x01d2, 499, + 0x01d4, 499, + 0x01d6, 499, + 0x01d8, 499, + 0x01da, 499, + 0x01dc, 499, + 0x01df, 499, + 0x01e1, 499, + 0x01e3, 499, + 0x01e5, 499, + 0x01e7, 499, + 0x01e9, 499, + 0x01eb, 499, + 0x01ed, 499, + 0x01ef, 499, + 0x01f2, 499, + 0x01f3, 498, + 0x01f5, 499, + 0x01fb, 499, + 0x01fd, 499, + 0x01ff, 499, + 0x0201, 499, + 0x0203, 499, + 0x0205, 499, + 0x0207, 499, + 0x0209, 499, + 0x020b, 499, + 0x020d, 499, + 0x020f, 499, + 0x0211, 499, + 0x0213, 499, + 0x0215, 499, + 0x0217, 499, + 0x0253, 290, + 0x0254, 294, + 0x025b, 297, + 0x0260, 295, + 0x0263, 293, + 0x0268, 291, + 0x0269, 289, + 0x026f, 289, + 0x0272, 287, + 0x0283, 282, + 0x0288, 282, + 0x0292, 281, + 0x03ac, 462, + 0x03cc, 436, + 0x03d0, 438, + 0x03d1, 443, + 0x03d5, 453, + 0x03d6, 446, + 0x03e3, 499, + 0x03e5, 499, + 0x03e7, 499, + 0x03e9, 499, + 0x03eb, 499, + 0x03ed, 499, + 0x03ef, 499, + 0x03f0, 414, + 0x03f1, 420, + 0x0461, 499, + 0x0463, 499, + 0x0465, 499, + 0x0467, 499, + 0x0469, 499, + 0x046b, 499, + 0x046d, 499, + 0x046f, 499, + 0x0471, 499, + 0x0473, 499, + 0x0475, 499, + 0x0477, 499, + 0x0479, 499, + 0x047b, 499, + 0x047d, 499, + 0x047f, 499, + 0x0481, 499, + 0x0491, 499, + 0x0493, 499, + 0x0495, 499, + 0x0497, 499, + 0x0499, 499, + 0x049b, 499, + 0x049d, 499, + 0x049f, 499, + 0x04a1, 499, + 0x04a3, 499, + 0x04a5, 499, + 0x04a7, 499, + 0x04a9, 499, + 0x04ab, 499, + 0x04ad, 499, + 0x04af, 499, + 0x04b1, 499, + 0x04b3, 499, + 0x04b5, 499, + 0x04b7, 499, + 0x04b9, 499, + 0x04bb, 499, + 0x04bd, 499, + 0x04bf, 499, + 0x04c2, 499, + 0x04c4, 499, + 0x04c8, 499, + 0x04cc, 499, + 0x04d1, 499, + 0x04d3, 499, + 0x04d5, 499, + 0x04d7, 499, + 0x04d9, 499, + 0x04db, 499, + 0x04dd, 499, + 0x04df, 499, + 0x04e1, 499, + 0x04e3, 499, + 0x04e5, 499, + 0x04e7, 499, + 0x04e9, 499, + 0x04eb, 499, + 0x04ef, 499, + 0x04f1, 499, + 0x04f3, 499, + 0x04f5, 499, + 0x04f9, 499, + 0x1e01, 499, + 0x1e03, 499, + 0x1e05, 499, + 0x1e07, 499, + 0x1e09, 499, + 0x1e0b, 499, + 0x1e0d, 499, + 0x1e0f, 499, + 0x1e11, 499, + 0x1e13, 499, + 0x1e15, 499, + 0x1e17, 499, + 0x1e19, 499, + 0x1e1b, 499, + 0x1e1d, 499, + 0x1e1f, 499, + 0x1e21, 499, + 0x1e23, 499, + 0x1e25, 499, + 0x1e27, 499, + 0x1e29, 499, + 0x1e2b, 499, + 0x1e2d, 499, + 0x1e2f, 499, + 0x1e31, 499, + 0x1e33, 499, + 0x1e35, 499, + 0x1e37, 499, + 0x1e39, 499, + 0x1e3b, 499, + 0x1e3d, 499, + 0x1e3f, 499, + 0x1e41, 499, + 0x1e43, 499, + 0x1e45, 499, + 0x1e47, 499, + 0x1e49, 499, + 0x1e4b, 499, + 0x1e4d, 499, + 0x1e4f, 499, + 0x1e51, 499, + 0x1e53, 499, + 0x1e55, 499, + 0x1e57, 499, + 0x1e59, 499, + 0x1e5b, 499, + 0x1e5d, 499, + 0x1e5f, 499, + 0x1e61, 499, + 0x1e63, 499, + 0x1e65, 499, + 0x1e67, 499, + 0x1e69, 499, + 0x1e6b, 499, + 0x1e6d, 499, + 0x1e6f, 499, + 0x1e71, 499, + 0x1e73, 499, + 0x1e75, 499, + 0x1e77, 499, + 0x1e79, 499, + 0x1e7b, 499, + 0x1e7d, 499, + 0x1e7f, 499, + 0x1e81, 499, + 0x1e83, 499, + 0x1e85, 499, + 0x1e87, 499, + 0x1e89, 499, + 0x1e8b, 499, + 0x1e8d, 499, + 0x1e8f, 499, + 0x1e91, 499, + 0x1e93, 499, + 0x1e95, 499, + 0x1ea1, 499, + 0x1ea3, 499, + 0x1ea5, 499, + 0x1ea7, 499, + 0x1ea9, 499, + 0x1eab, 499, + 0x1ead, 499, + 0x1eaf, 499, + 0x1eb1, 499, + 0x1eb3, 499, + 0x1eb5, 499, + 0x1eb7, 499, + 0x1eb9, 499, + 0x1ebb, 499, + 0x1ebd, 499, + 0x1ebf, 499, + 0x1ec1, 499, + 0x1ec3, 499, + 0x1ec5, 499, + 0x1ec7, 499, + 0x1ec9, 499, + 0x1ecb, 499, + 0x1ecd, 499, + 0x1ecf, 499, + 0x1ed1, 499, + 0x1ed3, 499, + 0x1ed5, 499, + 0x1ed7, 499, + 0x1ed9, 499, + 0x1edb, 499, + 0x1edd, 499, + 0x1edf, 499, + 0x1ee1, 499, + 0x1ee3, 499, + 0x1ee5, 499, + 0x1ee7, 499, + 0x1ee9, 499, + 0x1eeb, 499, + 0x1eed, 499, + 0x1eef, 499, + 0x1ef1, 499, + 0x1ef3, 499, + 0x1ef5, 499, + 0x1ef7, 499, + 0x1ef9, 499, + 0x1f51, 508, + 0x1f53, 508, + 0x1f55, 508, + 0x1f57, 508, + 0x1fb3, 509, + 0x1fc3, 509, + 0x1fe5, 507, + 0x1ff3, 509, +}; + +@(static) +to_lower_ranges := [?]i32{ + 0x0041, 0x005a, 532, // A-Z a-z + 0x00c0, 0x00d6, 532, // - - + 0x00d8, 0x00de, 532, // - - + 0x0189, 0x018a, 705, // - - + 0x018e, 0x018f, 702, // - - + 0x01b1, 0x01b2, 717, // - - + 0x0388, 0x038a, 537, // - - + 0x038e, 0x038f, 563, // - - + 0x0391, 0x03a1, 532, // - - + 0x03a3, 0x03ab, 532, // - - + 0x0401, 0x040c, 580, // - - + 0x040e, 0x040f, 580, // - - + 0x0410, 0x042f, 532, // - - + 0x0531, 0x0556, 548, // - - + 0x10a0, 0x10c5, 548, // - - + 0x1f08, 0x1f0f, 492, // - - + 0x1f18, 0x1f1d, 492, // - - + 0x1f28, 0x1f2f, 492, // - - + 0x1f38, 0x1f3f, 492, // - - + 0x1f48, 0x1f4d, 492, // - - + 0x1f68, 0x1f6f, 492, // - - + 0x1f88, 0x1f8f, 492, // - - + 0x1f98, 0x1f9f, 492, // - - + 0x1fa8, 0x1faf, 492, // - - + 0x1fb8, 0x1fb9, 492, // - - + 0x1fba, 0x1fbb, 426, // - - + 0x1fc8, 0x1fcb, 414, // - - + 0x1fd8, 0x1fd9, 492, // - - + 0x1fda, 0x1fdb, 400, // - - + 0x1fe8, 0x1fe9, 492, // - - + 0x1fea, 0x1feb, 388, // - - + 0x1ff8, 0x1ff9, 372, // - - + 0x1ffa, 0x1ffb, 374, // - - + 0x2160, 0x216f, 516, // - - + 0x24b6, 0x24cf, 526, // - - + 0xff21, 0xff3a, 532, // - - +}; + +@(static) +to_lower_singlets := [?]i32{ + 0x0100, 501, + 0x0102, 501, + 0x0104, 501, + 0x0106, 501, + 0x0108, 501, + 0x010a, 501, + 0x010c, 501, + 0x010e, 501, + 0x0110, 501, + 0x0112, 501, + 0x0114, 501, + 0x0116, 501, + 0x0118, 501, + 0x011a, 501, + 0x011c, 501, + 0x011e, 501, + 0x0120, 501, + 0x0122, 501, + 0x0124, 501, + 0x0126, 501, + 0x0128, 501, + 0x012a, 501, + 0x012c, 501, + 0x012e, 501, + 0x0130, 301, // i + 0x0132, 501, + 0x0134, 501, + 0x0136, 501, + 0x0139, 501, + 0x013b, 501, + 0x013d, 501, + 0x013f, 501, + 0x0141, 501, + 0x0143, 501, + 0x0145, 501, + 0x0147, 501, + 0x014a, 501, + 0x014c, 501, + 0x014e, 501, + 0x0150, 501, + 0x0152, 501, + 0x0154, 501, + 0x0156, 501, + 0x0158, 501, + 0x015a, 501, + 0x015c, 501, + 0x015e, 501, + 0x0160, 501, + 0x0162, 501, + 0x0164, 501, + 0x0166, 501, + 0x0168, 501, + 0x016a, 501, + 0x016c, 501, + 0x016e, 501, + 0x0170, 501, + 0x0172, 501, + 0x0174, 501, + 0x0176, 501, + 0x0178, 379, + 0x0179, 501, + 0x017b, 501, + 0x017d, 501, + 0x0181, 710, + 0x0182, 501, + 0x0184, 501, + 0x0186, 706, + 0x0187, 501, + 0x018b, 501, + 0x0190, 703, + 0x0191, 501, + 0x0193, 705, + 0x0194, 707, + 0x0196, 711, + 0x0197, 709, + 0x0198, 501, + 0x019c, 711, + 0x019d, 713, + 0x01a0, 501, + 0x01a2, 501, + 0x01a4, 501, + 0x01a7, 501, + 0x01a9, 718, + 0x01ac, 501, + 0x01ae, 718, + 0x01af, 501, + 0x01b3, 501, + 0x01b5, 501, + 0x01b7, 719, + 0x01b8, 501, + 0x01bc, 501, + 0x01c4, 502, + 0x01c5, 501, + 0x01c7, 502, + 0x01c8, 501, + 0x01ca, 502, + 0x01cb, 501, + 0x01cd, 501, + 0x01cf, 501, + 0x01d1, 501, + 0x01d3, 501, + 0x01d5, 501, + 0x01d7, 501, + 0x01d9, 501, + 0x01db, 501, + 0x01de, 501, + 0x01e0, 501, + 0x01e2, 501, + 0x01e4, 501, + 0x01e6, 501, + 0x01e8, 501, + 0x01ea, 501, + 0x01ec, 501, + 0x01ee, 501, + 0x01f1, 502, + 0x01f2, 501, + 0x01f4, 501, + 0x01fa, 501, + 0x01fc, 501, + 0x01fe, 501, + 0x0200, 501, + 0x0202, 501, + 0x0204, 501, + 0x0206, 501, + 0x0208, 501, + 0x020a, 501, + 0x020c, 501, + 0x020e, 501, + 0x0210, 501, + 0x0212, 501, + 0x0214, 501, + 0x0216, 501, + 0x0386, 538, + 0x038c, 564, + 0x03e2, 501, + 0x03e4, 501, + 0x03e6, 501, + 0x03e8, 501, + 0x03ea, 501, + 0x03ec, 501, + 0x03ee, 501, + 0x0460, 501, + 0x0462, 501, + 0x0464, 501, + 0x0466, 501, + 0x0468, 501, + 0x046a, 501, + 0x046c, 501, + 0x046e, 501, + 0x0470, 501, + 0x0472, 501, + 0x0474, 501, + 0x0476, 501, + 0x0478, 501, + 0x047a, 501, + 0x047c, 501, + 0x047e, 501, + 0x0480, 501, + 0x0490, 501, + 0x0492, 501, + 0x0494, 501, + 0x0496, 501, + 0x0498, 501, + 0x049a, 501, + 0x049c, 501, + 0x049e, 501, + 0x04a0, 501, + 0x04a2, 501, + 0x04a4, 501, + 0x04a6, 501, + 0x04a8, 501, + 0x04aa, 501, + 0x04ac, 501, + 0x04ae, 501, + 0x04b0, 501, + 0x04b2, 501, + 0x04b4, 501, + 0x04b6, 501, + 0x04b8, 501, + 0x04ba, 501, + 0x04bc, 501, + 0x04be, 501, + 0x04c1, 501, + 0x04c3, 501, + 0x04c7, 501, + 0x04cb, 501, + 0x04d0, 501, + 0x04d2, 501, + 0x04d4, 501, + 0x04d6, 501, + 0x04d8, 501, + 0x04da, 501, + 0x04dc, 501, + 0x04de, 501, + 0x04e0, 501, + 0x04e2, 501, + 0x04e4, 501, + 0x04e6, 501, + 0x04e8, 501, + 0x04ea, 501, + 0x04ee, 501, + 0x04f0, 501, + 0x04f2, 501, + 0x04f4, 501, + 0x04f8, 501, + 0x1e00, 501, + 0x1e02, 501, + 0x1e04, 501, + 0x1e06, 501, + 0x1e08, 501, + 0x1e0a, 501, + 0x1e0c, 501, + 0x1e0e, 501, + 0x1e10, 501, + 0x1e12, 501, + 0x1e14, 501, + 0x1e16, 501, + 0x1e18, 501, + 0x1e1a, 501, + 0x1e1c, 501, + 0x1e1e, 501, + 0x1e20, 501, + 0x1e22, 501, + 0x1e24, 501, + 0x1e26, 501, + 0x1e28, 501, + 0x1e2a, 501, + 0x1e2c, 501, + 0x1e2e, 501, + 0x1e30, 501, + 0x1e32, 501, + 0x1e34, 501, + 0x1e36, 501, + 0x1e38, 501, + 0x1e3a, 501, + 0x1e3c, 501, + 0x1e3e, 501, + 0x1e40, 501, + 0x1e42, 501, + 0x1e44, 501, + 0x1e46, 501, + 0x1e48, 501, + 0x1e4a, 501, + 0x1e4c, 501, + 0x1e4e, 501, + 0x1e50, 501, + 0x1e52, 501, + 0x1e54, 501, + 0x1e56, 501, + 0x1e58, 501, + 0x1e5a, 501, + 0x1e5c, 501, + 0x1e5e, 501, + 0x1e60, 501, + 0x1e62, 501, + 0x1e64, 501, + 0x1e66, 501, + 0x1e68, 501, + 0x1e6a, 501, + 0x1e6c, 501, + 0x1e6e, 501, + 0x1e70, 501, + 0x1e72, 501, + 0x1e74, 501, + 0x1e76, 501, + 0x1e78, 501, + 0x1e7a, 501, + 0x1e7c, 501, + 0x1e7e, 501, + 0x1e80, 501, + 0x1e82, 501, + 0x1e84, 501, + 0x1e86, 501, + 0x1e88, 501, + 0x1e8a, 501, + 0x1e8c, 501, + 0x1e8e, 501, + 0x1e90, 501, + 0x1e92, 501, + 0x1e94, 501, + 0x1ea0, 501, + 0x1ea2, 501, + 0x1ea4, 501, + 0x1ea6, 501, + 0x1ea8, 501, + 0x1eaa, 501, + 0x1eac, 501, + 0x1eae, 501, + 0x1eb0, 501, + 0x1eb2, 501, + 0x1eb4, 501, + 0x1eb6, 501, + 0x1eb8, 501, + 0x1eba, 501, + 0x1ebc, 501, + 0x1ebe, 501, + 0x1ec0, 501, + 0x1ec2, 501, + 0x1ec4, 501, + 0x1ec6, 501, + 0x1ec8, 501, + 0x1eca, 501, + 0x1ecc, 501, + 0x1ece, 501, + 0x1ed0, 501, + 0x1ed2, 501, + 0x1ed4, 501, + 0x1ed6, 501, + 0x1ed8, 501, + 0x1eda, 501, + 0x1edc, 501, + 0x1ede, 501, + 0x1ee0, 501, + 0x1ee2, 501, + 0x1ee4, 501, + 0x1ee6, 501, + 0x1ee8, 501, + 0x1eea, 501, + 0x1eec, 501, + 0x1eee, 501, + 0x1ef0, 501, + 0x1ef2, 501, + 0x1ef4, 501, + 0x1ef6, 501, + 0x1ef8, 501, + 0x1f59, 492, + 0x1f5b, 492, + 0x1f5d, 492, + 0x1f5f, 492, + 0x1fbc, 491, + 0x1fcc, 491, + 0x1fec, 493, + 0x1ffc, 491, +}; + +@(static) +to_title_singlets := [?]i32{ + 0x01c4, 501, + 0x01c6, 499, + 0x01c7, 501, + 0x01c9, 499, + 0x01ca, 501, + 0x01cc, 499, + 0x01f1, 501, + 0x01f3, 499, +}; From 6ee4f51670e983347feb4acb15f681d56117c137 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 11 Nov 2020 11:42:21 +0000 Subject: [PATCH 037/170] Add new math procedure: inf_f32 inf_f64 nan_f32 nan_f64 --- core/math/math.odin | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/math/math.odin b/core/math/math.odin index be9da46d3..0a1334e60 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -593,6 +593,30 @@ is_inf :: proc{is_inf_f32, is_inf_f64}; +inf_f32 :: proc(sign: int) -> f32 { + return f32(inf_f64(sign)); +} +inf_f64 :: proc(sign: int) -> f64 { + v: u64; + if sign >= 0 { + v = 0x7ff00000_00000000; + } else { + v = 0xfff00000_00000000; + } + return transmute(f64)v; +} + + +nan_f32 :: proc() -> f32 { + return f32(nan_f64()); +} +nan_f64 :: proc() -> f64 { + v: u64 = 0x7ff80000_00000001; + return transmute(f64)v; +} + + + is_power_of_two :: proc(x: int) -> bool { return x > 0 && (x & (x-1)) == 0; } From 70b8b3c7dde193c9c67e4b5fd86fd2f99ee1d987 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 12 Nov 2020 00:43:49 +0000 Subject: [PATCH 038/170] Update LLVM backend to begin work on a generic ABI system --- src/llvm_abi.cpp | 762 +++++++++++++++++++++++++++++++++++++++++++ src/llvm_backend.cpp | 723 ++++++++++++++++++++++++++++++---------- src/llvm_backend.hpp | 3 + src/types.cpp | 10 + 4 files changed, 1322 insertions(+), 176 deletions(-) create mode 100644 src/llvm_abi.cpp diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp new file mode 100644 index 000000000..2bede5822 --- /dev/null +++ b/src/llvm_abi.cpp @@ -0,0 +1,762 @@ +enum lbArgKind { + lbArg_Direct, + lbArg_Indirect, + lbArg_Ignore, +}; + +struct lbArgType { + lbArgKind kind; + LLVMTypeRef type; + LLVMTypeRef cast_type; // Optional + LLVMTypeRef pad_type; // Optional + LLVMAttributeRef attribute; // Optional +}; + +lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) { + return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr}; +} +lbArgType lb_arg_type_direct(LLVMTypeRef type) { + return lb_arg_type_direct(type, nullptr, nullptr, nullptr); +} + +lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) { + return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr}; +} + +lbArgType lb_arg_type_ignore(LLVMTypeRef type) { + return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr}; +} + +struct lbFunctionType { + LLVMContextRef ctx; + ProcCallingConvention calling_convention; + Array args; + lbArgType ret; +}; + + +bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) { + return LLVMGetTypeKind(type) == kind; +} + +LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) { + unsigned arg_count = cast(unsigned)ft->args.count; + unsigned offset = 0; + + LLVMTypeRef ret = nullptr; + if (ft->ret.kind == lbArg_Direct) { + if (ft->ret.cast_type != nullptr) { + ret = ft->ret.cast_type; + } else { + ret = ft->ret.type; + } + } else if (ft->ret.kind == lbArg_Indirect) { + offset += 1; + ret = LLVMVoidTypeInContext(ft->ctx); + } else if (ft->ret.kind == lbArg_Ignore) { + ret = LLVMVoidTypeInContext(ft->ctx); + } + GB_ASSERT_MSG(ret != nullptr, "%d", ft->ret.kind); + + unsigned maximum_arg_count = offset+arg_count; + LLVMTypeRef *args = gb_alloc_array(heap_allocator(), LLVMTypeRef, maximum_arg_count); + if (offset == 1) { + GB_ASSERT(ft->ret.kind == lbArg_Indirect); + args[0] = ft->ret.type; + } + + unsigned arg_index = offset; + for (unsigned i = 0; i < arg_count; i++) { + lbArgType *arg = &ft->args[i]; + if (arg->kind == lbArg_Direct) { + LLVMTypeRef arg_type = nullptr; + if (ft->args[i].cast_type != nullptr) { + arg_type = arg->cast_type; + } else { + arg_type = arg->type; + } + args[arg_index++] = arg_type; + } else if (arg->kind == lbArg_Indirect) { + GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind)); + args[arg_index++] = LLVMPointerType(arg->type, 0); + } else if (arg->kind == lbArg_Ignore) { + // ignore + } + } + unsigned total_arg_count = arg_index; + LLVMTypeRef func_type = LLVMFunctionType(ret, args, total_arg_count, is_var_arg); + return LLVMPointerType(func_type, 0); +} + + +void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCallingConvention calling_convention) { + if (ft == nullptr) { + return; + } + unsigned arg_count = cast(unsigned)ft->args.count; + unsigned offset = 0; + if (ft->ret.kind == lbArg_Indirect) { + offset += 1; + } + + unsigned arg_index = offset; + for (unsigned i = 0; i < arg_count; i++) { + lbArgType *arg = &ft->args[i]; + if (arg->kind == lbArg_Ignore) { + continue; + } + + if (arg->attribute) { + LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute); + } + + arg_index++; + } + + if (offset != 0 && ft->ret.kind == lbArg_Indirect && ft->ret.attribute != nullptr) { + LLVMAddAttributeAtIndex(fn, offset, ft->ret.attribute); + } + + lbCallingConventionKind cc_kind = lbCallingConvention_C; + // TODO(bill): Clean up this logic + if (build_context.metrics.os != TargetOs_js) { + cc_kind = lb_calling_convention_map[calling_convention]; + } + LLVMSetFunctionCallConv(fn, cc_kind); + if (calling_convention == ProcCC_Odin) { + unsigned context_index = offset+arg_count; + LLVMContextRef c = ft->ctx; + LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "noalias", true)); + LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "nonnull", true)); + LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "nocapture", true)); + } + +} + +i64 lb_sizeof(LLVMTypeRef type); +i64 lb_alignof(LLVMTypeRef type); + +i64 lb_sizeof(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMVoidTypeKind: + return 0; + case LLVMIntegerTypeKind: + { + unsigned w = LLVMGetIntTypeWidth(type); + return (w + 7)/8; + } + case LLVMFloatTypeKind: + return 4; + case LLVMDoubleTypeKind: + return 8; + case LLVMPointerTypeKind: + return build_context.word_size; + case LLVMStructTypeKind: + { + unsigned field_count = LLVMCountStructElementTypes(type); + i64 offset = 0; + if (LLVMIsPackedStruct(type)) { + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); + offset += lb_sizeof(field); + } + } else { + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); + i64 align = lb_alignof(field); + offset = align_formula(offset, align); + offset += lb_sizeof(field); + } + } + offset = align_formula(offset, lb_alignof(type)); + return offset; + } + break; + case LLVMArrayTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return size; + } + break; + + case LLVMX86_MMXTypeKind: + return 8; + case LLVMVectorTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return gb_clamp(next_pow2(size), 1, build_context.max_align); + } + + } + GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type)); + + // LLVMValueRef v = LLVMSizeOf(type); + // GB_ASSERT(LLVMIsConstant(v)); + // return cast(i64)LLVMConstIntGetSExtValue(v); + return 0; +} + +i64 lb_alignof(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMVoidTypeKind: + return 1; + case LLVMIntegerTypeKind: + { + unsigned w = LLVMGetIntTypeWidth(type); + return gb_clamp((w + 7)/8, 1, build_context.max_align); + } + case LLVMFloatTypeKind: + return 4; + case LLVMDoubleTypeKind: + return 8; + case LLVMPointerTypeKind: + return build_context.word_size; + case LLVMStructTypeKind: + { + if (LLVMIsPackedStruct(type)) { + return 1; + } else { + unsigned field_count = LLVMCountStructElementTypes(type); + i64 max_align = 1; + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); + i64 field_align = lb_alignof(field); + max_align = gb_max(max_align, field_align); + } + return max_align; + } + } + break; + case LLVMArrayTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return size; + } + break; + + case LLVMX86_MMXTypeKind: + return 8; + case LLVMVectorTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return gb_clamp(next_pow2(size), 1, build_context.max_align); + } + + } + GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type)); + + // LLVMValueRef v = LLVMAlignOf(type); + // GB_ASSERT(LLVMIsConstant(v)); + // return LLVMConstIntGetSExtValue(v); + return 1; +} + +Type *lb_abi_to_odin_type(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMVoidTypeKind: + return nullptr; + case LLVMIntegerTypeKind: + { + unsigned w = LLVMGetIntTypeWidth(type); + if (w == 1) { + return t_llvm_bool; + } + unsigned bytes = (w + 7)/8; + switch (bytes) { + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; + case 16: return t_u128; + } + GB_PANIC("Unhandled integer type"); + } + case LLVMFloatTypeKind: + return t_f32; + case LLVMDoubleTypeKind: + return t_f64; + case LLVMPointerTypeKind: + return t_rawptr; + case LLVMStructTypeKind: + { + GB_PANIC("HERE"); + } + break; + case LLVMArrayTypeKind: + { + + i64 count = LLVMGetVectorSize(type); + Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type)); + return alloc_type_array(elem, count); + } + break; + + case LLVMX86_MMXTypeKind: + return t_vector_x86_mmx; + case LLVMVectorTypeKind: + { + i64 count = LLVMGetVectorSize(type); + Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type)); + return alloc_type_simd_vector(count, elem); + } + + } + GB_PANIC("Unhandled type for lb_abi_to_odin_type -> %s", LLVMPrintTypeToString(type)); + + // LLVMValueRef v = LLVMSizeOf(type); + // GB_ASSERT(LLVMIsConstant(v)); + // return cast(i64)LLVMConstIntGetSExtValue(v); + return 0; +} + + + +#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention) +typedef LB_ABI_INFO(lbAbiInfoType); + + +// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything +namespace lbAbi386 { + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) { + if (build_context.metrics.os == TargetOs_windows && + build_context.word_size == 8 && + lb_is_type_kind(type, LLVMIntegerTypeKind) && + lb_sizeof(type) == 16) { + + LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2); + return lb_arg_type_direct(type, cast_type, nullptr, nullptr); + } + + + + LLVMAttributeRef attr = nullptr; + LLVMTypeRef i1 = LLVMInt1TypeInContext(c); + if (type == i1) { + // attr = lb_create_enum_attribute(c, "zext", true); + // return lb_arg_type_direct(type, i1, nullptr, attr); + } + return lb_arg_type_direct(type, nullptr, nullptr, attr); + } + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + if (sz == 0) { + args[i] = lb_arg_type_ignore(t); + } else { + args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true)); + } + } else { + args[i] = non_struct(c, t); + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) { + i64 sz = lb_sizeof(return_type); + switch (sz) { + case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr); + case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr); + case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); + } + return lb_arg_type_indirect(LLVMPointerType(return_type, 0), lb_create_enum_attribute(c, "sret", true)); + } + return non_struct(c, return_type); + } +}; + +namespace lbAbiAmd64Win64 { + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + switch (sz) { + case 1: + case 2: + case 4: + case 8: + args[i] = lb_arg_type_direct(t, LLVMIntTypeInContext(c, 8*cast(unsigned)sz), nullptr, nullptr); + break; + default: + args[i] = lb_arg_type_indirect(t, nullptr); + break; + } + } else { + args[i] = lbAbi386::non_struct(c, t); + } + } + return args; + } +}; + +// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything +namespace lbAbiAmd64SysV { + enum RegClass { + RegClass_NoClass, + RegClass_Int, + RegClass_SSEFs, + RegClass_SSEFv, + RegClass_SSEDs, + RegClass_SSEDv, + RegClass_SSEInt, + RegClass_SSEUp, + RegClass_X87, + RegClass_X87Up, + RegClass_ComplexX87, + RegClass_Memory, + }; + + bool is_sse(RegClass reg_class) { + switch (reg_class) { + case RegClass_SSEFs: + case RegClass_SSEFv: + case RegClass_SSEDv: + return true; + } + return false; + } + + void all_mem(Array *cs) { + for_array(i, *cs) { + (*cs)[i] = RegClass_Memory; + } + } + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off); + void fixup(LLVMTypeRef t, Array *cls); + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + // TODO(bill): THIS IS VERY VERY WRONG! + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) { + LLVMAttributeRef attr = nullptr; + LLVMTypeRef i1 = LLVMInt1TypeInContext(c); + if (type == i1) { + attr = lb_create_enum_attribute(c, "zext", true); + } + return lb_arg_type_direct(type, nullptr, nullptr, attr); + } + + Array classify(LLVMTypeRef t) { + i64 sz = lb_sizeof(t); + i64 words = (sz + 7)/8; + auto reg_classes = array_make(heap_allocator(), cast(isize)words); + if (words > 4) { + all_mem(®_classes); + } else { + classify_with(t, ®_classes, 0, 0); + fixup(t, ®_classes); + } + return reg_classes; + } + + void classify_struct(LLVMTypeRef *fields, unsigned field_count, Array *cls, i64 i, i64 off, LLVMBool packed) { + i64 field_off = off; + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef t = fields[i]; + if (!packed) { + field_off = align_formula(field_off, lb_alignof(t)); + } + classify_with(t, cls, i, field_off); + field_off += lb_sizeof(t); + } + } + + void unify(Array *cls, i64 i, RegClass newv) { + RegClass &oldv = (*cls)[i]; + if (oldv == newv) { + return; + } else if (oldv == RegClass_NoClass) { + oldv = newv; + } else if (newv == RegClass_NoClass) { + return; + } else if (oldv == RegClass_Memory || newv == RegClass_Memory) { + return; + } else if (oldv == RegClass_Int || newv == RegClass_Int) { + return; + } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 || + newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { + oldv = RegClass_Memory; + } else { + oldv = newv; + } + } + + void fixup(LLVMTypeRef t, Array *cls) { + i64 i = 0; + i64 e = cls->count; + if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) { + RegClass &oldv = (*cls)[i]; + if (is_sse(oldv)) { + for (i++; i < e; i++) { + if (oldv != RegClass_SSEUp) { + all_mem(cls); + return; + } + } + } else { + all_mem(cls); + return; + } + } else { + while (i < e) { + RegClass &oldv = (*cls)[i]; + if (oldv == RegClass_Memory) { + all_mem(cls); + return; + } else if (oldv == RegClass_X87Up) { + // NOTE(bill): Darwin + all_mem(cls); + return; + } else if (oldv == RegClass_SSEUp) { + oldv = RegClass_SSEDv; + } else if (is_sse(oldv)) { + i++; + while (i != e && oldv == RegClass_SSEUp) { + i++; + } + } else if (oldv == RegClass_X87) { + i++; + while (i != e && oldv == RegClass_X87Up) { + i++; + } + } else { + i++; + } + } + } + } + + unsigned llvec_len(Array const ®_classes) { + unsigned len = 1; + for_array(i, reg_classes) { + if (reg_classes[i] != RegClass_SSEUp) { + break; + } + len++; + } + return len; + } + + + LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes) {; + auto types = array_make(heap_allocator(), 0, reg_classes.count); + for_array(i, reg_classes) { + switch (reg_classes[i]) { + case RegClass_Int: + array_add(&types, LLVMIntTypeInContext(c, 64)); + break; + case RegClass_SSEFv: + { + unsigned vec_len = llvec_len(array_slice(reg_classes, i+1, reg_classes.count)); + LLVMTypeRef vec_type = LLVMVectorType(LLVMFloatTypeInContext(c), vec_len); + array_add(&types, vec_type); + i += vec_len; + continue; + } + break; + case RegClass_SSEFs: + array_add(&types, LLVMFloatTypeInContext(c)); + break; + case RegClass_SSEDs: + array_add(&types, LLVMDoubleTypeInContext(c)); + break; + default: + GB_PANIC("Unhandled RegClass"); + } + } + + return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false); + } + + void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off) { + i64 t_align = lb_alignof(t); + i64 t_size = lb_sizeof(t); + + i64 mis_align = off % t_align; + if (mis_align != 0) { + i64 e = (off + t_size + 7) / 8; + for (i64 i = off / 8; i < e; i++) { + unify(cls, ix+1, RegClass_Memory); + } + return; + } + + switch (LLVMGetTypeKind(t)) { + case LLVMIntegerTypeKind: + case LLVMPointerTypeKind: + unify(cls, ix+off / 8, RegClass_Int); + break; + case LLVMFloatTypeKind: + unify(cls, ix+off / 8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); + break; + case LLVMDoubleTypeKind: + unify(cls, ix+off / 8, RegClass_SSEDs); + break; + case LLVMStructTypeKind: + { + unsigned field_count = LLVMCountStructElementTypes(t); + LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); // HACK(bill): LEAK + defer (gb_free(heap_allocator(), fields)); + + LLVMGetStructElementTypes(t, fields); + + classify_struct(fields, field_count, cls, ix, off, LLVMIsPackedStruct(t)); + } + break; + case LLVMArrayTypeKind: + { + i64 len = LLVMGetArrayLength(t); + LLVMTypeRef elem = LLVMGetElementType(t); + i64 elem_sz = lb_sizeof(elem); + for (i64 i = 0; i < len; i++) { + classify_with(elem, cls, ix, off + i*elem_sz); + } + } + break; + default: + GB_PANIC("Unhandled type"); + break; + } + } + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + if (sz == 0) { + args[i] = lb_arg_type_ignore(t); + } else { + args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true)); + } + } else { + args[i] = non_struct(c, t); + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) { + i64 sz = lb_sizeof(return_type); + switch (sz) { + case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr); + case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr); + case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); + } + return lb_arg_type_indirect(LLVMPointerType(return_type, 0), lb_create_enum_attribute(c, "sret", true)); + } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) { + return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr); + } + return non_struct(c, return_type); + } +}; + + + + +LB_ABI_INFO(lb_get_abi_info) { + switch (calling_convention) { + case ProcCC_None: + case ProcCC_PureNone: + case ProcCC_InlineAsm: + { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = array_make(heap_allocator(), arg_count); + for (unsigned i = 0; i < arg_count; i++) { + ft->args[i] = lb_arg_type_direct(arg_types[i]); + } + if (return_is_defined) { + ft->ret = lb_arg_type_direct(return_type); + } else { + ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } + ft->calling_convention = calling_convention; + return ft; + } + } + + if (build_context.metrics.arch == TargetArch_amd64) { + if (build_context.metrics.os == TargetOs_windows) { + return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } else { + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } + } else if (build_context.metrics.arch == TargetArch_386) { + return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } else if (build_context.metrics.arch == TargetArch_wasm32) { + return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } + GB_PANIC("Unsupported ABI"); + return {}; +} diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index aab2248d9..a01dd7ede 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1,4 +1,11 @@ #include "llvm_backend.hpp" +#include "llvm_abi.cpp" + +#ifdef USE_NEW_LLVM_ABI_SYSTEM +#define USE_LLVM_ABI 1 +#else +#define USE_LLVM_ABI 0 +#endif gb_global lbAddr lb_global_type_info_data = {}; gb_global lbAddr lb_global_type_info_member_types = {}; @@ -459,7 +466,11 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { GB_ASSERT(value.value != nullptr); value = lb_emit_conv(p, value, lb_addr_type(addr)); - LLVMBuildStore(p->builder, value.value, addr.addr.value); + if (USE_LLVM_ABI) { + lb_emit_store(p, addr.addr, value); + } else { + LLVMBuildStore(p->builder, value.value, addr.addr.value); + } } void lb_const_store(lbValue ptr, lbValue value) { @@ -480,11 +491,25 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { Type *ca = core_type(a); if (ca->kind == Type_Basic) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); - } else { - GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type)); } - LLVMBuildStore(p->builder, value.value, ptr.value); + if (USE_LLVM_ABI && is_type_proc(a)) { + // NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be + // stored as regular pointer with no procedure information + + LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value)); + LLVMValueRef v = LLVMBuildPointerCast(p->builder, value.value, src_t, ""); + LLVMBuildStore(p->builder, v, ptr.value); + } else { + Type *ca = core_type(a); + if (ca->kind == Type_Basic) { + GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); + } else { + GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type)); + } + + LLVMBuildStore(p->builder, value.value, ptr.value); + } } lbValue lb_emit_load(lbProcedure *p, lbValue value) { @@ -1132,7 +1157,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { switch (base->kind) { case Type_Basic: - return lb_type(m, base); + return lb_type_internal(m, base); case Type_Named: case Type_Generic: @@ -1141,7 +1166,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { break; case Type_Opaque: - return lb_type(m, base->Opaque.elem); + return lb_type_internal(m, base->Opaque.elem); case Type_Pointer: case Type_Array: @@ -1152,14 +1177,14 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Type_Enum: case Type_BitSet: case Type_SimdVector: - return lb_type(m, base); + return lb_type_internal(m, base); // TODO(bill): Deal with this correctly. Can this be named? case Type_Proc: - return lb_type(m, base); + return lb_type_internal(m, base); case Type_Tuple: - return lb_type(m, base); + return lb_type_internal(m, base); } LLVMTypeRef *found = map_get(&m->types, hash_type(base)); @@ -1196,7 +1221,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } - return lb_type(m, base); + return lb_type_internal(m, base); } case Type_Pointer: @@ -1320,16 +1345,83 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { for_array(i, type->Tuple.variables) { Entity *field = type->Tuple.variables[i]; - fields[i] = lb_type(m, field->type); + + LLVMTypeRef param_type = nullptr; + param_type = lb_type(m, field->type); + + fields[i] = param_type; } return LLVMStructTypeInContext(ctx, fields, field_count, type->Tuple.is_packed); } case Type_Proc: - { - set_procedure_abi_types(heap_allocator(), type); + if (USE_LLVM_ABI) { + if (m->internal_type_level > 1) { + return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0); + } else { + unsigned param_count = 0; + if (type->Proc.calling_convention == ProcCC_Odin) { + param_count += 1; + } + if (type->Proc.param_count != 0) { + GB_ASSERT(type->Proc.params->kind == Type_Tuple); + for_array(i, type->Proc.params->Tuple.variables) { + Entity *e = type->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + param_count += 1; + } + } + + LLVMTypeRef ret = nullptr; + LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count); + if (type->Proc.result_count != 0) { + Type *single_ret = reduce_tuple_to_single_type(type->Proc.results); + ret = lb_type(m, type->Proc.results); + if (ret != nullptr) { + if (is_calling_convention_none(type->Proc.calling_convention) && + is_type_boolean(single_ret) && + type_size_of(single_ret) <= 1) { + ret = LLVMInt1TypeInContext(m->ctx); + } + } + } + + isize param_index = 0; + if (type->Proc.param_count != 0) { + GB_ASSERT(type->Proc.params->kind == Type_Tuple); + for_array(i, type->Proc.params->Tuple.variables) { + Entity *e = type->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + + LLVMTypeRef param_type = nullptr; + if (is_calling_convention_none(type->Proc.calling_convention) && + is_type_boolean(e->type) && + type_size_of(e->type) <= 1) { + param_type = LLVMInt1TypeInContext(m->ctx); + } else { + param_type = lb_type(m, e->type); + } + params[param_index++] = param_type; + } + } + if (param_index < param_count) { + params[param_index++] = lb_type(m, t_context_ptr); + } + GB_ASSERT(param_index == param_count); + + + lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention); + map_set(&m->function_type_map, hash_type(type), ft); + return lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg); + } + } else { + set_procedure_abi_types(heap_allocator(), type); LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx); if (type->Proc.return_by_pointer) { // Void @@ -1452,10 +1544,14 @@ LLVMTypeRef lb_type(lbModule *m, Type *type) { return *found; } - LLVMTypeRef llvm_type = lb_type_internal(m, type); - - map_set(&m->types, hash_type(type), llvm_type); + LLVMTypeRef llvm_type = nullptr; + m->internal_type_level += 1; + llvm_type = lb_type_internal(m, type); + m->internal_type_level -= 1; + if (USE_LLVM_ABI && m->internal_type_level == 0) { + map_set(&m->types, hash_type(type), llvm_type); + } return llvm_type; } @@ -1999,7 +2095,7 @@ lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) { LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) { unsigned kind = LLVMGetEnumAttributeKindForName(name, gb_strlen(name)); - GB_ASSERT(kind != 0); + GB_ASSERT_MSG(kind != 0, "unknown attribute: %s", name); return LLVMCreateEnumAttribute(ctx, kind, value); } @@ -2076,12 +2172,26 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { p->value = LLVMAddFunction(m->mod, c_link_name, func_type); - lbCallingConventionKind cc_kind = lbCallingConvention_C; - // TODO(bill): Clean up this logic - if (build_context.metrics.os != TargetOs_js) { - cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; + lbFunctionType **ft_found = map_get(&m->function_type_map, hash_type(p->type)); + if (USE_LLVM_ABI && ft_found) { + lbFunctionType *abi_ft = *ft_found; + p->abi_function_type = abi_ft; + lb_add_function_type_attributes(p->value, abi_ft, abi_ft->calling_convention); + } else { + lbCallingConventionKind cc_kind = lbCallingConvention_C; + // TODO(bill): Clean up this logic + if (build_context.metrics.os != TargetOs_js) { + cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; + } + LLVMSetFunctionCallConv(p->value, cc_kind); } - LLVMSetFunctionCallConv(p->value, cc_kind); + + // lbCallingConventionKind cc_kind = lbCallingConvention_C; + // // TODO(bill): Clean up this logic + // if (build_context.metrics.os != TargetOs_js) { + // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; + // } + // LLVMSetFunctionCallConv(p->value, cc_kind); lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); @@ -2116,8 +2226,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { // NOTE(bill): offset==0 is the return value isize offset = 1; if (pt->Proc.return_by_pointer) { - lb_add_proc_attribute_at_index(p, 1, "sret"); - lb_add_proc_attribute_at_index(p, 1, "noalias"); + if (!USE_LLVM_ABI) { + lb_add_proc_attribute_at_index(p, 1, "sret"); + lb_add_proc_attribute_at_index(p, 1, "noalias"); + } offset = 2; } @@ -2150,7 +2262,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { } } - if (pt->Proc.calling_convention == ProcCC_Odin) { + if (!USE_LLVM_ABI && pt->Proc.calling_convention == ProcCC_Odin) { lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias"); lb_add_proc_attribute_at_index(p, offset+parameter_index, "nonnull"); lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture"); @@ -2242,7 +2354,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type } isize parameter_index = 0; - if (pt->Proc.param_count) { + if (!USE_LLVM_ABI && pt->Proc.param_count) { TypeTuple *params = &pt->Proc.params->Tuple; for (isize i = 0; i < pt->Proc.param_count; i++) { Entity *e = params->variables[i]; @@ -2397,6 +2509,47 @@ void lb_start_block(lbProcedure *p, lbBlock *b) { p->curr_block = b; } +LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRef dst_type) { + LLVMTypeRef src_type = LLVMTypeOf(val); + GB_ASSERT(lb_sizeof(src_type) == lb_sizeof(dst_type)); + + LLVMTypeKind src_kind = LLVMGetTypeKind(src_type); + LLVMTypeKind dst_kind = LLVMGetTypeKind(dst_type); + if (src_kind == dst_kind) { + if (src_kind == LLVMPointerTypeKind) { + return LLVMBuildPointerCast(p->builder, val, dst_type, ""); + } else if (src_kind != LLVMStructTypeKind) { + return LLVMBuildBitCast(p->builder, val, dst_type, ""); + } + } else { + if (src_kind == LLVMPointerTypeKind && dst_kind == LLVMIntegerTypeKind) { + return LLVMBuildPtrToInt(p->builder, val, dst_type, ""); + } else if (src_kind == LLVMIntegerTypeKind && dst_kind == LLVMPointerTypeKind) { + return LLVMBuildIntToPtr(p->builder, val, dst_type, ""); + } + } + + if (LLVMIsALoadInst(val)) { + LLVMValueRef val_ptr = LLVMGetOperand(val, 0); + val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), ""); + return LLVMBuildLoad(p->builder, val_ptr, ""); + } else { + GB_ASSERT(p->decl_block != p->curr_block); + LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); + + LLVMValueRef ptr = LLVMBuildAlloca(p->builder, dst_type, ""); + LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type)); + max_align = gb_max(max_align, 4); + LLVMSetAlignment(ptr, cast(unsigned)max_align); + + LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); + LLVMBuildStore(p->builder, val, nptr); + + return LLVMBuildLoad(p->builder, ptr, ""); + } +} + void lb_begin_procedure_body(lbProcedure *p) { DeclInfo *decl = decl_info_of_entity(p->entity); @@ -2429,82 +2582,190 @@ void lb_begin_procedure_body(lbProcedure *p) { GB_ASSERT(p->type != nullptr); - i32 parameter_index = 0; + if (p->abi_function_type) { + lbFunctionType *ft = p->abi_function_type; + unsigned param_offset = 0; - lbValue return_ptr_value = {}; - if (p->type->Proc.return_by_pointer) { - // NOTE(bill): this must be parameter 0 - Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); - e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; + lbValue return_ptr_value = {}; + if (ft->ret.kind == lbArg_Indirect) { + // NOTE(bill): this must be parameter 0 + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); + Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; - return_ptr_value.value = LLVMGetParam(p->value, 0); - return_ptr_value.type = ptr_type; - p->return_ptr = lb_addr(return_ptr_value); + return_ptr_value.value = LLVMGetParam(p->value, 0); + return_ptr_value.type = ptr_type; + p->return_ptr = lb_addr(return_ptr_value); - lb_add_entity(p->module, e, return_ptr_value); + lb_add_entity(p->module, e, return_ptr_value); - parameter_index += 1; - } - - if (p->type->Proc.params != nullptr) { - TypeTuple *params = &p->type->Proc.params->Tuple; - auto abi_types = p->type->Proc.abi_compat_params; - - for_array(i, params->variables) { - Entity *e = params->variables[i]; - if (e->kind != Entity_Variable) { - continue; - } - Type *abi_type = e->type; - if (abi_types.count > 0) { - abi_type = abi_types[i]; - } - if (e->token.string != "") { - lb_add_param(p, e, nullptr, abi_type, parameter_index); - } - if (is_type_tuple(abi_type)) { - parameter_index += cast(i32)abi_type->Tuple.variables.count; - } else { - parameter_index += 1; - } + param_offset += 1; } - } + if (p->type->Proc.params != nullptr) { + TypeTuple *params = &p->type->Proc.params->Tuple; - if (p->type->Proc.has_named_results) { - GB_ASSERT(p->type->Proc.result_count > 0); - TypeTuple *results = &p->type->Proc.results->Tuple; + unsigned param_index = 0; + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } - for_array(i, results->variables) { - Entity *e = results->variables[i]; - GB_ASSERT(e->kind == Entity_Variable); + lbArgType *arg_type = &ft->args[param_index]; + if (arg_type->kind == lbArg_Ignore) { + continue; + } else if (arg_type->kind == lbArg_Direct) { + lbParamPasskind kind = lbParamPass_Value; + LLVMTypeRef param_type = lb_type(p->module, e->type); + if (param_type != arg_type->type) { + kind = lbParamPass_BitCast; + } + LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index); - if (e->token.string != "") { - GB_ASSERT(!is_blank_ident(e->token)); - - lbAddr res = {}; - if (p->type->Proc.return_by_pointer) { - lbValue ptr = return_ptr_value; - if (results->variables.count != 1) { - ptr = lb_emit_struct_ep(p, ptr, cast(i32)i); + if (USE_LLVM_ABI && LLVMTypeOf(value) == LLVMInt1TypeInContext(p->module->ctx)) { + value = LLVMBuildZExtOrBitCast(p->builder, value, param_type, ""); + } else { + value = OdinLLVMBuildTransmute(p, value, param_type); } - res = lb_addr(ptr); - lb_add_entity(p->module, e, ptr); - } else { - res = lb_add_local(p, e->type, e); - } + lbValue param = {}; + param.value = value; + param.type = e->type; + array_add(&p->params, param); - 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); + if (e->token.string.len != 0) { + lbAddr l = lb_add_local(p, e->type, e, false, param_index); + lb_addr_store(p, l, param); + } + + param_index += 1; + } else if (arg_type->kind == lbArg_Indirect) { + LLVMValueRef value_ptr = LLVMGetParam(p->value, param_offset+param_index); + LLVMValueRef value = LLVMBuildLoad(p->builder, value_ptr, ""); + + lbValue param = {}; + param.value = value; + param.type = e->type; + array_add(&p->params, param); + + lbValue ptr = {}; + ptr.value = value_ptr; + ptr.type = alloc_type_pointer(e->type); + + lb_add_entity(p->module, e, ptr); + param_index += 1; + } + } + } + + if (p->type->Proc.has_named_results) { + GB_ASSERT(p->type->Proc.result_count > 0); + TypeTuple *results = &p->type->Proc.results->Tuple; + + for_array(i, results->variables) { + Entity *e = results->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + + if (e->token.string != "") { + GB_ASSERT(!is_blank_ident(e->token)); + + lbAddr res = {}; + if (return_ptr_value.value) { + 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); + } else { + 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); + } + } + } + } + } else { + i32 parameter_index = 0; + lbValue return_ptr_value = {}; + if (p->type->Proc.return_by_pointer) { + // NOTE(bill): this must be parameter 0 + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); + Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; + + return_ptr_value.value = LLVMGetParam(p->value, 0); + return_ptr_value.type = ptr_type; + p->return_ptr = lb_addr(return_ptr_value); + + lb_add_entity(p->module, e, return_ptr_value); + + parameter_index += 1; + } + + if (p->type->Proc.params != nullptr) { + TypeTuple *params = &p->type->Proc.params->Tuple; + auto abi_types = p->type->Proc.abi_compat_params; + + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + Type *abi_type = e->type; + if (abi_types.count > 0) { + abi_type = abi_types[i]; + } + if (e->token.string != "") { + lb_add_param(p, e, nullptr, abi_type, parameter_index); + } + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; + } + } + } + + + if (p->type->Proc.has_named_results) { + GB_ASSERT(p->type->Proc.result_count > 0); + TypeTuple *results = &p->type->Proc.results->Tuple; + + for_array(i, results->variables) { + Entity *e = results->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + + if (e->token.string != "") { + GB_ASSERT(!is_blank_ident(e->token)); + + lbAddr res = {}; + if (p->type->Proc.return_by_pointer) { + 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); + } else { + 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); + } } } } } - if (p->type->Proc.calling_convention == ProcCC_Odin) { Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; @@ -6997,15 +7258,33 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, if (context_ptr.addr.value != nullptr) { args[arg_index++] = context_ptr.addr.value; } + LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder); + GB_ASSERT(curr_block != p->decl_block->block); - LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder); - GB_ASSERT(curr_block != p->decl_block->block); + if (USE_LLVM_ABI) { - LLVMValueRef ret = LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(p->module, value.type)), value.value, args, arg_count, "");; - lbValue res = {}; - res.value = ret; - res.type = abi_rt; - return res; + LLVMTypeRef ftp = lb_type(p->module, value.type); + LLVMTypeRef ft = LLVMGetElementType(ftp); + LLVMValueRef fn = value.value; + if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) { + fn = LLVMBuildPointerCast(p->builder, fn, ftp, ""); + } + LLVMTypeRef fnp = LLVMGetElementType(LLVMTypeOf(fn)); + GB_ASSERT_MSG(lb_is_type_kind(fnp, LLVMFunctionTypeKind), "%s", LLVMPrintTypeToString(fnp)); + + LLVMValueRef ret = LLVMBuildCall2(p->builder, ft, fn, args, arg_count, "");; + lbValue res = {}; + res.value = ret; + res.type = abi_rt; + return res; + } else { + + LLVMValueRef ret = LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(p->module, value.type)), value.value, args, arg_count, "");; + lbValue res = {}; + res.value = ret; + res.type = abi_rt; + return res; + } } lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array const &args) { @@ -7061,95 +7340,186 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count); } - auto processed_args = array_make(heap_allocator(), 0, args.count); - - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->Proc.params->Tuple.variables[i]; - if (e->kind != Entity_Variable) { - // array_add(&processed_args, args[i]); - continue; - } - GB_ASSERT(e->flags & EntityFlag_Param); - - Type *original_type = e->type; - Type *new_type = pt->Proc.abi_compat_params[i]; - Type *arg_type = args[i].type; - - if (are_types_identical(arg_type, new_type)) { - // NOTE(bill): Done - array_add(&processed_args, args[i]); - } else if (!are_types_identical(original_type, new_type)) { - if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { - Type *av = core_type(type_deref(new_type)); - if (are_types_identical(av, core_type(original_type))) { - if (e->flags&EntityFlag_ImplicitReference) { - array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i])); - } else if (!is_type_pointer(arg_type)) { - array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16)); - } - } else { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } - } else if (new_type == t_llvm_bool) { - array_add(&processed_args, lb_emit_conv(p, args[i], new_type)); - } else if (is_type_integer(new_type) || is_type_float(new_type) || is_type_boolean(new_type)) { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } else if (is_type_simd_vector(new_type)) { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } else if (is_type_tuple(new_type)) { - Type *abi_type = pt->Proc.abi_compat_params[i]; - Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); - lbValue x = {}; - i64 st_sz = type_size_of(st); - i64 arg_sz = type_size_of(args[i].type); - if (st_sz == arg_sz) { - x = lb_emit_transmute(p, args[i], st); - } else { - // NOTE(bill): struct{f32, f32, f32} != struct{#simd[2]f32, f32} - GB_ASSERT(st_sz > arg_sz); - lbAddr xx = lb_add_local_generated(p, st, false); - lbValue pp = lb_emit_conv(p, xx.addr, alloc_type_pointer(args[i].type)); - lb_emit_store(p, pp, args[i]); - x = lb_addr_load(p, xx); - } - for (isize j = 0; j < new_type->Tuple.variables.count; j++) { - lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j); - array_add(&processed_args, xx); - } - } - } else { - lbValue x = lb_emit_conv(p, args[i], new_type); - array_add(&processed_args, x); - } - } - - if (inlining == ProcInlining_none) { - inlining = p->inlining; - } - lbValue result = {}; - Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type); - Type *rt = reduce_tuple_to_single_type(results); - if (pt->Proc.return_by_pointer) { - lbValue return_ptr = {}; - if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { - if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { - return_ptr = p->return_ptr_hint_value; - p->return_ptr_hint_used = true; + auto processed_args = array_make(heap_allocator(), 0, args.count); + + if (USE_LLVM_ABI) { + lbFunctionType **ft_found = nullptr; + ft_found = map_get(&m->function_type_map, hash_type(pt)); + if (!ft_found) { + LLVMTypeRef llvm_proc_type = lb_type(p->module, pt); + ft_found = map_get(&m->function_type_map, hash_type(pt)); + } + GB_ASSERT(ft_found != nullptr); + + lbFunctionType *abi_ft = *ft_found; + bool return_by_pointer = abi_ft->ret.kind == lbArg_Indirect; + + unsigned param_index = 0; + for (isize i = 0; i < param_count; i++) { + Entity *e = pt->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + GB_ASSERT(e->flags & EntityFlag_Param); + + Type *original_type = e->type; + lbArgType *arg = &abi_ft->args[param_index]; + if (arg->kind == lbArg_Ignore) { + continue; + } + + lbValue x = lb_emit_conv(p, args[i], original_type); + LLVMTypeRef xt = lb_type(p->module, x.type); + + if (arg->kind == lbArg_Direct) { + LLVMTypeRef abi_type = arg->cast_type; + if (!abi_type) { + abi_type = arg->type; + } + if (xt == abi_type) { + array_add(&processed_args, x); + } else { + Type *at = lb_abi_to_odin_type(abi_type); + if (at == t_llvm_bool) { + x = lb_emit_conv(p, x, at); + } else { + x = lb_emit_transmute(p, x, at); + } + array_add(&processed_args, x); + } + + } else if (arg->kind == lbArg_Indirect) { + // lbValue ptr = lb_copy_value_to_ptr(p, x, original_type, 16); + lbValue ptr = lb_address_from_load_or_generate_local(p, x); + array_add(&processed_args, ptr); + } + + param_index += 1; + } + + if (inlining == ProcInlining_none) { + inlining = p->inlining; + } + + Type *rt = reduce_tuple_to_single_type(results); + if (return_by_pointer) { + + lbValue return_ptr = {}; + // if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { + // if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { + // return_ptr = p->return_ptr_hint_value; + // p->return_ptr_hint_used = true; + // } + // } + // if (return_ptr.value == nullptr) { + lbAddr r = lb_add_local_generated(p, rt, true); + return_ptr = r.addr; + // } + GB_ASSERT(is_type_pointer(return_ptr.type)); + lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); + result = lb_emit_load(p, return_ptr); + } else { + LLVMTypeRef ret_type = abi_ft->ret.cast_type; + if (!ret_type) { + ret_type = abi_ft->ret.type; + } + Type *abi_rt = lb_abi_to_odin_type(ret_type); + result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); + if (abi_rt != rt) { + result = lb_emit_transmute(p, result, rt); } } - if (return_ptr.value == nullptr) { - lbAddr r = lb_add_local_generated(p, rt, true); - return_ptr = r.addr; - } - GB_ASSERT(is_type_pointer(return_ptr.type)); - lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); - result = lb_emit_load(p, return_ptr); + } else { - result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); - if (abi_rt != rt) { - result = lb_emit_transmute(p, result, rt); + for (isize i = 0; i < param_count; i++) { + Entity *e = pt->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + // array_add(&processed_args, args[i]); + continue; + } + GB_ASSERT(e->flags & EntityFlag_Param); + + Type *original_type = e->type; + Type *new_type = pt->Proc.abi_compat_params[i]; + Type *arg_type = args[i].type; + + if (are_types_identical(arg_type, new_type)) { + // NOTE(bill): Done + array_add(&processed_args, args[i]); + } else if (!are_types_identical(original_type, new_type)) { + if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { + Type *av = core_type(type_deref(new_type)); + if (are_types_identical(av, core_type(original_type))) { + if (e->flags&EntityFlag_ImplicitReference) { + array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i])); + } else if (!is_type_pointer(arg_type)) { + array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16)); + } + } else { + array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); + } + } else if (new_type == t_llvm_bool) { + array_add(&processed_args, lb_emit_conv(p, args[i], new_type)); + } else if (is_type_integer(new_type) || is_type_float(new_type) || is_type_boolean(new_type)) { + array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); + } else if (is_type_simd_vector(new_type)) { + array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); + } else if (is_type_tuple(new_type)) { + Type *abi_type = pt->Proc.abi_compat_params[i]; + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + lbValue x = {}; + i64 st_sz = type_size_of(st); + i64 arg_sz = type_size_of(args[i].type); + if (st_sz == arg_sz) { + x = lb_emit_transmute(p, args[i], st); + } else { + // NOTE(bill): struct{f32, f32, f32} != struct{#simd[2]f32, f32} + GB_ASSERT(st_sz > arg_sz); + lbAddr xx = lb_add_local_generated(p, st, false); + lbValue pp = lb_emit_conv(p, xx.addr, alloc_type_pointer(args[i].type)); + lb_emit_store(p, pp, args[i]); + x = lb_addr_load(p, xx); + } + for (isize j = 0; j < new_type->Tuple.variables.count; j++) { + lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j); + array_add(&processed_args, xx); + } + } + } else { + lbValue x = lb_emit_conv(p, args[i], new_type); + array_add(&processed_args, x); + } + } + + if (inlining == ProcInlining_none) { + inlining = p->inlining; + } + + + Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type); + Type *rt = reduce_tuple_to_single_type(results); + if (pt->Proc.return_by_pointer) { + lbValue return_ptr = {}; + if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { + if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { + return_ptr = p->return_ptr_hint_value; + p->return_ptr_hint_used = true; + } + } + if (return_ptr.value == nullptr) { + lbAddr r = lb_add_local_generated(p, rt, true); + return_ptr = r.addr; + } + GB_ASSERT(is_type_pointer(return_ptr.type)); + lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); + result = lb_emit_load(p, return_ptr); + } else { + result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); + if (abi_rt != rt) { + result = lb_emit_transmute(p, result, rt); + } } } @@ -10969,6 +11339,7 @@ void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->procedures, a); string_map_init(&m->const_strings, a); map_init(&m->anonymous_proc_lits, a); + map_init(&m->function_type_map, a); array_init(&m->procedures_to_generate, a); array_init(&m->foreign_library_paths, a); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4a0816e62..fc5968c93 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -74,6 +74,7 @@ struct lbModule { gbMutex mutex; Map types; // Key: Type * + i32 internal_type_level; Map values; // Key: Entity * StringMap members; @@ -83,6 +84,7 @@ struct lbModule { StringMap const_strings; Map anonymous_proc_lits; // Key: Ast * + Map function_type_map; // Key: Type * u32 global_array_index; u32 global_generated_index; @@ -199,6 +201,7 @@ struct lbProcedure { bool is_entry_point; bool is_startup; + lbFunctionType *abi_function_type; LLVMValueRef value; LLVMBuilderRef builder; diff --git a/src/types.cpp b/src/types.cpp index acc1c7b2e..528ea3a2d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -886,6 +886,16 @@ Type *alloc_type_named(String name, Type *base, Entity *type_name) { return t; } +bool is_calling_convention_none(ProcCallingConvention calling_convention) { + switch (calling_convention) { + case ProcCC_None: + case ProcCC_PureNone: + case ProcCC_InlineAsm: + return true; + } + return false; +} + Type *alloc_type_tuple() { Type *t = alloc_type(Type_Tuple); return t; From a6c5c203abdeee683558543fdfe39ece59e17c9c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 12 Nov 2020 01:21:09 +0000 Subject: [PATCH 039/170] Begin work on Sys V for new ABI system --- src/llvm_abi.cpp | 182 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 164 insertions(+), 18 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 2bede5822..1fa004c8c 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -34,6 +34,10 @@ struct lbFunctionType { lbArgType ret; }; +i64 llvm_align_formula(i64 off, i64 a) { + return (off + a - 1) / a * a; +} + bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) { return LLVMGetTypeKind(type) == kind; @@ -165,11 +169,11 @@ i64 lb_sizeof(LLVMTypeRef type) { for (unsigned i = 0; i < field_count; i++) { LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); i64 align = lb_alignof(field); - offset = align_formula(offset, align); + offset = llvm_align_formula(offset, align); offset += lb_sizeof(field); } } - offset = align_formula(offset, lb_alignof(type)); + offset = llvm_align_formula(offset, lb_alignof(type)); return offset; } break; @@ -177,7 +181,7 @@ i64 lb_sizeof(LLVMTypeRef type) { { LLVMTypeRef elem = LLVMGetElementType(type); i64 elem_size = lb_sizeof(elem); - i64 count = LLVMGetVectorSize(type); + i64 count = LLVMGetArrayLength(type); i64 size = count * elem_size; return size; } @@ -239,7 +243,7 @@ i64 lb_alignof(LLVMTypeRef type) { { LLVMTypeRef elem = LLVMGetElementType(type); i64 elem_size = lb_sizeof(elem); - i64 count = LLVMGetVectorSize(type); + i64 count = LLVMGetArrayLength(type); i64 size = count * elem_size; return size; } @@ -300,7 +304,7 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type) { case LLVMArrayTypeKind: { - i64 count = LLVMGetVectorSize(type); + i64 count = LLVMGetArrayLength(type); Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type)); return alloc_type_array(elem, count); } @@ -451,7 +455,10 @@ namespace lbAbiAmd64SysV { RegClass_SSEFv, RegClass_SSEDs, RegClass_SSEDv, - RegClass_SSEInt, + RegClass_SSEInt8, + RegClass_SSEInt16, + RegClass_SSEInt32, + RegClass_SSEInt64, RegClass_SSEUp, RegClass_X87, RegClass_X87Up, @@ -475,21 +482,90 @@ namespace lbAbiAmd64SysV { } } + enum Amd64TypeAttributeKind { + Amd64TypeAttribute_None, + Amd64TypeAttribute_ByVal, + Amd64TypeAttribute_StructRect, + }; + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off); void fixup(LLVMTypeRef t, Array *cls); + lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind); + Array classify(LLVMTypeRef t); + LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes); LB_ABI_INFO(abi_info) { lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); ft->ctx = c; - // TODO(bill): THIS IS VERY VERY WRONG! - ft->args = compute_arg_types(c, arg_types, arg_count); - ft->ret = compute_return_type(c, return_type, return_is_defined); ft->calling_convention = calling_convention; + + ft->args = array_make(heap_allocator(), arg_count); + for (unsigned i = 0; i < arg_count; i++) { + ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal); + } + + if (return_is_defined) { + ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect); + } else { + ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } + return ft; } + bool is_mem_cls(Array const &cls, Amd64TypeAttributeKind attribute_kind) { + if (attribute_kind == Amd64TypeAttribute_ByVal) { + if (cls.count == 0) { + return false; + } + auto first = cls[0]; + return first == RegClass_Memory || first == RegClass_X87 || first == RegClass_ComplexX87; + } else if (attribute_kind == Amd64TypeAttribute_StructRect) { + if (cls.count == 0) { + return false; + } + return cls[0] == RegClass_Memory; + } + return false; + } + + bool is_register(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMIntegerTypeKind: + case LLVMFloatTypeKind: + case LLVMDoubleTypeKind: + case LLVMPointerTypeKind: + return true; + } + return false; + } + + lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind) { + if (is_register(type)) { + LLVMAttributeRef attribute = nullptr; + if (type == LLVMInt1TypeInContext(c)) { + attribute = lb_create_enum_attribute(c, "zext", true); + } + return lb_arg_type_direct(type, nullptr, nullptr, attribute); + } + + auto cls = classify(type); + if (is_mem_cls(cls, attribute_kind)) { + LLVMAttributeRef attribute = nullptr; + if (attribute_kind == Amd64TypeAttribute_ByVal) { + attribute = lb_create_enum_attribute(c, "byval", true); + } else if (attribute_kind == Amd64TypeAttribute_StructRect) { + attribute = lb_create_enum_attribute(c, "sret", true); + } + return lb_arg_type_indirect(type, attribute); + } else { + return lb_arg_type_direct(type, llreg(c, cls), nullptr, nullptr); + } + } + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) { LLVMAttributeRef attr = nullptr; LLVMTypeRef i1 = LLVMInt1TypeInContext(c); @@ -517,7 +593,7 @@ namespace lbAbiAmd64SysV { for (unsigned i = 0; i < field_count; i++) { LLVMTypeRef t = fields[i]; if (!packed) { - field_off = align_formula(field_off, lb_alignof(t)); + field_off = llvm_align_formula(field_off, lb_alignof(t)); } classify_with(t, cls, i, field_off); field_off += lb_sizeof(t); @@ -601,7 +677,7 @@ namespace lbAbiAmd64SysV { } - LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes) {; + LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes) { auto types = array_make(heap_allocator(), 0, reg_classes.count); for_array(i, reg_classes) { switch (reg_classes[i]) { @@ -609,9 +685,43 @@ namespace lbAbiAmd64SysV { array_add(&types, LLVMIntTypeInContext(c, 64)); break; case RegClass_SSEFv: + case RegClass_SSEDv: + case RegClass_SSEInt8: + case RegClass_SSEInt16: + case RegClass_SSEInt32: + case RegClass_SSEInt64: { + unsigned elems_per_word = 0; + LLVMTypeRef elem_type = nullptr; + switch (reg_classes[i]) { + case RegClass_SSEFv: + elems_per_word = 2; + elem_type = LLVMFloatTypeInContext(c); + break; + case RegClass_SSEDv: + elems_per_word = 1; + elem_type = LLVMDoubleTypeInContext(c); + break; + case RegClass_SSEInt8: + elems_per_word = 64/8; + elem_type = LLVMIntTypeInContext(c, 8); + break; + case RegClass_SSEInt16: + elems_per_word = 64/16; + elem_type = LLVMIntTypeInContext(c, 16); + break; + case RegClass_SSEInt32: + elems_per_word = 64/32; + elem_type = LLVMIntTypeInContext(c, 32); + break; + case RegClass_SSEInt64: + elems_per_word = 64/64; + elem_type = LLVMIntTypeInContext(c, 64); + break; + } + unsigned vec_len = llvec_len(array_slice(reg_classes, i+1, reg_classes.count)); - LLVMTypeRef vec_type = LLVMVectorType(LLVMFloatTypeInContext(c), vec_len); + LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word); array_add(&types, vec_type); i += vec_len; continue; @@ -635,11 +745,11 @@ namespace lbAbiAmd64SysV { i64 t_align = lb_alignof(t); i64 t_size = lb_sizeof(t); - i64 mis_align = off % t_align; - if (mis_align != 0) { + i64 misalign = off % t_align; + if (misalign != 0) { i64 e = (off + t_size + 7) / 8; for (i64 i = off / 8; i < e; i++) { - unify(cls, ix+1, RegClass_Memory); + unify(cls, ix+i, RegClass_Memory); } return; } @@ -647,13 +757,13 @@ namespace lbAbiAmd64SysV { switch (LLVMGetTypeKind(t)) { case LLVMIntegerTypeKind: case LLVMPointerTypeKind: - unify(cls, ix+off / 8, RegClass_Int); + unify(cls, ix + off/8, RegClass_Int); break; case LLVMFloatTypeKind: - unify(cls, ix+off / 8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); + unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); break; case LLVMDoubleTypeKind: - unify(cls, ix+off / 8, RegClass_SSEDs); + unify(cls, ix + off/8, RegClass_SSEDs); break; case LLVMStructTypeKind: { @@ -676,6 +786,42 @@ namespace lbAbiAmd64SysV { } } break; + case LLVMVectorTypeKind: + { + i64 len = LLVMGetVectorSize(t); + LLVMTypeRef elem = LLVMGetElementType(t); + i64 elem_sz = lb_sizeof(elem); + LLVMTypeKind elem_kind = LLVMGetTypeKind(elem); + RegClass reg = RegClass_NoClass; + switch (elem_kind) { + case LLVMIntegerTypeKind: + switch (LLVMGetIntTypeWidth(elem)) { + case 8: reg = RegClass_SSEInt8; + case 16: reg = RegClass_SSEInt16; + case 32: reg = RegClass_SSEInt32; + case 64: reg = RegClass_SSEInt64; + default: + GB_PANIC("Unhandled integer width for vector type"); + } + break; + case LLVMFloatTypeKind: + reg = RegClass_SSEFv; + break; + case LLVMDoubleTypeKind: + reg = RegClass_SSEDv; + break; + default: + GB_PANIC("Unhandled vector element type"); + } + + for (i64 i = 0; i < len; i++) { + unify(cls, ix + (off + i*elem_sz)/8, reg); + // NOTE(bill): Everything after the first one is the upper + // half of a register + reg = RegClass_SSEUp; + } + } + break; default: GB_PANIC("Unhandled type"); break; From 78b6948ff2e5fed0ef3e279b23686d1ac6c4d797 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 12 Nov 2020 14:17:41 +0000 Subject: [PATCH 040/170] Reorganize package strings --- core/runtime/internal.odin | 30 ++--- core/strings/conversion.odin | 254 ++++++++++++++++++++++++++++++++++ core/strings/strings.odin | 255 ----------------------------------- 3 files changed, 269 insertions(+), 270 deletions(-) create mode 100644 core/strings/conversion.odin diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 8662d045f..cd0c681b3 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -93,18 +93,18 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { when ODIN_USE_LLVM_API { when size_of(rawptr) == 8 { @(link_name="llvm.memmove.p0i8.p0i8.i64") - llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } else { @(link_name="llvm.memmove.p0i8.p0i8.i32") - llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } } else { when size_of(rawptr) == 8 { @(link_name="llvm.memmove.p0i8.p0i8.i64") - llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } else { @(link_name="llvm.memmove.p0i8.p0i8.i32") - llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } } } @@ -121,18 +121,18 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r when ODIN_USE_LLVM_API { when size_of(rawptr) == 8 { @(link_name="llvm.memcpy.p0i8.p0i8.i64") - llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } else { @(link_name="llvm.memcpy.p0i8.p0i8.i32") - llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } } else { when size_of(rawptr) == 8 { @(link_name="llvm.memcpy.p0i8.p0i8.i64") - llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } else { @(link_name="llvm.memcpy.p0i8.p0i8.i32") - llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } } } @@ -389,45 +389,45 @@ string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) { return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4; } -@(default_calling_convention = "c") +@(default_calling_convention = "none") foreign { @(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 --- @(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 --- } abs_f32 :: inline proc "contextless" (x: f32) -> f32 { foreign { - @(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 --- + @(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 --- } return _abs(x); } abs_f64 :: inline proc "contextless" (x: f64) -> f64 { foreign { - @(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 --- + @(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 --- } return _abs(x); } min_f32 :: proc(a, b: f32) -> f32 { foreign { - @(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 --- + @(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 --- } return _min(a, b); } min_f64 :: proc(a, b: f64) -> f64 { foreign { - @(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 --- + @(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 --- } return _min(a, b); } max_f32 :: proc(a, b: f32) -> f32 { foreign { - @(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 --- + @(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 --- } return _max(a, b); } max_f64 :: proc(a, b: f64) -> f64 { foreign { - @(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 --- + @(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 --- } return _max(a, b); } diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin new file mode 100644 index 000000000..44f501246 --- /dev/null +++ b/core/strings/conversion.odin @@ -0,0 +1,254 @@ +package strings + +to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string { + if len(s) == 0 { + return ""; + } + + b := make_builder_len_cap(0, 0, allocator); + + s := s; + for c, i in s { + if c != utf8.RUNE_ERROR { + continue; + } + + _, w := utf8.decode_rune_in_string(s[i:]); + if w == 1 { + grow_builder(&b, len(s) + len(replacement)); + write_string(&b, s[:i]); + s = s[i:]; + break; + } + } + + if builder_cap(b) == 0 { + return clone(s, allocator); + } + + invalid := false; + + for i := 0; i < len(s); /**/ { + c := s[i]; + if c < utf8.RUNE_SELF { + i += 1; + invalid = false; + write_byte(&b, c); + continue; + } + + _, w := utf8.decode_rune_in_string(s[i:]); + if w == 1 { + i += 1; + if !invalid { + invalid = true; + write_string(&b, replacement); + } + continue; + } + invalid = false; + write_string(&b, s[i:][:w]); + i += w; + } + return to_string(b); +} + +to_lower :: proc(s: string, allocator := context.allocator) -> string { + b := make_builder(0, len(s), allocator); + for r in s { + write_rune(&b, unicode.to_lower(r)); + } + return to_string(b); +} +to_upper :: proc(s: string, allocator := context.allocator) -> string { + b := make_builder(0, len(s), allocator); + for r in s { + write_rune(&b, unicode.to_upper(r)); + } + return to_string(b); +} + + + + +is_delimiter :: proc(c: rune) -> bool { + return c == '-' || c == '_' || is_space(c); +} + +is_separator :: proc(r: rune) -> bool { + if r <= 0x7f { + switch r { + case '0'..'9': return false; + case 'a'..'z': return false; + case 'A'..'Z': return false; + case '_': return false; + } + return true; + } + + // TODO(bill): unicode categories + // if unicode.is_letter(r) || unicode.is_digit(r) { + // return false; + // } + + return unicode.is_space(r); +} + + +string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, prev, curr, next: rune)) { + prev, curr: rune; + for next in s { + if curr == 0 { + prev = curr; + curr = next; + continue; + } + + callback(b, prev, curr, next); + + prev = curr; + curr = next; + } + + if len(s) > 0 { + callback(b, prev, curr, 0); + } +} + + +to_lower_camel_case :: to_camel_case; +to_camel_case :: proc(s: string, allocator := context.allocator) -> string { + s := s; + s = trim_space(s); + b := make_builder(0, len(s), allocator); + + string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { + if !is_delimiter(curr) { + if is_delimiter(prev) { + write_rune(b, unicode.to_upper(curr)); + } else if unicode.is_lower(prev) { + write_rune(b, curr); + } else { + write_rune(b, unicode.to_lower(curr)); + } + } + }); + + return to_string(b); +} + +to_upper_camel_case :: to_pascal_case; +to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { + s := s; + s = trim_space(s); + b := make_builder(0, len(s), allocator); + + string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { + if !is_delimiter(curr) { + if is_delimiter(prev) || prev == 0 { + write_rune(b, unicode.to_upper(curr)); + } else if unicode.is_lower(prev) { + write_rune(b, curr); + } else { + write_rune(b, unicode.to_lower(curr)); + } + } + }); + + return to_string(b); +} + +to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string { + s := s; + s = trim_space(s); + b := make_builder(0, len(s), allocator); + + adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower; + + prev, curr: rune; + + for next in s { + if is_delimiter(curr) { + if !is_delimiter(prev) { + write_rune(&b, delimiter); + } + } else if unicode.is_upper(curr) { + if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) { + write_rune(&b, delimiter); + } + write_rune(&b, adjust_case(curr)); + } else if curr != 0 { + write_rune(&b, adjust_case(curr)); + } + + prev = curr; + curr = next; + } + + if len(s) > 0 { + if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 { + write_rune(&b, delimiter); + } + write_rune(&b, adjust_case(curr)); + } + + return to_string(b); +} + + +to_snake_case :: proc(s: string, allocator := context.allocator) -> string { + return to_delimiter_case(s, '_', false, allocator); +} + +to_screaming_snake_case :: to_upper_snake_case; +to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string { + return to_delimiter_case(s, '_', true, allocator); +} + +to_kebab_case :: proc(s: string, allocator := context.allocator) -> string { + return to_delimiter_case(s, '-', false, allocator); +} + +to_upper_case :: proc(s: string, allocator := context.allocator) -> string { + return to_delimiter_case(s, '-', true, allocator); +} + +to_ada_case :: proc(s: string, allocator := context.allocator) -> string { + delimiter :: '_'; + + s := s; + s = trim_space(s); + b := make_builder(0, len(s), allocator); + + prev, curr: rune; + + for next in s { + if is_delimiter(curr) { + if !is_delimiter(prev) { + write_rune(&b, delimiter); + } + } else if unicode.is_upper(curr) { + if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) { + write_rune(&b, delimiter); + } + write_rune(&b, unicode.to_upper(curr)); + } else if curr != 0 { + write_rune(&b, unicode.to_lower(curr)); + } + + prev = curr; + curr = next; + } + + if len(s) > 0 { + if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 { + write_rune(&b, delimiter); + write_rune(&b, unicode.to_upper(curr)); + } else { + write_rune(&b, unicode.to_lower(curr)); + } + } + + return to_string(b); +} + diff --git a/core/strings/strings.odin b/core/strings/strings.odin index c706f5940..47620ed54 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -787,207 +787,6 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> } -to_lower :: proc(s: string, allocator := context.allocator) -> string { - b := make_builder(0, len(s), allocator); - for r in s { - write_rune(&b, unicode.to_lower(r)); - } - return to_string(b); -} -to_upper :: proc(s: string, allocator := context.allocator) -> string { - b := make_builder(0, len(s), allocator); - for r in s { - write_rune(&b, unicode.to_upper(r)); - } - return to_string(b); -} - - - - -is_delimiter :: proc(c: rune) -> bool { - return c == '-' || c == '_' || is_space(c); -} - -is_separator :: proc(r: rune) -> bool { - if r <= 0x7f { - switch r { - case '0'..'9': return false; - case 'a'..'z': return false; - case 'A'..'Z': return false; - case '_': return false; - } - return true; - } - - // TODO(bill): unicode categories - // if unicode.is_letter(r) || unicode.is_digit(r) { - // return false; - // } - - return unicode.is_space(r); -} - - -string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, prev, curr, next: rune)) { - prev, curr: rune; - for next in s { - if curr == 0 { - prev = curr; - curr = next; - continue; - } - - callback(b, prev, curr, next); - - prev = curr; - curr = next; - } - - if len(s) > 0 { - callback(b, prev, curr, 0); - } -} - - -to_lower_camel_case :: to_camel_case; -to_camel_case :: proc(s: string, allocator := context.allocator) -> string { - s := s; - s = trim_space(s); - b := make_builder(0, len(s), allocator); - - string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { - if !is_delimiter(curr) { - if is_delimiter(prev) { - write_rune(b, unicode.to_upper(curr)); - } else if unicode.is_lower(prev) { - write_rune(b, curr); - } else { - write_rune(b, unicode.to_lower(curr)); - } - } - }); - - return to_string(b); -} - -to_upper_camel_case :: to_pascal_case; -to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { - s := s; - s = trim_space(s); - b := make_builder(0, len(s), allocator); - - string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { - if !is_delimiter(curr) { - if is_delimiter(prev) || prev == 0 { - write_rune(b, unicode.to_upper(curr)); - } else if unicode.is_lower(prev) { - write_rune(b, curr); - } else { - write_rune(b, unicode.to_lower(curr)); - } - } - }); - - return to_string(b); -} - -to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string { - s := s; - s = trim_space(s); - b := make_builder(0, len(s), allocator); - - adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower; - - prev, curr: rune; - - for next in s { - if is_delimiter(curr) { - if !is_delimiter(prev) { - write_rune(&b, delimiter); - } - } else if unicode.is_upper(curr) { - if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) { - write_rune(&b, delimiter); - } - write_rune(&b, adjust_case(curr)); - } else if curr != 0 { - write_rune(&b, adjust_case(curr)); - } - - prev = curr; - curr = next; - } - - if len(s) > 0 { - if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 { - write_rune(&b, delimiter); - } - write_rune(&b, adjust_case(curr)); - } - - return to_string(b); -} - - -to_snake_case :: proc(s: string, allocator := context.allocator) -> string { - return to_delimiter_case(s, '_', false, allocator); -} - -to_screaming_snake_case :: to_upper_snake_case; -to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string { - return to_delimiter_case(s, '_', true, allocator); -} - -to_kebab_case :: proc(s: string, allocator := context.allocator) -> string { - return to_delimiter_case(s, '-', false, allocator); -} - -to_upper_case :: proc(s: string, allocator := context.allocator) -> string { - return to_delimiter_case(s, '-', true, allocator); -} - -to_ada_case :: proc(s: string, allocator := context.allocator) -> string { - delimiter :: '_'; - - s := s; - s = trim_space(s); - b := make_builder(0, len(s), allocator); - - prev, curr: rune; - - for next in s { - if is_delimiter(curr) { - if !is_delimiter(prev) { - write_rune(&b, delimiter); - } - } else if unicode.is_upper(curr) { - if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) { - write_rune(&b, delimiter); - } - write_rune(&b, unicode.to_upper(curr)); - } else if curr != 0 { - write_rune(&b, unicode.to_lower(curr)); - } - - prev = curr; - curr = next; - } - - if len(s) > 0 { - if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 { - write_rune(&b, delimiter); - write_rune(&b, unicode.to_upper(curr)); - } else { - write_rune(&b, unicode.to_lower(curr)); - } - } - - return to_string(b); -} - - - reverse :: proc(s: string, allocator := context.allocator) -> string { str := s; n := len(str); @@ -1120,60 +919,6 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex -to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string { - if len(s) == 0 { - return ""; - } - - b := make_builder_len_cap(0, 0, allocator); - - s := s; - for c, i in s { - if c != utf8.RUNE_ERROR { - continue; - } - - _, w := utf8.decode_rune_in_string(s[i:]); - if w == 1 { - grow_builder(&b, len(s) + len(replacement)); - write_string(&b, s[:i]); - s = s[i:]; - break; - } - } - - if builder_cap(b) == 0 { - return clone(s, allocator); - } - - invalid := false; - - for i := 0; i < len(s); /**/ { - c := s[i]; - if c < utf8.RUNE_SELF { - i += 1; - invalid = false; - write_byte(&b, c); - continue; - } - - _, w := utf8.decode_rune_in_string(s[i:]); - if w == 1 { - i += 1; - if !invalid { - invalid = true; - write_string(&b, replacement); - } - continue; - } - invalid = false; - write_string(&b, s[i:][:w]); - i += w; - } - return to_string(b); -} - - @private write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) { From fa284f9a5a6d68d2887b759df324d98b33f41b9c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 12 Nov 2020 14:20:33 +0000 Subject: [PATCH 041/170] Fix import paths --- core/strings/conversion.odin | 3 +++ core/strings/strings.odin | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index 44f501246..674fd9206 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -1,5 +1,8 @@ package strings +import "core:unicode" +import "core:unicode/utf8" + to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string { if len(s) == 0 { return ""; diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 47620ed54..93f8fdc69 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1,7 +1,6 @@ package strings import "core:mem" -import "core:unicode" import "core:unicode/utf8" clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string { From a64ea342df4cffa881b5829b8864e22fb9e116ab Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 12 Nov 2020 23:40:13 +0000 Subject: [PATCH 042/170] Improve USE_NEW_LLVM_ABI_SYSTEM's System V ABI --- src/llvm_abi.cpp | 116 ++++++++++++++++++------------ src/llvm_backend.cpp | 164 +++++++++++++++++++++++++++++++++---------- src/types.cpp | 1 - 3 files changed, 200 insertions(+), 81 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 1fa004c8c..d9e115a34 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -66,7 +66,7 @@ LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) { LLVMTypeRef *args = gb_alloc_array(heap_allocator(), LLVMTypeRef, maximum_arg_count); if (offset == 1) { GB_ASSERT(ft->ret.kind == lbArg_Indirect); - args[0] = ft->ret.type; + args[0] = LLVMPointerType(ft->ret.type, 0); } unsigned arg_index = offset; @@ -172,8 +172,8 @@ i64 lb_sizeof(LLVMTypeRef type) { offset = llvm_align_formula(offset, align); offset += lb_sizeof(field); } + offset = llvm_align_formula(offset, lb_alignof(type)); } - offset = llvm_align_formula(offset, lb_alignof(type)); return offset; } break; @@ -240,14 +240,7 @@ i64 lb_alignof(LLVMTypeRef type) { } break; case LLVMArrayTypeKind: - { - LLVMTypeRef elem = LLVMGetElementType(type); - i64 elem_size = lb_sizeof(elem); - i64 count = LLVMGetArrayLength(type); - i64 size = count * elem_size; - return size; - } - break; + return lb_alignof(LLVMGetElementType(type)); case LLVMX86_MMXTypeKind: return 8; @@ -269,7 +262,37 @@ i64 lb_alignof(LLVMTypeRef type) { return 1; } -Type *lb_abi_to_odin_type(LLVMTypeRef type) { +Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) { + Type *t = alloc_type_struct(); + t->Struct.fields = array_make(heap_allocator(), field_count); + + Scope *scope = nullptr; + for_array(i, t->Struct.fields) { + t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved); + } + t->Struct.is_packed = is_packed; + + return t; +} + +Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed) { + if (field_count == 1) { + return field_types[0]; + } + + Type *t = alloc_type_tuple(); + t->Tuple.variables = array_make(heap_allocator(), field_count); + + Scope *scope = nullptr; + for_array(i, t->Tuple.variables) { + t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false); + } + t->Tuple.is_packed = is_packed; + + return t; +} + +Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { LLVMTypeKind kind = LLVMGetTypeKind(type); switch (kind) { case LLVMVoidTypeKind: @@ -282,10 +305,10 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type) { } unsigned bytes = (w + 7)/8; switch (bytes) { - case 1: return t_u8; - case 2: return t_u16; - case 4: return t_u32; - case 8: return t_u64; + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; case 16: return t_u128; } GB_PANIC("Unhandled integer type"); @@ -298,14 +321,23 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type) { return t_rawptr; case LLVMStructTypeKind: { - GB_PANIC("HERE"); + unsigned field_count = LLVMCountStructElementTypes(type); + Type **fields = gb_alloc_array(heap_allocator(), Type *, field_count); + for (unsigned i = 0; i < field_count; i++) { + fields[i] = lb_abi_to_odin_type(LLVMStructGetTypeAtIndex(type, i), false, level+1); + } + if (is_return) { + return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type)); + } else { + return alloc_type_struct_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type)); + } } break; case LLVMArrayTypeKind: { i64 count = LLVMGetArrayLength(type); - Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type)); + Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type), false, level+1); return alloc_type_array(elem, count); } break; @@ -315,7 +347,7 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type) { case LLVMVectorTypeKind: { i64 count = LLVMGetVectorSize(type); - Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type)); + Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type), false, level+1); return alloc_type_simd_vector(count, elem); } @@ -400,7 +432,7 @@ namespace lbAbi386 { case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); } - return lb_arg_type_indirect(LLVMPointerType(return_type, 0), lb_create_enum_attribute(c, "sret", true)); + return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true)); } return non_struct(c, return_type); } @@ -588,18 +620,6 @@ namespace lbAbiAmd64SysV { return reg_classes; } - void classify_struct(LLVMTypeRef *fields, unsigned field_count, Array *cls, i64 i, i64 off, LLVMBool packed) { - i64 field_off = off; - for (unsigned i = 0; i < field_count; i++) { - LLVMTypeRef t = fields[i]; - if (!packed) { - field_off = llvm_align_formula(field_off, lb_alignof(t)); - } - classify_with(t, cls, i, field_off); - field_off += lb_sizeof(t); - } - } - void unify(Array *cls, i64 i, RegClass newv) { RegClass &oldv = (*cls)[i]; if (oldv == newv) { @@ -665,10 +685,10 @@ namespace lbAbiAmd64SysV { } } - unsigned llvec_len(Array const ®_classes) { + unsigned llvec_len(Array const ®_classes, isize offset) { unsigned len = 1; - for_array(i, reg_classes) { - if (reg_classes[i] != RegClass_SSEUp) { + for (isize i = offset+1; i < reg_classes.count; i++) { + if (reg_classes[offset] != RegClass_SSEFv && reg_classes[i] != RegClass_SSEUp) { break; } len++; @@ -680,7 +700,8 @@ namespace lbAbiAmd64SysV { LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes) { auto types = array_make(heap_allocator(), 0, reg_classes.count); for_array(i, reg_classes) { - switch (reg_classes[i]) { + RegClass reg_class = reg_classes[i]; + switch (reg_class) { case RegClass_Int: array_add(&types, LLVMIntTypeInContext(c, 64)); break; @@ -693,7 +714,7 @@ namespace lbAbiAmd64SysV { { unsigned elems_per_word = 0; LLVMTypeRef elem_type = nullptr; - switch (reg_classes[i]) { + switch (reg_class) { case RegClass_SSEFv: elems_per_word = 2; elem_type = LLVMFloatTypeInContext(c); @@ -720,7 +741,7 @@ namespace lbAbiAmd64SysV { break; } - unsigned vec_len = llvec_len(array_slice(reg_classes, i+1, reg_classes.count)); + unsigned vec_len = llvec_len(reg_classes, i); LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word); array_add(&types, vec_type); i += vec_len; @@ -738,6 +759,10 @@ namespace lbAbiAmd64SysV { } } + GB_ASSERT(types.count != 0); + if (types.count == 1) { + return types[0]; + } return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false); } @@ -767,13 +792,18 @@ namespace lbAbiAmd64SysV { break; case LLVMStructTypeKind: { + LLVMBool packed = LLVMIsPackedStruct(t); unsigned field_count = LLVMCountStructElementTypes(t); - LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); // HACK(bill): LEAK - defer (gb_free(heap_allocator(), fields)); - LLVMGetStructElementTypes(t, fields); - - classify_struct(fields, field_count, cls, ix, off, LLVMIsPackedStruct(t)); + i64 field_off = off; + for (unsigned field_index = 0; field_index < field_count; field_index++) { + LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(t, field_index); + if (!packed) { + field_off = llvm_align_formula(field_off, lb_alignof(field_type)); + } + classify_with(field_type, cls, ix, field_off); + field_off += lb_sizeof(field_type); + } } break; case LLVMArrayTypeKind: @@ -859,7 +889,7 @@ namespace lbAbiAmd64SysV { case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); } - return lb_arg_type_indirect(LLVMPointerType(return_type, 0), lb_create_enum_attribute(c, "sret", true)); + return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true)); } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) { return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a01dd7ede..fbddb893e 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1380,7 +1380,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count); if (type->Proc.result_count != 0) { Type *single_ret = reduce_tuple_to_single_type(type->Proc.results); - ret = lb_type(m, type->Proc.results); + ret = lb_type(m, single_ret); if (ret != nullptr) { if (is_calling_convention_none(type->Proc.calling_convention) && is_type_boolean(single_ret) && @@ -1399,13 +1399,15 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { continue; } + Type *e_type = reduce_tuple_to_single_type(e->type); + LLVMTypeRef param_type = nullptr; if (is_calling_convention_none(type->Proc.calling_convention) && - is_type_boolean(e->type) && - type_size_of(e->type) <= 1) { + is_type_boolean(e_type) && + type_size_of(e_type) <= 1) { param_type = LLVMInt1TypeInContext(m->ctx); } else { - param_type = lb_type(m, e->type); + param_type = lb_type(m, e_type); } params[param_index++] = param_type; } @@ -2511,7 +2513,13 @@ void lb_start_block(lbProcedure *p, lbBlock *b) { LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRef dst_type) { LLVMTypeRef src_type = LLVMTypeOf(val); - GB_ASSERT(lb_sizeof(src_type) == lb_sizeof(dst_type)); + i64 src_size = lb_sizeof(src_type); + i64 dst_size = lb_sizeof(dst_type); + if (src_size != dst_size && (lb_is_type_kind(src_type, LLVMVectorTypeKind) ^ lb_is_type_kind(dst_type, LLVMVectorTypeKind))) { + // Okay + } else { + GB_ASSERT_MSG(src_size == dst_size, "%s == %s", LLVMPrintTypeToString(src_type), LLVMPrintTypeToString(dst_type)); + } LLVMTypeKind src_kind = LLVMGetTypeKind(src_type); LLVMTypeKind dst_kind = LLVMGetTypeKind(dst_type); @@ -4538,27 +4546,51 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } - if (p->type->Proc.return_by_pointer) { - if (res.value != nullptr) { - lb_addr_store(p, p->return_ptr, res); + if (p->abi_function_type) { + if (p->abi_function_type->ret.kind == lbArg_Indirect) { + if (res.value != nullptr) { + LLVMBuildStore(p->builder, res.value, p->return_ptr.addr.value); + } else { + LLVMBuildStore(p->builder, LLVMConstNull(p->abi_function_type->ret.type), p->return_ptr.addr.value); + } + + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + + LLVMBuildRetVoid(p->builder); } else { - lb_addr_store(p, p->return_ptr, lb_const_nil(p->module, p->type->Proc.abi_compat_result_type)); + LLVMValueRef ret_val = res.value; + if (p->abi_function_type->ret.cast_type != nullptr) { + ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.cast_type); + } + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + LLVMBuildRet(p->builder, ret_val); } - - lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - - LLVMBuildRetVoid(p->builder); } else { - GB_ASSERT_MSG(res.value != nullptr, "%.*s", LIT(p->name)); - Type *abi_rt = p->type->Proc.abi_compat_result_type; - if (!are_types_identical(res.type, abi_rt)) { - res = lb_emit_transmute(p, res, abi_rt); + GB_ASSERT(!USE_LLVM_ABI); + + if (p->type->Proc.return_by_pointer) { + if (res.value != nullptr) { + lb_addr_store(p, p->return_ptr, res); + } else { + lb_addr_store(p, p->return_ptr, lb_const_nil(p->module, p->type->Proc.abi_compat_result_type)); + } + + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + + LLVMBuildRetVoid(p->builder); + } else { + GB_ASSERT_MSG(res.value != nullptr, "%.*s", LIT(p->name)); + + Type *abi_rt = p->type->Proc.abi_compat_result_type; + if (!are_types_identical(res.type, abi_rt)) { + res = lb_emit_transmute(p, res, abi_rt); + } + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + LLVMBuildRet(p->builder, res.value); } - - lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - - LLVMBuildRet(p->builder, res.value); } + + case_end; case_ast_node(is, IfStmt, node); @@ -6585,8 +6617,6 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } - - // []byte/[]u8 <-> string if (is_type_u8_slice(src) && is_type_string(dst)) { return lb_emit_transmute(p, value, t); @@ -6636,6 +6666,34 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return lb_addr_load(p, result); } + + i64 src_sz = type_size_of(src); + i64 dst_sz = type_size_of(dst); + + if (src_sz == dst_sz) { + // bit_set <-> integer + if (is_type_integer(src) && is_type_bit_set(dst)) { + lbValue res = lb_emit_conv(p, value, bit_set_to_int(dst)); + res.type = dst; + return res; + } + if (is_type_bit_set(src) && is_type_integer(dst)) { + lbValue bs = value; + bs.type = bit_set_to_int(src); + return lb_emit_conv(p, bs, dst); + } + + // typeid <-> integer + if (is_type_integer(src) && is_type_typeid(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_typeid(src) && is_type_integer(dst)) { + return lb_emit_transmute(p, value, dst); + } + } + + + if (is_type_untyped(src)) { if (is_type_string(src) && is_type_string(dst)) { lbAddr result = lb_add_local_generated(p, t, false); @@ -6716,6 +6774,14 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) { i64 sz = type_size_of(src); i64 dz = type_size_of(dst); + if (sz != dz) { + LLVMTypeRef s = lb_type(m, src); + LLVMTypeRef d = lb_type(m, dst); + i64 llvm_sz = lb_sizeof(s); + i64 llvm_dz = lb_sizeof(d); + GB_ASSERT_MSG(llvm_sz == llvm_dz, "%s %s", LLVMPrintTypeToString(s), LLVMPrintTypeToString(d)); + } + GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: '%s' to '%s'", type_to_string(src_type), type_to_string(t)); // NOTE(bill): Casting between an integer and a pointer cannot be done through a bitcast @@ -7353,8 +7419,8 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, } GB_ASSERT(ft_found != nullptr); - lbFunctionType *abi_ft = *ft_found; - bool return_by_pointer = abi_ft->ret.kind == lbArg_Indirect; + lbFunctionType *ft = *ft_found; + bool return_by_pointer = ft->ret.kind == lbArg_Indirect; unsigned param_index = 0; for (isize i = 0; i < param_count; i++) { @@ -7365,7 +7431,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, GB_ASSERT(e->flags & EntityFlag_Param); Type *original_type = e->type; - lbArgType *arg = &abi_ft->args[param_index]; + lbArgType *arg = &ft->args[param_index]; if (arg->kind == lbArg_Ignore) { continue; } @@ -7381,9 +7447,15 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, if (xt == abi_type) { array_add(&processed_args, x); } else { - Type *at = lb_abi_to_odin_type(abi_type); + Type *at = lb_abi_to_odin_type(abi_type, false); if (at == t_llvm_bool) { x = lb_emit_conv(p, x, at); + } else if (is_type_simd_vector(at) && lb_sizeof(abi_type) > lb_sizeof(xt)) { + lbAddr v = lb_add_local_generated(p, at, false); + lbValue ptr = lb_addr_get_ptr(p, v); + ptr = lb_emit_conv(p, ptr, alloc_type_pointer(x.type)); + lb_emit_store(p, ptr, x); + x = lb_addr_load(p, v); } else { x = lb_emit_transmute(p, x, at); } @@ -7420,16 +7492,27 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, GB_ASSERT(is_type_pointer(return_ptr.type)); lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); result = lb_emit_load(p, return_ptr); - } else { - LLVMTypeRef ret_type = abi_ft->ret.cast_type; + } else if (rt != nullptr) { + LLVMTypeRef ret_type = ft->ret.cast_type; if (!ret_type) { - ret_type = abi_ft->ret.type; + ret_type = ft->ret.type; } - Type *abi_rt = lb_abi_to_odin_type(ret_type); + Type *abi_rt = lb_abi_to_odin_type(ret_type, true); result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); - if (abi_rt != rt) { - result = lb_emit_transmute(p, result, rt); + if (ret_type != lb_type(m, rt)) { + if (is_type_simd_vector(abi_rt) && lb_sizeof(ret_type) > type_size_of(rt)) { + lbValue ptr = lb_address_from_load_or_generate_local(p, result); + ptr = lb_emit_conv(p, ptr, alloc_type_pointer(rt)); + result = lb_emit_load(p, ptr); + } else { + result = lb_emit_transmute(p, result, rt); + } } + if (!is_type_tuple(rt)) { + result = lb_emit_conv(p, result, rt); + } + } else { + lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining); } } else { @@ -12856,6 +12939,10 @@ void lb_generate_code(lbGenerator *gen) { LLVMRunFunctionPassManager(default_function_pass_manager, p->value); } + + String filepath_ll = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".ll")); + defer (gb_free(heap_allocator(), filepath_ll.text)); + TIME_SECTION("LLVM Procedure Generation"); for_array(i, m->procedures_to_generate) { lbProcedure *p = m->procedures_to_generate[i]; @@ -12886,7 +12973,11 @@ void lb_generate_code(lbGenerator *gen) { gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); LLVMDumpValue(p->value); gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + } + LLVMVerifyFunction(p->value, LLVMPrintMessageAction); + gb_exit(1); } } @@ -12933,9 +13024,6 @@ void lb_generate_code(lbGenerator *gen) { llvm_error = nullptr; defer (LLVMDisposeMessage(llvm_error)); - String filepath_ll = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".ll")); - defer (gb_free(heap_allocator(), filepath_ll.text)); - String filepath_obj = {}; LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile; @@ -12962,6 +13050,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMDIBuilderFinalize(m->debug_builder); if (LLVMVerifyModule(mod, LLVMAbortProcessAction, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); return; } llvm_error = nullptr; @@ -12969,6 +13058,7 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Print Module to File"); if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); return; } } diff --git a/src/types.cpp b/src/types.cpp index 528ea3a2d..1a55c9b05 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2388,7 +2388,6 @@ Selection lookup_field_from_index(Type *type, i64 index) { return empty_selection; } - Entity *scope_lookup_current(Scope *s, String const &name); Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) { From 9f930421632f4f6d5352ec01f12a1a7fb1050ba7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 14 Nov 2020 17:09:42 +0000 Subject: [PATCH 043/170] Improve lb_abi_to_odin_type --- src/llvm_abi.cpp | 49 ++++++++++++++++++++++++++++++++++++++++---- src/llvm_backend.cpp | 4 ++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index d9e115a34..79f7948a3 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -275,8 +275,8 @@ Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, return t; } -Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed) { - if (field_count == 1) { +Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) { + if (!must_be_tuple && field_count == 1) { return field_types[0]; } @@ -292,6 +292,21 @@ Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, b return t; } +Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg) { + + Type *params = alloc_type_tuple_from_field_types(param_types, param_count, false, true); + isize results_count = 0; + if (results != nullptr) { + GB_ASSERT(results->kind == Type_Tuple); + results_count = results->Tuple.variables.count; + } + + Scope *scope = nullptr; + Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, /*not sure what to put here*/ProcCC_CDecl); + t->Proc.c_vararg = is_c_vararg; + return t; +} + Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { LLVMTypeKind kind = LLVMGetTypeKind(type); switch (kind) { @@ -318,7 +333,33 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { case LLVMDoubleTypeKind: return t_f64; case LLVMPointerTypeKind: - return t_rawptr; + { + LLVMTypeRef elem = LLVMGetElementType(type); + if (lb_is_type_kind(elem, LLVMFunctionTypeKind)) { + unsigned param_count = LLVMCountParamTypes(elem); + LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count); + defer (gb_free(heap_allocator(), params)); + LLVMGetParamTypes(elem, params); + + Type **param_types = gb_alloc_array(heap_allocator(), Type *, param_count); + defer (gb_free(heap_allocator(), param_types)); + + for (unsigned i = 0; i < param_count; i++) { + param_types[i] = lb_abi_to_odin_type(params[i], false, /*level*/0); + } + + LLVMTypeRef ret = LLVMGetReturnType(elem); + Type *ret_type = lb_abi_to_odin_type(ret, true, /*level*/0); + + bool is_c_vararg = !!LLVMIsFunctionVarArg(elem); + return alloc_type_proc_from_types(param_types, param_count, ret_type, is_c_vararg); + } + return alloc_type_pointer(lb_abi_to_odin_type(elem, false, level)); + } + case LLVMFunctionTypeKind: + GB_PANIC("LLVMFunctionTypeKind should not be seen on its own"); + break; + case LLVMStructTypeKind: { unsigned field_count = LLVMCountStructElementTypes(type); @@ -327,7 +368,7 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { fields[i] = lb_abi_to_odin_type(LLVMStructGetTypeAtIndex(type, i), false, level+1); } if (is_return) { - return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type)); + return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type), false); } else { return alloc_type_struct_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type)); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index fbddb893e..1f20c607d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1278,6 +1278,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { offset = 1; } + m->internal_type_level += 1; + defer (m->internal_type_level -= 1); + unsigned field_count = cast(unsigned)(type->Struct.fields.count + offset); LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); GB_ASSERT(fields != nullptr); @@ -1288,6 +1291,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { fields[i+offset] = lb_type(m, field->type); } + if (type->Struct.custom_align > 0) { fields[0] = lb_alignment_prefix_type_hack(m, type->Struct.custom_align); } From 3c1c10a1785a97831a69fb6d94356d5cc4989bd6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 18:08:52 +0000 Subject: [PATCH 044/170] Begin clarifying allocation patterns by changing from `heap_allocator` to specific arenas --- src/checker.cpp | 4 +- src/checker.hpp | 2 - src/common.cpp | 48 ++++++-- src/entity.cpp | 2 +- src/llvm_abi.cpp | 7 +- src/llvm_backend.cpp | 279 ++++++++++++++++++------------------------- src/main.cpp | 16 ++- src/parser.cpp | 43 ++++--- src/parser.hpp | 1 + src/types.cpp | 8 +- 10 files changed, 209 insertions(+), 201 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index fe669ab8c..a07a3ffbe 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -690,8 +690,10 @@ void add_global_type_entity(String name, Type *type) { void init_universal(void) { BuildContext *bc = &build_context; + // NOTE(bill): No need to free these - gbAllocator a = heap_allocator(); + // gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); builtin_pkg = gb_alloc_item(a, AstPackage); builtin_pkg->name = str_lit("builtin"); diff --git a/src/checker.hpp b/src/checker.hpp index 3d21a4cec..88e9451ee 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -337,8 +337,6 @@ struct Checker { - - gb_global AstPackage *builtin_pkg = nullptr; gb_global AstPackage *intrinsics_pkg = nullptr; gb_global AstPackage *config_pkg = nullptr; diff --git a/src/common.cpp b/src/common.cpp index 350127e1e..567655c04 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -373,8 +373,8 @@ typedef struct Arena { gbAllocator backing; isize block_size; gbMutex mutex; - isize total_used; + bool use_mutex; } Arena; #define ARENA_MIN_ALIGNMENT 16 @@ -388,8 +388,9 @@ void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAUL } void arena_grow(Arena *arena, isize min_size) { - // gb_mutex_lock(&arena->mutex); - // defer (gb_mutex_unlock(&arena->mutex)); + if (arena->use_mutex) { + gb_mutex_lock(&arena->mutex); + } isize size = gb_max(arena->block_size, min_size); size = ALIGN_UP(size, ARENA_MIN_ALIGNMENT); @@ -399,11 +400,16 @@ void arena_grow(Arena *arena, isize min_size) { GB_ASSERT(arena->ptr == ALIGN_DOWN_PTR(arena->ptr, ARENA_MIN_ALIGNMENT)); arena->end = arena->ptr + size; array_add(&arena->blocks, arena->ptr); + + if (arena->use_mutex) { + gb_mutex_unlock(&arena->mutex); + } } void *arena_alloc(Arena *arena, isize size, isize alignment) { - // gb_mutex_lock(&arena->mutex); - // defer (gb_mutex_unlock(&arena->mutex)); + if (arena->use_mutex) { + gb_mutex_lock(&arena->mutex); + } arena->total_used += size; @@ -419,12 +425,17 @@ void *arena_alloc(Arena *arena, isize size, isize alignment) { GB_ASSERT(arena->ptr <= arena->end); GB_ASSERT(ptr == ALIGN_DOWN_PTR(ptr, align)); // zero_size(ptr, size); + + if (arena->use_mutex) { + gb_mutex_unlock(&arena->mutex); + } return ptr; } void arena_free_all(Arena *arena) { - // gb_mutex_lock(&arena->mutex); - // defer (gb_mutex_unlock(&arena->mutex)); + if (arena->use_mutex) { + gb_mutex_lock(&arena->mutex); + } for_array(i, arena->blocks) { gb_free(arena->backing, arena->blocks[i]); @@ -432,6 +443,10 @@ void arena_free_all(Arena *arena) { array_clear(&arena->blocks); arena->ptr = nullptr; arena->end = nullptr; + + if (arena->use_mutex) { + gb_mutex_unlock(&arena->mutex); + } } @@ -460,7 +475,14 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) { // GB_PANIC("gbAllocation_Free not supported"); break; case gbAllocation_Resize: - GB_PANIC("gbAllocation_Resize: not supported"); + if (size == 0) { + ptr = nullptr; + } else if (size <= old_size) { + ptr = old_memory; + } else { + ptr = arena_alloc(arena, size, alignment); + gb_memmove(ptr, old_memory, old_size); + } break; case gbAllocation_FreeAll: arena_free_all(arena); @@ -472,6 +494,16 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) { +gb_global Arena permanent_arena = {}; +gb_global Arena temporary_arena = {}; + +gbAllocator permanent_allocator() { + return arena_allocator(&permanent_arena); +} +gbAllocator temporary_allocator() { + return arena_allocator(&temporary_arena); +} + diff --git a/src/entity.cpp b/src/entity.cpp index a9d598735..708b0862c 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -220,7 +220,7 @@ bool entity_has_deferred_procedure(Entity *e) { gb_global u64 global_entity_id = 0; Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) { - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); Entity *entity = gb_alloc_item(a, Entity); entity->kind = kind; entity->state = EntityState_Unresolved; diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 79f7948a3..f0422e0f8 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -276,6 +276,9 @@ Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, } Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) { + if (field_count == 0) { + return nullptr; + } if (!must_be_tuple && field_count == 1) { return field_types[0]; } @@ -297,7 +300,9 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type Type *params = alloc_type_tuple_from_field_types(param_types, param_count, false, true); isize results_count = 0; if (results != nullptr) { - GB_ASSERT(results->kind == Type_Tuple); + if (results->kind != Type_Tuple) { + results = alloc_type_tuple_from_field_types(&results, 1, false, true); + } results_count = results->Tuple.variables.count; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1f20c607d..43f4125ba 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -144,7 +144,7 @@ lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { lbValue h = lb_gen_map_header(p, addr.addr, map_type); lbValue key = lb_gen_map_key(p, addr.map.key, map_type->Map.key); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = h; args[1] = key; @@ -209,7 +209,7 @@ void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index, lbValue le lbValue line = lb_const_int(p->module, t_int, token.pos.line); lbValue column = lb_const_int(p->module, t_int, token.pos.column); - auto args = array_make(heap_allocator(), 5); + auto args = array_make(permanent_allocator(), 5); args[0] = file; args[1] = line; args[2] = column; @@ -233,7 +233,7 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu high = lb_emit_conv(p, high, t_int); if (!lower_value_used) { - auto args = array_make(heap_allocator(), 5); + auto args = array_make(permanent_allocator(), 5); args[0] = file; args[1] = line; args[2] = column; @@ -245,7 +245,7 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu // No need to convert unless used low = lb_emit_conv(p, low, t_int); - auto args = array_make(heap_allocator(), 6); + auto args = array_make(permanent_allocator(), 6); args[0] = file; args[1] = line; args[2] = column; @@ -357,7 +357,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { } - auto args = array_make(heap_allocator(), gb_max(arg_count, param_count)); + auto args = array_make(permanent_allocator(), gb_max(arg_count, param_count)); args[0] = ptr; args[1] = index; args[2] = value; @@ -594,7 +594,7 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { lbValue h = lb_gen_map_header(p, addr.addr, map_type); lbValue key = lb_gen_map_key(p, addr.map.key, map_type->Map.key); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = h; args[1] = key; @@ -773,7 +773,6 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia } void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) { - gbAllocator a = heap_allocator(); lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); lb_emit_store(p, underlying, variant); @@ -783,10 +782,9 @@ void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) { unsigned field_count = LLVMCountStructElementTypes(src); - LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); + LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); LLVMGetStructElementTypes(src, fields); LLVMStructSetBody(dst, fields, field_count, LLVMIsPackedStruct(src)); - gb_free(heap_allocator(), fields); } LLVMTypeRef lb_alignment_prefix_type_hack(lbModule *m, i64 alignment) { @@ -821,8 +819,6 @@ bool lb_is_elem_const(Ast *elem, Type *elem_type) { } String lb_mangle_name(lbModule *m, Entity *e) { - gbAllocator a = heap_allocator(); - String name = e->token.string; AstPackage *pkg = e->pkg; @@ -848,7 +844,7 @@ String lb_mangle_name(lbModule *m, Entity *e) { max_len += 21; } - char *new_name = gb_alloc_array(a, char, max_len); + char *new_name = gb_alloc_array(permanent_allocator(), char, max_len); isize new_name_len = gb_snprintf( new_name, max_len, "%.*s.%.*s", LIT(pkgn), LIT(name) @@ -899,7 +895,7 @@ String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p) { if (p != nullptr) { isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1; - char *name_text = gb_alloc_array(heap_allocator(), char, name_len); + char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); u32 guid = ++p->module->nested_type_name_guid; name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid); @@ -909,7 +905,7 @@ String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p) { } else { // NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1; - char *name_text = gb_alloc_array(heap_allocator(), char, name_len); + char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); static u32 guid = 0; guid += 1; name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid); @@ -1191,7 +1187,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { if (found) { LLVMTypeKind kind = LLVMGetTypeKind(*found); if (kind == LLVMStructTypeKind) { - char const *name = alloc_cstring(heap_allocator(), lb_get_entity_name(m, type->Named.type_name)); + char const *name = alloc_cstring(permanent_allocator(), lb_get_entity_name(m, type->Named.type_name)); LLVMTypeRef llvm_type = LLVMGetTypeByName(m->mod, name); if (llvm_type != nullptr) { return llvm_type; @@ -1208,7 +1204,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Type_Union: case Type_BitField: { - char const *name = alloc_cstring(heap_allocator(), lb_get_entity_name(m, type->Named.type_name)); + char const *name = alloc_cstring(permanent_allocator(), lb_get_entity_name(m, type->Named.type_name)); LLVMTypeRef llvm_type = LLVMGetTypeByName(m->mod, name); if (llvm_type != nullptr) { return llvm_type; @@ -1265,7 +1261,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { { if (type->Struct.is_raw_union) { unsigned field_count = 2; - LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); + LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count); i64 alignment = type_align_of(type); unsigned size_of_union = cast(unsigned)type_size_of(type); fields[0] = lb_alignment_prefix_type_hack(m, alignment); @@ -1282,9 +1278,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { defer (m->internal_type_level -= 1); unsigned field_count = cast(unsigned)(type->Struct.fields.count + offset); - LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); + LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); GB_ASSERT(fields != nullptr); - defer (gb_free(heap_allocator(), fields)); for_array(i, type->Struct.fields) { Entity *field = type->Struct.fields[i]; @@ -1344,8 +1339,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return lb_type(m, type->Tuple.variables[0]->type); } else { unsigned field_count = cast(unsigned)(type->Tuple.variables.count); - LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); - defer (gb_free(heap_allocator(), fields)); + LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); for_array(i, type->Tuple.variables) { Entity *field = type->Tuple.variables[i]; @@ -1444,8 +1438,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } isize param_count = type->Proc.abi_compat_params.count + extra_param_count; - auto param_types = array_make(heap_allocator(), 0, param_count); - defer (array_free(¶m_types)); + auto param_types = array_make(temporary_allocator(), 0, param_count); if (type->Proc.return_by_pointer) { array_add(¶m_types, LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0)); @@ -1492,8 +1485,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { { GB_ASSERT(type->BitField.fields.count == type->BitField.sizes.count); unsigned field_count = cast(unsigned)type->BitField.fields.count; - LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); - defer (gb_free(heap_allocator(), fields)); + LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); for_array(i, type->BitField.sizes) { u32 size = type->BitField.sizes[i]; @@ -2135,7 +2127,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { } - lbProcedure *p = gb_alloc_item(heap_allocator(), lbProcedure); + lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure); p->module = m; entity->code_gen_module = m; @@ -2149,7 +2141,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { Type *pt = base_type(entity->type); GB_ASSERT(pt->kind == Type_Proc); - set_procedure_abi_types(heap_allocator(), entity->type); + set_procedure_abi_types(permanent_allocator(), entity->type); p->type = entity->type; p->type_expr = decl->type_expr; @@ -2172,7 +2164,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); } - char *c_link_name = alloc_cstring(heap_allocator(), p->name); + char *c_link_name = alloc_cstring(permanent_allocator(), p->name); LLVMTypeRef func_ptr_type = lb_type(m, p->type); LLVMTypeRef func_type = LLVMGetElementType(func_ptr_type); @@ -2209,19 +2201,19 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { LLVMSetVisibility(p->value, LLVMDefaultVisibility); if (build_context.metrics.os == TargetOs_js) { - char const *export_name = alloc_cstring(heap_allocator(), p->name); + char const *export_name = alloc_cstring(permanent_allocator(), p->name); LLVMAddTargetDependentFunctionAttr(p->value, "wasm-export-name", export_name); } } if (p->is_foreign) { if (build_context.metrics.os == TargetOs_js) { - char const *import_name = alloc_cstring(heap_allocator(), p->name); + char const *import_name = alloc_cstring(permanent_allocator(), p->name); char const *module_name = "env"; if (entity->Procedure.foreign_library != nullptr) { Entity *foreign_library = entity->Procedure.foreign_library; GB_ASSERT(foreign_library->kind == Entity_LibraryName); if (foreign_library->LibraryName.paths.count > 0) { - module_name = alloc_cstring(heap_allocator(), foreign_library->LibraryName.paths[0]); + module_name = alloc_cstring(permanent_allocator(), foreign_library->LibraryName.paths[0]); } } LLVMAddTargetDependentFunctionAttr(p->value, "wasm-import-name", import_name); @@ -2310,7 +2302,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type GB_ASSERT(found == nullptr); } - lbProcedure *p = gb_alloc_item(heap_allocator(), lbProcedure); + lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure); p->module = m; p->name = link_name; @@ -2324,7 +2316,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type p->is_export = false; p->is_entry_point = false; - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); p->children.allocator = a; p->params.allocator = a; p->defer_stmts.allocator = a; @@ -2333,7 +2325,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type p->context_stack.allocator = a; - char *c_link_name = alloc_cstring(heap_allocator(), p->name); + char *c_link_name = alloc_cstring(permanent_allocator(), p->name); LLVMTypeRef func_ptr_type = lb_type(m, p->type); LLVMTypeRef func_type = LLVMGetElementType(func_ptr_type); @@ -2833,7 +2825,7 @@ void lb_add_edge(lbBlock *from, lbBlock *to) { lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append) { - lbBlock *b = gb_alloc_item(heap_allocator(), lbBlock); + lbBlock *b = gb_alloc_item(permanent_allocator(), lbBlock); b->block = LLVMCreateBasicBlockInContext(p->module->ctx, name); b->appended = false; if (append) { @@ -2932,7 +2924,7 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p char const *name = ""; if (e != nullptr) { - // name = alloc_cstring(heap_allocator(), e->token.string); + // name = alloc_cstring(permanent_allocator(), e->token.string); } LLVMTypeRef llvm_type = lb_type(p->module, type); @@ -2980,13 +2972,13 @@ void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) { isize name_len = p->name.len + 1 + pd_name.len + 1 + 10 + 1; - char *name_text = gb_alloc_array(heap_allocator(), char, name_len); + char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); i32 guid = cast(i32)p->children.count; name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%d", LIT(p->name), LIT(pd_name), guid); String name = make_string(cast(u8 *)name_text, name_len-1); - set_procedure_abi_types(heap_allocator(), e->type); + set_procedure_abi_types(permanent_allocator(), e->type); e->Procedure.link_name = name; @@ -3123,7 +3115,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { return; } - set_procedure_abi_types(heap_allocator(), e->type); + set_procedure_abi_types(permanent_allocator(), e->type); e->Procedure.link_name = name; lbProcedure *nested_proc = lb_create_procedure(p->module, e); @@ -3179,7 +3171,7 @@ lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) { lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) { - lbTargetList *tl = gb_alloc_item(heap_allocator(), lbTargetList); + lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList); tl->prev = p->target_list; tl->break_ = break_; tl->continue_ = continue_; @@ -3400,7 +3392,7 @@ void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type, lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset); lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int); - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = lb_emit_string(p, str_elem, str_len); lbValue rune_and_len = lb_emit_runtime_call(p, "string_decode_rune", args); lbValue len = lb_emit_struct_ev(p, rune_and_len, 1); @@ -4275,14 +4267,14 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { String mangled_name = {}; { - gbString str = gb_string_make_length(heap_allocator(), p->name.text, p->name.len); + gbString str = gb_string_make_length(permanent_allocator(), p->name.text, p->name.len); str = gb_string_appendc(str, "-"); str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id); mangled_name.text = cast(u8 *)str; mangled_name.len = gb_string_length(str); } - char *c_name = alloc_cstring(heap_allocator(), mangled_name); + char *c_name = alloc_cstring(permanent_allocator(), mangled_name); LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name); LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); @@ -4329,8 +4321,8 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } } else { // Tuple(s) - auto lvals = array_make(heap_allocator(), 0, vd->names.count); - auto inits = array_make(heap_allocator(), 0, vd->names.count); + auto lvals = array_make(permanent_allocator(), 0, vd->names.count); + auto inits = array_make(permanent_allocator(), 0, vd->names.count); for_array(i, vd->names) { Ast *name = vd->names[i]; @@ -4367,7 +4359,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { case_ast_node(as, AssignStmt, node); if (as->op.kind == Token_Eq) { - auto lvals = array_make(heap_allocator(), 0, as->lhs.count); + auto lvals = array_make(permanent_allocator(), 0, as->lhs.count); for_array(i, as->lhs) { Ast *lhs = as->lhs[i]; @@ -4385,7 +4377,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { lbValue init = lb_build_expr(p, rhs); lb_addr_store(p, lvals[0], init); } else { - auto inits = array_make(heap_allocator(), 0, lvals.count); + auto inits = array_make(permanent_allocator(), 0, lvals.count); for_array(i, as->rhs) { lbValue init = lb_build_expr(p, as->rhs[i]); @@ -4399,7 +4391,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } } else { - auto inits = array_make(heap_allocator(), 0, lvals.count); + auto inits = array_make(permanent_allocator(), 0, lvals.count); for_array(i, as->rhs) { lbValue init = lb_build_expr(p, as->rhs[i]); @@ -4491,7 +4483,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } else { - auto results = array_make(heap_allocator(), 0, return_count); + auto results = array_make(permanent_allocator(), 0, return_count); if (res_count != 0) { for (isize res_index = 0; res_index < res_count; res_index++) { @@ -4794,9 +4786,8 @@ lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) { y = lb_emit_conv(p, y, t); if (is_type_float(t)) { - gbAllocator a = heap_allocator(); i64 sz = 8*type_size_of(t); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = x; args[1] = y; switch (sz) { @@ -4812,9 +4803,8 @@ lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) { y = lb_emit_conv(p, y, t); if (is_type_float(t)) { - gbAllocator a = heap_allocator(); i64 sz = 8*type_size_of(t); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = x; args[1] = y; switch (sz) { @@ -4850,7 +4840,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { isize max_len = 7+8+1; - char *name = gb_alloc_array(heap_allocator(), char, max_len); + char *name = gb_alloc_array(permanent_allocator(), char, max_len); isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index); len -= 1; m->global_array_index++; @@ -4892,7 +4882,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) char *name = nullptr; { isize max_len = 7+8+1; - name = gb_alloc_array(heap_allocator(), char, max_len); + name = gb_alloc_array(permanent_allocator(), char, max_len); isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index); len -= 1; m->global_array_index++; @@ -5103,7 +5093,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } } else { isize max_len = 7+8+1; - char *str = gb_alloc_array(heap_allocator(), char, max_len); + char *str = gb_alloc_array(permanent_allocator(), char, max_len); isize len = gb_snprintf(str, max_len, "csba$%x", m->global_array_index); m->global_array_index++; @@ -5151,7 +5141,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc lbValue single_elem = lb_const_value(m, elem, value, allow_local); - LLVMValueRef *elems = gb_alloc_array(heap_allocator(), LLVMValueRef, count); + LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); for (i64 i = 0; i < count; i++) { elems[i] = single_elem.value; } @@ -5204,7 +5194,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc isize byte_len = gb_size_of(u64)*len; u8 *old_bytes = cast(u8 *)words; // TODO(bill): Use a different allocator here for a temporary allocation - u8 *new_bytes = cast(u8 *)gb_alloc_align(heap_allocator(), byte_len, gb_align_of(u64)); + u8 *new_bytes = cast(u8 *)gb_alloc_align(permanent_allocator(), byte_len, gb_align_of(u64)); for (i64 i = 0; i < sz; i++) { new_bytes[i] = old_bytes[sz-1-i]; } @@ -5291,8 +5281,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc if (cl->elems[0]->kind == Ast_FieldValue) { // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand - LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->Array.count); - defer (gb_free(heap_allocator(), values)); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count); isize value_index = 0; for (i64 i = 0; i < type->Array.count; i++) { @@ -5349,8 +5338,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); - LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->Array.count); - defer (gb_free(heap_allocator(), values)); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count); for (isize i = 0; i < elem_count; i++) { TypeAndValue tav = cl->elems[i]->tav; @@ -5374,8 +5362,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc if (cl->elems[0]->kind == Ast_FieldValue) { // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand - LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->EnumeratedArray.count); - defer (gb_free(heap_allocator(), values)); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count); isize value_index = 0; @@ -5436,8 +5423,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count); - LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->EnumeratedArray.count); - defer (gb_free(heap_allocator(), values)); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count); for (isize i = 0; i < elem_count; i++) { TypeAndValue tav = cl->elems[i]->tav; @@ -5462,8 +5448,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc GB_ASSERT(elem_type_can_be_constant(elem_type)); isize total_elem_count = type->SimdVector.count; - LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, total_elem_count); - defer (gb_free(heap_allocator(), values)); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count); for (isize i = 0; i < elem_count; i++) { TypeAndValue tav = cl->elems[i]->tav; @@ -5489,11 +5474,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } isize value_count = type->Struct.fields.count + offset; - LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, value_count); - bool *visited = gb_alloc_array(heap_allocator(), bool, value_count); - defer (gb_free(heap_allocator(), values)); - defer (gb_free(heap_allocator(), visited)); - + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count); + bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count); if (cl->elems.count > 0) { @@ -5822,7 +5804,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty Type *ft = base_complex_elem_type(type); if (op == Token_Quo) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = lhs; args[1] = rhs; @@ -5896,7 +5878,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty return lb_addr_load(p, res); } else if (op == Token_Mul) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = lhs; args[1] = rhs; @@ -5906,7 +5888,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty default: GB_PANIC("Unknown float type"); break; } } else if (op == Token_Quo) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = lhs; args[1] = rhs; @@ -6132,7 +6114,7 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { lbValue h = lb_gen_map_header(p, addr, rt); lbValue key = lb_gen_map_key(p, left, rt->Map.key); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = h; args[1] = key; @@ -6378,7 +6360,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) { lbValue c = lb_emit_conv(p, value, t_cstring); - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = c; lbValue s = lb_emit_runtime_call(p, "cstring_to_string", args); return lb_emit_conv(p, s, dst); @@ -6395,7 +6377,6 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { // float -> float if (is_type_float(src) && is_type_float(dst)) { - gbAllocator a = heap_allocator(); i64 sz = type_size_of(src); i64 dz = type_size_of(dst); @@ -6837,8 +6818,7 @@ void lb_emit_init_context(lbProcedure *p, lbAddr addr) { GB_ASSERT(addr.ctx.sel.index.count == 0); lbModule *m = p->module; - gbAllocator a = heap_allocator(); - auto args = array_make(a, 1); + auto args = array_make(permanent_allocator(), 1); args[0] = addr.addr; lb_emit_runtime_call(p, "__init_context", args); } @@ -6907,7 +6887,6 @@ lbValue lb_copy_value_to_ptr(lbProcedure *p, lbValue val, Type *new_type, i64 al } lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) { - gbAllocator a = heap_allocator(); GB_ASSERT(is_type_pointer(s.type)); Type *t = base_type(type_deref(s.type)); Type *result_type = nullptr; @@ -7015,7 +6994,6 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) { return lb_emit_load(p, ptr); } - gbAllocator a = heap_allocator(); Type *t = base_type(s.type); Type *result_type = nullptr; @@ -7121,7 +7099,6 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) { lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) { GB_ASSERT(sel.index.count > 0); Type *type = type_deref(e.type); - gbAllocator a = heap_allocator(); for_array(i, sel.index) { i32 index = cast(i32)sel.index[i]; @@ -7292,14 +7269,14 @@ Array lb_value_to_array(lbProcedure *p, lbValue value) { GB_ASSERT(t->kind == Type_Tuple); auto *rt = &t->Tuple; if (rt->variables.count > 0) { - array = array_make(heap_allocator(), rt->variables.count); + array = array_make(permanent_allocator(), rt->variables.count); for_array(i, rt->variables) { lbValue elem = lb_emit_struct_ev(p, value, cast(i32)i); array[i] = elem; } } } else { - array = array_make(heap_allocator(), 1); + array = array_make(permanent_allocator(), 1); array[0] = value; } return array; @@ -7316,7 +7293,7 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, arg_count += 1; } - LLVMValueRef *args = gb_alloc_array(heap_allocator(), LLVMValueRef, arg_count); + LLVMValueRef *args = gb_alloc_array(permanent_allocator(), LLVMValueRef, arg_count); isize arg_index = 0; if (return_ptr.value != nullptr) { args[arg_index++] = return_ptr.value; @@ -7399,7 +7376,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, LLVMBuildUnreachable(p->builder); }); - set_procedure_abi_types(heap_allocator(), pt); + set_procedure_abi_types(permanent_allocator(), pt); bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; @@ -7412,7 +7389,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, lbValue result = {}; - auto processed_args = array_make(heap_allocator(), 0, args.count); + auto processed_args = array_make(permanent_allocator(), 0, args.count); if (USE_LLVM_ABI) { lbFunctionType **ft_found = nullptr; @@ -7635,7 +7612,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, case DeferredProcedure_in_out: { auto out_args = lb_value_to_array(p, result); - array_init(&result_as_args, heap_allocator(), in_args.count + out_args.count); + array_init(&result_as_args, permanent_allocator(), in_args.count + out_args.count); array_copy(&result_as_args, in_args, 0); array_copy(&result_as_args, out_args, in_args.count); } @@ -7744,7 +7721,7 @@ lbValue lb_string_len(lbProcedure *p, lbValue string) { lbValue lb_cstring_len(lbProcedure *p, lbValue value) { GB_ASSERT(is_type_cstring(value.type)); - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = lb_emit_conv(p, value, t_cstring); return lb_emit_runtime_call(p, "cstring_len", args); } @@ -7782,7 +7759,6 @@ lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da) { } lbValue lb_map_entries(lbProcedure *p, lbValue value) { - gbAllocator a = heap_allocator(); Type *t = base_type(value.type); GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t)); init_map_internal_types(t); @@ -7793,7 +7769,6 @@ lbValue lb_map_entries(lbProcedure *p, lbValue value) { } lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value) { - gbAllocator a = heap_allocator(); Type *t = base_type(type_deref(value.type)); GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t)); init_map_internal_types(t); @@ -7917,7 +7892,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } GB_ASSERT(is_type_typeid(tav.type)); - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = lb_build_expr(p, arg); return lb_emit_runtime_call(p, "__type_info_of", args); } @@ -7994,7 +7969,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } unsigned mask_len = cast(unsigned)index_count; - LLVMValueRef *mask_elems = gb_alloc_array(heap_allocator(), LLVMValueRef, index_count); + LLVMValueRef *mask_elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, index_count); for (isize i = 1; i < ce->args.count; i++) { TypeAndValue tv = type_and_value_of_expr(ce->args[i]); GB_ASSERT(is_type_integer(tv.type)); @@ -8224,7 +8199,6 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_abs: { - gbAllocator a = heap_allocator(); lbValue x = lb_build_expr(p, ce->args[0]); Type *t = x.type; if (is_type_unsigned(t)) { @@ -8232,7 +8206,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } if (is_type_quaternion(t)) { i64 sz = 8*type_size_of(t); - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = x; switch (sz) { case 128: return lb_emit_runtime_call(p, "abs_quaternion128", args); @@ -8241,7 +8215,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, GB_PANIC("Unknown complex type"); } else if (is_type_complex(t)) { i64 sz = 8*type_size_of(t); - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = x; switch (sz) { case 64: return lb_emit_runtime_call(p, "abs_complex64", args); @@ -8250,7 +8224,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, GB_PANIC("Unknown complex type"); } else if (is_type_float(t)) { i64 sz = 8*type_size_of(t); - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = x; switch (sz) { case 32: return lb_emit_runtime_call(p, "abs_f32", args); @@ -8506,7 +8480,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, GB_ASSERT(tv.type->kind == Type_Tuple); Type *fix_typed = alloc_type_tuple(); - array_init(&fix_typed->Tuple.variables, heap_allocator(), 2); + array_init(&fix_typed->Tuple.variables, permanent_allocator(), 2); fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0]; fix_typed->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); @@ -8621,10 +8595,10 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { Type *proc_type_ = base_type(value.type); GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; - set_procedure_abi_types(heap_allocator(), proc_type_); + set_procedure_abi_types(permanent_allocator(), proc_type_); if (is_call_expr_field_value(ce)) { - auto args = array_make(heap_allocator(), pt->param_count); + auto args = array_make(permanent_allocator(), pt->param_count); for_array(arg_index, ce->args) { Ast *arg = ce->args[arg_index]; @@ -8693,7 +8667,7 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { param_count = pt->params->Tuple.variables.count; } - auto args = array_make(heap_allocator(), cast(isize)gb_max(param_count, arg_count)); + auto args = array_make(permanent_allocator(), cast(isize)gb_max(param_count, arg_count)); isize variadic_index = pt->variadic_index; bool variadic = pt->variadic && variadic_index >= 0; bool vari_expand = ce->ellipsis.pos.line != 0; @@ -8801,7 +8775,6 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { if (variadic && !vari_expand && !is_c_vararg) { // variadic call argument generation - gbAllocator allocator = heap_allocator(); Type *slice_type = param_tuple->variables[variadic_index]->type; Type *elem_type = base_type(slice_type)->Slice.elem; lbAddr slice = lb_add_local_generated(p, slice_type, true); @@ -9077,7 +9050,7 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) { lbValue invalid_typeid = lb_const_value(p->module, t_typeid, exact_value_i64(0)); return lb_emit_comp(p, op_kind, x, invalid_typeid); } else if (is_type_bit_field(t)) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); lbValue lhs = lb_address_from_load_or_generate_local(p, x); args[0] = lb_emit_conv(p, lhs, t_rawptr); args[1] = lb_const_int(p->module, t_int, type_size_of(t)); @@ -9106,7 +9079,7 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) { } } } else if (is_type_struct(t) && type_has_nil(t)) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); lbValue lhs = lb_address_from_load_or_generate_local(p, x); args[0] = lb_emit_conv(p, lhs, t_rawptr); args[1] = lb_const_int(p->module, t_int, type_size_of(t)); @@ -9141,8 +9114,6 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } else if (lb_is_const(right) || lb_is_const_nil(right)) { right = lb_emit_conv(p, right, left.type); } else { - gbAllocator a = heap_allocator(); - Type *lt = left.type; Type *rt = right.type; @@ -9222,7 +9193,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } else { if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) { // TODO(bill): Test to see if this is actually faster!!!! - auto args = array_make(heap_allocator(), 3); + auto args = array_make(permanent_allocator(), 3); args[0] = lb_emit_conv(p, lhs, t_rawptr); args[1] = lb_emit_conv(p, rhs, t_rawptr); args[2] = lb_const_int(p->module, t_int, type_size_of(tl)); @@ -9265,7 +9236,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } GB_ASSERT(runtime_procedure != nullptr); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; return lb_emit_runtime_call(p, runtime_procedure, args); @@ -9290,7 +9261,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } GB_ASSERT(runtime_procedure != nullptr); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; return lb_emit_runtime_call(p, runtime_procedure, args); @@ -9315,7 +9286,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } GB_ASSERT(runtime_procedure != nullptr); - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; return lb_emit_runtime_call(p, runtime_procedure, args); @@ -9464,14 +9435,14 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A // NOTE(bill): Generate a new name // parent$count isize name_len = prefix_name.len + 1 + 8 + 1; - char *name_text = gb_alloc_array(heap_allocator(), char, name_len); + char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); i32 name_id = cast(i32)m->anonymous_proc_lits.entries.count; name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id); String name = make_string((u8 *)name_text, name_len-1); Type *type = type_of_expr(expr); - set_procedure_abi_types(heap_allocator(), type); + set_procedure_abi_types(permanent_allocator(), type); Token token = {}; @@ -9567,7 +9538,7 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p Type *dst_type = tuple->Tuple.variables[0]->type; lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(heap_allocator(), 6); + auto args = array_make(permanent_allocator(), 6); args[0] = ok; args[1] = lb_const_string(m, pos.file); @@ -9628,7 +9599,7 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos // NOTE(bill): Panic on invalid conversion lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(heap_allocator(), 6); + auto args = array_make(permanent_allocator(), 6); args[0] = ok; args[1] = lb_const_string(m, pos.file); @@ -9912,7 +9883,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { return addr.addr; } else if (ue_expr->kind == Ast_TypeAssertion) { - gbAllocator a = heap_allocator(); GB_ASSERT(is_type_pointer(tv.type)); ast_node(ta, TypeAssertion, ue_expr); @@ -9934,7 +9904,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbValue dst_tag = lb_const_union_tag(p->module, src_type, dst_type); lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); - auto args = array_make(heap_allocator(), 6); + auto args = array_make(permanent_allocator(), 6); args[0] = ok; args[1] = lb_find_or_add_entity_string(p->module, pos.file); @@ -9959,7 +9929,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); - auto args = array_make(heap_allocator(), 6); + auto args = array_make(permanent_allocator(), 6); args[0] = ok; args[1] = lb_find_or_add_entity_string(p->module, pos.file); @@ -10110,7 +10080,6 @@ lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) { 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)); - gbAllocator a = heap_allocator(); 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); @@ -10154,7 +10123,7 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { u64 hs = fnv64a(v.text, v.len); hashed_str = lb_const_int(p->module, t_u64, hs); } else { - auto args = array_make(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = str; hashed_str = lb_emit_runtime_call(p, "default_hash_string", args); } @@ -10167,7 +10136,7 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { i64 sz = type_size_of(t); GB_ASSERT(sz <= 8); if (sz != 0) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = lb_address_from_load_or_generate_local(p, key); args[1] = lb_const_int(p->module, t_int, sz); lbValue hash = lb_emit_runtime_call(p, "default_hash_ptr", args); @@ -10197,7 +10166,7 @@ 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(heap_allocator(), 4); + auto args = array_make(permanent_allocator(), 4); args[0] = h; args[1] = key; args[2] = lb_emit_conv(p, value_addr.addr, t_rawptr); @@ -10356,7 +10325,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(ta, TypeAssertion, expr); - gbAllocator a = heap_allocator(); TokenPos pos = ast_token(expr).pos; lbValue e = lb_build_expr(p, ta->expr); Type *t = type_deref(e.type); @@ -10393,7 +10361,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_ast_node(ie, IndexExpr, expr); Type *t = base_type(type_of_expr(ie->expr)); - gbAllocator a = heap_allocator(); bool deref = is_type_pointer(t); t = base_type(type_deref(t)); @@ -10593,7 +10560,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SliceExpr, expr); - gbAllocator a = heap_allocator(); lbValue low = lb_const_int(p->module, t_int, 0); lbValue high = {}; @@ -10892,9 +10858,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (cl->elems.count == 0) { break; } - gbAllocator a = heap_allocator(); { - auto args = array_make(a, 3); + 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); @@ -10915,8 +10880,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (cl->elems.count > 0) { lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -11015,8 +10979,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (cl->elems.count > 0) { lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -11124,8 +11087,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { lbValue data = lb_slice_elem(p, slice); - auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -11218,14 +11180,13 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { break; } Type *et = bt->DynamicArray.elem; - gbAllocator a = heap_allocator(); lbValue size = lb_const_int(p->module, t_int, type_size_of(et)); lbValue align = lb_const_int(p->module, t_int, type_align_of(et)); i64 item_count = gb_max(cl->max_count, cl->elems.count); { - auto args = array_make(a, 5); + auto args = array_make(permanent_allocator(), 5); args[0] = lb_emit_conv(p, lb_addr_get_ptr(p, v), t_rawptr); args[1] = size; args[2] = align; @@ -11279,7 +11240,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { } { - auto args = array_make(a, 6); + auto args = array_make(permanent_allocator(), 6); args[0] = lb_emit_conv(p, v.addr, t_rawptr); args[1] = size; args[2] = align; @@ -11491,7 +11452,7 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { type = default_type(type); isize max_len = 7+8+1; - u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len); + u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len); isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", m->global_generated_index); m->global_generated_index++; String name = make_string(str, len-1); @@ -11571,12 +11532,11 @@ 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) { - gbAllocator a = heap_allocator(); Token token = {Token_Ident}; isize name_len = prefix.len + 1 + 20; auto suffix_id = cast(unsigned long long)id; - char *text = gb_alloc_array(a, char, name_len+1); + char *text = gb_alloc_array(permanent_allocator(), char, name_len+1); gb_snprintf(text, name_len, "%.*s-%llu", LIT(prefix), suffix_id); text[name_len] = 0; @@ -11598,7 +11558,6 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data lbModule *m = p->module; LLVMContextRef ctx = m->ctx; - gbAllocator a = heap_allocator(); CheckerInfo *info = m->info; { @@ -11976,10 +11935,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da str_lit("$enum_values"), cast(i64)entry_index); - LLVMValueRef *name_values = gb_alloc_array(heap_allocator(), LLVMValueRef, fields.count); - LLVMValueRef *value_values = gb_alloc_array(heap_allocator(), LLVMValueRef, fields.count); - defer (gb_free(heap_allocator(), name_values)); - defer (gb_free(heap_allocator(), value_values)); + LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); + LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); GB_ASSERT(is_type_integer(t->Enum.base_type)); @@ -12328,10 +12285,6 @@ void lb_generate_code(lbGenerator *gen) { LLVMModuleRef mod = gen->module.mod; CheckerInfo *info = gen->info; - Arena temp_arena = {}; - arena_init(&temp_arena, heap_allocator()); - gbAllocator temp_allocator = arena_allocator(&temp_arena); - auto *min_dep_set = &info->minimum_dependency_set; @@ -12344,8 +12297,8 @@ void lb_generate_code(lbGenerator *gen) { LLVMInitializeNativeTarget(); - char const *target_triple = alloc_cstring(heap_allocator(), build_context.metrics.target_triplet); - char const *target_data_layout = alloc_cstring(heap_allocator(), build_context.metrics.target_data_layout); + char const *target_triple = alloc_cstring(permanent_allocator(), build_context.metrics.target_triplet); + char const *target_data_layout = alloc_cstring(permanent_allocator(), build_context.metrics.target_data_layout); LLVMSetTarget(mod, target_triple); LLVMTargetRef target = {}; @@ -12367,7 +12320,7 @@ void lb_generate_code(lbGenerator *gen) { if (build_context.microarch == "native") { llvm_cpu = host_cpu_name; } else { - llvm_cpu = alloc_cstring(heap_allocator(), build_context.microarch); + llvm_cpu = alloc_cstring(permanent_allocator(), build_context.microarch); } if (gb_strcmp(llvm_cpu, host_cpu_name) == 0) { llvm_features = LLVMGetHostCPUFeatures(); @@ -12538,7 +12491,7 @@ void lb_generate_code(lbGenerator *gen) { lbValue init; DeclInfo *decl; }; - auto global_variables = array_make(heap_allocator(), 0, global_variable_max_count); + auto global_variables = array_make(permanent_allocator(), 0, global_variable_max_count); for_array(i, info->variable_init_order) { DeclInfo *d = info->variable_init_order[i]; @@ -12565,7 +12518,7 @@ void lb_generate_code(lbGenerator *gen) { lbValue g = {}; - g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(heap_allocator(), name)); + g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); g.type = alloc_type_pointer(e->type); if (e->Variable.thread_local_model != "") { LLVMSetThreadLocal(g.value, true); @@ -12619,9 +12572,6 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Global Procedures and Types"); for_array(i, info->entities) { - // arena_free_all(&temp_arena); - // gbAllocator a = temp_allocator; - Entity *e = info->entities[i]; String name = e->token.string; DeclInfo *decl = e->decl_info; @@ -12906,12 +12856,12 @@ void lb_generate_code(lbGenerator *gen) { if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { name = str_lit("mainCRTStartup"); } else { - array_init(¶ms->Tuple.variables, heap_allocator(), 2); + array_init(¶ms->Tuple.variables, permanent_allocator(), 2); params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true); params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), alloc_type_pointer(t_cstring), false, true); } - array_init(&results->Tuple.variables, heap_allocator(), 1); + array_init(&results->Tuple.variables, permanent_allocator(), 1); results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"), t_i32, false, true); Type *proc_type = alloc_type_proc(nullptr, @@ -12944,8 +12894,7 @@ void lb_generate_code(lbGenerator *gen) { } - String filepath_ll = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".ll")); - defer (gb_free(heap_allocator(), filepath_ll.text)); + String filepath_ll = concatenate_strings(temporary_allocator(), gen->output_base, STR_LIT(".ll")); TIME_SECTION("LLVM Procedure Generation"); for_array(i, m->procedures_to_generate) { @@ -13032,20 +12981,20 @@ void lb_generate_code(lbGenerator *gen) { LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile; if (build_context.build_mode == BuildMode_Assembly) { - filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".S")); + filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".S")); code_gen_file_type = LLVMAssemblyFile; } else { switch (build_context.metrics.os) { case TargetOs_windows: - filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".obj")); + filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".obj")); break; case TargetOs_darwin: case TargetOs_linux: case TargetOs_essence: - filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".o")); + filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".o")); break; case TargetOs_js: - filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".wasm-obj")); + filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".wasm-obj")); break; } } diff --git a/src/main.cpp b/src/main.cpp index 3717a4147..d0d2e2bbb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1643,12 +1643,15 @@ int main(int arg_count, char const **arg_ptr) { timings_init(timings, str_lit("Total Time"), 128); defer (timings_destroy(timings)); + arena_init(&permanent_arena, heap_allocator()); + arena_init(&temporary_arena, heap_allocator()); + arena_init(&global_ast_arena, heap_allocator()); + init_string_buffer_memory(); init_string_interner(); init_global_error_collector(); init_keyword_hash_table(); global_big_int_init(); - arena_init(&global_ast_arena, heap_allocator()); array_init(&library_collections, heap_allocator()); // NOTE(bill): 'core' cannot be (re)defined by the user @@ -1795,6 +1798,8 @@ int main(int arg_count, char const **arg_ptr) { return 1; } + arena_free_all(&temporary_arena); + if (build_context.generate_docs) { // generate_documentation(&parser); return 0; @@ -1812,6 +1817,7 @@ int main(int arg_count, char const **arg_ptr) { check_parsed_files(&checker); } + arena_free_all(&temporary_arena); if (build_context.no_output_files) { if (build_context.query_data_set_settings.ok) { @@ -1842,6 +1848,8 @@ int main(int arg_count, char const **arg_ptr) { } lb_generate_code(&gen); + arena_free_all(&temporary_arena); + switch (build_context.build_mode) { case BuildMode_Executable: case BuildMode_DynamicLibrary: @@ -1919,12 +1927,18 @@ int main(int arg_count, char const **arg_ptr) { timings_start_section(timings, str_lit("llvm ir gen")); ir_gen_tree(&ir_gen); + arena_free_all(&temporary_arena); + timings_start_section(timings, str_lit("llvm ir opt tree")); ir_opt_tree(&ir_gen); + arena_free_all(&temporary_arena); + timings_start_section(timings, str_lit("llvm ir print")); print_llvm_ir(&ir_gen); + arena_free_all(&temporary_arena); + String output_name = ir_gen.output_name; String output_base = ir_gen.output_base; diff --git a/src/parser.cpp b/src/parser.cpp index 794ff231d..5e04aea17 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -108,6 +108,30 @@ Token ast_token(Ast *node) { return empty_token; } + +gb_global gbAtomic64 total_allocated_node_memory = {0}; +gb_global gbAtomic64 total_subtype_node_memory_test = {0}; + +isize ast_node_size(AstKind kind) { + return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *)); + +} +// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ +Ast *alloc_ast_node(AstFile *f, AstKind kind) { + gbAllocator a = ast_allocator(f); + + isize size = ast_node_size(kind); + + gb_atomic64_fetch_add(&total_allocated_node_memory, cast(i64)(gb_size_of(Ast))); + gb_atomic64_fetch_add(&total_subtype_node_memory_test, cast(i64)(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind])); + + // Ast *node = gb_alloc_item(a, Ast); + Ast *node = cast(Ast *)gb_alloc(a, size); + node->kind = kind; + node->file = f; + return node; +} + Ast *clone_ast(Ast *node); Array clone_ast_array(Array array) { Array result = {}; @@ -125,7 +149,7 @@ Ast *clone_ast(Ast *node) { return nullptr; } Ast *n = alloc_ast_node(node->file, node->kind); - gb_memmove(n, node, gb_size_of(Ast)); + gb_memmove(n, node, ast_node_size(node->kind)); switch (n->kind) { default: GB_PANIC("Unhandled Ast %.*s", LIT(ast_strings[n->kind])); break; @@ -463,23 +487,6 @@ bool ast_node_expect(Ast *node, AstKind kind) { return true; } - -gb_global gbAtomic64 total_allocated_node_memory = {0}; -gb_global gbAtomic64 total_subtype_node_memory_test = {0}; - -// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ -Ast *alloc_ast_node(AstFile *f, AstKind kind) { - gbAllocator a = ast_allocator(f); - - gb_atomic64_fetch_add(&total_allocated_node_memory, cast(i64)(gb_size_of(Ast))); - gb_atomic64_fetch_add(&total_subtype_node_memory_test, cast(i64)(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind])); - - Ast *node = gb_alloc_item(a, Ast); - node->kind = kind; - node->file = f; - return node; -} - Ast *ast_bad_expr(AstFile *f, Token begin, Token end) { Ast *result = alloc_ast_node(f, Ast_BadExpr); result->BadExpr.begin = begin; diff --git a/src/parser.hpp b/src/parser.hpp index 8e210876f..0804652a3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -655,6 +655,7 @@ struct Ast { Scope * scope; TypeAndValue tav; + // IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant union { #define AST_KIND(_kind_name_, name, ...) GB_JOIN2(Ast, _kind_name_) _kind_name_; AST_KINDS diff --git a/src/types.cpp b/src/types.cpp index 1a55c9b05..17dcedf45 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -771,7 +771,8 @@ void set_base_type(Type *t, Type *base) { Type *alloc_type(TypeKind kind) { - gbAllocator a = heap_allocator(); + // gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); Type *t = gb_alloc_item(a, Type); zero_item(t); t->kind = kind; @@ -2340,7 +2341,7 @@ Selection lookup_field_from_index(Type *type, i64 index) { GB_ASSERT(is_type_struct(type) || is_type_union(type) || is_type_tuple(type)); type = base_type(type); - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); isize max_count = 0; switch (type->kind) { case Type_Struct: max_count = type->Struct.fields.count; break; @@ -2397,7 +2398,6 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty return empty_selection; } - gbAllocator a = heap_allocator(); Type *type = type_deref(type_); bool is_ptr = type != type_; sel.indirect = sel.indirect || is_ptr; @@ -2986,7 +2986,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) { } Array type_set_offsets_of(Array const &fields, bool is_packed, bool is_raw_union) { - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); auto offsets = array_make(a, fields.count); i64 curr_offset = 0; if (is_raw_union) { From 30d922b05938ccde7e49f027e2e65ca28849b218 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 18:11:49 +0000 Subject: [PATCH 045/170] Make `set_procedure_abi_types` use the permanent_allocator --- src/check_expr.cpp | 4 ++-- src/check_type.cpp | 10 ++++++---- src/ir.cpp | 20 ++++++++++---------- src/ir_print.cpp | 8 ++++---- src/llvm_backend.cpp | 16 ++++++++-------- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a8e7550a1..cf4304053 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -89,7 +89,7 @@ Type * check_init_variable (CheckerContext *c, Entity *e, Operand * Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); -void set_procedure_abi_types(gbAllocator a, Type *type); +void set_procedure_abi_types(Type *type); void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); @@ -1012,7 +1012,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } if (modify_type) { - set_procedure_abi_types(c->allocator, source); + set_procedure_abi_types(source); } return true; diff --git a/src/check_type.cpp b/src/check_type.cpp index ace1ef898..6c9e82aa1 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1390,7 +1390,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) { if (show_error) { - set_procedure_abi_types(ctx->allocator, poly_type); + set_procedure_abi_types(poly_type); } return poly_type; } @@ -2508,7 +2508,7 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type return false; } -void set_procedure_abi_types(gbAllocator allocator, Type *type) { +void set_procedure_abi_types(Type *type) { type = base_type(type); if (type->kind != Type_Proc) { return; @@ -2518,6 +2518,8 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) { return; } + gbAllocator allocator = permanent_allocator(); + u32 flags = type->flags; type->flags |= TypeFlag_InProcessOfCheckingABI; @@ -2550,13 +2552,13 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) { for (i32 i = 0; i < type->Proc.param_count; i++) { Entity *e = type->Proc.params->Tuple.variables[i]; if (e->kind == Entity_Variable) { - set_procedure_abi_types(allocator, e->type); + set_procedure_abi_types(e->type); } } for (i32 i = 0; i < type->Proc.result_count; i++) { Entity *e = type->Proc.results->Tuple.variables[i]; if (e->kind == Entity_Variable) { - set_procedure_abi_types(allocator, e->type); + set_procedure_abi_types(e->type); } } diff --git a/src/ir.cpp b/src/ir.cpp index dc77906e8..ee177edd6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1159,7 +1159,7 @@ irValue *ir_instr_atomic_cxchg(irProcedure *p, Type *type, irValue *address, irV GB_ASSERT(type->Tuple.variables.count == 2); Type *elem = type->Tuple.variables[0]->type; // LEAK TODO(bill): LLVM returns {T, i1} whilst Odin does {T, bool}, fix this mapping hack - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); Type *llvm_type = alloc_type_tuple(); array_init(&llvm_type->Tuple.variables, a, 0, 2); array_add (&llvm_type->Tuple.variables, alloc_entity_field(nullptr, blank_token, elem, false, 0)); @@ -1799,7 +1799,7 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial if (zero_initialized) { ir_emit_zero_init(proc, instr, expr); } - set_procedure_abi_types(heap_allocator(), e->type); + set_procedure_abi_types(e->type); // if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) { // if (proc->module->generate_debug_info && proc->entity != nullptr) { @@ -3282,7 +3282,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array const &ar context_ptr = ir_find_or_generate_context_ptr(p); } - set_procedure_abi_types(heap_allocator(), pt); + set_procedure_abi_types(pt); bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; @@ -6636,7 +6636,7 @@ void ir_mangle_add_sub_type_name(irModule *m, Entity *field, String parent) { return; } if (is_type_proc(field->type)) { - set_procedure_abi_types(heap_allocator(), field->type); + set_procedure_abi_types(field->type); } String cn = field->token.string; @@ -6733,7 +6733,7 @@ irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, i String name = make_string(name_text, name_len-1); Type *type = type_of_expr(expr); - set_procedure_abi_types(heap_allocator(), type); + set_procedure_abi_types(type); irValue *value = ir_value_procedure(m, nullptr, type, pl->type, pl->body, name); value->Proc.tags = pl->tags; @@ -7584,7 +7584,7 @@ irValue *ir_build_call_expr(irProcedure *proc, Ast *expr) { Type *proc_type_ = base_type(ir_type(value)); GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; - set_procedure_abi_types(heap_allocator(), proc_type_); + set_procedure_abi_types(proc_type_); if (is_call_expr_field_value(ce)) { auto args = array_make(ir_allocator(), pt->param_count); @@ -9574,7 +9574,7 @@ void ir_build_nested_proc(irProcedure *proc, AstProcLit *pd, Entity *e) { name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid); String name = make_string(name_text, name_len-1); - set_procedure_abi_types(heap_allocator(), e->type); + set_procedure_abi_types(e->type); irValue *value = ir_value_procedure(proc->module, e, e->type, pd->type, pd->body, name); value->Proc.tags = pd->tags; @@ -9673,7 +9673,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) { return; } - set_procedure_abi_types(heap_allocator(), e->type); + set_procedure_abi_types(e->type); irValue *value = ir_value_procedure(proc->module, e, e->type, pl->type, pl->body, name); value->Proc.tags = pl->tags; @@ -11471,7 +11471,7 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) { void ir_build_proc(irValue *value, irProcedure *parent) { irProcedure *proc = &value->Proc; - set_procedure_abi_types(heap_allocator(), proc->type); + set_procedure_abi_types(proc->type); proc->parent = parent; @@ -12612,7 +12612,7 @@ void ir_gen_tree(irGen *s) { Ast *type_expr = pl->type; - set_procedure_abi_types(heap_allocator(), e->type); + set_procedure_abi_types(e->type); irValue *p = ir_value_procedure(m, e, e->type, type_expr, body, name); p->Proc.tags = pl->tags; p->Proc.inlining = pl->inlining; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index ceb95c5c3..2d15f176b 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -296,7 +296,7 @@ void ir_print_alignment_prefix_hack(irFileBuffer *f, i64 alignment) { void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) { - set_procedure_abi_types(heap_allocator(), t); + set_procedure_abi_types(t); GB_ASSERT(is_type_proc(t)); t = base_type(t); @@ -325,7 +325,7 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) { void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) { - set_procedure_abi_types(heap_allocator(), t); + set_procedure_abi_types(t); i64 word_bits = 8*build_context.word_size; t = base_type(t); @@ -2189,7 +2189,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { irInstrCall *call = &instr->Call; Type *proc_type = base_type(ir_type(call->value)); GB_ASSERT(is_type_proc(proc_type)); - set_procedure_abi_types(heap_allocator(), proc_type); + set_procedure_abi_types(proc_type); bool is_c_vararg = proc_type->Proc.c_vararg; Type *result_type = call->type; @@ -2396,7 +2396,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { - set_procedure_abi_types(heap_allocator(), proc->type); + set_procedure_abi_types(proc->type); if (proc->body == nullptr) { ir_write_str_lit(f, "declare "); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 43f4125ba..121917740 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1421,7 +1421,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg); } } else { - set_procedure_abi_types(heap_allocator(), type); + set_procedure_abi_types(type); LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx); if (type->Proc.return_by_pointer) { // Void @@ -1950,7 +1950,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Type_Proc: { return nullptr; - // set_procedure_abi_types(heap_allocator(), type); + // set_procedure_abi_types(type); // LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx); // isize offset = 0; @@ -2141,7 +2141,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { Type *pt = base_type(entity->type); GB_ASSERT(pt->kind == Type_Proc); - set_procedure_abi_types(permanent_allocator(), entity->type); + set_procedure_abi_types(entity->type); p->type = entity->type; p->type_expr = decl->type_expr; @@ -2978,7 +2978,7 @@ void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) { name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%d", LIT(p->name), LIT(pd_name), guid); String name = make_string(cast(u8 *)name_text, name_len-1); - set_procedure_abi_types(permanent_allocator(), e->type); + set_procedure_abi_types(e->type); e->Procedure.link_name = name; @@ -3115,7 +3115,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { return; } - set_procedure_abi_types(permanent_allocator(), e->type); + set_procedure_abi_types(e->type); e->Procedure.link_name = name; lbProcedure *nested_proc = lb_create_procedure(p->module, e); @@ -7376,7 +7376,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, LLVMBuildUnreachable(p->builder); }); - set_procedure_abi_types(permanent_allocator(), pt); + set_procedure_abi_types(pt); bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; @@ -8595,7 +8595,7 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { Type *proc_type_ = base_type(value.type); GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; - set_procedure_abi_types(permanent_allocator(), proc_type_); + set_procedure_abi_types(proc_type_); if (is_call_expr_field_value(ce)) { auto args = array_make(permanent_allocator(), pt->param_count); @@ -9442,7 +9442,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A String name = make_string((u8 *)name_text, name_len-1); Type *type = type_of_expr(expr); - set_procedure_abi_types(permanent_allocator(), type); + set_procedure_abi_types(type); Token token = {}; From 17ec3e72a68b805fc202174722778545c956f433 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 18:45:40 +0000 Subject: [PATCH 046/170] Add SCOPED_TEMPORARY_BLOCK for temporary allocations within a block --- src/check_decl.cpp | 9 ++++---- src/check_expr.cpp | 53 ++++++++++++++++++++++---------------------- src/check_stmt.cpp | 30 ++++++++++++------------- src/check_type.cpp | 45 ++++++++++++++++++------------------- src/checker.cpp | 35 +++++++++++++---------------- src/checker.hpp | 2 -- src/common.cpp | 35 ++++++++++++++++++++++++++++- src/gb/gb.h | 2 +- src/ir.cpp | 31 ++++++++++++-------------- src/ir_print.cpp | 6 ++--- src/llvm_backend.cpp | 28 +++++++++++++++++++---- src/main.cpp | 1 + 12 files changed, 159 insertions(+), 118 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1aafa6e1c..bfe703853 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -121,8 +121,8 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be // an extra allocation - auto operands = array_make(ctx->allocator, 0, 2*lhs_count); - defer (array_free(&operands)); + SCOPED_TEMPORARY_BLOCK(); + auto operands = array_make(temporary_allocator(), 0, 2*lhs_count); check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false); isize rhs_count = operands.count; @@ -317,7 +317,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) break; default: error(e->token, "Only struct types can have custom atom operations"); - gb_free(heap_allocator(), ac.atom_op_table); break; } } @@ -638,7 +637,7 @@ String handle_link_name(CheckerContext *ctx, Token token, String link_name, Stri error(token, "'link_name' and 'link_prefix' cannot be used together"); } else { isize len = link_prefix.len + token.string.len; - u8 *name = gb_alloc_array(ctx->allocator, u8, len+1); + u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1); gb_memmove(name, &link_prefix[0], link_prefix.len); gb_memmove(name+link_prefix.len, &token.string[0], token.string.len); name[len] = 0; @@ -975,7 +974,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) ast_node(pg, ProcGroup, d->init_expr); - pge->entities = array_make(ctx->allocator, 0, pg->args.count); + pge->entities = array_make(permanent_allocator(), 0, pg->args.count); // NOTE(bill): This must be set here to prevent cycles in checking if someone // places the entity within itself diff --git a/src/check_expr.cpp b/src/check_expr.cpp index cf4304053..755ceb634 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -267,7 +267,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti CheckerContext nctx = *c; - Scope *scope = create_scope(base_entity->scope, a); + Scope *scope = create_scope(base_entity->scope); scope->flags |= ScopeFlag_Proc; nctx.scope = scope; nctx.allow_polymorphic_types = true; @@ -366,7 +366,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti u64 tags = base_entity->Procedure.tags; Ast *ident = clone_ast(base_entity->identifier); Token token = ident->Ident.token; - DeclInfo *d = make_decl_info(nctx.allocator, scope, old_decl->parent); + DeclInfo *d = make_decl_info(scope, old_decl->parent); d->gen_proc_type = final_proc_type; d->type_expr = pl->type; d->proc_lit = proc_lit; @@ -1832,12 +1832,9 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } + SCOPED_TEMPORARY_BLOCK(); gbString err_str = nullptr; - defer (if (err_str != nullptr) { - gb_string_free(err_str); - }); - if (check_is_assignable_to(c, x, y->type) || check_is_assignable_to(c, y, x->type)) { Type *err_type = x->type; @@ -1867,8 +1864,8 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } gbString type_string = type_to_string(err_type); defer (gb_string_free(type_string)); - err_str = gb_string_make(c->allocator, - gb_bprintf("operator '%.*s' not defined for type '%s'", LIT(token_strings[op]), type_string)); + err_str = gb_string_make(temporary_allocator(), + gb_bprintf("operator '%.*s' not defined for type '%s'", LIT(token_strings[op]), type_string)); } } else { gbString xt, yt; @@ -1882,8 +1879,7 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } else { yt = type_to_string(y->type); } - err_str = gb_string_make(c->allocator, - gb_bprintf("mismatched types '%s' and '%s'", xt, yt)); + err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt)); gb_string_free(yt); gb_string_free(xt); } @@ -2978,9 +2974,10 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { case Type_Union: if (!is_operand_nil(*operand) && !is_operand_undef(*operand)) { + SCOPED_TEMPORARY_BLOCK(); + isize count = t->Union.variants.count; - ValidIndexAndScore *valids = gb_alloc_array(c->allocator, ValidIndexAndScore, count); - defer (gb_free(c->allocator, valids)); + ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count); isize valid_count = 0; isize first_success_index = -1; for_array(i, t->Union.variants) { @@ -4739,7 +4736,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 gb_string_free(type_str); return false; } - gbAllocator a = c->allocator; + gbAllocator a = permanent_allocator(); Type *tuple = alloc_type_tuple(); @@ -5356,7 +5353,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(c->scope, c->allocator); + scope = create_scope(c->scope); soa_struct->Struct.scope = scope; String params_xyzw[4] = { @@ -5389,7 +5386,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(old_struct->Struct.scope->parent, c->allocator); + scope = create_scope(old_struct->Struct.scope->parent); soa_struct->Struct.scope = scope; for_array(i, old_struct->Struct.fields) { @@ -6539,11 +6536,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { bool show_error = show_error_mode == CallArgumentMode_ShowErrors; CallArgumentError err = CallArgumentError_None; + SCOPED_TEMPORARY_BLOCK(); + isize param_count = pt->param_count; - bool *visited = gb_alloc_array(c->allocator, bool, param_count); - defer (gb_free(c->allocator, visited)); - auto ordered_operands = array_make(c->allocator, param_count); - defer (array_free(&ordered_operands)); + bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); + auto ordered_operands = array_make(temporary_allocator(), param_count); defer ({ for_array(i, ordered_operands) { Operand const &o = ordered_operands[i]; @@ -7385,13 +7382,15 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper Array ordered_operands = operands; if (!named_fields) { - ordered_operands = array_make(c->allocator, param_count); + ordered_operands = array_make(permanent_allocator(), param_count); array_copy(&ordered_operands, operands, 0); } else { - bool *visited = gb_alloc_array(c->allocator, bool, param_count); + SCOPED_TEMPORARY_BLOCK(); + + bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); // LEAK(bill) - ordered_operands = array_make(c->allocator, param_count); + ordered_operands = array_make(permanent_allocator(), param_count); for_array(i, ce->args) { Ast *arg = ce->args[i]; @@ -7549,8 +7548,6 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper } { - gbAllocator a = c->allocator; - bool failure = false; Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure); if (found_entity) { @@ -8213,7 +8210,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type Type *type = alloc_type(Type_Proc); check_open_scope(&ctx, pl->type); { - decl = make_decl_info(ctx.allocator, ctx.scope, ctx.decl); + decl = make_decl_info(ctx.scope, ctx.decl); decl->proc_lit = node; ctx.decl = decl; defer (ctx.decl = ctx.decl->parent); @@ -8510,7 +8507,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } if (cl->elems[0]->kind == Ast_FieldValue) { - bool *fields_visited = gb_alloc_array(c->allocator, bool, field_count); + SCOPED_TEMPORARY_BLOCK(); + + bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -10092,7 +10091,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type error(x.expr, "Expected a constant string for the inline asm constraints parameter"); } - Scope *scope = create_scope(c->scope, heap_allocator()); + Scope *scope = create_scope(c->scope); scope->flags |= ScopeFlag_Proc; Type *params = alloc_type_tuple(); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e6902f6a3..d722ea8ee 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -640,9 +640,10 @@ void add_constant_switch_case(CheckerContext *ctx, Map *seen, Oper HashKey key = hash_exact_value(operand.value); TypeAndToken *found = map_get(seen, key); if (found != nullptr) { + SCOPED_TEMPORARY_BLOCK(); + isize count = multi_map_count(seen, key); - TypeAndToken *taps = gb_alloc_array(ctx->allocator, TypeAndToken, count); - defer (gb_free(ctx->allocator, taps)); + TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); multi_map_get_all(seen, key, taps); for (isize i = 0; i < count; i++) { @@ -859,7 +860,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { token.pos = ast_token(ss->body).pos; token.string = str_lit("true"); - x.expr = gb_alloc_item(ctx->allocator, Ast); + x.expr = gb_alloc_item(permanent_allocator(), Ast); x.expr->kind = Ast_Ident; x.expr->Ident.token = token; } @@ -1025,8 +1026,8 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_ASSERT(is_type_enum(et)); auto fields = et->Enum.fields; - auto unhandled = array_make(ctx->allocator, 0, fields.count); - defer (array_free(&unhandled)); + SCOPED_TEMPORARY_BLOCK(); + auto unhandled = array_make(temporary_allocator(), 0, fields.count); for_array(i, fields) { Entity *f = fields[i]; @@ -1265,8 +1266,8 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_ASSERT(is_type_union(ut)); auto variants = ut->Union.variants; - auto unhandled = array_make(ctx->allocator, 0, variants.count); - defer (array_free(&unhandled)); + SCOPED_TEMPORARY_BLOCK(); + auto unhandled = array_make(temporary_allocator(), 0, variants.count); for_array(i, variants) { Type *t = variants[i]; @@ -1433,12 +1434,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { return; } + SCOPED_TEMPORARY_BLOCK(); + // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be // an extra allocation - auto lhs_operands = array_make(ctx->allocator, lhs_count); - auto rhs_operands = array_make(ctx->allocator, 0, 2*lhs_count); - defer (array_free(&lhs_operands)); - defer (array_free(&rhs_operands)); + auto lhs_operands = array_make(temporary_allocator(), lhs_count); + auto rhs_operands = array_make(temporary_allocator(), 0, 2*lhs_count); for_array(i, as->lhs) { if (is_blank_ident(as->lhs[i])) { @@ -1462,8 +1463,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } - auto lhs_to_ignore = array_make(ctx->allocator, lhs_count); - defer (array_free(&lhs_to_ignore)); + auto lhs_to_ignore = array_make(temporary_allocator(), lhs_count); isize max = gb_min(lhs_count, rhs_count); // NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working @@ -1878,7 +1878,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { DeclInfo *d = decl_info_of_entity(e); GB_ASSERT(d == nullptr); add_entity(ctx->checker, ctx->scope, e->identifier, e); - d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl); + d = make_decl_info(ctx->scope, ctx->decl); add_entity_and_decl_info(ctx, e->identifier, e, d); } @@ -2036,7 +2036,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { case_ast_node(vd, ValueDecl, node); if (vd->is_mutable) { - Entity **entities = gb_alloc_array(ctx->allocator, Entity *, vd->names.count); + Entity **entities = gb_alloc_array(permanent_allocator(), Entity *, vd->names.count); isize entity_count = 0; isize new_name_count = 0; diff --git a/src/check_type.cpp b/src/check_type.cpp index 6c9e82aa1..af0c70119 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -400,7 +400,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< } } - auto entities = array_make(ctx->allocator, 0, variable_count); + auto entities = array_make(permanent_allocator(), 0, variable_count); for_array(i, params) { Ast *param = params[i]; @@ -596,7 +596,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array(ctx->allocator, 0, variant_count); + auto variants = array_make(permanent_allocator(), 0, variant_count); union_type->Union.scope = ctx->scope; @@ -618,7 +618,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array(ctx->allocator, 0, variable_count); + auto entities = array_make(permanent_allocator(), 0, variable_count); for_array(i, params) { Ast *param = params[i]; @@ -869,7 +869,7 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast enum_type->Enum.base_type = base_type; enum_type->Enum.scope = ctx->scope; - auto fields = array_make(ctx->allocator, 0, et->fields.count); + auto fields = array_make(permanent_allocator(), 0, et->fields.count); Type *constant_type = enum_type; if (named_type != nullptr) { @@ -986,9 +986,9 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node) ast_node(bft, BitFieldType, node); GB_ASSERT(is_type_bit_field(bit_field_type)); - auto fields = array_make(ctx->allocator, 0, bft->fields.count); - auto sizes = array_make (ctx->allocator, 0, bft->fields.count); - auto offsets = array_make (ctx->allocator, 0, bft->fields.count); + auto fields = array_make(permanent_allocator(), 0, bft->fields.count); + auto sizes = array_make (permanent_allocator(), 0, bft->fields.count); + auto offsets = array_make (permanent_allocator(), 0, bft->fields.count); scope_reserve(ctx->scope, bft->fields.count); @@ -1549,7 +1549,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is bool is_variadic = false; isize variadic_index = -1; bool is_c_vararg = false; - auto variables = array_make(ctx->allocator, 0, variable_count); + auto variables = array_make(permanent_allocator(), 0, variable_count); for_array(i, params) { Ast *param = params[i]; if (param->kind != Ast_Field) { @@ -1891,7 +1891,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { } } - auto variables = array_make(ctx->allocator, 0, variable_count); + auto variables = array_make(permanent_allocator(), 0, variable_count); for_array(i, results) { ast_node(field, Field, results[i]); Ast *default_value = unparen_expr(field->default_value); @@ -2781,7 +2781,6 @@ void init_map_entry_type(Type *type) { // NOTE(bill): The preload types may have not been set yet GB_ASSERT(t_map_key != nullptr); - gbAllocator a = heap_allocator(); Type *entry_type = alloc_type_struct(); /* @@ -2793,9 +2792,9 @@ void init_map_entry_type(Type *type) { } */ Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid); - Scope *s = create_scope(builtin_pkg->scope, a); + Scope *s = create_scope(builtin_pkg->scope); - auto fields = array_make(a, 0, 3); + auto fields = array_make(permanent_allocator(), 0, 3); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), t_map_key, false, 0, EntityState_Resolved)); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved)); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 2, EntityState_Resolved)); @@ -2803,7 +2802,6 @@ void init_map_entry_type(Type *type) { entry_type->Struct.fields = fields; - // type_set_offsets(a, entry_type); type->Map.entry_type = entry_type; } @@ -2826,15 +2824,14 @@ void init_map_internal_types(Type *type) { entries: [dynamic]EntryType; } */ - gbAllocator a = heap_allocator(); Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid); - Scope *s = create_scope(builtin_pkg->scope, a); + Scope *s = create_scope(builtin_pkg->scope); Type *hashes_type = alloc_type_slice(t_int); Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type); - auto fields = array_make(a, 0, 2); + auto fields = array_make(permanent_allocator(), 0, 2); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved)); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved)); @@ -2902,7 +2899,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_ soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(ctx->scope, ctx->allocator); + scope = create_scope(ctx->scope); soa_struct->Struct.scope = scope; String params_xyzw[4] = { @@ -2935,7 +2932,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_ soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator); + scope = create_scope(old_struct->Struct.scope->parent); soa_struct->Struct.scope = scope; for_array(i, old_struct->Struct.fields) { @@ -2996,7 +2993,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_ soa_struct->Struct.soa_count = 0; soa_struct->Struct.is_polymorphic = true; - scope = create_scope(ctx->scope, ctx->allocator); + scope = create_scope(ctx->scope); soa_struct->Struct.scope = scope; } else if (is_type_array(elem)) { Type *old_array = base_type(elem); @@ -3010,7 +3007,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_ soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = 0; - scope = create_scope(ctx->scope, ctx->allocator); + scope = create_scope(ctx->scope); soa_struct->Struct.scope = scope; String params_xyzw[4] = { @@ -3046,7 +3043,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_ soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = 0; - scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator); + scope = create_scope(old_struct->Struct.scope->parent); soa_struct->Struct.scope = scope; for_array(i, old_struct->Struct.fields) { @@ -3113,7 +3110,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As soa_struct->Struct.soa_count = 0; soa_struct->Struct.is_polymorphic = true; - scope = create_scope(ctx->scope, ctx->allocator); + scope = create_scope(ctx->scope); soa_struct->Struct.scope = scope; } else if (is_type_array(elem)) { Type *old_array = base_type(elem); @@ -3127,7 +3124,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = 0; - scope = create_scope(ctx->scope, ctx->allocator); + scope = create_scope(ctx->scope); soa_struct->Struct.scope = scope; String params_xyzw[4] = { @@ -3162,7 +3159,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = 0; - scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator); + scope = create_scope(old_struct->Struct.scope->parent); soa_struct->Struct.scope = scope; for_array(i, old_struct->Struct.fields) { diff --git a/src/checker.cpp b/src/checker.cpp index a07a3ffbe..76d8cceb3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -187,8 +187,8 @@ void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { array_init (&d->labels, heap_allocator()); } -DeclInfo *make_decl_info(gbAllocator a, Scope *scope, DeclInfo *parent) { - DeclInfo *d = gb_alloc_item(a, DeclInfo); +DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { + DeclInfo *d = gb_alloc_item(permanent_allocator(), DeclInfo); init_decl_info(d, scope, parent); return d; } @@ -219,8 +219,8 @@ bool decl_info_has_init(DeclInfo *d) { -Scope *create_scope(Scope *parent, gbAllocator allocator, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) { - Scope *s = gb_alloc_item(allocator, Scope); +Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) { + Scope *s = gb_alloc_item(permanent_allocator(), Scope); s->parent = parent; string_map_init(&s->elements, heap_allocator(), init_elements_capacity); ptr_set_init(&s->imported, heap_allocator(), 0); @@ -244,7 +244,7 @@ Scope *create_scope_from_file(CheckerContext *c, AstFile *f) { GB_ASSERT(f->pkg != nullptr); GB_ASSERT(f->pkg->scope != nullptr); - Scope *s = create_scope(f->pkg->scope, c->allocator); + Scope *s = create_scope(f->pkg->scope); array_reserve(&s->delayed_imports, f->imports.count); array_reserve(&s->delayed_directives, f->directive_count); @@ -264,7 +264,7 @@ Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) { decl_count += pkg->files[i]->decls.count; } isize init_elements_capacity = 2*decl_count; - Scope *s = create_scope(builtin_pkg->scope, c->allocator, init_elements_capacity); + Scope *s = create_scope(builtin_pkg->scope, init_elements_capacity); s->flags |= ScopeFlag_Pkg; s->pkg = pkg; @@ -324,7 +324,7 @@ void check_open_scope(CheckerContext *c, Ast *node) { GB_ASSERT(node->kind == Ast_Invalid || is_ast_stmt(node) || is_ast_type(node)); - Scope *scope = create_scope(c->scope, c->allocator); + Scope *scope = create_scope(c->scope); add_scope(c, node, scope); switch (node->kind) { case Ast_ProcType: @@ -699,7 +699,7 @@ void init_universal(void) { builtin_pkg->name = str_lit("builtin"); builtin_pkg->kind = Package_Normal; - builtin_pkg->scope = create_scope(nullptr, a); + builtin_pkg->scope = create_scope(nullptr); builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; builtin_pkg->scope->pkg = builtin_pkg; @@ -707,7 +707,7 @@ void init_universal(void) { intrinsics_pkg->name = str_lit("intrinsics"); intrinsics_pkg->kind = Package_Normal; - intrinsics_pkg->scope = create_scope(nullptr, a); + intrinsics_pkg->scope = create_scope(nullptr); intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; intrinsics_pkg->scope->pkg = intrinsics_pkg; @@ -715,7 +715,7 @@ void init_universal(void) { config_pkg->name = str_lit("config"); config_pkg->kind = Package_Normal; - config_pkg->scope = create_scope(nullptr, a); + config_pkg->scope = create_scope(nullptr); config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; config_pkg->scope->pkg = config_pkg; @@ -872,7 +872,6 @@ CheckerContext make_checker_context(Checker *c) { CheckerContext ctx = c->init_ctx; ctx.checker = c; ctx.info = &c->info; - ctx.allocator = c->allocator; ctx.scope = builtin_pkg->scope; ctx.pkg = builtin_pkg; @@ -906,8 +905,6 @@ bool init_checker(Checker *c, Parser *parser) { isize total_token_count = c->parser->total_token_count; isize arena_size = 2 * item_size * total_token_count; - c->allocator = heap_allocator(); - c->init_ctx = make_checker_context(c); return true; } @@ -2597,7 +2594,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { if (valid && build_context.use_llvm_api) { if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable); + ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); } ac->atom_op_table->op[TypeAtomOp_index_get] = e; } @@ -2656,7 +2653,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { if (valid && build_context.use_llvm_api) { if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable); + ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); } ac->atom_op_table->op[TypeAtomOp_index_set] = e; } @@ -2738,7 +2735,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { if (valid && build_context.use_llvm_api) { if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable); + ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); } ac->atom_op_table->op[TypeAtomOp_slice] = e; } @@ -3090,7 +3087,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { } Ast *init_expr = value; - DeclInfo *d = make_decl_info(heap_allocator(), c->scope, c->decl); + DeclInfo *d = make_decl_info(c->scope, c->decl); d->entity = e; d->type_expr = vd->type; d->init_expr = init_expr; @@ -3118,7 +3115,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Token token = name->Ident.token; Ast *fl = c->foreign_context.curr_library; - DeclInfo *d = make_decl_info(c->allocator, c->scope, c->decl); + DeclInfo *d = make_decl_info(c->scope, c->decl); Entity *e = nullptr; d->attributes = vd->attributes; @@ -4317,7 +4314,7 @@ void check_parsed_files(Checker *c) { for_array(i, c->parser->packages) { AstPackage *p = c->parser->packages[i]; Scope *scope = create_scope_from_package(&c->init_ctx, p); - p->decl_info = make_decl_info(c->allocator, scope, c->init_ctx.decl); + p->decl_info = make_decl_info(scope, c->init_ctx.decl); string_map_set(&c->info.packages, p->fullpath, p); if (scope->flags&ScopeFlag_Init) { diff --git a/src/checker.hpp b/src/checker.hpp index 88e9451ee..ed4809748 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -301,7 +301,6 @@ struct CheckerContext { ProcCallingConvention curr_proc_calling_convention; bool in_proc_sig; ForeignContext foreign_context; - gbAllocator allocator; CheckerTypePath *type_path; isize type_level; // TODO(bill): Actually handle correctly @@ -331,7 +330,6 @@ struct Checker { Array procs_with_deferred_to_check; CheckerContext *curr_ctx; - gbAllocator allocator; CheckerContext init_ctx; }; diff --git a/src/common.cpp b/src/common.cpp index 567655c04..05ebdd4c5 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -451,7 +451,6 @@ void arena_free_all(Arena *arena) { - GB_ALLOCATOR_PROC(arena_allocator_proc); gbAllocator arena_allocator(Arena *arena) { @@ -492,6 +491,38 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) { return ptr; } +struct SCOPED_TEMP_ARENA_MEMORY { + Arena *arena; + u8 * ptr; + u8 * end; + u8 * prev; + isize total_used; + isize block_count; + + SCOPED_TEMP_ARENA_MEMORY(Arena *the_arena) { + GB_ASSERT(!the_arena->use_mutex); + arena = the_arena; + ptr = arena->ptr; + end = arena->end; + prev = arena->prev; + total_used = arena->total_used; + block_count = arena->blocks.count; + } + ~SCOPED_TEMP_ARENA_MEMORY() { + if (arena->blocks.count != block_count) { + for (isize i = block_count; i < arena->blocks.count; i++) { + gb_free(arena->backing, arena->blocks[i]); + } + arena->blocks.count = block_count; + } + arena->ptr = ptr; + arena->end = end; + arena->prev = prev; + arena->total_used = total_used; + } +}; + + gb_global Arena permanent_arena = {}; @@ -504,6 +535,8 @@ gbAllocator temporary_allocator() { return arena_allocator(&temporary_arena); } +#define SCOPED_TEMPORARY_BLOCK() auto GB_DEFER_3(_SCOPED_TEMPORARY_BLOCK_) = SCOPED_TEMP_ARENA_MEMORY(&temporary_arena) + diff --git a/src/gb/gb.h b/src/gb/gb.h index 848f27628..f13693000 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -5156,7 +5156,7 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { index = core * a->threads_per_core + thread_index; thread = pthread_self(); - + cpuset_t mn; CPU_ZERO(&mn); diff --git a/src/ir.cpp b/src/ir.cpp index ee177edd6..7b6301e30 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2132,7 +2132,7 @@ irDebugInfo *ir_add_debug_info_field(irModule *module, irDebugInfo *scope, Entit if (e->token.string.len == 0) { // If no name available for field, use its field index as its name. isize max_len = 8; - u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len); + u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len); isize len = gb_snprintf(cast(char *)str, 8, "%d", index); di->DerivedType.name = make_string(str, len-1); } @@ -3293,7 +3293,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array const &ar GB_ASSERT_MSG(param_count == args.count, "%.*s %td == %td", LIT(p->entity->token.string), param_count, args.count); } - auto processed_args = array_make(heap_allocator(), 0, args.count); + auto processed_args = array_make(permanent_allocator(), 0, args.count); for (isize i = 0; i < param_count; i++) { Entity *e = pt->Proc.params->Tuple.variables[i]; @@ -3416,7 +3416,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array const &ar case DeferredProcedure_in_out: { auto out_args = ir_value_to_array(p, result); - array_init(&result_as_args, heap_allocator(), in_args.count + out_args.count); + array_init(&result_as_args, permanent_allocator(), in_args.count + out_args.count); array_copy(&result_as_args, in_args, 0); array_copy(&result_as_args, out_args, in_args.count); } @@ -4537,7 +4537,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * Type *ft = base_complex_elem_type(t_left); if (op == Token_Quo) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; @@ -4615,7 +4615,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * return ir_emit_load(proc, res); } else if (op == Token_Mul) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; @@ -4625,7 +4625,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * default: GB_PANIC("Unknown float type"); break; } } else if (op == Token_Quo) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; @@ -4828,7 +4828,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue irValue *invalid_typeid = ir_value_constant(t_typeid, exact_value_i64(0)); return ir_emit_comp(proc, op_kind, x, invalid_typeid); } else if (is_type_bit_field(t)) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); irValue *lhs = ir_address_from_load_or_generate_local(proc, x); args[0] = ir_emit_conv(proc, lhs, t_rawptr); args[1] = ir_const_int(type_size_of(t)); @@ -4848,7 +4848,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue return ir_emit_comp(proc, op_kind, cap, v_zero); } } else if (is_type_struct(t) && type_has_nil(t)) { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); irValue *lhs = ir_address_from_load_or_generate_local(proc, x); args[0] = ir_emit_conv(proc, lhs, t_rawptr); args[1] = ir_const_int(type_size_of(t)); @@ -4966,7 +4966,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal } else { if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) { // TODO(bill): Test to see if this is actually faster!!!! - auto args = array_make(heap_allocator(), 3); + auto args = array_make(permanent_allocator(), 3); args[0] = ir_emit_conv(proc, lhs, t_rawptr); args[1] = ir_emit_conv(proc, rhs, t_rawptr); args[2] = ir_const_int(type_size_of(tl)); @@ -7355,7 +7355,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu // "Intrinsics" case BuiltinProc_alloca: { - auto args = array_make(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = ir_emit_conv(proc, ir_build_expr(proc, ce->args[0]), t_i32); args[1] = ir_build_expr(proc, ce->args[1]); return ir_emit(proc, ir_instr_inline_code(proc, id, args, t_u8_ptr)); @@ -9024,8 +9024,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (cl->elems.count > 0) { ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr))); - auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -9123,8 +9122,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (cl->elems.count > 0) { ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr))); - auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -9232,8 +9230,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { irValue *data = ir_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32); - auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -10179,7 +10176,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { String mangled_name = {}; { - gbString str = gb_string_make_length(heap_allocator(), proc->name.text, proc->name.len); + gbString str = gb_string_make_length(permanent_allocator(), proc->name.text, proc->name.len); str = gb_string_appendc(str, "-"); str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id); mangled_name.text = cast(u8 *)str; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 2d15f176b..1a306365f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -76,7 +76,8 @@ void ir_write_u64(irFileBuffer *f, u64 i) { } void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) { if (x.len == 2) { - gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator + SCOPED_TEMPORARY_BLOCK(); + u64 words[2] = {}; BigInt y = x; if (swap_endian) { @@ -88,9 +89,8 @@ void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_en y.d.words = words; } - String s = big_int_to_string(a, &y, 10); + String s = big_int_to_string(temporary_allocator(), &y, 10); ir_write_string(f, s); - gb_free(a, s.text); } else { i64 i = 0; if (x.neg) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 121917740..0e0fa904b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -781,6 +781,8 @@ void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) { + SCOPED_TEMPORARY_BLOCK(); + unsigned field_count = LLVMCountStructElementTypes(src); LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); LLVMGetStructElementTypes(src, fields); @@ -1277,9 +1279,10 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { m->internal_type_level += 1; defer (m->internal_type_level -= 1); + SCOPED_TEMPORARY_BLOCK(); + unsigned field_count = cast(unsigned)(type->Struct.fields.count + offset); LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); - GB_ASSERT(fields != nullptr); for_array(i, type->Struct.fields) { Entity *field = type->Struct.fields[i]; @@ -1338,6 +1341,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { if (type->Tuple.variables.count == 1) { return lb_type(m, type->Tuple.variables[0]->type); } else { + SCOPED_TEMPORARY_BLOCK(); + unsigned field_count = cast(unsigned)(type->Tuple.variables.count); LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); @@ -1437,6 +1442,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { extra_param_count += 1; } + SCOPED_TEMPORARY_BLOCK(); + isize param_count = type->Proc.abi_compat_params.count + extra_param_count; auto param_types = array_make(temporary_allocator(), 0, param_count); @@ -1483,6 +1490,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { { LLVMTypeRef internal_type = nullptr; { + SCOPED_TEMPORARY_BLOCK(); + GB_ASSERT(type->BitField.fields.count == type->BitField.sizes.count); unsigned field_count = cast(unsigned)type->BitField.fields.count; LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); @@ -5279,8 +5288,9 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } if (cl->elems[0]->kind == Ast_FieldValue) { - // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + SCOPED_TEMPORARY_BLOCK(); + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count); isize value_index = 0; @@ -5338,6 +5348,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); + SCOPED_TEMPORARY_BLOCK(); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count); for (isize i = 0; i < elem_count; i++) { @@ -5360,8 +5371,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } if (cl->elems[0]->kind == Ast_FieldValue) { + SCOPED_TEMPORARY_BLOCK(); // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand - LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count); isize value_index = 0; @@ -5423,6 +5434,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count); + SCOPED_TEMPORARY_BLOCK(); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count); for (isize i = 0; i < elem_count; i++) { @@ -5447,6 +5459,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } GB_ASSERT(elem_type_can_be_constant(elem_type)); + SCOPED_TEMPORARY_BLOCK(); + isize total_elem_count = type->SimdVector.count; LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count); @@ -5473,6 +5487,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc offset = 1; } + SCOPED_TEMPORARY_BLOCK(); + isize value_count = type->Struct.fields.count + offset; LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count); bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count); @@ -10880,6 +10896,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (cl->elems.count > 0) { lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); + SCOPED_TEMPORARY_BLOCK(); auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks @@ -10979,6 +10996,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (cl->elems.count > 0) { lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); + SCOPED_TEMPORARY_BLOCK(); auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks @@ -11087,6 +11105,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { lbValue data = lb_slice_elem(p, slice); + SCOPED_TEMPORARY_BLOCK(); auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); for_array(i, cl->elems) { @@ -11935,6 +11954,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da str_lit("$enum_values"), cast(i64)entry_index); + SCOPED_TEMPORARY_BLOCK(); LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); @@ -12894,7 +12914,7 @@ void lb_generate_code(lbGenerator *gen) { } - String filepath_ll = concatenate_strings(temporary_allocator(), gen->output_base, STR_LIT(".ll")); + String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll")); TIME_SECTION("LLVM Procedure Generation"); for_array(i, m->procedures_to_generate) { diff --git a/src/main.cpp b/src/main.cpp index d0d2e2bbb..2dbac3390 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1646,6 +1646,7 @@ int main(int arg_count, char const **arg_ptr) { arena_init(&permanent_arena, heap_allocator()); arena_init(&temporary_arena, heap_allocator()); arena_init(&global_ast_arena, heap_allocator()); + permanent_arena.use_mutex = true; init_string_buffer_memory(); init_string_interner(); From 0d6f5cec37e8815ff2e1c82575a05db98e4043d4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 19:36:37 +0000 Subject: [PATCH 047/170] Implement custom temporary allocator using ring buffer --- src/check_decl.cpp | 1 - src/check_expr.cpp | 9 --- src/check_stmt.cpp | 5 -- src/common.cpp | 138 ++++++++++++++++++++++++++++++------------- src/ir_print.cpp | 2 - src/llvm_backend.cpp | 24 -------- src/main.cpp | 14 ++--- 7 files changed, 105 insertions(+), 88 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index bfe703853..248069b97 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -121,7 +121,6 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be // an extra allocation - SCOPED_TEMPORARY_BLOCK(); auto operands = array_make(temporary_allocator(), 0, 2*lhs_count); check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 755ceb634..110e83ef2 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1832,7 +1832,6 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } - SCOPED_TEMPORARY_BLOCK(); gbString err_str = nullptr; if (check_is_assignable_to(c, x, y->type) || @@ -2974,8 +2973,6 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { case Type_Union: if (!is_operand_nil(*operand) && !is_operand_undef(*operand)) { - SCOPED_TEMPORARY_BLOCK(); - isize count = t->Union.variants.count; ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count); isize valid_count = 0; @@ -6536,8 +6533,6 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { bool show_error = show_error_mode == CallArgumentMode_ShowErrors; CallArgumentError err = CallArgumentError_None; - SCOPED_TEMPORARY_BLOCK(); - isize param_count = pt->param_count; bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); auto ordered_operands = array_make(temporary_allocator(), param_count); @@ -7385,8 +7380,6 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper ordered_operands = array_make(permanent_allocator(), param_count); array_copy(&ordered_operands, operands, 0); } else { - SCOPED_TEMPORARY_BLOCK(); - bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); // LEAK(bill) @@ -8507,8 +8500,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } if (cl->elems[0]->kind == Ast_FieldValue) { - SCOPED_TEMPORARY_BLOCK(); - bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); for_array(i, cl->elems) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d722ea8ee..a480e0fdf 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -640,8 +640,6 @@ void add_constant_switch_case(CheckerContext *ctx, Map *seen, Oper HashKey key = hash_exact_value(operand.value); TypeAndToken *found = map_get(seen, key); if (found != nullptr) { - SCOPED_TEMPORARY_BLOCK(); - isize count = multi_map_count(seen, key); TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); @@ -1026,7 +1024,6 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_ASSERT(is_type_enum(et)); auto fields = et->Enum.fields; - SCOPED_TEMPORARY_BLOCK(); auto unhandled = array_make(temporary_allocator(), 0, fields.count); for_array(i, fields) { @@ -1266,7 +1263,6 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_ASSERT(is_type_union(ut)); auto variants = ut->Union.variants; - SCOPED_TEMPORARY_BLOCK(); auto unhandled = array_make(temporary_allocator(), 0, variants.count); for_array(i, variants) { @@ -1434,7 +1430,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { return; } - SCOPED_TEMPORARY_BLOCK(); // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be // an extra allocation diff --git a/src/common.cpp b/src/common.cpp index 05ebdd4c5..0147f27d5 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -56,6 +56,14 @@ gb_inline isize align_formula_isize(isize size, isize align) { } return size; } +gb_inline void *align_formula_ptr(void *ptr, isize align) { + if (align > 0) { + uintptr result = (cast(uintptr)ptr) + align-1; + return (void *)(result - result%align); + } + return ptr; +} + GB_ALLOCATOR_PROC(heap_allocator_proc); @@ -380,6 +388,9 @@ typedef struct Arena { #define ARENA_MIN_ALIGNMENT 16 #define ARENA_DEFAULT_BLOCK_SIZE (8*1024*1024) + +gb_global Arena permanent_arena = {}; + void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) { arena->backing = backing; arena->block_size = block_size; @@ -491,51 +502,98 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) { return ptr; } -struct SCOPED_TEMP_ARENA_MEMORY { - Arena *arena; - u8 * ptr; - u8 * end; - u8 * prev; - isize total_used; - isize block_count; - - SCOPED_TEMP_ARENA_MEMORY(Arena *the_arena) { - GB_ASSERT(!the_arena->use_mutex); - arena = the_arena; - ptr = arena->ptr; - end = arena->end; - prev = arena->prev; - total_used = arena->total_used; - block_count = arena->blocks.count; - } - ~SCOPED_TEMP_ARENA_MEMORY() { - if (arena->blocks.count != block_count) { - for (isize i = block_count; i < arena->blocks.count; i++) { - gb_free(arena->backing, arena->blocks[i]); - } - arena->blocks.count = block_count; - } - arena->ptr = ptr; - arena->end = end; - arena->prev = prev; - arena->total_used = total_used; - } -}; - - - - -gb_global Arena permanent_arena = {}; -gb_global Arena temporary_arena = {}; gbAllocator permanent_allocator() { return arena_allocator(&permanent_arena); -} -gbAllocator temporary_allocator() { - return arena_allocator(&temporary_arena); + // return heap_allocator(); } -#define SCOPED_TEMPORARY_BLOCK() auto GB_DEFER_3(_SCOPED_TEMPORARY_BLOCK_) = SCOPED_TEMP_ARENA_MEMORY(&temporary_arena) + + +struct Temp_Allocator { + u8 *data; + isize len; + isize curr_offset; + gbAllocator backup_allocator; + Array leaked_allocations; +}; + +gb_global Temp_Allocator temporary_allocator_data = {}; + +void temp_allocator_init(Temp_Allocator *s, isize size) { + s->backup_allocator = heap_allocator(); + s->data = cast(u8 *)gb_alloc_align(s->backup_allocator, size, 16); + s->curr_offset = 0; + s->leaked_allocations.allocator = s->backup_allocator; +} + +void *temp_allocator_alloc(Temp_Allocator *s, isize size, isize alignment) { + size = align_formula_isize(size, alignment); + if (s->curr_offset+size <= s->len) { + u8 *start = s->data; + u8 *ptr = start + s->curr_offset; + ptr = cast(u8 *)align_formula_ptr(ptr, alignment); + // assume memory is zero + + isize offset = ptr - start; + s->curr_offset = offset + size; + return ptr; + } else if (size <= s->len) { + u8 *start = s->data; + u8 *ptr = cast(u8 *)align_formula_ptr(start, alignment); + // assume memory is zero + + isize offset = ptr - start; + s->curr_offset = offset + size; + return ptr; + } + + void *ptr = gb_alloc_align(s->backup_allocator, size, alignment); + array_add(&s->leaked_allocations, ptr); + return ptr; +} + +void temp_allocator_free_all(Temp_Allocator *s) { + s->curr_offset = 0; + for_array(i, s->leaked_allocations) { + gb_free(s->backup_allocator, s->leaked_allocations[i]); + } + array_clear(&s->leaked_allocations); + gb_zero_size(s->data, s->len); +} + +GB_ALLOCATOR_PROC(temp_allocator_proc) { + void *ptr = nullptr; + Temp_Allocator *s = cast(Temp_Allocator *)allocator_data; + GB_ASSERT_NOT_NULL(s); + + switch (type) { + case gbAllocation_Alloc: + return temp_allocator_alloc(s, size, alignment); + case gbAllocation_Free: + break; + case gbAllocation_Resize: + if (size == 0) { + ptr = nullptr; + } else if (size <= old_size) { + ptr = old_memory; + } else { + ptr = temp_allocator_alloc(s, size, alignment); + gb_memmove(ptr, old_memory, old_size); + } + break; + case gbAllocation_FreeAll: + temp_allocator_free_all(s); + break; + } + + return ptr; +} + + +gbAllocator temporary_allocator() { + return {temp_allocator_proc, &temporary_allocator_data}; +} diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 1a306365f..0605058ee 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -76,8 +76,6 @@ void ir_write_u64(irFileBuffer *f, u64 i) { } void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) { if (x.len == 2) { - SCOPED_TEMPORARY_BLOCK(); - u64 words[2] = {}; BigInt y = x; if (swap_endian) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 0e0fa904b..9eb641716 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -781,8 +781,6 @@ void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) { - SCOPED_TEMPORARY_BLOCK(); - unsigned field_count = LLVMCountStructElementTypes(src); LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); LLVMGetStructElementTypes(src, fields); @@ -1279,8 +1277,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { m->internal_type_level += 1; defer (m->internal_type_level -= 1); - SCOPED_TEMPORARY_BLOCK(); - unsigned field_count = cast(unsigned)(type->Struct.fields.count + offset); LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); @@ -1341,8 +1337,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { if (type->Tuple.variables.count == 1) { return lb_type(m, type->Tuple.variables[0]->type); } else { - SCOPED_TEMPORARY_BLOCK(); - unsigned field_count = cast(unsigned)(type->Tuple.variables.count); LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); @@ -1442,8 +1436,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { extra_param_count += 1; } - SCOPED_TEMPORARY_BLOCK(); - isize param_count = type->Proc.abi_compat_params.count + extra_param_count; auto param_types = array_make(temporary_allocator(), 0, param_count); @@ -1490,8 +1482,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { { LLVMTypeRef internal_type = nullptr; { - SCOPED_TEMPORARY_BLOCK(); - GB_ASSERT(type->BitField.fields.count == type->BitField.sizes.count); unsigned field_count = cast(unsigned)type->BitField.fields.count; LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count); @@ -5288,8 +5278,6 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } if (cl->elems[0]->kind == Ast_FieldValue) { - SCOPED_TEMPORARY_BLOCK(); - // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count); @@ -5348,7 +5336,6 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); - SCOPED_TEMPORARY_BLOCK(); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count); for (isize i = 0; i < elem_count; i++) { @@ -5371,7 +5358,6 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } if (cl->elems[0]->kind == Ast_FieldValue) { - SCOPED_TEMPORARY_BLOCK(); // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count); @@ -5434,7 +5420,6 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count); - SCOPED_TEMPORARY_BLOCK(); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count); for (isize i = 0; i < elem_count; i++) { @@ -5459,8 +5444,6 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } GB_ASSERT(elem_type_can_be_constant(elem_type)); - SCOPED_TEMPORARY_BLOCK(); - isize total_elem_count = type->SimdVector.count; LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count); @@ -5487,13 +5470,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc offset = 1; } - SCOPED_TEMPORARY_BLOCK(); - isize value_count = type->Struct.fields.count + offset; LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count); bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count); - if (cl->elems.count > 0) { if (cl->elems[0]->kind == Ast_FieldValue) { isize elem_count = cl->elems.count; @@ -10896,7 +10876,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (cl->elems.count > 0) { lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - SCOPED_TEMPORARY_BLOCK(); auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks @@ -10996,7 +10975,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (cl->elems.count > 0) { lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - SCOPED_TEMPORARY_BLOCK(); auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks @@ -11105,7 +11083,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { lbValue data = lb_slice_elem(p, slice); - SCOPED_TEMPORARY_BLOCK(); auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); for_array(i, cl->elems) { @@ -11954,7 +11931,6 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da str_lit("$enum_values"), cast(i64)entry_index); - SCOPED_TEMPORARY_BLOCK(); LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); diff --git a/src/main.cpp b/src/main.cpp index 2dbac3390..97fecb094 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1644,7 +1644,7 @@ int main(int arg_count, char const **arg_ptr) { defer (timings_destroy(timings)); arena_init(&permanent_arena, heap_allocator()); - arena_init(&temporary_arena, heap_allocator()); + temp_allocator_init(&temporary_allocator_data, 16*1024*1024); arena_init(&global_ast_arena, heap_allocator()); permanent_arena.use_mutex = true; @@ -1799,7 +1799,7 @@ int main(int arg_count, char const **arg_ptr) { return 1; } - arena_free_all(&temporary_arena); + temp_allocator_free_all(&temporary_allocator_data); if (build_context.generate_docs) { // generate_documentation(&parser); @@ -1818,7 +1818,7 @@ int main(int arg_count, char const **arg_ptr) { check_parsed_files(&checker); } - arena_free_all(&temporary_arena); + temp_allocator_free_all(&temporary_allocator_data); if (build_context.no_output_files) { if (build_context.query_data_set_settings.ok) { @@ -1849,7 +1849,7 @@ int main(int arg_count, char const **arg_ptr) { } lb_generate_code(&gen); - arena_free_all(&temporary_arena); + temp_allocator_free_all(&temporary_allocator_data); switch (build_context.build_mode) { case BuildMode_Executable: @@ -1928,17 +1928,17 @@ int main(int arg_count, char const **arg_ptr) { timings_start_section(timings, str_lit("llvm ir gen")); ir_gen_tree(&ir_gen); - arena_free_all(&temporary_arena); + temp_allocator_free_all(&temporary_allocator_data); timings_start_section(timings, str_lit("llvm ir opt tree")); ir_opt_tree(&ir_gen); - arena_free_all(&temporary_arena); + temp_allocator_free_all(&temporary_allocator_data); timings_start_section(timings, str_lit("llvm ir print")); print_llvm_ir(&ir_gen); - arena_free_all(&temporary_arena); + temp_allocator_free_all(&temporary_allocator_data); String output_name = ir_gen.output_name; From db0bcbc4f47afbb69bb172401ead7f484eed6b6c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 21:19:08 +0000 Subject: [PATCH 048/170] Fix calling convention for new LLVM ABI, and change`PtrSet` index to be `u32` rather than `isize` --- src/checker.cpp | 9 +++---- src/llvm_abi.cpp | 24 ++++++++--------- src/llvm_backend.cpp | 27 ++++++++++++-------- src/ptr_set.cpp | 61 +++++++++++++++++++++++--------------------- src/types.cpp | 9 +++++++ 5 files changed, 71 insertions(+), 59 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 76d8cceb3..de1d8091d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1679,8 +1679,6 @@ void add_dependency_to_set(Checker *c, Entity *entity) { CheckerInfo *info = &c->info; auto *set = &info->minimum_dependency_set; - String name = entity->token.string; - if (entity->type != nullptr && is_type_polymorphic(entity->type)) { @@ -1714,16 +1712,15 @@ void add_dependency_to_set(Checker *c, Entity *entity) { if (fl != nullptr) { GB_ASSERT_MSG(fl->kind == Entity_LibraryName && (fl->flags&EntityFlag_Used), - "%.*s", LIT(name)); + "%.*s", LIT(entity->token.string)); add_dependency_to_set(c, fl); } - } - if (e->kind == Entity_Variable && e->Variable.is_foreign) { + } else if (e->kind == Entity_Variable && e->Variable.is_foreign) { Entity *fl = e->Variable.foreign_library; if (fl != nullptr) { GB_ASSERT_MSG(fl->kind == Entity_LibraryName && (fl->flags&EntityFlag_Used), - "%.*s", LIT(name)); + "%.*s", LIT(entity->token.string)); add_dependency_to_set(c, fl); } } diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index f0422e0f8..9722bf302 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -103,6 +103,11 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa offset += 1; } + LLVMContextRef c = ft->ctx; + LLVMAttributeRef noalias_attr = lb_create_enum_attribute(c, "noalias", true); + LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(c, "nonnull", true); + LLVMAttributeRef nocapture_attr = lb_create_enum_attribute(c, "nocapture", true); + unsigned arg_index = offset; for (unsigned i = 0; i < arg_count; i++) { lbArgType *arg = &ft->args[i]; @@ -129,10 +134,9 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa LLVMSetFunctionCallConv(fn, cc_kind); if (calling_convention == ProcCC_Odin) { unsigned context_index = offset+arg_count; - LLVMContextRef c = ft->ctx; - LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "noalias", true)); - LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "nonnull", true)); - LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "nocapture", true)); + LLVMAddAttributeAtIndex(fn, context_index, noalias_attr); + LLVMAddAttributeAtIndex(fn, context_index, nonnull_attr); + LLVMAddAttributeAtIndex(fn, context_index, nocapture_attr); } } @@ -201,9 +205,6 @@ i64 lb_sizeof(LLVMTypeRef type) { } GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type)); - // LLVMValueRef v = LLVMSizeOf(type); - // GB_ASSERT(LLVMIsConstant(v)); - // return cast(i64)LLVMConstIntGetSExtValue(v); return 0; } @@ -400,9 +401,6 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { } GB_PANIC("Unhandled type for lb_abi_to_odin_type -> %s", LLVMPrintTypeToString(type)); - // LLVMValueRef v = LLVMSizeOf(type); - // GB_ASSERT(LLVMIsConstant(v)); - // return cast(i64)LLVMConstIntGetSExtValue(v); return 0; } @@ -441,7 +439,7 @@ namespace lbAbi386 { LLVMAttributeRef attr = nullptr; LLVMTypeRef i1 = LLVMInt1TypeInContext(c); if (type == i1) { - // attr = lb_create_enum_attribute(c, "zext", true); + attr = lb_create_enum_attribute(c, "zeroext", true); // return lb_arg_type_direct(type, i1, nullptr, attr); } return lb_arg_type_direct(type, nullptr, nullptr, attr); @@ -625,7 +623,7 @@ namespace lbAbiAmd64SysV { if (is_register(type)) { LLVMAttributeRef attribute = nullptr; if (type == LLVMInt1TypeInContext(c)) { - attribute = lb_create_enum_attribute(c, "zext", true); + attribute = lb_create_enum_attribute(c, "zeroext", true); } return lb_arg_type_direct(type, nullptr, nullptr, attribute); } @@ -648,7 +646,7 @@ namespace lbAbiAmd64SysV { LLVMAttributeRef attr = nullptr; LLVMTypeRef i1 = LLVMInt1TypeInContext(c); if (type == i1) { - attr = lb_create_enum_attribute(c, "zext", true); + attr = lb_create_enum_attribute(c, "zeroext", true); } return lb_arg_type_direct(type, nullptr, nullptr, attr); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9eb641716..6542da69b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -7440,8 +7440,14 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, } } else if (arg->kind == lbArg_Indirect) { - // lbValue ptr = lb_copy_value_to_ptr(p, x, original_type, 16); - lbValue ptr = lb_address_from_load_or_generate_local(p, x); + lbValue ptr = {}; + if (is_calling_convention_odin(pt->Proc.calling_convention)) { + // NOTE(bill): Odin parameters are immutable so the original value can be passed if possible + // i.e. `T const &` in C++ + ptr = lb_address_from_load_or_generate_local(p, x); + } else { + ptr = lb_copy_value_to_ptr(p, x, original_type, 16); + } array_add(&processed_args, ptr); } @@ -7454,18 +7460,17 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, Type *rt = reduce_tuple_to_single_type(results); if (return_by_pointer) { - lbValue return_ptr = {}; - // if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { - // if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { - // return_ptr = p->return_ptr_hint_value; - // p->return_ptr_hint_used = true; - // } - // } - // if (return_ptr.value == nullptr) { + if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { + if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { + return_ptr = p->return_ptr_hint_value; + p->return_ptr_hint_used = true; + } + } + if (return_ptr.value == nullptr) { lbAddr r = lb_add_local_generated(p, rt, true); return_ptr = r.addr; - // } + } GB_ASSERT(is_type_pointer(return_ptr.type)); lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); result = lb_emit_load(p, return_ptr); diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index e343628af..890a0df1d 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -1,19 +1,23 @@ +typedef u32 PtrSetIndex; + struct PtrSetFindResult { - isize hash_index; - isize entry_prev; - isize entry_index; + PtrSetIndex hash_index; + PtrSetIndex entry_prev; + PtrSetIndex entry_index; }; +enum : PtrSetIndex { PTR_SET_SENTINEL = ~(PtrSetIndex)0 }; + template struct PtrSetEntry { - T ptr; - isize next; + T ptr; + PtrSetIndex next; }; template struct PtrSet { - Array hashes; + Array hashes; Array> entries; }; @@ -29,10 +33,12 @@ template void ptr_set_rehash (PtrSet *s, isize new_count); template void ptr_set_init(PtrSet *s, gbAllocator a, isize capacity) { + capacity = next_pow2(gb_max(16, capacity)); + array_init(&s->hashes, a, capacity); array_init(&s->entries, a, 0, capacity); for (isize i = 0; i < capacity; i++) { - s->hashes.data[i] = -1; + s->hashes.data[i] = PTR_SET_SENTINEL; } } @@ -43,24 +49,24 @@ void ptr_set_destroy(PtrSet *s) { } template -gb_internal isize ptr_set__add_entry(PtrSet *s, T ptr) { +gb_internal PtrSetIndex ptr_set__add_entry(PtrSet *s, T ptr) { PtrSetEntry e = {}; e.ptr = ptr; - e.next = -1; + e.next = PTR_SET_SENTINEL; array_add(&s->entries, e); - return s->entries.count-1; + return cast(PtrSetIndex)(s->entries.count-1); } template gb_internal PtrSetFindResult ptr_set__find(PtrSet *s, T ptr) { - PtrSetFindResult fr = {-1, -1, -1}; - if (s->hashes.count > 0) { + PtrSetFindResult fr = {PTR_SET_SENTINEL, PTR_SET_SENTINEL, PTR_SET_SENTINEL}; + if (s->hashes.count != 0) { u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr; u64 n = cast(u64)s->hashes.count; - fr.hash_index = cast(isize)(hash % n); + fr.hash_index = cast(PtrSetIndex)(hash & (n-1)); fr.entry_index = s->hashes[fr.hash_index]; - while (fr.entry_index >= 0) { + while (fr.entry_index != PTR_SET_SENTINEL) { if (s->entries[fr.entry_index].ptr == ptr) { return fr; } @@ -72,28 +78,25 @@ gb_internal PtrSetFindResult ptr_set__find(PtrSet *s, T ptr) { } template -gb_internal b32 ptr_set__full(PtrSet *s) { +gb_internal bool ptr_set__full(PtrSet *s) { return 0.75f * s->hashes.count <= s->entries.count; } -#define PTR_ARRAY_GROW_FORMULA(x) (4*(x) + 7) -GB_STATIC_ASSERT(PTR_ARRAY_GROW_FORMULA(0) > 0); - template gb_inline void ptr_set_grow(PtrSet *s) { - isize new_count = PTR_ARRAY_GROW_FORMULA(s->entries.count); + isize new_count = s->hashes.count*2; ptr_set_rehash(s, new_count); } template void ptr_set_rehash(PtrSet *s, isize new_count) { - isize i, j; + PtrSetIndex i, j; PtrSet ns = {}; ptr_set_init(&ns, s->hashes.allocator); array_resize(&ns.hashes, new_count); array_reserve(&ns.entries, s->entries.count); for (i = 0; i < new_count; i++) { - ns.hashes[i] = -1; + ns.hashes[i] = PTR_SET_SENTINEL; } for (i = 0; i < s->entries.count; i++) { PtrSetEntry *e = &s->entries[i]; @@ -103,7 +106,7 @@ void ptr_set_rehash(PtrSet *s, isize new_count) { } fr = ptr_set__find(&ns, e->ptr); j = ptr_set__add_entry(&ns, e->ptr); - if (fr.entry_prev < 0) { + if (fr.entry_prev == PTR_SET_SENTINEL) { ns.hashes[fr.hash_index] = j; } else { ns.entries[fr.entry_prev].next = j; @@ -120,23 +123,23 @@ void ptr_set_rehash(PtrSet *s, isize new_count) { template gb_inline bool ptr_set_exists(PtrSet *s, T ptr) { isize index = ptr_set__find(s, ptr).entry_index; - return index >= 0; + return index != PTR_SET_SENTINEL; } // Returns true if it already exists template T ptr_set_add(PtrSet *s, T ptr) { - isize index; + PtrSetIndex index; PtrSetFindResult fr; if (s->hashes.count == 0) { ptr_set_grow(s); } fr = ptr_set__find(s, ptr); - if (fr.entry_index >= 0) { + if (fr.entry_index != PTR_SET_SENTINEL) { index = fr.entry_index; } else { index = ptr_set__add_entry(s, ptr); - if (fr.entry_prev >= 0) { + if (fr.entry_prev != PTR_SET_SENTINEL) { s->entries[fr.entry_prev].next = index; } else { s->hashes[fr.hash_index] = index; @@ -152,7 +155,7 @@ T ptr_set_add(PtrSet *s, T ptr) { template void ptr_set__erase(PtrSet *s, PtrSetFindResult fr) { PtrSetFindResult last; - if (fr.entry_prev < 0) { + if (fr.entry_prev == PTR_SET_SENTINEL) { s->hashes[fr.hash_index] = s->entries[fr.entry_index].next; } else { s->entries[fr.entry_prev].next = s->entries[fr.entry_index].next; @@ -163,7 +166,7 @@ void ptr_set__erase(PtrSet *s, PtrSetFindResult fr) { } s->entries[fr.entry_index] = s->entries[s->entries.count-1]; last = ptr_set__find(s, s->entries[fr.entry_index].ptr); - if (last.entry_prev >= 0) { + if (last.entry_prev != PTR_SET_SENTINEL) { s->entries[last.entry_prev].next = fr.entry_index; } else { s->hashes[last.hash_index] = fr.entry_index; @@ -173,7 +176,7 @@ void ptr_set__erase(PtrSet *s, PtrSetFindResult fr) { template void ptr_set_remove(PtrSet *s, T ptr) { PtrSetFindResult fr = ptr_set__find(s, ptr); - if (fr.entry_index >= 0) { + if (fr.entry_index != PTR_SET_SENTINEL) { ptr_set__erase(s, fr); } } diff --git a/src/types.cpp b/src/types.cpp index 17dcedf45..1147beb33 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -897,6 +897,15 @@ bool is_calling_convention_none(ProcCallingConvention calling_convention) { return false; } +bool is_calling_convention_odin(ProcCallingConvention calling_convention) { + switch (calling_convention) { + case ProcCC_Odin: + case ProcCC_Contextless: + return true; + } + return false; +} + Type *alloc_type_tuple() { Type *t = alloc_type(Type_Tuple); return t; From 3a229397e48a3e8b408a51b52f366c63fc52f043 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 21:22:26 +0000 Subject: [PATCH 049/170] Add next_pow2_isize for PtrSet --- src/ptr_set.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 890a0df1d..44bc1eca7 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -31,9 +31,26 @@ template void ptr_set_grow (PtrSet *s); template void ptr_set_rehash (PtrSet *s, isize new_count); +isize next_pow2_isize(isize n) { + if (n <= 0) { + return 0; + } + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + if (gb_size_of(isize) == 8) { + n |= n >> 32; + } + n++; + return n; +} + template void ptr_set_init(PtrSet *s, gbAllocator a, isize capacity) { - capacity = next_pow2(gb_max(16, capacity)); + capacity = next_pow2_isize(gb_max(16, capacity)); array_init(&s->hashes, a, capacity); array_init(&s->entries, a, 0, capacity); From 5fafb17d81c2bfb07402e04d34c717a7abf42f84 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 22:46:07 +0000 Subject: [PATCH 050/170] Improve generate_entity_dependency_graph: Calculate edges for graph M - Part 2 --- src/checker.cpp | 51 ++++++++++++++++++++++++++++++++++++++++--------- src/ptr_set.cpp | 32 +++++++++++++++---------------- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index de1d8091d..ef8e39ed9 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1729,7 +1729,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) { void generate_minimum_dependency_set(Checker *c, Entity *start) { - ptr_set_init(&c->info.minimum_dependency_set, heap_allocator()); + isize entity_count = c->info.entities.count; + isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor + + ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap); ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator()); String required_runtime_entities[] = { @@ -1893,19 +1896,17 @@ void add_entity_dependency_from_procedure_parameters(Map *M, } -Array generate_entity_dependency_graph(CheckerInfo *info) { +Array generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) - gbAllocator a = heap_allocator(); - Map M = {}; // Key: Entity * - map_init(&M, a, info->entities.count); + map_init(&M, allocator, info->entities.count); defer (map_destroy(&M)); for_array(i, info->entities) { Entity *e = info->entities[i]; DeclInfo *d = e->decl_info; if (is_entity_a_dependency(e)) { - EntityGraphNode *n = gb_alloc_item(a, EntityGraphNode); + EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode); n->entity = e; map_set(&M, hash_pointer(e), n); } @@ -1940,7 +1941,7 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { // This means that the entity graph node set will have to be thread safe TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2"); - auto G = array_make(a, 0, M.entries.count); + auto G = array_make(allocator, 0, M.entries.count); for_array(i, M.entries) { auto *entry = &M.entries[i]; @@ -1961,17 +1962,27 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *s = n->succ.entries[k].ptr; // Ignore self-cycles if (s != n) { + if (p->entity->kind == Entity_Procedure && + s->entity->kind == Entity_Procedure) { + // NOTE(bill, 2020-11-15): Only care about variable initialization ordering + // TODO(bill): This is probably wrong!!!! + continue; + } + // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the + // the time to process + entity_graph_node_set_add(&p->succ, s); entity_graph_node_set_add(&s->pred, p); // Remove edge to 'n' entity_graph_node_set_remove(&s->pred, n); } } + // Remove edge to 'n' entity_graph_node_set_remove(&p->succ, n); } } - } else { + } else if (e->kind == Entity_Variable) { array_add(&G, n); } } @@ -1984,6 +1995,28 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { GB_ASSERT(n->dep_count >= 0); } + // f64 succ_count = 0.0; + // f64 pred_count = 0.0; + // f64 succ_capacity = 0.0; + // f64 pred_capacity = 0.0; + // f64 succ_max = 0.0; + // f64 pred_max = 0.0; + // for_array(i, G) { + // EntityGraphNode *n = G[i]; + // succ_count += n->succ.entries.count; + // pred_count += n->pred.entries.count; + // succ_capacity += n->succ.entries.capacity; + // pred_capacity += n->pred.entries.capacity; + + // succ_max = gb_max(succ_max, n->succ.entries.capacity); + // pred_max = gb_max(pred_max, n->pred.entries.capacity); + + // } + // f64 count = cast(f64)G.count; + // gb_printf_err(">>>count pred: %f succ: %f\n", pred_count/count, succ_count/count); + // gb_printf_err(">>>capacity pred: %f succ: %f\n", pred_capacity/count, succ_capacity/count); + // gb_printf_err(">>>max pred: %f succ: %f\n", pred_max, succ_max); + return G; #undef TIME_SECTION @@ -4174,7 +4207,7 @@ void calculate_global_init_order(Checker *c) { CheckerInfo *info = &c->info; TIME_SECTION("calculate_global_init_order: generate entity dependency graph"); - Array dep_graph = generate_entity_dependency_graph(info); + Array dep_graph = generate_entity_dependency_graph(info, heap_allocator()); defer ({ for_array(i, dep_graph) { entity_graph_node_destroy(dep_graph[i], heap_allocator()); diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 44bc1eca7..e75202663 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -82,13 +82,13 @@ gb_internal PtrSetFindResult ptr_set__find(PtrSet *s, T ptr) { u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr; u64 n = cast(u64)s->hashes.count; fr.hash_index = cast(PtrSetIndex)(hash & (n-1)); - fr.entry_index = s->hashes[fr.hash_index]; + fr.entry_index = s->hashes.data[fr.hash_index]; while (fr.entry_index != PTR_SET_SENTINEL) { - if (s->entries[fr.entry_index].ptr == ptr) { + if (s->entries.data[fr.entry_index].ptr == ptr) { return fr; } fr.entry_prev = fr.entry_index; - fr.entry_index = s->entries[fr.entry_index].next; + fr.entry_index = s->entries.data[fr.entry_index].next; } } return fr; @@ -113,10 +113,10 @@ void ptr_set_rehash(PtrSet *s, isize new_count) { array_resize(&ns.hashes, new_count); array_reserve(&ns.entries, s->entries.count); for (i = 0; i < new_count; i++) { - ns.hashes[i] = PTR_SET_SENTINEL; + ns.hashes.data[i] = PTR_SET_SENTINEL; } for (i = 0; i < s->entries.count; i++) { - PtrSetEntry *e = &s->entries[i]; + PtrSetEntry *e = &s->entries.data[i]; PtrSetFindResult fr; if (ns.hashes.count == 0) { ptr_set_grow(&ns); @@ -124,11 +124,11 @@ void ptr_set_rehash(PtrSet *s, isize new_count) { fr = ptr_set__find(&ns, e->ptr); j = ptr_set__add_entry(&ns, e->ptr); if (fr.entry_prev == PTR_SET_SENTINEL) { - ns.hashes[fr.hash_index] = j; + ns.hashes.data[fr.hash_index] = j; } else { - ns.entries[fr.entry_prev].next = j; + ns.entries.data[fr.entry_prev].next = j; } - ns.entries[j].next = fr.entry_index; + ns.entries.data[j].next = fr.entry_index; if (ptr_set__full(&ns)) { ptr_set_grow(&ns); } @@ -157,9 +157,9 @@ T ptr_set_add(PtrSet *s, T ptr) { } else { index = ptr_set__add_entry(s, ptr); if (fr.entry_prev != PTR_SET_SENTINEL) { - s->entries[fr.entry_prev].next = index; + s->entries.data[fr.entry_prev].next = index; } else { - s->hashes[fr.hash_index] = index; + s->hashes.data[fr.hash_index] = index; } } if (ptr_set__full(s)) { @@ -173,20 +173,20 @@ template void ptr_set__erase(PtrSet *s, PtrSetFindResult fr) { PtrSetFindResult last; if (fr.entry_prev == PTR_SET_SENTINEL) { - s->hashes[fr.hash_index] = s->entries[fr.entry_index].next; + s->hashes.data[fr.hash_index] = s->entries.data[fr.entry_index].next; } else { - s->entries[fr.entry_prev].next = s->entries[fr.entry_index].next; + s->entries.data[fr.entry_prev].next = s->entries.data[fr.entry_index].next; } if (fr.entry_index == s->entries.count-1) { array_pop(&s->entries); return; } - s->entries[fr.entry_index] = s->entries[s->entries.count-1]; - last = ptr_set__find(s, s->entries[fr.entry_index].ptr); + s->entries.data[fr.entry_index] = s->entries.data[s->entries.count-1]; + last = ptr_set__find(s, s->entries.data[fr.entry_index].ptr); if (last.entry_prev != PTR_SET_SENTINEL) { - s->entries[last.entry_prev].next = fr.entry_index; + s->entries.data[last.entry_prev].next = fr.entry_index; } else { - s->hashes[last.hash_index] = fr.entry_index; + s->hashes.data[last.hash_index] = fr.entry_index; } } From 939878df50cf314dd2cd0e5da737ac93e88b5b25 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Nov 2020 23:54:18 +0000 Subject: [PATCH 051/170] Improve logic for x->y() shorthand --- src/check_expr.cpp | 16 ++++++++++++++++ src/check_stmt.cpp | 3 +-- src/checker.hpp | 3 ++- src/main.cpp | 3 --- src/parser.cpp | 24 ++++++++++++------------ 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 110e83ef2..11ccf2dab 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3437,6 +3437,13 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ Entity *entity = nullptr; Selection sel = {}; // NOTE(bill): Not used if it's an import name + if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) { + error(node, "Illegal use of -> selector shorthand outside of a call"); + operand->mode = Addressing_Invalid; + operand->expr = node; + return nullptr; + } + operand->expr = node; Ast *op_expr = se->expr; @@ -9492,8 +9499,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type // // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I? + bool allow_arrow_right_selector_expr; + allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; + c->allow_arrow_right_selector_expr = true; Operand x = {}; ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); + c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; + if (x.mode == Addressing_Invalid || x.type == t_invalid) { o->mode = Addressing_Invalid; o->type = t_invalid; @@ -9594,7 +9606,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type ce->args = modified_args; se->modified_call = true; + allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; + c->allow_arrow_right_selector_expr = true; check_expr_base(c, o, se->call, type_hint); + c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; + o->expr = node; return Expr_Expr; case_end; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a480e0fdf..4cafa8df5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -858,8 +858,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { token.pos = ast_token(ss->body).pos; token.string = str_lit("true"); - x.expr = gb_alloc_item(permanent_allocator(), Ast); - x.expr->kind = Ast_Ident; + x.expr = alloc_ast_node(nullptr, Ast_Ident); x.expr->Ident.token = token; } diff --git a/src/checker.hpp b/src/checker.hpp index ed4809748..e6111d2af 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -45,7 +45,7 @@ enum StmtFlag { Stmt_TypeSwitch = 1<<4, - Stmt_CheckScopeDecls = 1<<5, + Stmt_CheckScopeDecls = 1<<5, }; enum BuiltinProcPkg { @@ -316,6 +316,7 @@ struct CheckerContext { bool no_polymorphic_errors; bool hide_polymorphic_errors; bool in_polymorphic_specialization; + bool allow_arrow_right_selector_expr; Scope * polymorphic_scope; Ast *assignment_lhs_hint; diff --git a/src/main.cpp b/src/main.cpp index 97fecb094..67dc5da00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1876,9 +1876,6 @@ int main(int arg_count, char const **arg_ptr) { SIZE_T virtual_mem_used_by_me = pmc.PrivateUsage; gb_printf_err("virtual_memory_used: %tu B\n", virtual_mem_used_by_me); - gb_printf_err("total_allocated_node_memory: %lld B\n", total_allocated_node_memory.value); - gb_printf_err("total_subtype_node_memory_test: %lld B\n", total_subtype_node_memory_test.value); - gb_printf_err("fraction: %.6f\n", (f64)total_subtype_node_memory_test.value/(f64)total_allocated_node_memory.value); Parser *p = checker.parser; isize lines = p->total_line_count; isize tokens = p->total_token_count; diff --git a/src/parser.cpp b/src/parser.cpp index 5e04aea17..476504d52 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -109,9 +109,6 @@ Token ast_token(Ast *node) { } -gb_global gbAtomic64 total_allocated_node_memory = {0}; -gb_global gbAtomic64 total_subtype_node_memory_test = {0}; - isize ast_node_size(AstKind kind) { return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *)); @@ -122,10 +119,6 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) { isize size = ast_node_size(kind); - gb_atomic64_fetch_add(&total_allocated_node_memory, cast(i64)(gb_size_of(Ast))); - gb_atomic64_fetch_add(&total_subtype_node_memory_test, cast(i64)(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind])); - - // Ast *node = gb_alloc_item(a, Ast); Ast *node = cast(Ast *)gb_alloc(a, size); node->kind = kind; node->file = f; @@ -2511,7 +2504,15 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) { f->expr_level--; close_paren = expect_closing(f, Token_CloseParen, str_lit("argument list")); - return ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis); + + Ast *call = ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis); + + Ast *o = unparen_expr(operand); + if (o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) { + return ast_selector_call_expr(f, o->SelectorExpr.token, o, call); + } + + return call; } Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { @@ -2563,11 +2564,10 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { case Token_ArrowRight: { Token token = advance_token(f); - // syntax_error(token, "Selector expressions use '.' rather than '->'"); - Ast *sel = ast_selector_expr(f, token, operand, parse_ident(f)); - Ast *call = parse_call_expr(f, sel); - operand = ast_selector_call_expr(f, token, sel, call); + operand = ast_selector_expr(f, token, operand, parse_ident(f)); + // Ast *call = parse_call_expr(f, sel); + // operand = ast_selector_call_expr(f, token, sel, call); break; } From adf6c85fd3130ed22e98f2cf5b7f88079f056488 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 16 Nov 2020 01:42:30 +0000 Subject: [PATCH 052/170] Minimize Ast flags usage --- src/checker.cpp | 16 ++++++++-------- src/parser.hpp | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index ef8e39ed9..380872f24 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3012,8 +3012,8 @@ void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array *attr } void check_collect_value_decl(CheckerContext *c, Ast *decl) { - if (decl->been_handled) return; - decl->been_handled = true; + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; ast_node(vd, ValueDecl, decl); @@ -3231,8 +3231,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { } void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) { - if (decl->been_handled) return; - decl->been_handled = true; + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; ast_node(fb, ForeignBlockDecl, decl); Ast *foreign_library = fb->foreign_library; @@ -3616,8 +3616,8 @@ Array find_import_path(Checker *c, AstPackage *start, AstPackage } #endif void check_add_import_decl(CheckerContext *ctx, Ast *decl) { - if (decl->been_handled) return; - decl->been_handled = true; + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; ast_node(id, ImportDecl, decl); Token token = id->relpath; @@ -3731,8 +3731,8 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { } void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { - if (decl->been_handled) return; - decl->been_handled = true; + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; ast_node(fl, ForeignImportDecl, decl); diff --git a/src/parser.hpp b/src/parser.hpp index 0804652a3..9a7ddd4b9 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -217,14 +217,16 @@ enum ProcCallingConvention { ProcCC_ForeignBlockDefault = -1, }; -enum StateFlag { +enum StateFlag : u16 { StateFlag_bounds_check = 1<<0, StateFlag_no_bounds_check = 1<<1, StateFlag_no_deferred = 1<<5, + + StateFlag_BeenHandled = 1<<15, }; -enum ViralStateFlag { +enum ViralStateFlag : u16 { ViralStateFlag_ContainsDeferredProcedure = 1<<0, }; @@ -638,9 +640,8 @@ isize const ast_variant_sizes[] = { struct AstCommonStuff { AstKind kind; - u32 state_flags; - u32 viral_state_flags; - bool been_handled; + u16 state_flags; + u16 viral_state_flags; AstFile * file; Scope * scope; TypeAndValue tav; @@ -648,9 +649,8 @@ struct AstCommonStuff { struct Ast { AstKind kind; - u32 state_flags; - u32 viral_state_flags; - bool been_handled; + u16 state_flags; + u16 viral_state_flags; AstFile * file; Scope * scope; TypeAndValue tav; From ca4b0527e80bda39aa677f013415eff0c62f433d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 16 Nov 2020 15:18:25 +0000 Subject: [PATCH 053/170] Minimize memory usage for AST nodes by using Slice rather than Array when the parameter doesn't need to grow --- src/array.cpp | 89 ++++++++++++++++++++++++++++++- src/check_decl.cpp | 2 +- src/check_expr.cpp | 24 ++++----- src/check_stmt.cpp | 6 +-- src/check_type.cpp | 10 ++-- src/checker.cpp | 12 ++--- src/checker.hpp | 2 +- src/entity.cpp | 4 +- src/ir.cpp | 6 +-- src/llvm_backend.cpp | 4 +- src/parser.cpp | 121 ++++++++++++++++++++++++------------------- src/parser.hpp | 57 ++++++++++---------- 12 files changed, 218 insertions(+), 119 deletions(-) diff --git a/src/array.cpp b/src/array.cpp index 6fe54b847..dc52eeb8d 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -43,11 +43,96 @@ template void array_set_capacity (Array *array, isize capac template Array array_slice (Array const &array, isize lo, isize hi); template Array array_clone (gbAllocator const &a, Array const &array); - - template void array_ordered_remove (Array *array, isize index); template void array_unordered_remove(Array *array, isize index); +template void array_copy(Array *array, Array const &data, isize offset); +template void array_copy(Array *array, Array const &data, isize offset, isize count); + +template T *array_end_ptr(Array *array); + + +template +struct Slice { + T *data; + isize count; + + T &operator[](isize index) { + #if !defined(NO_ARRAY_BOUNDS_CHECK) + GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #endif + return data[index]; + } + + T const &operator[](isize index) const { + #if !defined(NO_ARRAY_BOUNDS_CHECK) + GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #endif + return data[index]; + } +}; + +template Slice slice_from_array(Array const &a); + + + +template +Slice slice_make(gbAllocator const &allocator, isize count) { + Slice s = {}; + s.data = gb_alloc_array(allocator, T, count); + s.count = count; + return s; +} + + +template +Slice slice_from_array(Array const &a) { + return {a.data, a.count}; +} +template +Slice slice_clone(gbAllocator const &allocator, Slice const &a) { + T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T)); + return {data, a.count}; +} + +template +Slice slice_clone_from_array(gbAllocator const &allocator, Array const &a) { + auto c = array_clone(allocator, a); + return {c.data, c.count}; +} + + +template +void slice_copy(Slice *slice, Slice const &data, isize offset) { + gb_memmove(slice->data+offset, data.data, gb_size_of(T)*data.count); +} +template +void slice_copy(Slice *slice, Slice const &data, isize offset, isize count) { + gb_memmove(slice->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count)); +} + + + +template +void slice_ordered_remove(Slice *array, isize index) { + GB_ASSERT(0 <= index && index < array->count); + + isize bytes = gb_size_of(T) * (array->count-(index+1)); + gb_memmove(array->data+index, array->data+index+1, bytes); + array->count -= 1; +} + +template +void slice_unordered_remove(Slice *array, isize index) { + GB_ASSERT(0 <= index && index < array->count); + + isize n = array->count-1; + if (index != n) { + gb_memmove(array->data+index, array->data+n, gb_size_of(T)); + } + array->count -= 1; +} + template void array_copy(Array *array, Array const &data, isize offset) { diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 248069b97..da07fe4bc 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -113,7 +113,7 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri return e->type; } -void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array const &inits, String context_name) { +void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Slice const &inits, String context_name) { if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) { return; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 11ccf2dab..94a3467d2 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -73,7 +73,7 @@ void update_expr_type (CheckerContext *c, Ast *e, Type *type, bool check_is_terminating (Ast *node, String const &label); bool check_has_break (Ast *stmt, String const &label, bool implicit); void check_stmt (CheckerContext *c, Ast *node, u32 flags); -void check_stmt_list (CheckerContext *c, Array const &stmts, u32 flags); +void check_stmt_list (CheckerContext *c, Slice const &stmts, u32 flags); void check_init_constant (CheckerContext *c, Entity *e, Operand *operand); bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value); bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array *operands = nullptr); @@ -133,7 +133,7 @@ void error_operand_no_value(Operand *o) { } -void check_scope_decls(CheckerContext *c, Array const &nodes, isize reserve_size) { +void check_scope_decls(CheckerContext *c, Slice const &nodes, isize reserve_size) { Scope *s = c->scope; check_collect_entities(c, nodes); @@ -6062,7 +6062,7 @@ isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs } -bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, Array *operands, Array const &rhs) { +bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, Array *operands, Slice const &rhs) { bool optional_ok = false; isize tuple_index = 0; for_array(i, rhs) { @@ -6146,7 +6146,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, -bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Array const &rhs, bool allow_ok, bool is_variadic) { +bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Slice const &rhs, bool allow_ok, bool is_variadic) { bool optional_ok = false; isize tuple_index = 0; for_array(i, rhs) { @@ -6748,7 +6748,7 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize } -bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Array *clauses, bool print_err) { +bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Slice *clauses, bool print_err) { if (clauses != nullptr) { for_array(i, *clauses) { Ast *clause = (*clauses)[i]; @@ -6813,7 +6813,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, A } -CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Array const &args) { +CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice const &args) { ast_node(ce, CallExpr, call); CallArgumentCheckerType *call_checker = check_call_arguments_internal; @@ -7598,7 +7598,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper -ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Array const &args, ProcInlining inlining, Type *type_hint) { +ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice const &args, ProcInlining inlining, Type *type_hint) { if (proc != nullptr && proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, proc); @@ -8150,7 +8150,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bl, BasicLit, node); Type *t = t_invalid; - switch (bl->value.kind) { + switch (node->tav.value.kind) { case ExactValue_String: t = t_untyped_string; break; case ExactValue_Float: t = t_untyped_float; break; case ExactValue_Complex: t = t_untyped_complex; break; @@ -8168,7 +8168,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->mode = Addressing_Constant; o->type = t; - o->value = bl->value; + o->value = node->tav.value; case_end; case_ast_node(bd, BasicDirective, node); @@ -9600,9 +9600,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type - auto modified_args = array_make(heap_allocator(), ce->args.count+1); + auto modified_args = slice_make(heap_allocator(), ce->args.count+1); modified_args[0] = first_arg; - array_copy(&modified_args, ce->args, 1); + slice_copy(&modified_args, ce->args, 1); ce->args = modified_args; se->modified_call = true; @@ -10210,7 +10210,7 @@ void check_expr_or_type(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) gbString write_expr_to_string(gbString str, Ast *node); -gbString write_struct_fields_to_string(gbString str, Array const ¶ms) { +gbString write_struct_fields_to_string(gbString str, Slice const ¶ms) { for_array(i, params) { if (i > 0) { str = gb_string_appendc(str, ", "); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 4cafa8df5..ad72256c3 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -15,7 +15,7 @@ bool is_divigering_stmt(Ast *stmt) { return t->kind == Type_Proc && t->Proc.diverging; } -void check_stmt_list(CheckerContext *ctx, Array const &stmts, u32 flags) { +void check_stmt_list(CheckerContext *ctx, Slice const &stmts, u32 flags) { if (stmts.count == 0) { return; } @@ -78,7 +78,7 @@ void check_stmt_list(CheckerContext *ctx, Array const &stmts, u32 flags) } } -bool check_is_terminating_list(Array const &stmts, String const &label) { +bool check_is_terminating_list(Slice const &stmts, String const &label) { // Iterate backwards for (isize n = stmts.count-1; n >= 0; n--) { Ast *stmt = stmts[n]; @@ -96,7 +96,7 @@ bool check_is_terminating_list(Array const &stmts, String const &label) { return false; } -bool check_has_break_list(Array const &stmts, String const &label, bool implicit) { +bool check_has_break_list(Slice const &stmts, String const &label, bool implicit) { for_array(i, stmts) { Ast *stmt = stmts[i]; if (check_has_break(stmt, label, implicit)) { diff --git a/src/check_type.cpp b/src/check_type.cpp index af0c70119..6ea17eca6 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -117,7 +117,7 @@ bool does_field_type_allow_using(Type *t) { return false; } -void check_struct_fields(CheckerContext *ctx, Ast *node, Array *fields, Array *tags, Array const ¶ms, +void check_struct_fields(CheckerContext *ctx, Ast *node, Array *fields, Array *tags, Slice const ¶ms, isize init_field_capacity, Type *struct_type, String context) { *fields = array_make(heap_allocator(), 0, init_field_capacity); *tags = array_make(heap_allocator(), 0, init_field_capacity); @@ -389,7 +389,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< if (st->polymorphic_params != nullptr) { ast_node(field_list, FieldList, st->polymorphic_params); - Array params = field_list->list; + Slice params = field_list->list; if (params.count != 0) { isize variable_count = 0; for_array(i, params) { @@ -607,7 +607,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraypolymorphic_params != nullptr) { ast_node(field_list, FieldList, ut->polymorphic_params); - Array params = field_list->list; + Slice params = field_list->list; if (params.count != 0) { isize variable_count = 0; for_array(i, params) { @@ -1516,7 +1516,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is bool success = true; ast_node(field_list, FieldList, _params); - Array params = field_list->list; + Slice params = field_list->list; if (params.count == 0) { if (success_) *success_ = success; @@ -1875,7 +1875,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { return nullptr; } ast_node(field_list, FieldList, _results); - Array results = field_list->list; + Slice results = field_list->list; if (results.count == 0) { return nullptr; diff --git a/src/checker.cpp b/src/checker.cpp index 380872f24..f02b927c3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2854,7 +2854,7 @@ void check_decl_attributes(CheckerContext *c, Array const &attributes, De } -isize get_total_value_count(Array const &values) { +isize get_total_value_count(Slice const &values) { isize count = 0; for_array(i, values) { Type *t = type_of_expr(values[i]); @@ -3068,7 +3068,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { } else { entity_visibility_kind = kind; } - array_unordered_remove(elems, j); + slice_unordered_remove(elems, j); j -= 1; } } @@ -3252,7 +3252,7 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) { } // NOTE(bill): If file_scopes == nullptr, this will act like a local scope -void check_collect_entities(CheckerContext *c, Array const &nodes) { +void check_collect_entities(CheckerContext *c, Slice const &nodes) { for_array(decl_index, nodes) { Ast *decl = nodes[decl_index]; if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) { @@ -3783,7 +3783,7 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { } } -bool collect_checked_packages_from_decl_list(Checker *c, Array const &decls) { +bool collect_checked_packages_from_decl_list(Checker *c, Slice const &decls) { bool new_files = false; for_array(i, decls) { Ast *decl = decls[i]; @@ -3805,7 +3805,7 @@ bool collect_checked_packages_from_decl_list(Checker *c, Array const &dec } // Returns true if a new package is present -bool collect_file_decls(CheckerContext *ctx, Array const &decls); +bool collect_file_decls(CheckerContext *ctx, Slice const &decls); bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws); bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) { @@ -3880,7 +3880,7 @@ bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) { return false; } -bool collect_file_decls(CheckerContext *ctx, Array const &decls) { +bool collect_file_decls(CheckerContext *ctx, Slice const &decls) { GB_ASSERT(ctx->scope->flags&ScopeFlag_File); if (collect_checked_packages_from_decl_list(ctx->checker, decls)) { diff --git a/src/checker.hpp b/src/checker.hpp index e6111d2af..9c9b77ac3 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -384,7 +384,7 @@ void check_add_foreign_import_decl(CheckerContext *c, Ast *decl); bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false); -void check_collect_entities(CheckerContext *c, Array const &nodes); +void check_collect_entities(CheckerContext *c, Slice const &nodes); void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); diff --git a/src/entity.cpp b/src/entity.cpp index 708b0862c..0aece39c3 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -165,7 +165,7 @@ struct Entity { Scope *scope; } ImportName; struct { - Array paths; + Slice paths; String name; } LibraryName; i32 Nil; @@ -333,7 +333,7 @@ Entity *alloc_entity_import_name(Scope *scope, Token token, Type *type, } Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type, - Array paths, String name) { + Slice paths, String name) { Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type); entity->LibraryName.paths = paths; entity->LibraryName.name = name; diff --git a/src/ir.cpp b/src/ir.cpp index 7b6301e30..1f2819ccf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6884,7 +6884,7 @@ irValue *ir_find_global_variable(irProcedure *proc, String name) { return *value; } -void ir_build_stmt_list(irProcedure *proc, Array stmts); +void ir_build_stmt_list(irProcedure *proc, Slice stmts); void ir_build_assign_op(irProcedure *proc, irAddr const &lhs, irValue *value, TokenKind op); bool is_double_pointer(Type *t) { @@ -9689,7 +9689,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) { } } -void ir_build_stmt_list(irProcedure *proc, Array stmts) { +void ir_build_stmt_list(irProcedure *proc, Slice stmts) { // NOTE(bill): Precollect constant entities for_array(i, stmts) { Ast *stmt = stmts[i]; @@ -10899,7 +10899,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { ast_node(body, BlockStmt, ss->body); - Array default_stmts = {}; + Slice default_stmts = {}; irBlock *default_fall = nullptr; irBlock *default_block = nullptr; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6542da69b..50d200551 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3134,7 +3134,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { } -void lb_build_stmt_list(lbProcedure *p, Array const &stmts) { +void lb_build_stmt_list(lbProcedure *p, Slice const &stmts) { for_array(i, stmts) { Ast *stmt = stmts[i]; switch (stmt->kind) { @@ -3865,7 +3865,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss) { ast_node(body, BlockStmt, ss->body); - Array default_stmts = {}; + Slice default_stmts = {}; lbBlock *default_fall = nullptr; lbBlock *default_block = nullptr; diff --git a/src/parser.cpp b/src/parser.cpp index 476504d52..cf464f149 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -126,7 +126,7 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) { } Ast *clone_ast(Ast *node); -Array clone_ast_array(Array array) { +Array clone_ast_array(Array const &array) { Array result = {}; if (array.count > 0) { result = array_make(ast_allocator(nullptr), array.count); @@ -136,6 +136,16 @@ Array clone_ast_array(Array array) { } return result; } +Slice clone_ast_array(Slice const &array) { + Slice result = {}; + if (array.count > 0) { + result = slice_clone(permanent_allocator(), array); + for_array(i, array) { + result[i] = clone_ast(array[i]); + } + } + return result; +} Ast *clone_ast(Ast *node) { if (node == nullptr) { @@ -537,10 +547,10 @@ Ast *ast_paren_expr(AstFile *f, Ast *expr, Token open, Token close) { return result; } -Ast *ast_call_expr(AstFile *f, Ast *proc, Array args, Token open, Token close, Token ellipsis) { +Ast *ast_call_expr(AstFile *f, Ast *proc, Array const &args, Token open, Token close, Token ellipsis) { Ast *result = alloc_ast_node(f, Ast_CallExpr); result->CallExpr.proc = proc; - result->CallExpr.args = args; + result->CallExpr.args = slice_from_array(args); result->CallExpr.open = open; result->CallExpr.close = close; result->CallExpr.ellipsis = ellipsis; @@ -624,7 +634,8 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; - result->BasicLit.value = exact_value_from_basic_literal(basic_lit); + result->tav.mode = Addressing_Constant; + result->tav.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -643,12 +654,12 @@ Ast *ast_ellipsis(AstFile *f, Token token, Ast *expr) { } -Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array args) { +Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array const &args) { Ast *result = alloc_ast_node(f, Ast_ProcGroup); result->ProcGroup.token = token; result->ProcGroup.open = open; result->ProcGroup.close = close; - result->ProcGroup.args = args; + result->ProcGroup.args = slice_from_array(args); return result; } @@ -658,7 +669,7 @@ Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token, result->ProcLit.body = body; result->ProcLit.tags = tags; result->ProcLit.where_token = where_token; - result->ProcLit.where_clauses = where_clauses; + result->ProcLit.where_clauses = slice_from_array(where_clauses); return result; } @@ -670,10 +681,10 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) { return result; } -Ast *ast_compound_lit(AstFile *f, Ast *type, Array elems, Token open, Token close) { +Ast *ast_compound_lit(AstFile *f, Ast *type, Array const &elems, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_CompoundLit); result->CompoundLit.type = type; - result->CompoundLit.elems = elems; + result->CompoundLit.elems = slice_from_array(elems); result->CompoundLit.open = open; result->CompoundLit.close = close; return result; @@ -736,7 +747,7 @@ Ast *ast_inline_asm_expr(AstFile *f, Token token, Token open, Token close, result->InlineAsmExpr.token = token; result->InlineAsmExpr.open = open; result->InlineAsmExpr.close = close; - result->InlineAsmExpr.param_types = param_types; + result->InlineAsmExpr.param_types = slice_from_array(param_types); result->InlineAsmExpr.return_type = return_type; result->InlineAsmExpr.asm_string = asm_string; result->InlineAsmExpr.constraints_string = constraints_string; @@ -768,18 +779,18 @@ Ast *ast_expr_stmt(AstFile *f, Ast *expr) { return result; } -Ast *ast_assign_stmt(AstFile *f, Token op, Array lhs, Array rhs) { +Ast *ast_assign_stmt(AstFile *f, Token op, Array const &lhs, Array const &rhs) { Ast *result = alloc_ast_node(f, Ast_AssignStmt); result->AssignStmt.op = op; - result->AssignStmt.lhs = lhs; - result->AssignStmt.rhs = rhs; + result->AssignStmt.lhs = slice_from_array(lhs); + result->AssignStmt.rhs = slice_from_array(rhs); return result; } -Ast *ast_block_stmt(AstFile *f, Array stmts, Token open, Token close) { +Ast *ast_block_stmt(AstFile *f, Array const &stmts, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_BlockStmt); - result->BlockStmt.stmts = stmts; + result->BlockStmt.stmts = slice_from_array(stmts); result->BlockStmt.open = open; result->BlockStmt.close = close; return result; @@ -805,10 +816,10 @@ Ast *ast_when_stmt(AstFile *f, Token token, Ast *cond, Ast *body, Ast *else_stmt } -Ast *ast_return_stmt(AstFile *f, Token token, Array results) { +Ast *ast_return_stmt(AstFile *f, Token token, Array const &results) { Ast *result = alloc_ast_node(f, Ast_ReturnStmt); result->ReturnStmt.token = token; - result->ReturnStmt.results = results; + result->ReturnStmt.results = slice_from_array(results); return result; } @@ -866,11 +877,11 @@ Ast *ast_type_switch_stmt(AstFile *f, Token token, Ast *tag, Ast *body) { return result; } -Ast *ast_case_clause(AstFile *f, Token token, Array list, Array stmts) { +Ast *ast_case_clause(AstFile *f, Token token, Array const &list, Array const &stmts) { Ast *result = alloc_ast_node(f, Ast_CaseClause); result->CaseClause.token = token; - result->CaseClause.list = list; - result->CaseClause.stmts = stmts; + result->CaseClause.list = slice_from_array(list); + result->CaseClause.stmts = slice_from_array(stmts); return result; } @@ -889,10 +900,10 @@ Ast *ast_branch_stmt(AstFile *f, Token token, Ast *label) { return result; } -Ast *ast_using_stmt(AstFile *f, Token token, Array list) { +Ast *ast_using_stmt(AstFile *f, Token token, Array const &list) { Ast *result = alloc_ast_node(f, Ast_UsingStmt); result->UsingStmt.token = token; - result->UsingStmt.list = list; + result->UsingStmt.list = slice_from_array(list); return result; } @@ -905,10 +916,10 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) { return result; } -Ast *ast_field(AstFile *f, Array names, Ast *type, Ast *default_value, u32 flags, Token tag, +Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast *default_value, u32 flags, Token tag, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_Field); - result->Field.names = names; + result->Field.names = slice_from_array(names); result->Field.type = type; result->Field.default_value = default_value; result->Field.flags = flags; @@ -918,10 +929,10 @@ Ast *ast_field(AstFile *f, Array names, Ast *type, Ast *default_value, u3 return result; } -Ast *ast_field_list(AstFile *f, Token token, Array list) { +Ast *ast_field_list(AstFile *f, Token token, Array const &list) { Ast *result = alloc_ast_node(f, Ast_FieldList); result->FieldList.token = token; - result->FieldList.list = list; + result->FieldList.list = slice_from_array(list); return result; } @@ -1002,7 +1013,7 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { return result; } -Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_count, +Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, Ast *align, Token where_token, Array const &where_clauses) { @@ -1015,38 +1026,38 @@ Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_c result->StructType.is_raw_union = is_raw_union; result->StructType.align = align; result->StructType.where_token = where_token; - result->StructType.where_clauses = where_clauses; + result->StructType.where_clauses = slice_from_array(where_clauses); return result; } -Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, +Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; - result->UnionType.variants = variants; + result->UnionType.variants = slice_from_array(variants); result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; result->UnionType.no_nil = no_nil; - result->UnionType.maybe = maybe; + result->UnionType.maybe = maybe; result->UnionType.where_token = where_token; - result->UnionType.where_clauses = where_clauses; + result->UnionType.where_clauses = slice_from_array(where_clauses); return result; } -Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array fields) { +Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array const &fields) { Ast *result = alloc_ast_node(f, Ast_EnumType); result->EnumType.token = token; result->EnumType.base_type = base_type; - result->EnumType.fields = fields; + result->EnumType.fields = slice_from_array(fields); return result; } -Ast *ast_bit_field_type(AstFile *f, Token token, Array fields, Ast *align) { +Ast *ast_bit_field_type(AstFile *f, Token token, Array const &fields, Ast *align) { Ast *result = alloc_ast_node(f, Ast_BitFieldType); result->BitFieldType.token = token; - result->BitFieldType.fields = fields; + result->BitFieldType.fields = slice_from_array(fields); result->BitFieldType.align = align; return result; } @@ -1069,7 +1080,7 @@ Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) { Ast *ast_foreign_block_decl(AstFile *f, Token token, Ast *foreign_library, Ast *body, - CommentGroup *docs) { + CommentGroup *docs) { Ast *result = alloc_ast_node(f, Ast_ForeignBlockDecl); result->ForeignBlockDecl.token = token; result->ForeignBlockDecl.foreign_library = foreign_library; @@ -1087,12 +1098,12 @@ Ast *ast_label_decl(AstFile *f, Token token, Ast *name) { return result; } -Ast *ast_value_decl(AstFile *f, Array names, Ast *type, Array values, bool is_mutable, +Ast *ast_value_decl(AstFile *f, Array const &names, Ast *type, Array const &values, bool is_mutable, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ValueDecl); - result->ValueDecl.names = names; + result->ValueDecl.names = slice_from_array(names); result->ValueDecl.type = type; - result->ValueDecl.values = values; + result->ValueDecl.values = slice_from_array(values); result->ValueDecl.is_mutable = is_mutable; result->ValueDecl.docs = docs; result->ValueDecl.comment = comment; @@ -1126,7 +1137,7 @@ Ast *ast_foreign_import_decl(AstFile *f, Token token, Array filepaths, To CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl); result->ForeignImportDecl.token = token; - result->ForeignImportDecl.filepaths = filepaths; + result->ForeignImportDecl.filepaths = slice_from_array(filepaths); result->ForeignImportDecl.library_name = library_name; result->ForeignImportDecl.docs = docs; result->ForeignImportDecl.comment = comment; @@ -1136,11 +1147,11 @@ Ast *ast_foreign_import_decl(AstFile *f, Token token, Array filepaths, To } -Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array elems) { +Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array const &elems) { Ast *result = alloc_ast_node(f, Ast_Attribute); result->Attribute.token = token; result->Attribute.open = open; - result->Attribute.elems = elems; + result->Attribute.elems = slice_from_array(elems); result->Attribute.close = close; return result; } @@ -1192,7 +1203,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) { CommentGroup *comments = nullptr; if (list.count > 0) { comments = gb_alloc_item(heap_allocator(), CommentGroup); - comments->list = list; + comments->list = slice_from_array(list); array_add(&f->comments, comments); } return comments; @@ -2181,7 +2192,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Ast *fields = parse_struct_field_list(f, &name_count); Token close = expect_token(f, Token_CloseBrace); - Array decls = {}; + Slice decls = {}; if (fields != nullptr) { GB_ASSERT(fields->kind == Ast_FieldList); decls = fields->FieldList.list; @@ -4887,7 +4898,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, -void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array &decls); +void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls); void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) { if (ws->body != nullptr) { @@ -4908,7 +4919,7 @@ void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenS } } -void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array &decls) { +void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls) { for_array(i, decls) { Ast *node = decls[i]; if (!is_ast_decl(node) && @@ -4947,8 +4958,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array } else if (node->kind == Ast_ForeignImportDecl) { ast_node(fl, ForeignImportDecl, node); - fl->fullpaths.allocator = heap_allocator(); - array_reserve(&fl->fullpaths, fl->filepaths.count); + auto fullpaths = array_make(permanent_allocator(), 0, fl->filepaths.count); for_array(fp_idx, fl->filepaths) { String file_str = fl->filepaths[fp_idx].string; @@ -4962,14 +4972,17 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array } fullpath = foreign_path; } - array_add(&fl->fullpaths, fullpath); + array_add(&fullpaths, fullpath); } - if (fl->fullpaths.count == 0) { + if (fullpaths.count == 0) { syntax_error(decls[i], "No foreign paths found"); decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]); goto end; } + fl->fullpaths = slice_from_array(fullpaths); + + } else if (node->kind == Ast_WhenStmt) { ast_node(ws, WhenStmt, node); parse_setup_file_when_stmt(p, f, base_dir, ws); @@ -5131,12 +5144,12 @@ bool parse_file(Parser *p, AstFile *f) { f->pkg_decl = pd; if (f->error_count == 0) { - f->decls = array_make(heap_allocator()); + auto decls = array_make(heap_allocator()); while (f->curr_token.kind != Token_EOF) { Ast *stmt = parse_stmt(f); if (stmt && stmt->kind != Ast_EmptyStmt) { - array_add(&f->decls, stmt); + array_add(&decls, stmt); if (stmt->kind == Ast_ExprStmt && stmt->ExprStmt.expr != nullptr && stmt->ExprStmt.expr->kind == Ast_ProcLit) { @@ -5145,6 +5158,8 @@ bool parse_file(Parser *p, AstFile *f) { } } + f->decls = slice_from_array(decls); + parse_setup_file_decls(p, f, base_dir, f->decls); } diff --git a/src/parser.hpp b/src/parser.hpp index 9a7ddd4b9..3be7939fa 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -46,7 +46,7 @@ enum ParseFileError { }; struct CommentGroup { - Array list; // Token_Comment + Slice list; // Token_Comment }; @@ -98,8 +98,8 @@ struct AstFile { bool in_foreign_block; bool allow_type; - Array decls; - Array imports; // 'import' 'using import' + Slice decls; + Array imports; // 'import' isize directive_count; Ast * curr_proc; @@ -277,7 +277,6 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { AST_KIND(Undef, "undef", Token) \ AST_KIND(BasicLit, "basic literal", struct { \ Token token; \ - ExactValue value; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ Token token; \ @@ -291,7 +290,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { Token token; \ Token open; \ Token close; \ - Array args; \ + Slice args; \ }) \ AST_KIND(ProcLit, "procedure literal", struct { \ Ast *type; \ @@ -299,12 +298,12 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { u64 tags; \ ProcInlining inlining; \ Token where_token; \ - Array where_clauses; \ + Slice where_clauses; \ DeclInfo *decl; \ }) \ AST_KIND(CompoundLit, "compound literal", struct { \ Ast *type; \ - Array elems; \ + Slice elems; \ Token open, close; \ i64 max_count; \ }) \ @@ -327,7 +326,7 @@ AST_KIND(_ExprBegin, "", bool) \ }) \ AST_KIND(CallExpr, "call expression", struct { \ Ast * proc; \ - Array args; \ + Slice args; \ Token open; \ Token close; \ Token ellipsis; \ @@ -344,7 +343,7 @@ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(InlineAsmExpr, "inline asm expression", struct { \ Token token; \ Token open, close; \ - Array param_types; \ + Slice param_types; \ Ast *return_type; \ Ast *asm_string; \ Ast *constraints_string; \ @@ -364,11 +363,11 @@ AST_KIND(_StmtBegin, "", bool) \ }) \ AST_KIND(AssignStmt, "assign statement", struct { \ Token op; \ - Array lhs, rhs; \ + Slice lhs, rhs; \ }) \ AST_KIND(_ComplexStmtBegin, "", bool) \ AST_KIND(BlockStmt, "block statement", struct { \ - Array stmts; \ + Slice stmts; \ Ast *label; \ Token open, close; \ }) \ @@ -390,7 +389,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ }) \ AST_KIND(ReturnStmt, "return statement", struct { \ Token token; \ - Array results; \ + Slice results; \ }) \ AST_KIND(ForStmt, "for statement", struct { \ Token token; \ @@ -420,8 +419,8 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ }) \ AST_KIND(CaseClause, "case clause", struct { \ Token token; \ - Array list; \ - Array stmts; \ + Slice list; \ + Slice stmts; \ Entity *implicit_entity; \ }) \ AST_KIND(SwitchStmt, "switch statement", struct { \ @@ -438,12 +437,12 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ Ast *tag; \ Ast *body; \ bool partial; \ -}) \ + }) \ AST_KIND(DeferStmt, "defer statement", struct { Token token; Ast *stmt; }) \ AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \ AST_KIND(UsingStmt, "using statement", struct { \ Token token; \ - Array list; \ + Slice list; \ }) \ AST_KIND(_ComplexStmtEnd, "", bool) \ AST_KIND(_StmtEnd, "", bool) \ @@ -461,9 +460,9 @@ AST_KIND(_DeclBegin, "", bool) \ Ast *name; \ }) \ AST_KIND(ValueDecl, "value declaration", struct { \ - Array names; \ + Slice names; \ Ast * type; \ - Array values; \ + Slice values; \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ @@ -488,10 +487,10 @@ AST_KIND(_DeclBegin, "", bool) \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ - Array filepaths; \ + Slice filepaths; \ Token library_name; \ String collection_name; \ - Array fullpaths; \ + Slice fullpaths; \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ @@ -499,11 +498,11 @@ AST_KIND(_DeclBegin, "", bool) \ AST_KIND(_DeclEnd, "", bool) \ AST_KIND(Attribute, "attribute", struct { \ Token token; \ - Array elems; \ + Slice elems; \ Token open, close; \ }) \ AST_KIND(Field, "field", struct { \ - Array names; \ + Slice names; \ Ast * type; \ Ast * default_value; \ Token tag; \ @@ -513,7 +512,7 @@ AST_KIND(_DeclEnd, "", bool) \ }) \ AST_KIND(FieldList, "field list", struct { \ Token token; \ - Array list; \ + Slice list; \ }) \ AST_KIND(_TypeBegin, "", bool) \ AST_KIND(TypeidType, "typeid", struct { \ @@ -567,34 +566,34 @@ AST_KIND(_TypeBegin, "", bool) \ }) \ AST_KIND(StructType, "struct type", struct { \ Token token; \ - Array fields; \ + Slice fields; \ isize field_count; \ Ast *polymorphic_params; \ Ast *align; \ Token where_token; \ - Array where_clauses; \ + Slice where_clauses; \ bool is_packed; \ bool is_raw_union; \ }) \ AST_KIND(UnionType, "union type", struct { \ Token token; \ - Array variants; \ + Slice variants; \ Ast *polymorphic_params; \ Ast * align; \ bool maybe; \ bool no_nil; \ Token where_token; \ - Array where_clauses; \ + Slice where_clauses; \ }) \ AST_KIND(EnumType, "enum type", struct { \ Token token; \ Ast * base_type; \ - Array fields; /* FieldValue */ \ + Slice fields; /* FieldValue */ \ bool is_using; \ }) \ AST_KIND(BitFieldType, "bit field type", struct { \ Token token; \ - Array fields; /* FieldValue with : */ \ + Slice fields; /* FieldValue with : */ \ Ast * align; \ }) \ AST_KIND(BitSetType, "bit set type", struct { \ From 6f71d1f2a97887d7039c4e4cc39477a1f474ccae Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 12:10:25 +0000 Subject: [PATCH 054/170] Add `-show-unused` (Shows unused package declarations of all imported packages) Crude output at the moment but better than nothing --- src/build_settings.cpp | 1 + src/check_decl.cpp | 3 +- src/checker.cpp | 28 +++----- src/checker.hpp | 13 ++-- src/main.cpp | 159 +++++++++++++++++++++++++++++++++++++++++ src/parser.cpp | 6 +- src/ptr_set.cpp | 31 +++++++- 7 files changed, 208 insertions(+), 33 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4f06c2913..5c1babe0c 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -143,6 +143,7 @@ struct BuildContext { bool generate_docs; i32 optimization_level; bool show_timings; + bool show_unused; bool show_more_timings; bool show_system_calls; bool keep_temp_files; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index da07fe4bc..5234955fb 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1007,11 +1007,10 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) continue; } - if (ptr_set_exists(&entity_set, e)) { + if (ptr_set_update(&entity_set, e)) { error(arg, "Previous use of `%.*s` in procedure group", LIT(e->token.string)); continue; } - ptr_set_add(&entity_set, e); array_add(&pge->entities, e); } diff --git a/src/checker.cpp b/src/checker.cpp index f02b927c3..f8018506c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -700,7 +700,7 @@ void init_universal(void) { builtin_pkg->kind = Package_Normal; builtin_pkg->scope = create_scope(nullptr); - builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; + builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; builtin_pkg->scope->pkg = builtin_pkg; intrinsics_pkg = gb_alloc_item(a, AstPackage); @@ -708,7 +708,7 @@ void init_universal(void) { intrinsics_pkg->kind = Package_Normal; intrinsics_pkg->scope = create_scope(nullptr); - intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; + intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; intrinsics_pkg->scope->pkg = intrinsics_pkg; config_pkg = gb_alloc_item(a, AstPackage); @@ -716,7 +716,7 @@ void init_universal(void) { config_pkg->kind = Package_Normal; config_pkg->scope = create_scope(nullptr); - config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; + config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; config_pkg->scope->pkg = config_pkg; @@ -1501,11 +1501,10 @@ void add_min_dep_type_info(Checker *c, Type *t) { ti_index = type_info_index(&c->info, t, false); } GB_ASSERT(ti_index >= 0); - if (ptr_set_exists(set, ti_index)) { + if (ptr_set_update(set, ti_index)) { // Type Already exists return; } - ptr_set_add(set, ti_index); // Add nested types if (t->kind == Type_Named) { @@ -1688,12 +1687,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) { } } - if (ptr_set_exists(set, entity)) { + if (ptr_set_update(set, entity)) { return; } - - ptr_set_add(set, entity); DeclInfo *decl = decl_info_of_entity(entity); if (decl == nullptr) { return; @@ -3567,11 +3564,9 @@ struct ImportPathItem { Array find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet *visited) { Array empty_path = {}; - if (ptr_set_exists(visited, start)) { + if (ptr_set_update(visited, start)) { return empty_path; } - ptr_set_add(visited, start); - String path = start->fullpath; AstPackage **found = string_map_get(&c->info.packages, path); @@ -3657,10 +3652,8 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { GB_ASSERT(scope->flags&ScopeFlag_Pkg); - if (ptr_set_exists(&parent_scope->imported, scope)) { + if (ptr_set_update(&parent_scope->imported, scope)) { // error(token, "Multiple import of the same file within this scope"); - } else { - ptr_set_add(&parent_scope->imported, scope); } String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false); @@ -4013,10 +4006,9 @@ void check_import_entities(Checker *c) { if (pkg == nullptr) { continue; } - if (ptr_set_exists(&emitted, pkg)) { + if (ptr_set_update(&emitted, pkg)) { continue; } - ptr_set_add(&emitted, pkg); array_add(&package_order, n); } @@ -4259,11 +4251,9 @@ void calculate_global_init_order(Checker *c) { // if (!decl_info_has_init(d)) { // continue; // } - if (ptr_set_exists(&emitted, d)) { + if (ptr_set_update(&emitted, d)) { continue; } - ptr_set_add(&emitted, d); - array_add(&info->variable_init_order, d); } diff --git a/src/checker.hpp b/src/checker.hpp index 9c9b77ac3..e672a477b 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -160,12 +160,13 @@ struct ProcInfo { enum ScopeFlag : i32 { - ScopeFlag_Pkg = 1<<1, - ScopeFlag_Global = 1<<2, - ScopeFlag_File = 1<<3, - ScopeFlag_Init = 1<<4, - ScopeFlag_Proc = 1<<5, - ScopeFlag_Type = 1<<6, + ScopeFlag_Pkg = 1<<1, + ScopeFlag_Builtin = 1<<2, + ScopeFlag_Global = 1<<3, + ScopeFlag_File = 1<<4, + ScopeFlag_Init = 1<<5, + ScopeFlag_Proc = 1<<6, + ScopeFlag_Type = 1<<7, ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes diff --git a/src/main.cpp b/src/main.cpp index 67dc5da00..17477384e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -568,6 +568,7 @@ enum BuildFlagKind { BuildFlag_OutFile, BuildFlag_OptimizationLevel, BuildFlag_ShowTimings, + BuildFlag_ShowUnused, BuildFlag_ShowMoreTimings, BuildFlag_ShowSystemCalls, BuildFlag_ThreadCount, @@ -669,6 +670,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer); @@ -860,6 +862,14 @@ bool parse_build_flags(Array args) { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; break; + case BuildFlag_ShowUnused: + GB_ASSERT(value.kind == ExactValue_Invalid); + build_context.show_unused = true; + if (build_context.command != "check") { + gb_printf_err("%.*s is only allowed with 'odin check'\n", LIT(name)); + bad_flags = true; + } + break; case BuildFlag_ShowMoreTimings: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; @@ -1487,6 +1497,7 @@ void print_show_help(String const arg0, String const &command) { bool build = command == "build"; bool run_or_build = command == "run" || command == "build"; + bool check_only = command == "check"; bool check = command == "run" || command == "build" || command == "check"; print_usage_line(0, ""); @@ -1521,6 +1532,12 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); } + if (check_only) { + print_usage_line(1, "-show-unused"); + print_usage_line(2, "Shows unused package declarations within the current project"); + print_usage_line(0, ""); + } + if (run_or_build) { print_usage_line(1, "-keep-temp-files"); print_usage_line(2, "Keeps the temporary files generated during compilation"); @@ -1599,6 +1616,23 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-extra-linker-flags:"); print_usage_line(2, "Adds extra linker specific flags in a string"); print_usage_line(0, ""); + + print_usage_line(1, "-microarch:"); + print_usage_line(2, "Specifies the specific micro-architecture for the build in a string"); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-microarch:sandybridge"); + print_usage_line(3, "-microarch:native"); + print_usage_line(0, ""); + } + + if (check) { + print_usage_line(1, "-disallow-do"); + print_usage_line(2, "Disallows the 'do' keyword in the project"); + print_usage_line(0, ""); + + print_usage_line(1, "-default-to-nil-allocator"); + print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing"); + print_usage_line(0, ""); } if (run_or_build) { @@ -1632,6 +1666,127 @@ void print_show_help(String const arg0, String const &command) { } } +int unused_entity_kind_ordering[Entity_Count] = { + /*Invalid*/ -1, + /*Constant*/ 0, + /*Variable*/ 1, + /*TypeName*/ 4, + /*Procedure*/ 2, + /*ProcGroup*/ 3, + /*Builtin*/ -1, + /*ImportName*/ -1, + /*LibraryName*/ -1, + /*Nil*/ -1, + /*Label*/ -1, +}; +char const *unused_entity_names[Entity_Count] = { + /*Invalid*/ "", + /*Constant*/ "constants", + /*Variable*/ "variables", + /*TypeName*/ "types", + /*Procedure*/ "procedures", + /*ProcGroup*/ "proc_group", + /*Builtin*/ "", + /*ImportName*/ "import names", + /*LibraryName*/ "library names", + /*Nil*/ "", + /*Label*/ "", +}; + + +GB_COMPARE_PROC(cmp_entities_for_unused) { + GB_ASSERT(a != nullptr); + GB_ASSERT(b != nullptr); + Entity *x = *cast(Entity **)a; + Entity *y = *cast(Entity **)b; + int res = 0; + res = string_compare(x->pkg->name, y->pkg->name); + if (res != 0) { + return res; + } + int ox = unused_entity_kind_ordering[x->kind]; + int oy = unused_entity_kind_ordering[y->kind]; + if (ox < oy) { + return -1; + } else if (ox > oy) { + return +1; + } + res = string_compare(x->token.string, y->token.string); + return res; +} + + +void print_show_unused(Checker *c) { + CheckerInfo *info = &c->info; + + auto unused = array_make(permanent_allocator(), 0, info->entities.count); + for_array(i, info->entities) { + Entity *e = info->entities[i]; + if (e == nullptr) { + continue; + } + if (e->pkg == nullptr || e->pkg->scope == nullptr) { + continue; + } + if (e->pkg->scope->flags & ScopeFlag_Builtin) { + continue; + } + switch (e->kind) { + case Entity_Invalid: + case Entity_Builtin: + case Entity_Nil: + case Entity_Label: + continue; + case Entity_Constant: + case Entity_Variable: + case Entity_TypeName: + case Entity_Procedure: + case Entity_ProcGroup: + case Entity_ImportName: + case Entity_LibraryName: + // Fine + break; + } + if ((e->scope->flags & (ScopeFlag_Pkg|ScopeFlag_File)) == 0) { + continue; + } + if (e->token.string.len == 0) { + continue; + } + if (e->token.string == "_") { + continue; + } + if (ptr_set_exists(&info->minimum_dependency_set, e)) { + continue; + } + array_add(&unused, e); + } + + gb_sort_array(unused.data, unused.count, cmp_entities_for_unused); + + print_usage_line(0, "Unused Package Declarations"); + + AstPackage *curr_pkg = nullptr; + EntityKind curr_entity_kind = Entity_Invalid; + for_array(i, unused) { + Entity *e = unused[i]; + if (curr_pkg != e->pkg) { + curr_pkg = e->pkg; + curr_entity_kind = Entity_Invalid; + print_usage_line(0, ""); + print_usage_line(0, "package %.*s", LIT(curr_pkg->name)); + } + if (curr_entity_kind != e->kind) { + curr_entity_kind = e->kind; + print_usage_line(1, "%s", unused_entity_names[e->kind]); + } + // TokenPos pos = e->token.pos; + // print_usage_line(2, "%.*s(%td:%td) %.*s", LIT(pos.file), pos.line, pos.column, LIT(e->token.string)); + print_usage_line(2, "%.*s", LIT(e->token.string)); + } + print_usage_line(0, ""); +} + int main(int arg_count, char const **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -1821,6 +1976,10 @@ int main(int arg_count, char const **arg_ptr) { temp_allocator_free_all(&temporary_allocator_data); if (build_context.no_output_files) { + if (build_context.show_unused) { + print_show_unused(&checker); + } + if (build_context.query_data_set_settings.ok) { generate_and_print_query_data(&checker, timings); } else { diff --git a/src/parser.cpp b/src/parser.cpp index cf464f149..4470f979b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1099,7 +1099,7 @@ Ast *ast_label_decl(AstFile *f, Token token, Ast *name) { } Ast *ast_value_decl(AstFile *f, Array const &names, Ast *type, Array const &values, bool is_mutable, - CommentGroup *docs, CommentGroup *comment) { + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ValueDecl); result->ValueDecl.names = slice_from_array(names); result->ValueDecl.type = type; @@ -1122,7 +1122,7 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C } Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name, - CommentGroup *docs, CommentGroup *comment) { + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ImportDecl); result->ImportDecl.token = token; result->ImportDecl.is_using = is_using; @@ -1134,7 +1134,7 @@ Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Toke } Ast *ast_foreign_import_decl(AstFile *f, Token token, Array filepaths, Token library_name, - CommentGroup *docs, CommentGroup *comment) { + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl); result->ForeignImportDecl.token = token; result->ForeignImportDecl.filepaths = slice_from_array(filepaths); diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index e75202663..5432fa094 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -24,6 +24,7 @@ struct PtrSet { template void ptr_set_init (PtrSet *s, gbAllocator a, isize capacity = 16); template void ptr_set_destroy(PtrSet *s); template T ptr_set_add (PtrSet *s, T ptr); +template bool ptr_set_update (PtrSet *s, T ptr); // returns true if it previously existsed template bool ptr_set_exists (PtrSet *s, T ptr); template void ptr_set_remove (PtrSet *s, T ptr); template void ptr_set_clear (PtrSet *s); @@ -152,9 +153,7 @@ T ptr_set_add(PtrSet *s, T ptr) { ptr_set_grow(s); } fr = ptr_set__find(s, ptr); - if (fr.entry_index != PTR_SET_SENTINEL) { - index = fr.entry_index; - } else { + if (fr.entry_index == PTR_SET_SENTINEL) { index = ptr_set__add_entry(s, ptr); if (fr.entry_prev != PTR_SET_SENTINEL) { s->entries.data[fr.entry_prev].next = index; @@ -168,6 +167,32 @@ T ptr_set_add(PtrSet *s, T ptr) { return ptr; } +template +bool ptr_set_update(PtrSet *s, T ptr) { // returns true if it previously existsed + bool exists = false; + PtrSetIndex index; + PtrSetFindResult fr; + if (s->hashes.count == 0) { + ptr_set_grow(s); + } + fr = ptr_set__find(s, ptr); + if (fr.entry_index != PTR_SET_SENTINEL) { + exists = true; + } else { + index = ptr_set__add_entry(s, ptr); + if (fr.entry_prev != PTR_SET_SENTINEL) { + s->entries.data[fr.entry_prev].next = index; + } else { + s->hashes.data[fr.hash_index] = index; + } + } + if (ptr_set__full(s)) { + ptr_set_grow(s); + } + return exists; +} + + template void ptr_set__erase(PtrSet *s, PtrSetFindResult fr) { From fea8c63ab3168728c4b7d8c432bbb2eb41069100 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 12:19:28 +0000 Subject: [PATCH 055/170] Fix string_compare --- src/string.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/string.cpp b/src/string.cpp index 6a0192507..408f53d72 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -164,6 +164,7 @@ int string_compare(String const &x, String const &y) { return cast(int)x[offset] - cast(int)y[offset]; } } + return cast(int)(x.len - y.len); } return 0; } From edd9d5e50b8c976c32f7227e0dfef8ddb9443c40 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 13:02:10 +0000 Subject: [PATCH 056/170] Add `-show-unused-with-location` --- src/build_settings.cpp | 1 + src/docs.cpp | 127 ++++++++++++++++++----------------------- src/main.cpp | 98 +++++++++++-------------------- 3 files changed, 90 insertions(+), 136 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 5c1babe0c..3210c914c 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -144,6 +144,7 @@ struct BuildContext { i32 optimization_level; bool show_timings; bool show_unused; + bool show_unused_with_location; bool show_more_timings; bool show_system_calls; bool keep_temp_files; diff --git a/src/docs.cpp b/src/docs.cpp index 3bc0da649..a4a980e68 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -1,6 +1,58 @@ // Generates Documentation + +gb_global int print_entity_kind_ordering[Entity_Count] = { + /*Invalid*/ -1, + /*Constant*/ 0, + /*Variable*/ 1, + /*TypeName*/ 4, + /*Procedure*/ 2, + /*ProcGroup*/ 3, + /*Builtin*/ -1, + /*ImportName*/ -1, + /*LibraryName*/ -1, + /*Nil*/ -1, + /*Label*/ -1, +}; +gb_global char const *print_entity_names[Entity_Count] = { + /*Invalid*/ "", + /*Constant*/ "constants", + /*Variable*/ "variables", + /*TypeName*/ "types", + /*Procedure*/ "procedures", + /*ProcGroup*/ "proc_group", + /*Builtin*/ "", + /*ImportName*/ "import names", + /*LibraryName*/ "library names", + /*Nil*/ "", + /*Label*/ "", +}; + + +GB_COMPARE_PROC(cmp_entities_for_printing) { + GB_ASSERT(a != nullptr); + GB_ASSERT(b != nullptr); + Entity *x = *cast(Entity **)a; + Entity *y = *cast(Entity **)b; + int res = 0; + res = string_compare(x->pkg->name, y->pkg->name); + if (res != 0) { + return res; + } + int ox = print_entity_kind_ordering[x->kind]; + int oy = print_entity_kind_ordering[y->kind]; + if (ox < oy) { + return -1; + } else if (ox > oy) { + return +1; + } + res = string_compare(x->token.string, y->token.string); + return res; +} + + gbString expr_to_string(Ast *expression); +gbString type_to_string(Type *type); String alloc_comment_group_string(gbAllocator a, CommentGroup g) { isize len = 0; @@ -32,76 +84,7 @@ String alloc_comment_group_string(gbAllocator a, CommentGroup g) { return make_string(text, len); } -#if 0 -void print_type_spec(Ast *spec) { - ast_node(ts, TypeSpec, spec); - GB_ASSERT(ts->name->kind == Ast_Ident); - String name = ts->name->Ident.string; - if (name.len == 0) { - return; - } - if (name[0] == '_') { - return; - } - gb_printf("type %.*s\n", LIT(name)); -} - -void print_proc_decl(AstProcDecl *pd) { - GB_ASSERT(pd->name->kind == Ast_Ident); - String name = pd->name->Ident.string; - if (name.len == 0) { - return; - } - if (name[0] == '_') { - return; - } - - String docs = alloc_comment_group_string(heap_allocator(), pd->docs); - defer (gb_free(heap_allocator(), docs.text)); - - if (docs.len > 0) { - gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len); - } else { - return; - } - - ast_node(proc_type, ProcType, pd->type); - - gbString params = expr_to_string(proc_type->params); - defer (gb_string_free(params)); - gb_printf("proc %.*s(%s)", LIT(name), params); - if (proc_type->results != nullptr) { - ast_node(fl, FieldList, proc_type->results); - isize count = fl->list.count; - if (count > 0) { - gbString results = expr_to_string(proc_type->results); - defer (gb_string_free(results)); - gb_printf(" -> "); - if (count != 1) { - gb_printf("("); - } - gb_printf("%s", results); - if (count != 1) { - gb_printf(")"); - } - } - } - gb_printf("\n\n"); -} -#endif -void print_declaration(Ast *decl) { -} - -void generate_documentation(Parser *parser) { - // for_array(file_index, parser->files) { - // AstFile *file = parser->files[file_index]; - // Tokenizer *tokenizer = &file->tokenizer; - // String fullpath = tokenizer->fullpath; - // gb_printf("%.*s\n", LIT(fullpath)); - - // for_array(decl_index, file->decls) { - // Ast *decl = file->decls[decl_index]; - // print_declaration(decl); - // } - // } +void generate_documentation(Checker *c) { + CheckerInfo *info = &c->info; + } diff --git a/src/main.cpp b/src/main.cpp index 17477384e..0f27d5079 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,8 +18,8 @@ gb_global Timings global_timings = {0}; #include "checker.hpp" #include "parser.cpp" -#include "docs.cpp" #include "checker.cpp" +#include "docs.cpp" #if defined(LLVM_BACKEND_SUPPORT) @@ -522,7 +522,7 @@ void usage(String argv0) { print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); print_usage_line(1, "check parse and type check .odin file"); print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); - print_usage_line(1, "docs generate documentation for a .odin file"); + print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files"); print_usage_line(1, "version print version"); print_usage_line(0, ""); print_usage_line(0, "For more information of flags, apply the flag to see what is possible"); @@ -569,6 +569,7 @@ enum BuildFlagKind { BuildFlag_OptimizationLevel, BuildFlag_ShowTimings, BuildFlag_ShowUnused, + BuildFlag_ShowUnusedWithLocation, BuildFlag_ShowMoreTimings, BuildFlag_ShowSystemCalls, BuildFlag_ThreadCount, @@ -671,6 +672,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer); @@ -870,6 +872,15 @@ bool parse_build_flags(Array args) { bad_flags = true; } break; + case BuildFlag_ShowUnusedWithLocation: + GB_ASSERT(value.kind == ExactValue_Invalid); + build_context.show_unused = true; + build_context.show_unused_with_location = true; + if (build_context.command != "check") { + gb_printf_err("%.*s is only allowed with 'odin check'\n", LIT(name)); + bad_flags = true; + } + break; case BuildFlag_ShowMoreTimings: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; @@ -1489,12 +1500,13 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "check parse and type check .odin file"); } else if (command == "query") { print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program"); - } else if (command == "docs") { - print_usage_line(1, "docs generate documentation for a .odin file"); + } else if (command == "doc") { + print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files"); } else if (command == "version") { print_usage_line(1, "version print version"); } + bool doc = command == "doc"; bool build = command == "build"; bool run_or_build = command == "run" || command == "build"; bool check_only = command == "check"; @@ -1536,6 +1548,9 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-show-unused"); print_usage_line(2, "Shows unused package declarations within the current project"); print_usage_line(0, ""); + print_usage_line(1, "-show-unused-with-location"); + print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location"); + print_usage_line(0, ""); } if (run_or_build) { @@ -1666,56 +1681,6 @@ void print_show_help(String const arg0, String const &command) { } } -int unused_entity_kind_ordering[Entity_Count] = { - /*Invalid*/ -1, - /*Constant*/ 0, - /*Variable*/ 1, - /*TypeName*/ 4, - /*Procedure*/ 2, - /*ProcGroup*/ 3, - /*Builtin*/ -1, - /*ImportName*/ -1, - /*LibraryName*/ -1, - /*Nil*/ -1, - /*Label*/ -1, -}; -char const *unused_entity_names[Entity_Count] = { - /*Invalid*/ "", - /*Constant*/ "constants", - /*Variable*/ "variables", - /*TypeName*/ "types", - /*Procedure*/ "procedures", - /*ProcGroup*/ "proc_group", - /*Builtin*/ "", - /*ImportName*/ "import names", - /*LibraryName*/ "library names", - /*Nil*/ "", - /*Label*/ "", -}; - - -GB_COMPARE_PROC(cmp_entities_for_unused) { - GB_ASSERT(a != nullptr); - GB_ASSERT(b != nullptr); - Entity *x = *cast(Entity **)a; - Entity *y = *cast(Entity **)b; - int res = 0; - res = string_compare(x->pkg->name, y->pkg->name); - if (res != 0) { - return res; - } - int ox = unused_entity_kind_ordering[x->kind]; - int oy = unused_entity_kind_ordering[y->kind]; - if (ox < oy) { - return -1; - } else if (ox > oy) { - return +1; - } - res = string_compare(x->token.string, y->token.string); - return res; -} - - void print_show_unused(Checker *c) { CheckerInfo *info = &c->info; @@ -1762,7 +1727,7 @@ void print_show_unused(Checker *c) { array_add(&unused, e); } - gb_sort_array(unused.data, unused.count, cmp_entities_for_unused); + gb_sort_array(unused.data, unused.count, cmp_entities_for_printing); print_usage_line(0, "Unused Package Declarations"); @@ -1778,11 +1743,14 @@ void print_show_unused(Checker *c) { } if (curr_entity_kind != e->kind) { curr_entity_kind = e->kind; - print_usage_line(1, "%s", unused_entity_names[e->kind]); + print_usage_line(1, "%s", print_entity_names[e->kind]); + } + if (build_context.show_unused_with_location) { + TokenPos pos = e->token.pos; + print_usage_line(2, "%.*s(%td:%td) %.*s", LIT(pos.file), pos.line, pos.column, LIT(e->token.string)); + } else { + print_usage_line(2, "%.*s", LIT(e->token.string)); } - // TokenPos pos = e->token.pos; - // print_usage_line(2, "%.*s(%td:%td) %.*s", LIT(pos.file), pos.line, pos.column, LIT(e->token.string)); - print_usage_line(2, "%.*s", LIT(e->token.string)); } print_usage_line(0, ""); } @@ -1867,7 +1835,7 @@ int main(int arg_count, char const **arg_ptr) { build_context.no_output_files = true; build_context.query_data_set_settings.ok = true; init_filename = args[2]; - } else if (command == "docs") { + } else if (command == "doc") { if (args.count < 3) { usage(args[0]); return 1; @@ -1875,6 +1843,7 @@ int main(int arg_count, char const **arg_ptr) { init_filename = args[2]; build_context.generate_docs = true; + build_context.no_entry_point = true; // ignore entry point #if 1 print_usage_line(0, "Documentation generation is not yet supported"); return 1; @@ -1956,10 +1925,6 @@ int main(int arg_count, char const **arg_ptr) { temp_allocator_free_all(&temporary_allocator_data); - if (build_context.generate_docs) { - // generate_documentation(&parser); - return 0; - } timings_start_section(timings, str_lit("type check")); Checker checker = {0}; @@ -1975,6 +1940,11 @@ int main(int arg_count, char const **arg_ptr) { temp_allocator_free_all(&temporary_allocator_data); + if (build_context.generate_docs) { + generate_documentation(&checker); + return global_error_collector.count ? 1 : 0; + } + if (build_context.no_output_files) { if (build_context.show_unused) { print_show_unused(&checker); From 00192bb349993dbdd02d13f31b51809bba4d875e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 13:55:08 +0000 Subject: [PATCH 057/170] Improve flag handling to check for invalid uses --- src/build_settings.cpp | 32 ++++++++++ src/docs.cpp | 40 +++++++++++++ src/main.cpp | 129 ++++++++++++++++++++++++----------------- 3 files changed, 147 insertions(+), 54 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 3210c914c..8cd516505 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -104,6 +104,35 @@ enum BuildModeKind { BuildMode_Assembly, }; +enum CommandKind : u32 { + Command_run = 1<<0, + Command_build = 1<<1, + Command_check = 1<<3, + Command_query = 1<<4, + Command_doc = 1<<5, + Command_version = 1<<6, + + Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc, + Command__does_build = Command_run|Command_build, + Command_all = ~(u32)0, +}; + +char const *odin_command_strings[32] = { + "run", + "build", + "check", + "query", + "doc", + "version", +}; + + + +enum CmdDocFlag : u32 { + CmdDocFlag_All = 1<<0, +}; + + // This stores the information for the specify architecture of this build struct BuildContext { @@ -124,6 +153,7 @@ struct BuildContext { i64 word_size; // Size of a pointer, must be >= 4 i64 max_align; // max alignment, must be >= 1 (and typically >= word_size) + CommandKind command_kind; String command; TargetMetrics metrics; @@ -167,6 +197,8 @@ struct BuildContext { bool ignore_microsoft_magic; bool linker_map_file; + u32 cmd_doc_flags; + QueryDataSetSettings query_data_set_settings; gbAffinity affinity; diff --git a/src/docs.cpp b/src/docs.cpp index a4a980e68..237323af3 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -50,6 +50,14 @@ GB_COMPARE_PROC(cmp_entities_for_printing) { return res; } +GB_COMPARE_PROC(cmp_ast_package_by_name) { + GB_ASSERT(a != nullptr); + GB_ASSERT(b != nullptr); + AstPackage *x = *cast(AstPackage **)a; + AstPackage *y = *cast(AstPackage **)b; + return string_compare(x->name, y->name); +} + gbString expr_to_string(Ast *expression); gbString type_to_string(Type *type); @@ -84,7 +92,39 @@ String alloc_comment_group_string(gbAllocator a, CommentGroup g) { return make_string(text, len); } + +void print_doc_line(i32 indent, char const *fmt, ...) { + while (indent --> 0) { + gb_printf("\t"); + } + va_list va; + va_start(va, fmt); + gb_printf_va(fmt, va); + va_end(va); + gb_printf("\n"); +} + +void print_doc_package(CheckerInfo *info, AstPackage *pkg) { + print_doc_line(0, "%.*s", LIT(pkg->name)); +} + void generate_documentation(Checker *c) { CheckerInfo *info = &c->info; + if (build_context.cmd_doc_flags & CmdDocFlag_All) { + auto pkgs = array_make(permanent_allocator(), info->packages.entries.count); + for_array(i, info->packages.entries) { + array_add(&pkgs, info->packages.entries[i].value); + } + + gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + + for_array(i, pkgs) { + print_doc_package(info, pkgs[i]); + } + } else { + GB_ASSERT(info->init_scope->flags & ScopeFlag_Pkg); + AstPackage *pkg = info->init_scope->pkg; + print_doc_package(info, pkg); + } } diff --git a/src/main.cpp b/src/main.cpp index 0f27d5079..45fb9ac85 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -623,10 +623,13 @@ struct BuildFlag { BuildFlagKind kind; String name; BuildFlagParamKind param_kind; + u32 command_support; + bool allow_mulitple; }; -void add_flag(Array *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) { - BuildFlag flag = {kind, name, param_kind}; + +void add_flag(Array *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_mulitple=false) { + BuildFlag flag = {kind, name, param_kind, command_support, allow_mulitple}; array_add(build_flags, flag); } @@ -667,46 +670,46 @@ ExactValue build_param_to_exact_value(String name, String param) { bool parse_build_flags(Array args) { auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT); - add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); - add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer); - add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String); + add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build); + add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check); + add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check); + add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all); + add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true); + add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message + add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); #if defined(GB_SYSTEM_WINDOWS) - add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String); + add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build); #endif GB_ASSERT(args.count >= 3); @@ -736,11 +739,19 @@ bool parse_build_flags(Array args) { String param = {}; if (end < flag.len-1) param = substring(flag, 2+end, flag.len); + bool is_supported = true; bool found = false; + BuildFlag found_bf = {}; for_array(build_flag_index, build_flags) { BuildFlag bf = build_flags[build_flag_index]; if (bf.name == name) { found = true; + found_bf = bf; + if ((bf.command_support & build_context.command_kind) == 0) { + is_supported = false; + break; + } + if (set_flags[bf.kind]) { gb_printf_err("Previous flag set: '%.*s'\n", LIT(name)); bad_flags = true; @@ -867,19 +878,11 @@ bool parse_build_flags(Array args) { case BuildFlag_ShowUnused: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_unused = true; - if (build_context.command != "check") { - gb_printf_err("%.*s is only allowed with 'odin check'\n", LIT(name)); - bad_flags = true; - } break; case BuildFlag_ShowUnusedWithLocation: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_unused = true; build_context.show_unused_with_location = true; - if (build_context.command != "check") { - gb_printf_err("%.*s is only allowed with 'odin check'\n", LIT(name)); - bad_flags = true; - } break; case BuildFlag_ShowMoreTimings: GB_ASSERT(value.kind == ExactValue_Invalid); @@ -1252,20 +1255,30 @@ bool parse_build_flags(Array args) { } } - - switch (bf.kind) { - case BuildFlag_Define: - // Allow for multiple - break; - default: + if (!bf.allow_mulitple) { set_flags[bf.kind] = ok; - break; } } break; } } - if (!found) { + if (found && !is_supported) { + gb_printf_err("Unknown flag for 'odin %.*s': '%.*s'\n", LIT(build_context.command), LIT(name)); + gb_printf_err("'%.*s' is supported with the following commands:\n", LIT(name)); + gb_printf_err("\t"); + i32 count = 0; + for (u32 i = 0; i < 32; i++) { + if (found_bf.command_support & (1< 0) { + gb_printf_err(", "); + } + gb_printf_err("%s", odin_command_strings[i]); + count += 1; + } + } + gb_printf_err("\n"); + bad_flags = true; + } else if (!found) { gb_printf_err("Unknown flag: '%.*s'\n", LIT(name)); bad_flags = true; } @@ -1795,6 +1808,7 @@ int main(int arg_count, char const **arg_ptr) { usage(args[0]); return 1; } + build_context.command_kind = Command_run; Array run_args = array_make(heap_allocator(), 0, arg_count); defer (array_free(&run_args)); @@ -1814,17 +1828,20 @@ int main(int arg_count, char const **arg_ptr) { run_args_string = string_join_and_quote(heap_allocator(), run_args); init_filename = args[2]; run_output = true; + } else if (command == "build") { if (args.count < 3) { usage(args[0]); return 1; } + build_context.command_kind = Command_build; init_filename = args[2]; } else if (command == "check") { if (args.count < 3) { usage(args[0]); return 1; } + build_context.command_kind = Command_check; build_context.no_output_files = true; init_filename = args[2]; } else if (command == "query") { @@ -1832,6 +1849,7 @@ int main(int arg_count, char const **arg_ptr) { usage(args[0]); return 1; } + build_context.command_kind = Command_query; build_context.no_output_files = true; build_context.query_data_set_settings.ok = true; init_filename = args[2]; @@ -1841,14 +1859,17 @@ int main(int arg_count, char const **arg_ptr) { return 1; } + build_context.command_kind = Command_doc; init_filename = args[2]; + build_context.no_output_files = true; build_context.generate_docs = true; build_context.no_entry_point = true; // ignore entry point - #if 1 + #if 0 print_usage_line(0, "Documentation generation is not yet supported"); return 1; #endif } else if (command == "version") { + build_context.command_kind = Command_version; gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION)); #ifdef NIGHTLY From d90fc18bef8300da0fc6102d57b9e970bd7fe935 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 15:05:16 +0000 Subject: [PATCH 058/170] Basic `odin doc` support --- src/build_settings.cpp | 2 + src/check_expr.cpp | 208 ++++++++++++++++++++++++---------------- src/checker.cpp | 4 +- src/checker.hpp | 1 + src/docs.cpp | 212 +++++++++++++++++++++++++++++++++++++++-- src/exact_value.cpp | 6 +- src/main.cpp | 23 +++++ src/parser.cpp | 6 +- 8 files changed, 363 insertions(+), 99 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 8cd516505..0e8f2c5bc 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -130,6 +130,7 @@ char const *odin_command_strings[32] = { enum CmdDocFlag : u32 { CmdDocFlag_All = 1<<0, + CmdDocFlag_AllPackages = 1<<1, }; @@ -198,6 +199,7 @@ struct BuildContext { bool linker_map_file; u32 cmd_doc_flags; + Array doc_packages; QueryDataSetSettings query_data_set_settings; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 94a3467d2..f6530df51 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10208,14 +10208,14 @@ void check_expr_or_type(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) } -gbString write_expr_to_string(gbString str, Ast *node); +gbString write_expr_to_string(gbString str, Ast *node, bool shorthand); gbString write_struct_fields_to_string(gbString str, Slice const ¶ms) { for_array(i, params) { if (i > 0) { str = gb_string_appendc(str, ", "); } - str = write_expr_to_string(str, params[i]); + str = write_expr_to_string(str, params[i], false); } return str; } @@ -10229,11 +10229,23 @@ gbString string_append_string(gbString str, String string) { gbString string_append_token(gbString str, Token token) { - return string_append_string(str, token.string); + if (token.kind == Token_String) { + str = gb_string_append_rune(str, '"'); + } else if (token.kind == Token_Rune) { + str = gb_string_append_rune(str, '\''); + } + str = string_append_string(str, token.string); + if (token.kind == Token_String) { + str = gb_string_append_rune(str, '"'); + } else if (token.kind == Token_Rune) { + str = gb_string_append_rune(str, '\''); + } + + return str; } -gbString write_expr_to_string(gbString str, Ast *node) { +gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { if (node == nullptr) return str; @@ -10271,21 +10283,30 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = gb_string_appendc(str, "proc{"); for_array(i, pg->args) { if (i > 0) str = gb_string_appendc(str, ", "); - str = write_expr_to_string(str, pg->args[i]); + str = write_expr_to_string(str, pg->args[i], shorthand); } str = gb_string_append_rune(str, '}'); case_end; case_ast_node(pl, ProcLit, node); - str = write_expr_to_string(str, pl->type); + str = write_expr_to_string(str, pl->type, shorthand); + if (pl->body) { + str = gb_string_appendc(str, " {...}"); + } else { + str = gb_string_appendc(str, " ---"); + } case_end; case_ast_node(cl, CompoundLit, node); - str = write_expr_to_string(str, cl->type); + str = write_expr_to_string(str, cl->type, shorthand); str = gb_string_append_rune(str, '{'); - for_array(i, cl->elems) { - if (i > 0) str = gb_string_appendc(str, ", "); - str = write_expr_to_string(str, cl->elems[i]); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + for_array(i, cl->elems) { + if (i > 0) str = gb_string_appendc(str, ", "); + str = write_expr_to_string(str, cl->elems[i], shorthand); + } } str = gb_string_append_rune(str, '}'); case_end; @@ -10294,71 +10315,71 @@ gbString write_expr_to_string(gbString str, Ast *node) { case_ast_node(te, TagExpr, node); str = gb_string_append_rune(str, '#'); str = string_append_token(str, te->name); - str = write_expr_to_string(str, te->expr); + str = write_expr_to_string(str, te->expr, shorthand); case_end; case_ast_node(ue, UnaryExpr, node); str = string_append_token(str, ue->op); - str = write_expr_to_string(str, ue->expr); + str = write_expr_to_string(str, ue->expr, shorthand); case_end; case_ast_node(de, DerefExpr, node); - str = write_expr_to_string(str, de->expr); + str = write_expr_to_string(str, de->expr, shorthand); str = gb_string_append_rune(str, '^'); case_end; case_ast_node(be, BinaryExpr, node); - str = write_expr_to_string(str, be->left); + str = write_expr_to_string(str, be->left, shorthand); str = gb_string_append_rune(str, ' '); str = string_append_token(str, be->op); str = gb_string_append_rune(str, ' '); - str = write_expr_to_string(str, be->right); + str = write_expr_to_string(str, be->right, shorthand); case_end; case_ast_node(te, TernaryExpr, node); - str = write_expr_to_string(str, te->cond); + str = write_expr_to_string(str, te->cond, shorthand); str = gb_string_appendc(str, " ? "); - str = write_expr_to_string(str, te->x); + str = write_expr_to_string(str, te->x, shorthand); str = gb_string_appendc(str, " : "); - str = write_expr_to_string(str, te->y); + str = write_expr_to_string(str, te->y, shorthand); case_end; case_ast_node(te, TernaryIfExpr, node); - str = write_expr_to_string(str, te->x); + str = write_expr_to_string(str, te->x, shorthand); str = gb_string_appendc(str, " if "); - str = write_expr_to_string(str, te->cond); + str = write_expr_to_string(str, te->cond, shorthand); str = gb_string_appendc(str, " else "); - str = write_expr_to_string(str, te->y); + str = write_expr_to_string(str, te->y, shorthand); case_end; case_ast_node(te, TernaryWhenExpr, node); - str = write_expr_to_string(str, te->x); + str = write_expr_to_string(str, te->x, shorthand); str = gb_string_appendc(str, " when "); - str = write_expr_to_string(str, te->cond); + str = write_expr_to_string(str, te->cond, shorthand); str = gb_string_appendc(str, " else "); - str = write_expr_to_string(str, te->y); + str = write_expr_to_string(str, te->y, shorthand); case_end; case_ast_node(pe, ParenExpr, node); str = gb_string_append_rune(str, '('); - str = write_expr_to_string(str, pe->expr); + str = write_expr_to_string(str, pe->expr, shorthand); str = gb_string_append_rune(str, ')'); case_end; case_ast_node(se, SelectorExpr, node); - str = write_expr_to_string(str, se->expr); + str = write_expr_to_string(str, se->expr, shorthand); str = string_append_token(str, se->token); - str = write_expr_to_string(str, se->selector); + str = write_expr_to_string(str, se->selector, shorthand); case_end; case_ast_node(se, ImplicitSelectorExpr, node); str = gb_string_append_rune(str, '.'); - str = write_expr_to_string(str, se->selector); + str = write_expr_to_string(str, se->selector, shorthand); case_end; case_ast_node(se, SelectorCallExpr, node); - str = write_expr_to_string(str, se->expr); + str = write_expr_to_string(str, se->expr, shorthand); str = gb_string_appendc(str, "("); ast_node(ce, CallExpr, se->call); isize start = se->modified_call ? 1 : 0; @@ -10367,86 +10388,86 @@ gbString write_expr_to_string(gbString str, Ast *node) { if (i > start) { str = gb_string_appendc(str, ", "); } - str = write_expr_to_string(str, arg); + str = write_expr_to_string(str, arg, shorthand); } str = gb_string_appendc(str, ")"); case_end; case_ast_node(ta, TypeAssertion, node); - str = write_expr_to_string(str, ta->expr); + str = write_expr_to_string(str, ta->expr, shorthand); str = gb_string_appendc(str, ".("); - str = write_expr_to_string(str, ta->type); + str = write_expr_to_string(str, ta->type, shorthand); str = gb_string_append_rune(str, ')'); case_end; case_ast_node(tc, TypeCast, node); str = string_append_token(str, tc->token); str = gb_string_append_rune(str, '('); - str = write_expr_to_string(str, tc->type); + str = write_expr_to_string(str, tc->type, shorthand); str = gb_string_append_rune(str, ')'); - str = write_expr_to_string(str, tc->expr); + str = write_expr_to_string(str, tc->expr, shorthand); case_end; case_ast_node(ac, AutoCast, node); str = string_append_token(str, ac->token); str = gb_string_append_rune(str, ' '); - str = write_expr_to_string(str, ac->expr); + str = write_expr_to_string(str, ac->expr, shorthand); case_end; case_ast_node(ie, IndexExpr, node); - str = write_expr_to_string(str, ie->expr); + str = write_expr_to_string(str, ie->expr, shorthand); str = gb_string_append_rune(str, '['); - str = write_expr_to_string(str, ie->index); + str = write_expr_to_string(str, ie->index, shorthand); str = gb_string_append_rune(str, ']'); case_end; case_ast_node(se, SliceExpr, node); - str = write_expr_to_string(str, se->expr); + str = write_expr_to_string(str, se->expr, shorthand); str = gb_string_append_rune(str, '['); - str = write_expr_to_string(str, se->low); + str = write_expr_to_string(str, se->low, shorthand); str = string_append_token(str, se->interval); - str = write_expr_to_string(str, se->high); + str = write_expr_to_string(str, se->high, shorthand); str = gb_string_append_rune(str, ']'); case_end; case_ast_node(e, Ellipsis, node); str = gb_string_appendc(str, ".."); - str = write_expr_to_string(str, e->expr); + str = write_expr_to_string(str, e->expr, shorthand); case_end; case_ast_node(fv, FieldValue, node); - str = write_expr_to_string(str, fv->field); + str = write_expr_to_string(str, fv->field, shorthand); str = gb_string_appendc(str, " = "); - str = write_expr_to_string(str, fv->value); + str = write_expr_to_string(str, fv->value, shorthand); case_end; case_ast_node(ht, HelperType, node); str = gb_string_appendc(str, "#type "); - str = write_expr_to_string(str, ht->type); + str = write_expr_to_string(str, ht->type, shorthand); case_end; case_ast_node(ht, DistinctType, node); str = gb_string_appendc(str, "distinct "); - str = write_expr_to_string(str, ht->type); + str = write_expr_to_string(str, ht->type, shorthand); case_end; case_ast_node(ht, OpaqueType, node); str = gb_string_appendc(str, "opaque "); - str = write_expr_to_string(str, ht->type); + str = write_expr_to_string(str, ht->type, shorthand); case_end; case_ast_node(pt, PolyType, node); str = gb_string_append_rune(str, '$'); - str = write_expr_to_string(str, pt->type); + str = write_expr_to_string(str, pt->type, shorthand); if (pt->specialization != nullptr) { str = gb_string_append_rune(str, '/'); - str = write_expr_to_string(str, pt->specialization); + str = write_expr_to_string(str, pt->specialization, shorthand); } case_end; case_ast_node(pt, PointerType, node); str = gb_string_append_rune(str, '^'); - str = write_expr_to_string(str, pt->type); + str = write_expr_to_string(str, pt->type, shorthand); case_end; case_ast_node(at, ArrayType, node); @@ -10456,40 +10477,44 @@ gbString write_expr_to_string(gbString str, Ast *node) { at->count->UnaryExpr.op.kind == Token_Question) { str = gb_string_appendc(str, "?"); } else { - str = write_expr_to_string(str, at->count); + str = write_expr_to_string(str, at->count, shorthand); } str = gb_string_append_rune(str, ']'); - str = write_expr_to_string(str, at->elem); + str = write_expr_to_string(str, at->elem, shorthand); case_end; case_ast_node(at, DynamicArrayType, node); str = gb_string_appendc(str, "[dynamic]"); - str = write_expr_to_string(str, at->elem); + str = write_expr_to_string(str, at->elem, shorthand); case_end; case_ast_node(bf, BitFieldType, node); str = gb_string_appendc(str, "bit_field "); if (bf->align) { str = gb_string_appendc(str, "#align "); - str = write_expr_to_string(str, bf->align); + str = write_expr_to_string(str, bf->align, shorthand); } str = gb_string_appendc(str, "{"); - str = write_struct_fields_to_string(str, bf->fields); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + str = write_struct_fields_to_string(str, bf->fields); + } str = gb_string_appendc(str, "}"); case_end; case_ast_node(bs, BitSetType, node); str = gb_string_appendc(str, "bit_set["); - str = write_expr_to_string(str, bs->elem); + str = write_expr_to_string(str, bs->elem, shorthand); str = gb_string_appendc(str, "]"); case_end; case_ast_node(mt, MapType, node); str = gb_string_appendc(str, "map["); - str = write_expr_to_string(str, mt->key); + str = write_expr_to_string(str, mt->key, shorthand); str = gb_string_append_rune(str, ']'); - str = write_expr_to_string(str, mt->value); + str = write_expr_to_string(str, mt->value, shorthand); case_end; case_ast_node(f, Field, node); @@ -10509,7 +10534,7 @@ gbString write_expr_to_string(gbString str, Ast *node) { for_array(i, f->names) { Ast *name = f->names[i]; if (i > 0) str = gb_string_appendc(str, ", "); - str = write_expr_to_string(str, name); + str = write_expr_to_string(str, name, shorthand); } if (f->names.count > 0) { if (f->type == nullptr && f->default_value != nullptr) { @@ -10519,14 +10544,14 @@ gbString write_expr_to_string(gbString str, Ast *node) { } if (f->type != nullptr) { str = gb_string_append_rune(str, ' '); - str = write_expr_to_string(str, f->type); + str = write_expr_to_string(str, f->type, shorthand); } if (f->default_value != nullptr) { if (f->type != nullptr) { str = gb_string_append_rune(str, ' '); } str = gb_string_appendc(str, "= "); - str = write_expr_to_string(str, f->default_value); + str = write_expr_to_string(str, f->default_value, shorthand); } case_end; @@ -10552,7 +10577,7 @@ gbString write_expr_to_string(gbString str, Ast *node) { for_array(i, f->list) { if (i > 0) str = gb_string_appendc(str, ", "); if (has_name) { - str = write_expr_to_string(str, f->list[i]); + str = write_expr_to_string(str, f->list[i], shorthand); } else { ast_node(field, Field, f->list[i]); @@ -10566,7 +10591,7 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = gb_string_appendc(str, "#c_vararg "); } - str = write_expr_to_string(str, field->type); + str = write_expr_to_string(str, field->type, shorthand); } } case_end; @@ -10581,7 +10606,7 @@ gbString write_expr_to_string(gbString str, Ast *node) { break; } - str = write_expr_to_string(str, ce->proc); + str = write_expr_to_string(str, ce->proc, shorthand); str = gb_string_appendc(str, "("); for_array(i, ce->args) { @@ -10589,7 +10614,7 @@ gbString write_expr_to_string(gbString str, Ast *node) { if (i > 0) { str = gb_string_appendc(str, ", "); } - str = write_expr_to_string(str, arg); + str = write_expr_to_string(str, arg, shorthand); } str = gb_string_appendc(str, ")"); case_end; @@ -10598,17 +10623,17 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = gb_string_appendc(str, "typeid"); if (tt->specialization) { str = gb_string_appendc(str, "/"); - str = write_expr_to_string(str, tt->specialization); + str = write_expr_to_string(str, tt->specialization, shorthand); } case_end; case_ast_node(pt, ProcType, node); str = gb_string_appendc(str, "proc("); - str = write_expr_to_string(str, pt->params); + str = write_expr_to_string(str, pt->params, shorthand); str = gb_string_appendc(str, ")"); if (pt->results != nullptr) { str = gb_string_appendc(str, " -> "); - str = write_expr_to_string(str, pt->results); + str = write_expr_to_string(str, pt->results, shorthand); } case_end; @@ -10618,7 +10643,11 @@ gbString write_expr_to_string(gbString str, Ast *node) { if (st->is_packed) str = gb_string_appendc(str, "#packed "); if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union "); str = gb_string_append_rune(str, '{'); - str = write_struct_fields_to_string(str, st->fields); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + str = write_struct_fields_to_string(str, st->fields); + } str = gb_string_append_rune(str, '}'); case_end; @@ -10626,30 +10655,38 @@ gbString write_expr_to_string(gbString str, Ast *node) { case_ast_node(st, UnionType, node); str = gb_string_appendc(str, "union "); str = gb_string_append_rune(str, '{'); - str = write_struct_fields_to_string(str, st->variants); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + str = write_struct_fields_to_string(str, st->variants); + } str = gb_string_append_rune(str, '}'); case_end; case_ast_node(et, EnumType, node); str = gb_string_appendc(str, "enum "); if (et->base_type != nullptr) { - str = write_expr_to_string(str, et->base_type); + str = write_expr_to_string(str, et->base_type, shorthand); str = gb_string_append_rune(str, ' '); } str = gb_string_append_rune(str, '{'); - for_array(i, et->fields) { - if (i > 0) { - str = gb_string_appendc(str, ", "); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + for_array(i, et->fields) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, et->fields[i], shorthand); } - str = write_expr_to_string(str, et->fields[i]); } str = gb_string_append_rune(str, '}'); case_end; case_ast_node(rt, RelativeType, node); - str = write_expr_to_string(str, rt->tag); + str = write_expr_to_string(str, rt->tag, shorthand); str = gb_string_appendc(str, "" ); - str = write_expr_to_string(str, rt->type); + str = write_expr_to_string(str, rt->type, shorthand); case_end; @@ -10659,12 +10696,12 @@ gbString write_expr_to_string(gbString str, Ast *node) { if (i > 0) { str = gb_string_appendc(str, ", "); } - str = write_expr_to_string(str, ia->param_types[i]); + str = write_expr_to_string(str, ia->param_types[i], shorthand); } str = gb_string_appendc(str, ")"); if (ia->return_type != nullptr) { str = gb_string_appendc(str, " -> "); - str = write_expr_to_string(str, ia->return_type); + str = write_expr_to_string(str, ia->return_type, shorthand); } if (ia->has_side_effects) { str = gb_string_appendc(str, " #side_effects"); @@ -10677,9 +10714,13 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = gb_string_appendc(str, inline_asm_dialect_strings[ia->dialect]); } str = gb_string_appendc(str, " {"); - str = write_expr_to_string(str, ia->asm_string); - str = gb_string_appendc(str, ", "); - str = write_expr_to_string(str, ia->constraints_string); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + str = write_expr_to_string(str, ia->asm_string, shorthand); + str = gb_string_appendc(str, ", "); + str = write_expr_to_string(str, ia->constraints_string, shorthand); + } str = gb_string_appendc(str, "}"); case_end; } @@ -10688,5 +10729,8 @@ gbString write_expr_to_string(gbString str, Ast *node) { } gbString expr_to_string(Ast *expression) { - return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression); + return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression, false); +} +gbString expr_to_string_shorthand(Ast *expression) { + return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression, true); } diff --git a/src/checker.cpp b/src/checker.cpp index f8018506c..8f8aa7381 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3115,6 +3115,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Ast *init_expr = value; DeclInfo *d = make_decl_info(c->scope, c->decl); + d->decl_node = decl; d->entity = e; d->type_expr = vd->type; d->init_expr = init_expr; @@ -3142,9 +3143,10 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Token token = name->Ident.token; Ast *fl = c->foreign_context.curr_library; - DeclInfo *d = make_decl_info(c->scope, c->decl); Entity *e = nullptr; + DeclInfo *d = make_decl_info(c->scope, c->decl); + d->decl_node = decl; d->attributes = vd->attributes; d->type_expr = vd->type; d->init_expr = init; diff --git a/src/checker.hpp b/src/checker.hpp index e672a477b..97469908b 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -132,6 +132,7 @@ struct DeclInfo { Entity *entity; + Ast * decl_node; Ast * type_expr; Ast * init_expr; Array attributes; diff --git a/src/docs.cpp b/src/docs.cpp index 237323af3..76d8c5433 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -59,9 +59,6 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) { } -gbString expr_to_string(Ast *expression); -gbString type_to_string(Type *type); - String alloc_comment_group_string(gbAllocator a, CommentGroup g) { isize len = 0; for_array(i, g.list) { @@ -103,18 +100,216 @@ void print_doc_line(i32 indent, char const *fmt, ...) { va_end(va); gb_printf("\n"); } +void print_doc_line_no_newline(i32 indent, char const *fmt, ...) { + while (indent --> 0) { + gb_printf("\t"); + } + va_list va; + va_start(va, fmt); + gb_printf_va(fmt, va); + va_end(va); +} + +bool print_doc_comment_group_string(i32 indent, CommentGroup const &g) { + isize len = 0; + for_array(i, g.list) { + String comment = g.list[i].string; + len += comment.len; + len += 1; // for \n + } + if (len == 0) { + return false; + } + + isize count = 0; + for_array(i, g.list) { + String comment = g.list[i].string; + if (comment[1] == '/') { + comment.text += 2; + comment.len -= 2; + } else if (comment[1] == '*') { + comment.text += 2; + comment.len -= 4; + } + comment = string_trim_whitespace(comment); + if (string_starts_with(comment, str_lit("@("))) { + continue; + } + + print_doc_line(indent, "%.*s", LIT(comment)); + count += 1; + } + return count > 0; +} + + + + +void print_doc_expr(Ast *expr) { + gbString s = nullptr; + if (build_context.cmd_doc_flags & CmdDocFlag_All) { + s = expr_to_string(expr); + } else { + s = expr_to_string_shorthand(expr); + } + gb_file_write(gb_file_get_standard(gbFileStandard_Output), s, gb_string_length(s)); + gb_string_free(s); +} + void print_doc_package(CheckerInfo *info, AstPackage *pkg) { - print_doc_line(0, "%.*s", LIT(pkg->name)); + if (pkg == nullptr) { + return; + } + + print_doc_line(0, "package %.*s", LIT(pkg->name)); + + if (pkg->scope != nullptr) { + auto entities = array_make(heap_allocator(), 0, pkg->scope->elements.entries.count); + defer (array_free(&entities)); + for_array(i, pkg->scope->elements.entries) { + Entity *e = pkg->scope->elements.entries[i].value; + switch (e->kind) { + case Entity_Invalid: + case Entity_Builtin: + case Entity_Nil: + case Entity_Label: + continue; + case Entity_Constant: + case Entity_Variable: + case Entity_TypeName: + case Entity_Procedure: + case Entity_ProcGroup: + case Entity_ImportName: + case Entity_LibraryName: + // Fine + break; + } + array_add(&entities, e); + } + gb_sort_array(entities.data, entities.count, cmp_entities_for_printing); + + AstPackage *curr_pkg = nullptr; + EntityKind curr_entity_kind = Entity_Invalid; + for_array(i, entities) { + Entity *e = entities[i]; + if (e->pkg != pkg) { + continue; + } + if (!is_entity_exported(e)) { + continue; + } + + if (curr_entity_kind != e->kind) { + curr_entity_kind = e->kind; + print_doc_line(0, ""); + print_doc_line(1, "%s", print_entity_names[e->kind]); + } + + Ast *type_expr = nullptr; + Ast *init_expr = nullptr; + Ast *decl_node = nullptr; + if (e->decl_info != nullptr) { + type_expr = e->decl_info->type_expr; + init_expr = e->decl_info->init_expr; + decl_node = e->decl_info->decl_node; + } + GB_ASSERT(type_expr != nullptr || init_expr != nullptr); + print_doc_line_no_newline(2, "%.*s", LIT(e->token.string)); + if (type_expr != nullptr) { + gbString t = expr_to_string(type_expr); + gb_printf(": %s ", t); + gb_string_free(t); + } else { + gb_printf(" :"); + } + if (e->kind == Entity_Variable) { + if (init_expr != nullptr) { + gb_printf("= "); + print_doc_expr(init_expr); + } + } else { + gb_printf(": "); + print_doc_expr(init_expr); + } + + gb_printf(";\n"); + + + if (decl_node && (true || (build_context.cmd_doc_flags & CmdDocFlag_All))) { + CommentGroup *docs = nullptr; + CommentGroup *comment = nullptr; + switch (decl_node->kind) { + case_ast_node(vd, ValueDecl, decl_node); + docs = vd->docs; + comment = vd->comment; + case_end; + + case_ast_node(id, ImportDecl, decl_node); + docs = id->docs; + comment = id->comment; + case_end; + + case_ast_node(fl, ForeignImportDecl, decl_node); + docs = fl->docs; + comment = fl->comment; + case_end; + + case_ast_node(fb, ForeignBlockDecl, decl_node); + docs = fb->docs; + case_end; + } + if (comment) { + // gb_printf(" "); + } + if (docs) { + if (print_doc_comment_group_string(3, *docs)) { + gb_printf("\n"); + } + } + } + } + print_doc_line(0, ""); + } + + if (pkg->fullpath.len != 0) { + print_doc_line(0, ""); + print_doc_line(1, "fullpath: %.*s", LIT(pkg->fullpath)); + print_doc_line(1, "files:"); + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + String filename = remove_directory_from_path(f->fullpath); + print_doc_line(2, "%.*s", LIT(filename)); + } + } + } void generate_documentation(Checker *c) { CheckerInfo *info = &c->info; - if (build_context.cmd_doc_flags & CmdDocFlag_All) { - auto pkgs = array_make(permanent_allocator(), info->packages.entries.count); - for_array(i, info->packages.entries) { - array_add(&pkgs, info->packages.entries[i].value); + if (build_context.doc_packages.count != 0) { + auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); + bool was_error = false; + for_array(j, build_context.doc_packages) { + bool found = false; + String name = build_context.doc_packages[j]; + for_array(i, info->packages.entries) { + AstPackage *pkg = info->packages.entries[i].value; + if (name == pkg->name) { + found = true; + array_add(&pkgs, pkg); + break; + } + } + if (!found) { + gb_printf_err("Unknown package %.*s\n", LIT(name)); + was_error = true; + } + } + if (was_error) { + gb_exit(1); + return; } gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); @@ -126,5 +321,6 @@ void generate_documentation(Checker *c) { GB_ASSERT(info->init_scope->flags & ScopeFlag_Pkg); AstPackage *pkg = info->init_scope->pkg; print_doc_package(info, pkg); + } } diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 9e22c0483..051b6d76a 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -945,7 +945,7 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { Entity *strip_entity_wrapping(Ast *expr); Entity *strip_entity_wrapping(Entity *e); -gbString write_expr_to_string(gbString str, Ast *node); +gbString write_expr_to_string(gbString str, Ast *node, bool shorthand); gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) { switch (v.kind) { @@ -981,9 +981,9 @@ gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize st case ExactValue_Pointer: return str; case ExactValue_Compound: - return write_expr_to_string(str, v.value_compound); + return write_expr_to_string(str, v.value_compound, false); case ExactValue_Procedure: - return write_expr_to_string(str, v.value_procedure); + return write_expr_to_string(str, v.value_procedure, false); } return str; }; diff --git a/src/main.cpp b/src/main.cpp index 45fb9ac85..d6dc3f9b8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -598,6 +598,8 @@ enum BuildFlagKind { BuildFlag_GlobalDefinitions, BuildFlag_GoToDefinitions, + BuildFlag_Package, + #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, BuildFlag_ResourceFile, @@ -704,6 +706,8 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_Package, str_lit("package"), BuildFlagParam_String, Command_doc, true); + #if defined(GB_SYSTEM_WINDOWS) add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); @@ -1192,6 +1196,15 @@ bool parse_build_flags(Array args) { } break; + case BuildFlag_Package: + GB_ASSERT(value.kind == ExactValue_String); + if (value.value_string.len == 0) { + gb_printf_err("Invalid use of -package flag\n"); + } else { + array_add(&build_context.doc_packages, value.value_string); + } + break; + #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: GB_ASSERT(value.kind == ExactValue_Invalid); @@ -1529,6 +1542,14 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "Flags"); print_usage_line(0, ""); + if (doc) { + print_usage_line(1, "-package:"); + print_usage_line(2, "Add package name to generate documentation for"); + print_usage_line(2, "Multiple flags are allowed"); + print_usage_line(2, "Example: -doc:runtime"); + print_usage_line(0, ""); + } + if (run_or_build) { print_usage_line(1, "-out:"); print_usage_line(2, "Set the file name of the outputted executable"); @@ -1795,6 +1816,8 @@ int main(int arg_count, char const **arg_ptr) { add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core"))); map_init(&build_context.defined_values, heap_allocator()); + build_context.doc_packages.allocator = heap_allocator(); + Array args = setup_args(arg_count, arg_ptr); diff --git a/src/parser.cpp b/src/parser.cpp index 4470f979b..ce5e53d92 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4648,10 +4648,6 @@ void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, AstForeignFi // NOTE(bill): Returns true if it's added bool try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) { - if (build_context.generate_docs) { - return false; - } - String const FILE_EXT = str_lit(".odin"); gb_mutex_lock(&p->file_add_mutex); @@ -5253,7 +5249,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) { } TokenPos init_pos = {}; - if (!build_context.generate_docs) { + { String s = get_fullpath_core(heap_allocator(), str_lit("runtime")); try_add_import_path(p, s, s, init_pos, Package_Runtime); } From a0fbc563173318821af945c76d8417cf599abdbd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 15:13:38 +0000 Subject: [PATCH 059/170] Improve flags for `odin doc` --- src/checker.cpp | 4 ++++ src/checker.hpp | 3 +++ src/docs.cpp | 38 +++++++++++++++----------------------- src/main.cpp | 18 +++++++++++++++++- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 8f8aa7381..88aed4c62 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3116,6 +3116,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Ast *init_expr = value; DeclInfo *d = make_decl_info(c->scope, c->decl); d->decl_node = decl; + d->comment = vd->comment; + d->docs = vd->docs; d->entity = e; d->type_expr = vd->type; d->init_expr = init_expr; @@ -3147,6 +3149,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { DeclInfo *d = make_decl_info(c->scope, c->decl); d->decl_node = decl; + d->comment = vd->comment; + d->docs = vd->docs; d->attributes = vd->attributes; d->type_expr = vd->type; d->init_expr = init; diff --git a/src/checker.hpp b/src/checker.hpp index 97469908b..b986296e0 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -141,6 +141,9 @@ struct DeclInfo { bool is_using; bool where_clauses_evaluated; + CommentGroup *comment; + CommentGroup *docs; + PtrSet deps; PtrSet type_info_deps; Array labels; diff --git a/src/docs.cpp b/src/docs.cpp index 76d8c5433..f65b07746 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -209,6 +209,8 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { Ast *type_expr = nullptr; Ast *init_expr = nullptr; Ast *decl_node = nullptr; + CommentGroup *comment = nullptr; + CommentGroup *docs = nullptr; if (e->decl_info != nullptr) { type_expr = e->decl_info->type_expr; init_expr = e->decl_info->init_expr; @@ -236,29 +238,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { gb_printf(";\n"); - if (decl_node && (true || (build_context.cmd_doc_flags & CmdDocFlag_All))) { - CommentGroup *docs = nullptr; - CommentGroup *comment = nullptr; - switch (decl_node->kind) { - case_ast_node(vd, ValueDecl, decl_node); - docs = vd->docs; - comment = vd->comment; - case_end; - - case_ast_node(id, ImportDecl, decl_node); - docs = id->docs; - comment = id->comment; - case_end; - - case_ast_node(fl, ForeignImportDecl, decl_node); - docs = fl->docs; - comment = fl->comment; - case_end; - - case_ast_node(fb, ForeignBlockDecl, decl_node); - docs = fb->docs; - case_end; - } + if (build_context.cmd_doc_flags & CmdDocFlag_All) { if (comment) { // gb_printf(" "); } @@ -314,6 +294,18 @@ void generate_documentation(Checker *c) { gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + for_array(i, pkgs) { + print_doc_package(info, pkgs[i]); + } + } else if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) { + auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); + for_array(i, info->packages.entries) { + AstPackage *pkg = info->packages.entries[i].value; + array_add(&pkgs, pkg); + } + + gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + for_array(i, pkgs) { print_doc_package(info, pkgs[i]); } diff --git a/src/main.cpp b/src/main.cpp index d6dc3f9b8..2c7736762 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -599,6 +599,8 @@ enum BuildFlagKind { BuildFlag_GoToDefinitions, BuildFlag_Package, + BuildFlag_All, + BuildFlag_AllPackages, #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, @@ -706,7 +708,9 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); - add_flag(&build_flags, BuildFlag_Package, str_lit("package"), BuildFlagParam_String, Command_doc, true); + add_flag(&build_flags, BuildFlag_Package, str_lit("package"), BuildFlagParam_String, Command_doc, true); + add_flag(&build_flags, BuildFlag_All, str_lit("all"), BuildFlagParam_None, Command_doc); + add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc); #if defined(GB_SYSTEM_WINDOWS) @@ -1204,6 +1208,13 @@ bool parse_build_flags(Array args) { array_add(&build_context.doc_packages, value.value_string); } break; + case BuildFlag_All: + build_context.cmd_doc_flags |= CmdDocFlag_All; + break; + case BuildFlag_AllPackages: + build_context.cmd_doc_flags |= CmdDocFlag_AllPackages; + break; + #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: @@ -1297,6 +1308,11 @@ bool parse_build_flags(Array args) { } } + if (build_context.doc_packages.count > 0 && set_flags[BuildFlag_AllPackages]) { + gb_printf_err("'odin doc' does not allow both flags together '-all-packages' and '-package' together");; + bad_flags = true; + } + if (build_context.query_data_set_settings.ok) { if (build_context.query_data_set_settings.kind == QueryDataSet_Invalid) { From 4f303603e764c219e56dae559d7e36358b199c31 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 15:15:26 +0000 Subject: [PATCH 060/170] Add more documentation for odin doc flags --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 2c7736762..5faedbccb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1559,11 +1559,19 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); if (doc) { + print_usage_line(1, "-all"); + print_usage_line(2, "Show all documentation for the packages"); + print_usage_line(0, ""); + print_usage_line(1, "-package:"); print_usage_line(2, "Add package name to generate documentation for"); print_usage_line(2, "Multiple flags are allowed"); print_usage_line(2, "Example: -doc:runtime"); print_usage_line(0, ""); + + print_usage_line(1, "-all-packages"); + print_usage_line(2, "Generates documentation for all packages used in the current project"); + print_usage_line(0, ""); } if (run_or_build) { From 7442f4bab653917a99fb34a75276eab551ef4d58 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 15:17:36 +0000 Subject: [PATCH 061/170] Fix typo --- src/docs.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/docs.cpp b/src/docs.cpp index f65b07746..67e3ebbe5 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -215,6 +215,8 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { type_expr = e->decl_info->type_expr; init_expr = e->decl_info->init_expr; decl_node = e->decl_info->decl_node; + comment = e->decl_info->comment; + docs = e->decl_info->docs; } GB_ASSERT(type_expr != nullptr || init_expr != nullptr); print_doc_line_no_newline(2, "%.*s", LIT(e->token.string)); From 34ca4e92eb5316cebb66aa1c69d4ced5719e7773 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 15:45:55 +0000 Subject: [PATCH 062/170] Fix parser logic for first comment group line in a file --- src/docs.cpp | 36 +++++++++++++++++++++++++----------- src/parser.cpp | 6 ++++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/docs.cpp b/src/docs.cpp index 67e3ebbe5..50586ed8f 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -110,20 +110,23 @@ void print_doc_line_no_newline(i32 indent, char const *fmt, ...) { va_end(va); } -bool print_doc_comment_group_string(i32 indent, CommentGroup const &g) { +bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { + if (g == nullptr) { + return false; + } isize len = 0; - for_array(i, g.list) { - String comment = g.list[i].string; + for_array(i, g->list) { + String comment = g->list[i].string; len += comment.len; len += 1; // for \n } - if (len == 0) { + if (len <= g->list.count) { return false; } isize count = 0; - for_array(i, g.list) { - String comment = g.list[i].string; + for_array(i, g->list) { + String comment = g->list[i].string; if (comment[1] == '/') { comment.text += 2; comment.len -= 2; @@ -131,7 +134,11 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup const &g) { comment.text += 2; comment.len -= 4; } - comment = string_trim_whitespace(comment); + if (comment.len > 0 && comment[0] == ' ') { + comment.text += 1; + comment.len -= 1; + } + if (string_starts_with(comment, str_lit("@("))) { continue; } @@ -164,6 +171,15 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { print_doc_line(0, "package %.*s", LIT(pkg->name)); + + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + if (f->pkg_decl) { + GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl); + print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs); + } + } + if (pkg->scope != nullptr) { auto entities = array_make(heap_allocator(), 0, pkg->scope->elements.entries.count); defer (array_free(&entities)); @@ -244,10 +260,8 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { if (comment) { // gb_printf(" "); } - if (docs) { - if (print_doc_comment_group_string(3, *docs)) { - gb_printf("\n"); - } + if (print_doc_comment_group_string(3, docs)) { + gb_printf("\n"); } } } diff --git a/src/parser.cpp b/src/parser.cpp index ce5e53d92..9e9708f9c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1193,6 +1193,12 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) { Array list = {}; list.allocator = heap_allocator(); isize end_line = f->curr_token.pos.line; + if (f->curr_token_index == 1 && + f->prev_token.kind == Token_Comment && + f->prev_token.pos.line+1 == f->curr_token.pos.line) { + // NOTE(bill): Special logic for the first comment in the file + array_add(&list, f->prev_token); + } while (f->curr_token.kind == Token_Comment && f->curr_token.pos.line <= end_line+n) { array_add(&list, consume_comment(f, &end_line)); From d730c5b334afa28666eb3101baef14b36cc726cb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 15:48:18 +0000 Subject: [PATCH 063/170] Improve file doc logic --- src/docs.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/docs.cpp b/src/docs.cpp index 50586ed8f..6159fae9b 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -176,7 +176,9 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { AstFile *f = pkg->files[i]; if (f->pkg_decl) { GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl); - print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs); + if (print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs)) { + print_doc_line(0, ""); + } } } @@ -205,7 +207,6 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { } gb_sort_array(entities.data, entities.count, cmp_entities_for_printing); - AstPackage *curr_pkg = nullptr; EntityKind curr_entity_kind = Entity_Invalid; for_array(i, entities) { Entity *e = entities[i]; @@ -217,8 +218,10 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { } if (curr_entity_kind != e->kind) { + if (curr_entity_kind != Entity_Invalid) { + print_doc_line(0, ""); + } curr_entity_kind = e->kind; - print_doc_line(0, ""); print_doc_line(1, "%s", print_entity_names[e->kind]); } From aa5cb7f6a965305fc000735a4bc2a7e5b9a993d5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 15:54:22 +0000 Subject: [PATCH 064/170] Fix proc type printing --- src/check_expr.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f6530df51..aa3d67dae 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10633,7 +10633,26 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = gb_string_appendc(str, ")"); if (pt->results != nullptr) { str = gb_string_appendc(str, " -> "); + + bool parens_needed = false; + if (pt->results && pt->results->kind == Ast_FieldList) { + for_array(i, pt->results->FieldList.list) { + Ast *field = pt->results->FieldList.list[i]; + ast_node(f, Field, field); + if (f->names.count != 0) { + parens_needed = true; + break; + } + } + } + + if (parens_needed) { + str = gb_string_append_rune(str, '('); + } str = write_expr_to_string(str, pt->results, shorthand); + if (parens_needed) { + str = gb_string_append_rune(str, ')'); + } } case_end; From ede25a88f80e34fdf7eabfbca50878c69cec59d9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 16:01:33 +0000 Subject: [PATCH 065/170] Ignore `+build` flags in packages comments with `odin doc` --- src/docs.cpp | 40 ++++++++-------------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/src/docs.cpp b/src/docs.cpp index 6159fae9b..d023bf09a 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -58,38 +58,6 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) { return string_compare(x->name, y->name); } - -String alloc_comment_group_string(gbAllocator a, CommentGroup g) { - isize len = 0; - for_array(i, g.list) { - String comment = g.list[i].string; - len += comment.len; - len += 1; // for \n - } - if (len == 0) { - return make_string(nullptr, 0); - } - - u8 *text = gb_alloc_array(a, u8, len+1); - len = 0; - for_array(i, g.list) { - String comment = g.list[i].string; - if (comment[1] == '/') { - comment.text += 2; - comment.len -= 2; - } else if (comment[1] == '*') { - comment.text += 2; - comment.len -= 4; - } - comment = string_trim_whitespace(comment); - gb_memmove(text+len, comment.text, comment.len); - len += comment.len; - text[len++] = '\n'; - } - return make_string(text, len); -} - - void print_doc_line(i32 indent, char const *fmt, ...) { while (indent --> 0) { gb_printf("\t"); @@ -127,6 +95,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { isize count = 0; for_array(i, g->list) { String comment = g->list[i].string; + bool slash_slash = comment[1] == '/'; if (comment[1] == '/') { comment.text += 2; comment.len -= 2; @@ -134,11 +103,18 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { comment.text += 2; comment.len -= 4; } + + // Ignore the first space if (comment.len > 0 && comment[0] == ' ') { comment.text += 1; comment.len -= 1; } + if (slash_slash) { + if (string_starts_with(comment, str_lit("+"))) { + continue; + } + } if (string_starts_with(comment, str_lit("@("))) { continue; } From 11577db6a84d17998478dc22b607a203cdf00a7e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 16:36:33 +0000 Subject: [PATCH 066/170] Minor fixes --- core/path/filepath/path_windows.odin | 3 ++- core/strings/strings.odin | 2 +- src/docs.cpp | 9 ++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index fc93bdfa2..b538e1640 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -8,7 +8,8 @@ SEPARATOR :: '\\'; SEPARATOR_STRING :: `\`; LIST_SEPARATOR :: ';'; -reserved_names := []string{ +@(private) +reserved_names := [?]string{ "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 93f8fdc69..fd9e7299b 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -224,7 +224,7 @@ index_byte :: proc(s: string, c: byte) -> int { return -1; } -// Returns i1 if c is not present +// Returns -1 if c is not present last_index_byte :: proc(s: string, c: byte) -> int { for i := len(s)-1; i >= 0; i -= 1 { if s[i] == c { diff --git a/src/docs.cpp b/src/docs.cpp index d023bf09a..d86f85c8d 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -1,6 +1,5 @@ // Generates Documentation - gb_global int print_entity_kind_ordering[Entity_Count] = { /*Invalid*/ -1, /*Constant*/ 0, @@ -41,10 +40,9 @@ GB_COMPARE_PROC(cmp_entities_for_printing) { } int ox = print_entity_kind_ordering[x->kind]; int oy = print_entity_kind_ordering[y->kind]; - if (ox < oy) { - return -1; - } else if (ox > oy) { - return +1; + res = ox - oy; + if (res != 0) { + return res; } res = string_compare(x->token.string, y->token.string); return res; @@ -96,6 +94,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { for_array(i, g->list) { String comment = g->list[i].string; bool slash_slash = comment[1] == '/'; + bool slash_star = comment[1] == '*'; if (comment[1] == '/') { comment.text += 2; comment.len -= 2; From 2bd0fd932a4eb1c2e6bc8702450c230186bb3c44 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 18:50:30 +0000 Subject: [PATCH 067/170] Begin rudimentary work on implementing `odin test` tooling with `*_test.odin` files --- src/build_settings.cpp | 18 +++++++++++++-- src/checker.cpp | 50 ++++++++++++++++++++++++++++++++++++++++-- src/checker.hpp | 2 ++ src/ir.cpp | 13 ++++++++--- src/llvm_backend.cpp | 18 +++++++++++---- src/main.cpp | 15 ++++++++----- src/parser.hpp | 2 ++ 7 files changed, 102 insertions(+), 16 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 0e8f2c5bc..35dc9a7c1 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -111,9 +111,10 @@ enum CommandKind : u32 { Command_query = 1<<4, Command_doc = 1<<5, Command_version = 1<<6, + Command_test = 1<<7, - Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc, - Command__does_build = Command_run|Command_build, + Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test, + Command__does_build = Command_run|Command_build|Command_test, Command_all = ~(u32)0, }; @@ -333,6 +334,19 @@ bool is_excluded_target_filename(String name) { String original_name = name; name = remove_extension_from_path(name); + if (string_starts_with(name, str_lit("."))) { + // Ignore .*.odin files + return true; + } + + String test_suffix = str_lit("_test"); + if (build_context.command_kind != Command_test) { + if (string_ends_with(name, test_suffix) && name != test_suffix) { + // Ignore *_test.odin files + return true; + } + } + String str1 = {}; String str2 = {}; isize n = 0; diff --git a/src/checker.cpp b/src/checker.cpp index 88aed4c62..61b1e4bae 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -839,6 +839,8 @@ void init_checker_info(CheckerInfo *i) { array_init(&i->variable_init_order, a); array_init(&i->required_foreign_imports_through_force, a); array_init(&i->required_global_variables, a); + array_init(&i->testing_procedures, a, 0, 0); + i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions; if (i->allow_identifier_uses) { @@ -1854,7 +1856,50 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { } } - add_dependency_to_set(c, start); + if (build_context.command_kind == Command_test) { + AstPackage *pkg = c->info.init_package; + Scope *s = pkg->scope; + for_array(i, s->elements.entries) { + Entity *e = s->elements.entries[i].value; + if (e->kind != Entity_Procedure) { + continue; + } + String name = e->token.string; + String prefix = str_lit("test_"); + + + if (!string_starts_with(name, prefix)) { + continue; + } + + bool is_tester = false; + if (name != prefix) { + is_tester = true; + } else { + if (e->file && e->file->is_test) { + error(e->token, "Invalid testing procedure name: %.*s", LIT(name)); + } + } + + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Proc); + if (t->Proc.param_count == 0 && t->Proc.result_count == 0) { + // Good + } else { + gbString str = type_to_string(t); + error(e->token, "Testing procedures must have a signature type of proc(), got %s", str); + gb_string_free(str); + is_tester = false; + } + + if (is_tester) { + add_dependency_to_set(c, e); + array_add(&c->info.testing_procedures, e); + } + } + } else { + add_dependency_to_set(c, start); + } } bool is_entity_a_dependency(Entity *e) { @@ -4344,6 +4389,7 @@ void check_parsed_files(Checker *c) { string_map_set(&c->info.packages, p->fullpath, p); if (scope->flags&ScopeFlag_Init) { + c->info.init_package = p; c->info.init_scope = scope; } if (p->kind == Package_Runtime) { @@ -4613,7 +4659,7 @@ void check_parsed_files(Checker *c) { TIME_SECTION("check entry point"); - if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point) { + if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) { Scope *s = c->info.init_scope; GB_ASSERT(s != nullptr); GB_ASSERT(s->flags&ScopeFlag_Init); diff --git a/src/checker.hpp b/src/checker.hpp index b986296e0..72c4b148c 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -273,6 +273,7 @@ struct CheckerInfo { AstPackage * builtin_package; AstPackage * runtime_package; + AstPackage * init_package; Scope * init_scope; Entity * entry_point; PtrSet minimum_dependency_set; @@ -283,6 +284,7 @@ struct CheckerInfo { Map atom_op_map; // Key: Ast * + Array testing_procedures; bool allow_identifier_uses; Array identifier_uses; // only used by 'odin query' diff --git a/src/ir.cpp b/src/ir.cpp index 1f2819ccf..819442030 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12781,11 +12781,18 @@ void ir_gen_tree(irGen *s) { ir_fill_slice(proc, global_args, argv, ir_emit_conv(proc, argc, t_int)); ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime)); - { + Array empty_args = {}; + if (build_context.command_kind == Command_test) { + for_array(i, m->info->testing_procedures) { + Entity *e = m->info->testing_procedures[i]; + irValue **found = map_get(&proc->module->values, hash_entity(e)); + GB_ASSERT(found != nullptr); + ir_emit_call(proc, *found, empty_args); + } + } else { irValue **found = map_get(&proc->module->values, hash_entity(entry_point)); if (found != nullptr) { - Array args = {}; - ir_emit_call(proc, *found, args); + ir_emit_call(proc, *found, empty_args); } } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 50d200551..ea5081be4 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12875,11 +12875,21 @@ void lb_generate_code(lbGenerator *gen) { lb_begin_procedure_body(p); - lbValue *found = map_get(&m->values, hash_entity(entry_point)); - GB_ASSERT(found != nullptr); - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, ""); + + if (build_context.command_kind == Command_test) { + for_array(i, m->info->testing_procedures) { + Entity *e = m->info->testing_procedures[i]; + lbValue *found = map_get(&m->values, hash_entity(e)); + GB_ASSERT(found != nullptr); + lb_emit_call(p, *found, {}); + } + } else { + lbValue *found = map_get(&m->values, hash_entity(entry_point)); + GB_ASSERT(found != nullptr); + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, ""); + } + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); lb_end_procedure_body(p); diff --git a/src/main.cpp b/src/main.cpp index 5faedbccb..b83c420ae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -675,7 +675,7 @@ ExactValue build_param_to_exact_value(String name, String param) { bool parse_build_flags(Array args) { auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT); add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all); - add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test); add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build); add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); @@ -693,7 +693,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); @@ -1540,6 +1540,8 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); } else if (command == "check") { print_usage_line(1, "check parse and type check .odin file"); + } else if (command == "test") { + print_usage_line(1, "test build ands runs 'test_*' procedures in the initial package"); } else if (command == "query") { print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program"); } else if (command == "doc") { @@ -1550,9 +1552,9 @@ void print_show_help(String const arg0, String const &command) { bool doc = command == "doc"; bool build = command == "build"; - bool run_or_build = command == "run" || command == "build"; + bool run_or_build = command == "run" || command == "build" || command == "test"; bool check_only = command == "check"; - bool check = command == "run" || command == "build" || command == "check"; + bool check = run_or_build || command == "check"; print_usage_line(0, ""); print_usage_line(1, "Flags"); @@ -1850,12 +1852,15 @@ int main(int arg_count, char const **arg_ptr) { String run_args_string = {}; bool run_output = false; - if (command == "run") { + if (command == "run" || command == "test") { if (args.count < 3) { usage(args[0]); return 1; } build_context.command_kind = Command_run; + if (command == "test") { + build_context.command_kind = Command_test; + } Array run_args = array_make(heap_allocator(), 0, arg_count); defer (array_free(&run_args)); diff --git a/src/parser.hpp b/src/parser.hpp index 3be7939fa..48af0b293 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -107,6 +107,8 @@ struct AstFile { f64 time_to_tokenize; // seconds f64 time_to_parse; // seconds + bool is_test; + CommentGroup *lead_comment; // Comment (block) before the decl CommentGroup *line_comment; // Comment after the semicolon CommentGroup *docs; // current docs From 30765475c683c03ba587e51534feda756036e45f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 18:59:48 +0000 Subject: [PATCH 068/170] Fix up `system_exec_command_line_app` exit code code --- src/main.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b83c420ae..40b26794f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -162,8 +162,11 @@ i32 linker_stage(lbGenerator *gen) { if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { #ifdef GB_SYSTEM_UNIX - system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s", - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + exit_code = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s", + LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + if (exit_code != 0) { + return exit_code; + } #else gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n", LIT(target_os_names[build_context.metrics.os]), @@ -265,7 +268,7 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.resource_filepath) ); - if (exit_code != 0) { + if (exit_code != 0) { return exit_code; } @@ -283,6 +286,10 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.extra_linker_flags), lib_str ); + + if (exit_code != 0) { + return exit_code; + } } else { exit_code = system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s " @@ -298,7 +305,10 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.extra_linker_flags), lib_str ); - } + + if (exit_code != 0) { + return exit_code; + } } } else { // lld exit_code = system_exec_command_line_app("msvc-link", "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s " @@ -315,6 +325,10 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.extra_linker_flags), lib_str ); + + if (exit_code != 0) { + return exit_code; + } } #else timings_start_section(timings, str_lit("ld-link")); From ef2f204c58f355daf516c25e6e59dcd60bce444d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Nov 2020 20:33:15 +0000 Subject: [PATCH 069/170] Improve `system_exec_command_line_app` functionality; Restrict `test_*` procedures to `*_test.odin` files --- src/checker.cpp | 10 +++-- src/main.cpp | 111 +++++++++++++----------------------------------- 2 files changed, 36 insertions(+), 85 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 61b1e4bae..3339b6bfc 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1864,10 +1864,14 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { if (e->kind != Entity_Procedure) { continue; } + + if (e->file == nullptr || !e->file->is_test) { + continue; + } + String name = e->token.string; String prefix = str_lit("test_"); - if (!string_starts_with(name, prefix)) { continue; } @@ -1876,9 +1880,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { if (name != prefix) { is_tester = true; } else { - if (e->file && e->file->is_test) { - error(e->token, "Invalid testing procedure name: %.*s", LIT(name)); - } + error(e->token, "Invalid testing procedure name: %.*s", LIT(name)); } Type *t = base_type(e->type); diff --git a/src/main.cpp b/src/main.cpp index 40b26794f..7eef5d2ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -85,6 +85,10 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { exit_code = -1; } + if (exit_code) { + exit(exit_code); + } + return exit_code; #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) @@ -133,6 +137,10 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { // exit_code = status; + if (exit_code) { + exit(exit_code); + } + return exit_code; #endif } @@ -141,32 +149,23 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { #if defined(LLVM_BACKEND_SUPPORT) -i32 linker_stage(lbGenerator *gen) { - i32 exit_code = 0; - +void linker_stage(lbGenerator *gen) { Timings *timings = &global_timings; String output_base = gen->output_base; if (build_context.metrics.os == TargetOs_js) { timings_start_section(timings, str_lit("wasm-ld")); - exit_code = system_exec_command_line_app("wasm-ld", + system_exec_command_line_app("wasm-ld", "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm-obj\" -o \"%.*s.wasm\" %.*s %.*s", LIT(build_context.ODIN_ROOT), LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); - if (exit_code != 0) { - return exit_code; - } - return exit_code; } if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { #ifdef GB_SYSTEM_UNIX - exit_code = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s", + system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s", LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); - if (exit_code != 0) { - return exit_code; - } #else gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n", LIT(target_os_names[build_context.metrics.os]), @@ -197,7 +196,7 @@ i32 linker_stage(lbGenerator *gen) { if (find_result.windows_sdk_version == 0) { gb_printf_err("Windows SDK not found.\n"); - return 1; + exit(1); } if (build_context.ignore_microsoft_magic) { @@ -262,17 +261,13 @@ i32 linker_stage(lbGenerator *gen) { char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE"; if (!build_context.use_lld) { // msvc if (build_context.has_resource) { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"", LIT(output_base), LIT(build_context.resource_filepath) ); - if (exit_code != 0) { - return exit_code; - } - - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -286,12 +281,8 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.extra_linker_flags), lib_str ); - - if (exit_code != 0) { - return exit_code; - } } else { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -305,12 +296,9 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.extra_linker_flags), lib_str ); - - if (exit_code != 0) { - return exit_code; - } } + } } else { // lld - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -325,10 +313,6 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.extra_linker_flags), lib_str ); - - if (exit_code != 0) { - return exit_code; - } } #else timings_start_section(timings, str_lit("ld-link")); @@ -433,7 +417,7 @@ i32 linker_stage(lbGenerator *gen) { } } - exit_code = system_exec_command_line_app("ld-link", + system_exec_command_line_app("ld-link", "%s %s -o \"%.*s%.*s\" %s " " %s " " %.*s " @@ -457,28 +441,21 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); - if (exit_code != 0) { - return exit_code; - } #if defined(GB_SYSTEM_OSX) if (build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe // to the symbols in the object file - exit_code = system_exec_command_line_app("dsymutil", + system_exec_command_line_app("dsymutil", "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext) ); - - if (exit_code != 0) { - return exit_code; - } } #endif #endif } - return exit_code; + return; } #endif @@ -2070,12 +2047,7 @@ int main(int arg_count, char const **arg_ptr) { switch (build_context.build_mode) { case BuildMode_Executable: case BuildMode_DynamicLibrary: - { - i32 linker_stage_exit_count = linker_stage(&gen); - if (linker_stage_exit_count != 0) { - return linker_stage_exit_count; - } - } + linker_stage(&gen); break; } @@ -2159,19 +2131,11 @@ int main(int arg_count, char const **arg_ptr) { build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3); - i32 exit_code = 0; - timings_start_section(timings, str_lit("llvm-opt")); - exit_code = exec_llvm_opt(output_base); - if (exit_code != 0) { - return exit_code; - } + exec_llvm_opt(output_base); timings_start_section(timings, str_lit("llvm-llc")); - exit_code = exec_llvm_llc(output_base); - if (exit_code != 0) { - return exit_code; - } + exec_llvm_llc(output_base); if (build_context.build_mode == BuildMode_Object) { // Ignore the linker @@ -2180,7 +2144,7 @@ int main(int arg_count, char const **arg_ptr) { } remove_temp_files(output_base); - return exit_code; + return 0; } if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { @@ -2277,17 +2241,13 @@ int main(int arg_count, char const **arg_ptr) { if (!build_context.use_lld) { // msvc if (build_context.has_resource) { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"", LIT(output_base), LIT(build_context.resource_filepath) ); - if (exit_code != 0) { - return exit_code; - } - - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -2302,7 +2262,7 @@ int main(int arg_count, char const **arg_ptr) { lib_str ); } else { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -2318,7 +2278,7 @@ int main(int arg_count, char const **arg_ptr) { ); } } else { // lld - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -2335,10 +2295,6 @@ int main(int arg_count, char const **arg_ptr) { ); } - if (exit_code != 0) { - return exit_code; - } - if (build_context.show_timings) { show_timings(&checker, timings); } @@ -2447,7 +2403,7 @@ int main(int arg_count, char const **arg_ptr) { } - exit_code = system_exec_command_line_app("ld-link", + system_exec_command_line_app("ld-link", "%s \"%.*s.o\" -o \"%.*s%.*s\" %s " " %s " " %.*s " @@ -2471,21 +2427,14 @@ int main(int arg_count, char const **arg_ptr) { LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); - if (exit_code != 0) { - return exit_code; - } #if defined(GB_SYSTEM_OSX) if (build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe // to the symbols in the object file - exit_code = system_exec_command_line_app("dsymutil", + system_exec_command_line_app("dsymutil", "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext) ); - - if (exit_code != 0) { - return exit_code; - } } #endif From 9408eb9580c1663195089ba18a53b704b382e40a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 18 Nov 2020 23:22:27 +0000 Subject: [PATCH 070/170] Update `odin doc` to support multiple package outputs by passing multiple paths; Replace `-all` with `-short` Example: odin doc core/path core/path/filepath --- core/path/match.odin | 2 - src/build_settings.cpp | 4 +- src/docs.cpp | 133 ++++++++++++++++++++++------------------- src/main.cpp | 67 ++++++++++----------- src/parser.cpp | 77 ++++++++++++++++++++---- src/parser.hpp | 1 + 6 files changed, 172 insertions(+), 112 deletions(-) diff --git a/core/path/match.odin b/core/path/match.odin index 555c1b05c..e77bf79c3 100644 --- a/core/path/match.odin +++ b/core/path/match.odin @@ -28,8 +28,6 @@ Match_Error :: enum { // match requires that the pattern matches the entirety of the name, not just a substring // The only possible error returned is .Syntax_Error // -// NOTE(bill): This is effectively the shell pattern matching system found -// match :: proc(pattern, name: string) -> (matched: bool, err: Match_Error) { pattern, name := pattern, name; pattern_loop: for len(pattern) > 0 { diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 35dc9a7c1..c251cba53 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -130,7 +130,7 @@ char const *odin_command_strings[32] = { enum CmdDocFlag : u32 { - CmdDocFlag_All = 1<<0, + CmdDocFlag_Short = 1<<0, CmdDocFlag_AllPackages = 1<<1, }; @@ -200,7 +200,7 @@ struct BuildContext { bool linker_map_file; u32 cmd_doc_flags; - Array doc_packages; + Array extra_packages; QueryDataSetSettings query_data_set_settings; diff --git a/src/docs.cpp b/src/docs.cpp index d86f85c8d..aa1b89560 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -93,6 +93,8 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { isize count = 0; for_array(i, g->list) { String comment = g->list[i].string; + String original_comment = comment; + bool slash_slash = comment[1] == '/'; bool slash_star = comment[1] == '*'; if (comment[1] == '/') { @@ -113,15 +115,51 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { if (string_starts_with(comment, str_lit("+"))) { continue; } - } - if (string_starts_with(comment, str_lit("@("))) { - continue; + if (string_starts_with(comment, str_lit("@("))) { + continue; + } } - print_doc_line(indent, "%.*s", LIT(comment)); - count += 1; + if (slash_slash) { + print_doc_line(indent, "%.*s", LIT(comment)); + count += 1; + } else { + isize pos = 0; + for (; pos < comment.len; pos++) { + isize end = pos; + for (; end < comment.len; end++) { + if (comment[end] == '\n') { + break; + } + } + String line = substring(comment, pos, end); + pos = end+1; + String trimmed_line = string_trim_whitespace(line); + if (trimmed_line.len == 0) { + if (count == 0) { + continue; + } + } + /* + * Remove comments with + * styles + * like this + */ + if (string_starts_with(line, str_lit("* "))) { + line = substring(line, 2, line.len); + } + + print_doc_line(indent, "%.*s", LIT(line)); + count += 1; + } + } } - return count > 0; + + if (count > 0) { + print_doc_line(0, ""); + return true; + } + return false; } @@ -129,10 +167,10 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { void print_doc_expr(Ast *expr) { gbString s = nullptr; - if (build_context.cmd_doc_flags & CmdDocFlag_All) { - s = expr_to_string(expr); - } else { + if (build_context.cmd_doc_flags & CmdDocFlag_Short) { s = expr_to_string_shorthand(expr); + } else { + s = expr_to_string(expr); } gb_file_write(gb_file_get_standard(gbFileStandard_Output), s, gb_string_length(s)); gb_string_free(s); @@ -151,9 +189,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { AstFile *f = pkg->files[i]; if (f->pkg_decl) { GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl); - if (print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs)) { - print_doc_line(0, ""); - } + print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs); } } @@ -182,6 +218,8 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { } gb_sort_array(entities.data, entities.count, cmp_entities_for_printing); + bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0; + EntityKind curr_entity_kind = Entity_Invalid; for_array(i, entities) { Entity *e = entities[i]; @@ -192,6 +230,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { continue; } + if (curr_entity_kind != e->kind) { if (curr_entity_kind != Entity_Invalid) { print_doc_line(0, ""); @@ -213,6 +252,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { docs = e->decl_info->docs; } GB_ASSERT(type_expr != nullptr || init_expr != nullptr); + print_doc_line_no_newline(2, "%.*s", LIT(e->token.string)); if (type_expr != nullptr) { gbString t = expr_to_string(type_expr); @@ -233,14 +273,8 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { gb_printf(";\n"); - - if (build_context.cmd_doc_flags & CmdDocFlag_All) { - if (comment) { - // gb_printf(" "); - } - if (print_doc_comment_group_string(3, docs)) { - gb_printf("\n"); - } + if (show_docs) { + print_doc_comment_group_string(3, docs); } } print_doc_line(0, ""); @@ -248,7 +282,8 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { if (pkg->fullpath.len != 0) { print_doc_line(0, ""); - print_doc_line(1, "fullpath: %.*s", LIT(pkg->fullpath)); + print_doc_line(1, "fullpath:"); + print_doc_line(2, "%.*s", LIT(pkg->fullpath)); print_doc_line(1, "files:"); for_array(i, pkg->files) { AstFile *f = pkg->files[i]; @@ -262,51 +297,23 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { void generate_documentation(Checker *c) { CheckerInfo *info = &c->info; - if (build_context.doc_packages.count != 0) { - auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); - bool was_error = false; - for_array(j, build_context.doc_packages) { - bool found = false; - String name = build_context.doc_packages[j]; - for_array(i, info->packages.entries) { - AstPackage *pkg = info->packages.entries[i].value; - if (name == pkg->name) { - found = true; - array_add(&pkgs, pkg); - break; - } - } - if (!found) { - gb_printf_err("Unknown package %.*s\n", LIT(name)); - was_error = true; - } - } - if (was_error) { - gb_exit(1); - return; - } - - gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); - - for_array(i, pkgs) { - print_doc_package(info, pkgs[i]); - } - } else if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) { - auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); - for_array(i, info->packages.entries) { - AstPackage *pkg = info->packages.entries[i].value; + auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); + for_array(i, info->packages.entries) { + AstPackage *pkg = info->packages.entries[i].value; + if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) { array_add(&pkgs, pkg); + } else { + if (pkg->kind == Package_Init) { + array_add(&pkgs, pkg); + } else if (pkg->is_extra) { + array_add(&pkgs, pkg); + } } + } - gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); - - for_array(i, pkgs) { - print_doc_package(info, pkgs[i]); - } - } else { - GB_ASSERT(info->init_scope->flags & ScopeFlag_Pkg); - AstPackage *pkg = info->init_scope->pkg; - print_doc_package(info, pkg); + gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + for_array(i, pkgs) { + print_doc_package(info, pkgs[i]); } } diff --git a/src/main.cpp b/src/main.cpp index 7eef5d2ad..dbade085d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -589,8 +589,7 @@ enum BuildFlagKind { BuildFlag_GlobalDefinitions, BuildFlag_GoToDefinitions, - BuildFlag_Package, - BuildFlag_All, + BuildFlag_Short, BuildFlag_AllPackages, #if defined(GB_SYSTEM_WINDOWS) @@ -699,9 +698,8 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); - add_flag(&build_flags, BuildFlag_Package, str_lit("package"), BuildFlagParam_String, Command_doc, true); - add_flag(&build_flags, BuildFlag_All, str_lit("all"), BuildFlagParam_None, Command_doc); - add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc); + add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc); + add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc); #if defined(GB_SYSTEM_WINDOWS) @@ -852,7 +850,7 @@ bool parse_build_flags(Array args) { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); - if (is_import_path_valid(path)) { + if (is_build_flag_path_valid(path)) { #if defined(GB_SYSTEM_WINDOWS) String ext = path_extension(path); if (ext == ".exe") { @@ -1191,22 +1189,15 @@ bool parse_build_flags(Array args) { } break; - case BuildFlag_Package: - GB_ASSERT(value.kind == ExactValue_String); - if (value.value_string.len == 0) { - gb_printf_err("Invalid use of -package flag\n"); - } else { - array_add(&build_context.doc_packages, value.value_string); - } - break; - case BuildFlag_All: - build_context.cmd_doc_flags |= CmdDocFlag_All; + case BuildFlag_Short: + build_context.cmd_doc_flags |= CmdDocFlag_Short; break; case BuildFlag_AllPackages: build_context.cmd_doc_flags |= CmdDocFlag_AllPackages; break; + #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: GB_ASSERT(value.kind == ExactValue_Invalid); @@ -1217,7 +1208,7 @@ bool parse_build_flags(Array args) { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); - if (is_import_path_valid(path)) { + if (is_build_flag_path_valid(path)) { if(!string_ends_with(path, str_lit(".rc"))) { gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path)); bad_flags = true; @@ -1235,7 +1226,7 @@ bool parse_build_flags(Array args) { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); - if (is_import_path_valid(path)) { + if (is_build_flag_path_valid(path)) { // #if defined(GB_SYSTEM_WINDOWS) // String ext = path_extension(path); // if (ext != ".pdb") { @@ -1299,12 +1290,6 @@ bool parse_build_flags(Array args) { } } - if (build_context.doc_packages.count > 0 && set_flags[BuildFlag_AllPackages]) { - gb_printf_err("'odin doc' does not allow both flags together '-all-packages' and '-package' together");; - bad_flags = true; - } - - if (build_context.query_data_set_settings.ok) { if (build_context.query_data_set_settings.kind == QueryDataSet_Invalid) { gb_printf_err("'odin query' requires a flag determining the kind of query data set to be returned\n"); @@ -1537,6 +1522,9 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program"); } else if (command == "doc") { print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files"); + print_usage_line(2, "Examples:"); + print_usage_line(3, "odin doc core/path"); + print_usage_line(3, "odin doc core/path core/path/filepath"); } else if (command == "version") { print_usage_line(1, "version print version"); } @@ -1552,14 +1540,8 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); if (doc) { - print_usage_line(1, "-all"); - print_usage_line(2, "Show all documentation for the packages"); - print_usage_line(0, ""); - - print_usage_line(1, "-package:"); - print_usage_line(2, "Add package name to generate documentation for"); - print_usage_line(2, "Multiple flags are allowed"); - print_usage_line(2, "Example: -doc:runtime"); + print_usage_line(1, "-short"); + print_usage_line(2, "Show shortened documentation for the packages"); print_usage_line(0, ""); print_usage_line(1, "-all-packages"); @@ -1833,7 +1815,7 @@ int main(int arg_count, char const **arg_ptr) { add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core"))); map_init(&build_context.defined_values, heap_allocator()); - build_context.doc_packages.allocator = heap_allocator(); + build_context.extra_packages.allocator = heap_allocator(); Array args = setup_args(arg_count, arg_ptr); @@ -1904,6 +1886,20 @@ int main(int arg_count, char const **arg_ptr) { build_context.command_kind = Command_doc; init_filename = args[2]; + for (isize i = 3; i < args.count; i++) { + auto arg = args[i]; + if (string_starts_with(arg, str_lit("-"))) { + break; + } + array_add(&build_context.extra_packages, arg); + } + isize extra_count = build_context.extra_packages.count; + if (extra_count > 0) { + gb_memmove(args.data + 3, args.data + 3 + extra_count, extra_count * gb_size_of(*args.data)); + args.count -= extra_count; + } + + build_context.no_output_files = true; build_context.generate_docs = true; build_context.no_entry_point = true; // ignore entry point @@ -2005,8 +2001,11 @@ int main(int arg_count, char const **arg_ptr) { temp_allocator_free_all(&temporary_allocator_data); if (build_context.generate_docs) { + if (global_error_collector.count != 0) { + return 1; + } generate_documentation(&checker); - return global_error_collector.count ? 1 : 0; + return 0; } if (build_context.no_output_files) { diff --git a/src/parser.cpp b/src/parser.cpp index 9e9708f9c..ec38dca9b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4653,14 +4653,14 @@ void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, AstForeignFi // NOTE(bill): Returns true if it's added -bool try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) { +AstPackage *try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) { String const FILE_EXT = str_lit(".odin"); gb_mutex_lock(&p->file_add_mutex); defer (gb_mutex_unlock(&p->file_add_mutex)); if (string_set_exists(&p->imported_files, path)) { - return false; + return nullptr; } string_set_add(&p->imported_files, path); @@ -4683,7 +4683,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path, pkg->is_single_file = true; parser_add_file_to_process(p, pkg, fi, pos); parser_add_package(p, pkg); - return true; + return pkg; } @@ -4699,22 +4699,22 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path, switch (rd_err) { case ReadDirectory_InvalidPath: syntax_error(pos, "Invalid path: %.*s", LIT(rel_path)); - return false; + return nullptr; case ReadDirectory_NotExists: syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path)); - return false; + return nullptr; case ReadDirectory_Permission: syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); - return false; + return nullptr; case ReadDirectory_NotDir: syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); - return false; + return nullptr; case ReadDirectory_Empty: syntax_error(pos, "Empty directory: %.*s", LIT(rel_path)); - return false; + return nullptr; case ReadDirectory_Unknown: syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); - return false; + return nullptr; } for_array(list_index, list) { @@ -4736,7 +4736,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path, parser_add_package(p, pkg); - return true; + return pkg; } gb_global Rune illegal_import_runes[] = { @@ -4755,7 +4755,7 @@ bool is_import_path_valid(String path) { u8 *curr = start; while (curr < end) { isize width = 1; - Rune r = curr[0]; + Rune r = *curr; if (r >= 0x80) { width = gb_utf8_decode(curr, end-curr, &r); if (r == GB_RUNE_INVALID && width == 1) { @@ -4780,6 +4780,45 @@ bool is_import_path_valid(String path) { return false; } +bool is_build_flag_path_valid(String path) { + if (path.len > 0) { + u8 *start = path.text; + u8 *end = path.text + path.len; + u8 *curr = start; + isize index = 0; + while (curr < end) { + isize width = 1; + Rune r = *curr; + if (r >= 0x80) { + width = gb_utf8_decode(curr, end-curr, &r); + if (r == GB_RUNE_INVALID && width == 1) { + return false; + } + else if (r == GB_RUNE_BOM && curr-start > 0) { + return false; + } + } + + for (isize i = 0; i < gb_count_of(illegal_import_runes); i++) { +#if defined(GB_SYSTEM_WINDOWS) + if (r == '\\') { + break; + } +#endif + if (r == illegal_import_runes[i]) { + return false; + } + } + + curr += width; + index += 1; + } + + return true; + } + return false; +} + bool is_package_name_reserved(String const &name) { if (name == "builtin") { @@ -5263,6 +5302,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) { try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init); p->init_fullpath = init_fullpath; + for_array(i, build_context.extra_packages) { + String path = build_context.extra_packages[i]; + String fullpath = path_to_full_path(heap_allocator(), path); // LEAK? + if (!path_is_directory(fullpath)) { + String const ext = str_lit(".odin"); + if (!string_ends_with(fullpath, ext)) { + error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath)); + return ParseFile_WrongExtension; + } + } + AstPackage *pkg = try_add_import_path(p, fullpath, fullpath, init_pos, Package_Normal); + if (pkg) { + pkg->is_extra = true; + } + } + thread_pool_start(&parser_thread_pool); thread_pool_wait_to_process(&parser_thread_pool); diff --git a/src/parser.hpp b/src/parser.hpp index 48af0b293..aa288304e 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -150,6 +150,7 @@ struct AstPackage { Scope * scope; DeclInfo *decl_info; bool used; + bool is_extra; }; From a39921aa6a6086739fa087c28864dfc449217d0c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 18 Nov 2020 23:33:16 +0000 Subject: [PATCH 071/170] Add `package text/scanner` --- core/text/scanner/scanner.odin | 583 +++++++++++++++++++++++++++++++++ 1 file changed, 583 insertions(+) create mode 100644 core/text/scanner/scanner.odin diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin new file mode 100644 index 000000000..ddfd0bb5f --- /dev/null +++ b/core/text/scanner/scanner.odin @@ -0,0 +1,583 @@ +package text_scanner + +import "core:fmt" +import "core:strings" +import "core:unicode" +import "core:unicode/utf8" + +Position :: struct { + filename: string, // filename, if present + offset: int, // byte offset, starting @ 0 + line: int, // line number, starting @ 1 + column: int, // column number, starting @ 1 (character count per line) +} + +position_is_valid :: proc(pos: Position) -> bool { + return pos.line > 0; +} + +position_to_string :: proc(pos: Position, allocator := context.temp_allocator) -> string { + s := pos.filename; + if s == "" { + s = ""; + } + + context.allocator = allocator; + if position_is_valid(pos) { + return fmt.aprintf("%s(%d:%d)", s, pos.line, pos.column); + } else { + return strings.clone(s); + } +} + +EOF :: -1; +Ident :: -2; +Int :: -3; +Float :: -4; +Char :: -5; +String :: -6; +Raw_String :: -7; +Comment :: -8; + +Scan_Flag :: enum u32 { + Scan_Idents, + Scan_Ints, + Scan_C_Int_Prefixes, + Scan_Floats, + Scan_Chars, + Scan_Strings, + Scan_Raw_Strings, + Scan_Comments, + Skip_Comments, +} +Scan_Flags :: bit_set[Scan_Flag; u32]; + +Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; +C_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; + +Odin_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '; +C_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<'\v' | 1<<'\f' | 1<<' '; + + +Scanner :: struct { + src: string, + + src_pos: int, + src_end: int, + + tok_pos: int, + tok_end: int, + + ch: rune, + + line: int, + column: int, + prev_line_len: int, + prev_char_len: int, + + error: proc(s: ^Scanner, msg: string), + error_count: int, + + flags: Scan_Flags, + whitespace: u64, + + is_ident_rune: proc(ch: rune, i: int) -> bool, + + pos: Position, +} + +init :: proc(s: ^Scanner, src: string, filename := "") -> ^Scanner { + s^ = {}; + + s.src = src; + s.pos.filename = filename; + + s.tok_pos = -1; + + s.ch = -2; // no char read yet, not an EOF + + s.line = 1; + + s.flags = Odin_Like_Tokens; + s.whitespace = Odin_Whitespace; + + return s; +} + + +@(private) +advance :: proc(s: ^Scanner) -> rune { + if s.src_pos >= len(s.src) { + s.prev_char_len = 0; + return EOF; + } + ch, width := rune(s.src[s.src_pos]), 1; + + if ch >= utf8.RUNE_SELF { + ch, width = utf8.decode_rune_in_string(s.src[s.src_pos:]); + if ch == utf8.RUNE_ERROR && width == 1 { + s.src_pos += width; + s.prev_char_len = width; + s.column += 1; + error(s, "invalid UTF-8 encoding"); + return ch; + } + } + + s.src_pos += width; + s.prev_char_len = width; + s.column += 1; + + switch ch { + case 0: + error(s, "invalid character NUL"); + case '\n': + s.line += 1; + s.prev_line_len = s.column; + s.column = 0; + } + + return ch; +} + +next :: proc(s: ^Scanner) -> rune { + s.tok_pos = -1; + s.pos.line = 0; + ch := peek(s); + if ch != EOF { + s.ch = advance(s); + } + return ch; +} + +peek :: proc(s: ^Scanner) -> rune { + if s.ch == -2 { + s.ch = advance(s); + if s.ch == '\ufeff' { // Ignore BOM + s.ch = advance(s); + } + } + return s.ch; +} + + +error :: proc(s: ^Scanner, msg: string) { + s.error_count += 1; + if s.error != nil { + s.error(s, msg); + return; + } + p := s.pos; + if !position_is_valid(p) { + p = position(s); + } + + s := p.filename; + if s == "" { + s = ""; + } + + if position_is_valid(p) { + fmt.eprintf("%s(%d:%d): %s\n", s, p.line, p.column, msg); + } else { + fmt.eprintf("%s: %s\n", s, msg); + } +} + +errorf :: proc(s: ^Scanner, format: string, args: ..any) { + error(s, fmt.tprintf(format, ..args)); +} + +@(private) +is_ident_rune :: proc(s: ^Scanner, ch: rune, i: int) -> bool { + if s.is_ident_rune != nil { + return s.is_ident_rune(ch, i); + } + return ch == '_' || unicode.is_letter(ch) || unicode.is_digit(ch) && i > 0; +} + +@(private) +scan_identifier :: proc(s: ^Scanner) -> rune { + ch := advance(s); + for i := 1; is_ident_rune(s, ch, i); i += 1 { + ch = advance(s); + } + return ch; +} + +@(private) lower :: proc(ch: rune) -> rune { return ('a' - 'A') | ch; } +@(private) is_decimal :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9'; } +@(private) is_hex :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f'; } + + + +@(private) +scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) { + lit_name :: proc(prefix: rune) -> string { + switch prefix { + case 'b': return "binary literal"; + case 'o': return "octal literal"; + case 'z': return "dozenal literal"; + case 'x': return "hexadecimal literal"; + } + return "decimal literal"; + } + + digits :: proc(s: ^Scanner, ch0: rune, base: int, invalid: ^rune) -> (ch: rune, digsep: int) { + ch = ch0; + if base <= 10 { + max := rune('0' + base); + for is_decimal(ch) || ch == '_' { + ds := 1; + if ch == '_' { + ds = 2; + } else if ch >= max && invalid^ == 0 { + invalid^ = ch; + } + digsep |= ds; + ch = advance(s); + } + } else { + for is_hex(ch) || ch == '_' { + ds := 1; + if ch == '_' { + ds = 2; + } + digsep |= ds; + ch = advance(s); + } + } + return; + } + + ch, seen_dot := ch, seen_dot; + + base := 10; + prefix := rune(0); + digsep := 0; + invalid := rune(0); + + tok: rune; + ds: int; + + if !seen_dot { + tok = Int; + ch0 := ch; + if ch == '0' { + ch = advance(s); + + p := lower(ch); + if .Scan_C_Int_Prefixes in s.flags { + switch lower(ch) { + case 'b': + ch = advance(s); + base, prefix = 2, 'b'; + case 'x': + ch = advance(s); + base, prefix = 16, 'x'; + case: + base, prefix = 8, 'o'; + digsep = 1; // Leading zero + } + } else { + switch lower(ch) { + case 'b': + ch = advance(s); + base, prefix = 2, 'b'; + case 'o': + ch = advance(s); + base, prefix = 8, 'o'; + case 'd': + ch = advance(s); + base, prefix = 10, 'd'; + case 'z': + ch = advance(s); + base, prefix = 12, 'z'; + case 'x': + ch = advance(s); + base, prefix = 16, 'x'; + case: + digsep = 1; // Leading zero + } + } + } + + ch, ds = digits(s, ch, base, &invalid); + digsep |= ds; + if ch == '.' && .Scan_Floats in s.flags { + ch = advance(s); + seen_dot = true; + } + } + + if seen_dot { + tok = Float; + if prefix != 0 && prefix != 'x' { + errorf(s, "invalid radix point in %s", lit_name(prefix)); + } + ch, ds = digits(s, ch, base, &invalid); + digsep |= ds; + } + + if digsep&1 == 0 { + errorf(s, "%s has no digits", lit_name(prefix)); + } + + if e := lower(ch); (e == 'e' || e == 'p') && .Scan_Floats in s.flags { + switch { + case e == 'e' && prefix != 0: + errorf(s, "%q exponent requires decimal mantissa", ch); + case e == 'p' && prefix != 'x': + errorf(s, "%q exponent requires hexadecimal mantissa", ch); + } + ch = advance(s); + tok = Float; + if ch == '+' || ch == '-' { + ch = advance(s); + } + ch, ds = digits(s, ch, 10, nil); + digsep |= ds; + if ds&1 == 0 { + error(s, "exponent has no digits"); + } + } else if prefix == 'x' && tok == Float { + error(s, "hexadecimal mantissa requires a 'p' exponent"); + } + + if tok == Int && invalid != 0 { + errorf(s, "invalid digit %q in %s", invalid, lit_name(prefix)); + } + + if digsep&2 != 0 { + s.tok_end = s.src_pos - s.prev_char_len; + } + return tok, ch; +} + +@(private) +scan_string :: proc(s: ^Scanner, quote: rune) -> (n: int) { + digit_val :: proc(ch: rune) -> int { + switch v := lower(ch); v { + case '0'..'9': return int(v - '0'); + case 'a'..'z': return int(v - 'a'); + } + return 16; + } + + scan_digits :: proc(s: ^Scanner, ch: rune, base, n: int) -> rune { + ch, n := ch, n; + for n > 0 && digit_val(ch) < base { + ch = advance(s); + n -= 1; + } + if n > 0 { + error(s, "invalid char escape"); + } + return ch; + } + + ch := advance(s); + for ch != quote { + if ch == '\n' || ch < 0 { + error(s, "literal no terminated"); + return; + } + if ch == '\\' { + ch = advance(s); + switch ch { + case quote, 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\': + ch = advance(s); + case '0'..'7': ch = scan_digits(s, ch, 8, 3); + case 'x': ch = scan_digits(s, ch, 16, 2); + case 'u': ch = scan_digits(s, ch, 16, 4); + case 'U': ch = scan_digits(s, ch, 16, 8); + case: + error(s, "invalid char escape"); + } + } else { + ch = advance(s); + } + n += 1; + } + return; +} + +@(private) +scan_raw_string :: proc(s: ^Scanner) { + ch := advance(s); + for ch != '`' { + if ch < 0 { + error(s, "literal not terminated"); + return; + } + ch = advance(s); + } +} + +@(private) +scan_char :: proc(s: ^Scanner) { + if scan_string(s, '\'') != 1 { + error(s, "invalid char literal"); + } +} + +@(private) +scan_comment :: proc(s: ^Scanner, ch: rune) -> rune { + ch := ch; + if ch == '/' { // line comment + ch = advance(s); + for ch != '\n' && ch >= 0 { + ch = advance(s); + } + return ch; + } + + // block /**/ comment + ch = advance(s); + for { + if ch < 0 { + error(s, "comment not terminated"); + break; + } + ch0 := ch; + ch = advance(s); + if ch0 == '*' && ch == '/' { + return advance(s); + } + } + return ch; +} + +scan :: proc(s: ^Scanner) -> (tok: rune) { + ch := peek(s); + if ch == EOF { + return ch; + } + + // reset position + s.tok_pos = -1; + s.pos.line = 0; + + redo: for { + for s.whitespace & (1< 0 { + s.pos.line = s.line; + s.pos.column = s.column; + } else { + // previous character was newline + s.pos.line = s.line - 1; + s.pos.column = s.prev_line_len; + } + + tok = ch; + if is_ident_rune(s, ch, 0) { + if .Scan_Idents in s.flags { + tok = Ident; + ch = scan_identifier(s); + } else { + ch = advance(s); + } + + } else if is_decimal(ch) { + if s.flags >= {.Scan_Ints, .Scan_Floats} { + tok, ch = scan_number(s, ch, false); + } else { + ch = advance(s); + } + } else { + switch ch { + case EOF: + break; + case '"': + if .Scan_Strings in s.flags { + scan_string(s, '"'); + tok = String; + } + ch = advance(s); + case '\'': + if .Scan_Chars in s.flags { + scan_string(s, '\''); + tok = Char; + } + ch = advance(s); + case '`': + if .Scan_Raw_Strings in s.flags { + scan_raw_string(s); + tok = Raw_String; + } + ch = advance(s); + case '.': + ch = advance(s); + if is_decimal(ch) && .Scan_Floats in s.flags { + tok, ch = scan_number(s, ch, true); + } + case '/': + ch = advance(s); + if (ch == '/' || ch == '*') && .Scan_Comments in s.flags { + if .Skip_Comments in s.flags { + s.tok_pos = -1; + ch = scan_comment(s, ch); + continue redo; + } + ch = scan_comment(s, ch); + tok = Comment; + } + case: + ch = advance(s); + } + } + + break redo; + } + + s.tok_end = s.src_pos - s.prev_char_len; + + s.ch = ch; + return tok; +} + +position :: proc(s: ^Scanner) -> Position { + pos: Position; + pos.filename = s.pos.filename; + pos.offset = s.src_pos - s.prev_char_len; + switch { + case s.column > 0: + pos.line = s.line; + pos.column = s.column; + case s.prev_line_len > 0: + pos.line = s.line-1; + pos.column = s.prev_line_len; + case: + pos.line = 1; + pos.column = 1; + } + return pos; +} + +token_text :: proc(s: ^Scanner) -> string { + if s.tok_pos < 0 { + return ""; + } + return string(s.src[s.tok_pos:s.tok_end]); +} + +token_string :: proc(tok: rune, allocator := context.temp_allocator) -> string { + context.allocator = allocator; + switch tok { + case EOF: return strings.clone("EOF"); + case Ident: return strings.clone("Ident"); + case Int: return strings.clone("Int"); + case Float: return strings.clone("Float"); + case Char: return strings.clone("Char"); + case String: return strings.clone("String"); + case Raw_String: return strings.clone("Raw_String"); + case Comment: return strings.clone("Comment"); + } + return fmt.aprintf("%q", tok); +} From fef51722783e20123b58018ba0863dcce3974671 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 18 Nov 2020 23:40:54 +0000 Subject: [PATCH 072/170] Add `%q` for runes in fmt --- core/fmt/fmt.odin | 2 ++ core/strings/builder.odin | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 8a0d475d8..7eca5f503 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -776,6 +776,8 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': strings.write_rune(fi.buf, r); + case 'q': + strings.write_quoted_rune(fi.buf, r); case: fmt_int(fi, u64(r), false, 32, verb); } diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 2683ea9c9..e5b93fb67 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -131,6 +131,22 @@ write_rune :: proc(b: ^Builder, r: rune) -> int { return n; } +write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { + quote := byte('\''); + n += write_byte(b, quote); + buf, width := utf8.encode_rune(r); + if width == 1 && r == utf8.RUNE_ERROR { + n += write_byte(b, '\\'); + n += write_byte(b, 'x'); + n += write_byte(b, DIGITS_LOWER[buf[0]>>4]); + n += write_byte(b, DIGITS_LOWER[buf[0]&0xf]); + } else { + n += write_escaped_rune(b, r, quote); + } + n += write_byte(b, quote); + return; +} + write_string :: proc(b: ^Builder, s: string) -> (n: int) { return write_bytes(b, transmute([]byte)s); } From 3b7fd4711f52f55d72862850a6b01580e6dde17c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 18 Nov 2020 23:48:01 +0000 Subject: [PATCH 073/170] Fix text/scanner.scan_string --- core/text/scanner/scanner.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index ddfd0bb5f..fefa9f5a1 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -387,10 +387,10 @@ scan_string :: proc(s: ^Scanner, quote: rune) -> (n: int) { switch ch { case quote, 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\': ch = advance(s); - case '0'..'7': ch = scan_digits(s, ch, 8, 3); - case 'x': ch = scan_digits(s, ch, 16, 2); - case 'u': ch = scan_digits(s, ch, 16, 4); - case 'U': ch = scan_digits(s, ch, 16, 8); + case '0'..'7': ch = scan_digits(s, advance(s), 8, 3); + case 'x': ch = scan_digits(s, advance(s), 16, 2); + case 'u': ch = scan_digits(s, advance(s), 16, 4); + case 'U': ch = scan_digits(s, advance(s), 16, 8); case: error(s, "invalid char escape"); } From 913eac13b1a92ddfcbef2de78bb680372d9eb836 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 19 Nov 2020 00:08:23 +0000 Subject: [PATCH 074/170] Reorganize runtime code into separate files --- core/runtime/core.odin | 1244 +--------------------- core/runtime/core_builtin.odin | 823 ++++++++++++++ core/runtime/dynamic_array_internal.odin | 100 ++ core/runtime/dynamic_map_internal.odin | 303 ++++++ 4 files changed, 1231 insertions(+), 1239 deletions(-) create mode 100644 core/runtime/core_builtin.odin create mode 100644 core/runtime/dynamic_array_internal.odin create mode 100644 core/runtime/dynamic_map_internal.odin diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 6b0c3eb84..dfbe0d053 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -1,11 +1,7 @@ // This is the runtime code required by the compiler // IMPORTANT NOTE(bill): Do not change the order of any of this data // The compiler relies upon this _exact_ order -package runtime - -import "intrinsics" -_ :: intrinsics; - +// // Naming Conventions: // In general, Ada_Case for types and snake_case for values // @@ -16,12 +12,13 @@ _ :: intrinsics; // Procedures: snake_case // Local Variables: snake_case // Constant Variables: SCREAMING_SNAKE_CASE - - +// // IMPORTANT NOTE(bill): `type_info_of` cannot be used within a // #shared_global_scope due to the internals of the compiler. // This could change at a later date if the all these data structures are // implemented within the compiler rather than in this "preload" file +// +package runtime // NOTE(bill): This must match the compiler's Calling_Convention :: enum u8 { @@ -87,7 +84,7 @@ Type_Info_Enumerated_Array :: struct { }; Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int}; Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int}; -Type_Info_Tuple :: struct { // Only really used for procedures +Type_Info_Tuple :: struct { // Only used for procedures parameters and results types: []^Type_Info, names: []string, }; @@ -237,9 +234,6 @@ args__: []cstring; // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) -@builtin -Maybe :: union(T: typeid) #maybe {T}; - Source_Code_Location :: struct { file_path: string, @@ -521,13 +515,6 @@ __init_context :: proc "contextless" (c: ^Context) { c.logger.data = nil; } -@thread_local global_default_temp_allocator_data: Default_Temp_Allocator; - -@builtin -init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { - default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator); -} - default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) { print_caller_location(loc); @@ -540,1224 +527,3 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code print_byte('\n'); debug_trap(); } - - - - -@builtin -copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int { - n := max(0, min(len(dst), len(src))); - if n > 0 { - mem_copy(raw_data(dst), raw_data(src), n*size_of(E)); - } - return n; -} -@builtin -copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int { - n := max(0, min(len(dst), len(src))); - if n > 0 { - mem_copy(raw_data(dst), raw_data(src), n); - } - return n; -} -@builtin -copy :: proc{copy_slice, copy_from_string}; - - - -@builtin -unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { - bounds_check_error_loc(loc, index, len(array)); - n := len(array)-1; - if index != n { - array[index] = array[n]; - } - pop(array); -} - -@builtin -ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { - bounds_check_error_loc(loc, index, len(array)); - if index+1 < len(array) { - copy(array[index:], array[index+1:]); - } - pop(array); -} - -@builtin -remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) { - slice_expr_error_lo_hi_loc(loc, lo, hi, len(array)); - n := max(hi-lo, 0); - if n > 0 { - if hi != len(array) { - copy(array[lo:], array[hi:]); - } - (^Raw_Dynamic_Array)(array).len -= n; - } -} - - -@builtin -pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc); - res = array[len(array)-1]; - (^Raw_Dynamic_Array)(array).len -= 1; - return res; -} - - -@builtin -pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { - if len(array) == 0 { - return; - } - res, ok = array[len(array)-1], true; - (^Raw_Dynamic_Array)(array).len -= 1; - return; -} - -@builtin -pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc); - res = array[0]; - if len(array) > 1 { - copy(array[0:], array[1:]); - } - (^Raw_Dynamic_Array)(array).len -= 1; - return res; -} - -@builtin -pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { - if len(array) == 0 { - return; - } - res, ok = array[0], true; - if len(array) > 1 { - copy(array[0:], array[1:]); - } - (^Raw_Dynamic_Array)(array).len -= 1; - return; -} - - -@builtin -clear :: proc{clear_dynamic_array, clear_map}; - -@builtin -reserve :: proc{reserve_dynamic_array, reserve_map}; - -@builtin -resize :: proc{resize_dynamic_array}; - - -@builtin -free :: proc{mem_free}; - -@builtin -free_all :: proc{mem_free_all}; - - - -@builtin -delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) { - mem_free(raw_data(str), allocator, loc); -} -@builtin -delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) { - mem_free((^byte)(str), allocator, loc); -} -@builtin -delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) { - mem_free(raw_data(array), array.allocator, loc); -} -@builtin -delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) { - mem_free(raw_data(array), allocator, loc); -} -@builtin -delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) { - raw := transmute(Raw_Map)m; - delete_slice(raw.hashes); - mem_free(raw.entries.data, raw.entries.allocator, loc); -} - - -@builtin -delete :: proc{ - delete_string, - delete_cstring, - delete_dynamic_array, - delete_slice, - delete_map, -}; - - -@builtin -new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T { - ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); - if ptr != nil { ptr^ = T{}; } - return ptr; -} - -@builtin -new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T { - ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); - if ptr != nil { ptr^ = data; } - return ptr; -} - -make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T { - make_slice_error_loc(loc, len); - data := mem_alloc(size_of(E)*len, alignment, allocator, loc); - if data == nil && size_of(E) != 0 { - return nil; - } - // mem_zero(data, size_of(E)*len); - s := Raw_Slice{data, len}; - return transmute(T)s; -} - -@builtin -make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { - return make_aligned(T, len, align_of(E), allocator, loc); -} - -@builtin -make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T { - return make_dynamic_array_len_cap(T, 0, 16, allocator, loc); -} - -@builtin -make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { - return make_dynamic_array_len_cap(T, len, len, allocator, loc); -} - -@builtin -make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T { - make_dynamic_array_error_loc(loc, len, cap); - data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc); - s := Raw_Dynamic_Array{data, len, cap, allocator}; - if data == nil && size_of(E) != 0 { - s.len, s.cap = 0, 0; - } - // mem_zero(data, size_of(E)*cap); - return transmute(T)s; -} - -@builtin -make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T { - make_map_expr_error_loc(loc, cap); - context.allocator = allocator; - - m: T; - reserve_map(&m, cap); - return m; -} - -@builtin -make :: proc{ - make_slice, - make_dynamic_array, - make_dynamic_array_len, - make_dynamic_array_len_cap, - make_map, -}; - - - -@builtin -clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) { - if m == nil { - return; - } - raw_map := (^Raw_Map)(m); - entries := (^Raw_Dynamic_Array)(&raw_map.entries); - entries.len = 0; - for _, i in raw_map.hashes { - raw_map.hashes[i] = -1; - } -} - -@builtin -reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { - if m != nil { - __dynamic_map_reserve(__get_map_header(m), capacity); - } -} - -@builtin -delete_key :: proc(m: ^$T/map[$K]$V, key: K) { - if m != nil { - __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key)); - } -} - - - -@builtin -append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { - if array == nil { - return; - } - - arg_len := 1; - - if cap(array) < len(array)+arg_len { - cap := 2 * cap(array) + max(8, arg_len); - _ = reserve(array, cap, loc); - } - arg_len = min(cap(array)-len(array), arg_len); - if arg_len > 0 { - a := (^Raw_Dynamic_Array)(array); - if size_of(E) != 0 { - data := (^E)(a.data); - assert(data != nil); - val := arg; - mem_copy(ptr_offset(data, a.len), &val, size_of(E)); - } - a.len += arg_len; - } -} -@builtin -append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) { - if array == nil { - return; - } - - arg_len := len(args); - if arg_len <= 0 { - return; - } - - - if cap(array) < len(array)+arg_len { - cap := 2 * cap(array) + max(8, arg_len); - _ = reserve(array, cap, loc); - } - arg_len = min(cap(array)-len(array), arg_len); - if arg_len > 0 { - a := (^Raw_Dynamic_Array)(array); - if size_of(E) != 0 { - data := (^E)(a.data); - assert(data != nil); - mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len); - } - a.len += arg_len; - } -} -@builtin -append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) { - args := transmute([]E)arg; - append_elems(array=array, args=args, loc=loc); -} - -@builtin -reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - - old_cap := cap(array); - if capacity <= old_cap { - return true; - } - - if array.allocator.procedure == nil { - array.allocator = context.allocator; - } - assert(array.allocator.procedure != nil); - - - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return true; - } - - cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr)); - assert(cap_ptr^ == old_cap); - - - old_size := 0; - new_size := 0; - - max_align := 0; - for i in 0.. 0 { - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return; - } - - data := (^rawptr)(array)^; - - len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); - - - soa_offset := 0; - item_offset := 0; - - arg_copy := arg; - arg_ptr := &arg_copy; - - max_align := 0; - for i in 0.. 0 { - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return; - } - - data := (^rawptr)(array)^; - - len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); - - - soa_offset := 0; - item_offset := 0; - - args_ptr := &args[0]; - - max_align := 0; - for i in 0.. (ok: bool) #no_bounds_check { - if array == nil { - return; - } - n := len(array); - m :: 1; - resize(array, n+m, loc); - if n+m <= len(array) { - when size_of(E) != 0 { - copy(array[index+m:], array[index:]); - array[index] = arg; - } - ok = true; - } - return; -} - -@builtin -insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { - if array == nil { - return; - } - if len(args) == 0 { - ok = true; - return; - } - - n := len(array); - m := len(args); - resize(array, n+m, loc); - if n+m <= len(array) { - when size_of(E) != 0 { - copy(array[index+m:], array[index:]); - copy(array[index:], args); - } - ok = true; - } - return; -} - -@builtin -insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { - if array == nil { - return; - } - if len(args) == 0 { - ok = true; - return; - } - - n := len(array); - m := len(args); - resize(array, n+m, loc); - if n+m <= len(array) { - copy(array[index+m:], array[index:]); - copy(array[index:], args); - ok = true; - } - return; -} - -@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}; - - - - -@builtin -clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) { - if array != nil { - (^Raw_Dynamic_Array)(array).len = 0; - } -} - -@builtin -reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - a := (^Raw_Dynamic_Array)(array); - - if capacity <= a.cap { - return true; - } - - if a.allocator.procedure == nil { - a.allocator = context.allocator; - } - assert(a.allocator.procedure != nil); - - old_size := a.cap * size_of(E); - new_size := capacity * size_of(E); - allocator := a.allocator; - - new_data := allocator.procedure( - allocator.data, .Resize, new_size, align_of(E), - a.data, old_size, 0, loc, - ); - if new_data == nil { - return false; - } - - a.data = new_data; - a.cap = capacity; - return true; -} - -@builtin -resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - a := (^Raw_Dynamic_Array)(array); - - if length <= a.cap { - a.len = max(length, 0); - return true; - } - - if a.allocator.procedure == nil { - a.allocator = context.allocator; - } - assert(a.allocator.procedure != nil); - - old_size := a.cap * size_of(E); - new_size := length * size_of(E); - allocator := a.allocator; - - new_data := allocator.procedure( - allocator.data, .Resize, new_size, align_of(E), - a.data, old_size, 0, loc, - ); - if new_data == nil { - return false; - } - - a.data = new_data; - a.len = length; - a.cap = length; - return true; -} - - - -@builtin -incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { - s^ |= {elem}; - return s^; -} -@builtin -incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { - for elem in elems { - s^ |= {elem}; - } - return s^; -} -@builtin -incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { - s^ |= other; - return s^; -} -@builtin -excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { - s^ &~= {elem}; - return s^; -} -@builtin -excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { - for elem in elems { - s^ &~= {elem}; - } - return s^; -} -@builtin -excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { - s^ &~= other; - return s^; -} - -@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}; -@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}; - - -@builtin -card :: proc(s: $S/bit_set[$E; $U]) -> int { - when size_of(S) == 1 { - foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- } - return int(count_ones(transmute(u8)s)); - } else when size_of(S) == 2 { - foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- } - return int(count_ones(transmute(u16)s)); - } else when size_of(S) == 4 { - foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- } - return int(count_ones(transmute(u32)s)); - } else when size_of(S) == 8 { - foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- } - return int(count_ones(transmute(u64)s)); - } else when size_of(S) == 16 { - foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- } - return int(count_ones(transmute(u128)s)); - } else { - #panic("Unhandled card bit_set size"); - } -} - - - -@builtin -raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { - return (^E)(a); -} -@builtin -raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E { - ptr := (transmute(Raw_Slice)s).data; - return (^E)(ptr); -} -@builtin -raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E { - ptr := (transmute(Raw_Dynamic_Array)s).data; - return (^E)(ptr); -} -@builtin -raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 { - return (transmute(Raw_String)s).data; -} - -@builtin -raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}; - - - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -assert :: proc(condition: bool, message := "", loc := #caller_location) { - if !condition { - proc(message: string, loc: Source_Code_Location) { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("runtime assertion", message, loc); - }(message, loc); - } -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -panic :: proc(message: string, loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("panic", message, loc); -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -unimplemented :: proc(message := "", loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("not yet implemented", message, loc); -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -unreachable :: proc(message := "", loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - if message != "" { - p("internal error", message, loc); - } else { - p("internal error", "entered unreachable code", loc); - } -} - - -// Dynamic Array - - -__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) { - array := (^Raw_Dynamic_Array)(array_); - array.allocator = context.allocator; - assert(array.allocator.procedure != nil); - - if cap > 0 { - __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc); - array.len = len; - } -} - -__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool { - array := (^Raw_Dynamic_Array)(array_); - - // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written - // assuming that appending/reserving will set the allocator, if it is not already set. - if array.allocator.procedure == nil { - array.allocator = context.allocator; - } - assert(array.allocator.procedure != nil); - - if cap <= array.cap { - return true; - } - - old_size := array.cap * elem_size; - new_size := cap * elem_size; - allocator := array.allocator; - - new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc); - if new_data != nil || elem_size == 0 { - array.data = new_data; - array.cap = cap; - return true; - } - return false; -} - -__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { - array := (^Raw_Dynamic_Array)(array_); - - ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc); - if ok { - array.len = len; - } - return ok; -} - - -__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, - items: rawptr, item_count: int, loc := #caller_location) -> int { - array := (^Raw_Dynamic_Array)(array_); - - if items == nil { - return 0; - } - if item_count <= 0 { - return 0; - } - - - ok := true; - if array.cap <= array.len+item_count { - cap := 2 * array.cap + max(8, item_count); - ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); - } - // TODO(bill): Better error handling for failed reservation - if !ok { - return array.len; - } - - assert(array.data != nil); - data := uintptr(array.data) + uintptr(elem_size*array.len); - - mem_copy(rawptr(data), items, elem_size * item_count); - array.len += item_count; - return array.len; -} - -__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int { - array := (^Raw_Dynamic_Array)(array_); - - ok := true; - if array.cap <= array.len+1 { - cap := 2 * array.cap + max(8, 1); - ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); - } - // TODO(bill): Better error handling for failed reservation - if !ok { - return array.len; - } - - assert(array.data != nil); - data := uintptr(array.data) + uintptr(elem_size*array.len); - mem_zero(rawptr(data), elem_size); - array.len += 1; - return array.len; -} - - - - -// Map - -__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { - header := Map_Header{m = (^Raw_Map)(m)}; - Entry :: struct { - key: Map_Key, - next: int, - value: V, - }; - - header.is_key_string = intrinsics.type_is_string(K); - header.entry_size = int(size_of(Entry)); - header.entry_align = int(align_of(Entry)); - header.value_offset = uintptr(offset_of(Entry, value)); - header.value_size = int(size_of(V)); - return header; -} - -__get_map_key :: proc "contextless" (k: $K) -> Map_Key { - key := k; - map_key: Map_Key; - - T :: intrinsics.type_core_type(K); - - when intrinsics.type_is_integer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); } - else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); } - else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled integer size"); } - } else when intrinsics.type_is_rune(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64((^rune)(&key)^); - } else when intrinsics.type_is_pointer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64(uintptr((^rawptr)(&key)^)); - } else when intrinsics.type_is_float(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled float size"); } - } else when intrinsics.type_is_string(T) { - #assert(T == string); - str := (^string)(&key)^; - map_key.hash = default_hash_string(str); - map_key.key.str = str; - } else { - #panic("Unhandled map key type"); - } - - return map_key; -} - -_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { - h: u64 = seed; - for b in data { - h = (h ~ u64(b)) * 0x100000001b3; - } - return h; -} - - -default_hash :: inline proc "contextless" (data: []byte) -> u64 { - return _fnv64a(data); -} -default_hash_string :: inline proc "contextless" (s: string) -> u64 { - return default_hash(transmute([]byte)(s)); -} -default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 { - s := Raw_Slice{data, size}; - return default_hash(transmute([]byte)(s)); -} - - -source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { - hash := _fnv64a(transmute([]byte)s.file_path); - hash = hash ~ (u64(s.line) * 0x100000001b3); - hash = hash ~ (u64(s.column) * 0x100000001b3); - return hash; -} - - - -__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { - array := (^Raw_Slice)(array_); - - if new_count < array.len { - return true; - } - - assert(allocator.procedure != nil); - - old_size := array.len*size_of(T); - new_size := new_count*size_of(T); - - new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc); - if new_data == nil { - return false; - } - array.data = new_data; - array.len = new_count; - return true; -} - -__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) { - __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc); - - old_len := len(m.hashes); - __slice_resize(&m.hashes, cap, m.entries.allocator, loc); - for i in old_len.. rawptr { - index := __dynamic_map_find(h, key).entry_index; - if index >= 0 { - data := uintptr(__dynamic_map_get_entry(h, index)); - return rawptr(data + h.value_offset); - } - return nil; -} - -__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check { - index: int; - assert(value != nil); - - if len(h.m.hashes) == 0 { - __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc); - __dynamic_map_grow(h, loc); - } - - fr := __dynamic_map_find(h, key); - if fr.entry_index >= 0 { - index = fr.entry_index; - } else { - index = __dynamic_map_add_entry(h, key, loc); - if fr.entry_prev >= 0 { - entry := __dynamic_map_get_entry(h, fr.entry_prev); - entry.next = index; - } else { - h.m.hashes[fr.hash_index] = index; - } - } - { - e := __dynamic_map_get_entry(h, index); - e.key = key; - val := (^byte)(uintptr(e) + h.value_offset); - mem_copy(val, value, h.value_size); - } - - if __dynamic_map_full(h) { - __dynamic_map_grow(h, loc); - } -} - - -__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { - // TODO(bill): Determine an efficient growing rate - new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP); - __dynamic_map_rehash(h, new_count, loc); -} - -__dynamic_map_full :: inline proc(using h: Map_Header) -> bool { - return int(0.75 * f64(len(m.hashes))) <= m.entries.cap; -} - - -__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool { - if a.hash == b.hash { - if h.is_key_string { - return a.key.str == b.key.str; - } else { - return a.key.val == b.key.val; - } - return true; - } - return false; -} - -__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check { - fr := Map_Find_Result{-1, -1, -1}; - if n := u64(len(m.hashes)); n > 0 { - fr.hash_index = int(key.hash % n); - fr.entry_index = m.hashes[fr.hash_index]; - for fr.entry_index >= 0 { - entry := __dynamic_map_get_entry(h, fr.entry_index); - if __dynamic_map_hash_equal(h, entry.key, key) { - return fr; - } - fr.entry_prev = fr.entry_index; - fr.entry_index = entry.next; - } - } - return fr; -} - -__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int { - prev := m.entries.len; - c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); - if c != prev { - end := __dynamic_map_get_entry(h, c-1); - end.key = key; - end.next = -1; - } - return prev; -} - -__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) { - fr := __dynamic_map_find(h, key); - if fr.entry_index >= 0 { - __dynamic_map_erase(h, fr); - } -} - -__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { - assert(0 <= index && index < m.entries.len); - return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)); -} - -__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { - if fr.entry_prev < 0 { - m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next; - } else { - prev := __dynamic_map_get_entry(h, fr.entry_prev); - curr := __dynamic_map_get_entry(h, fr.entry_index); - prev.next = curr.next; - } - if (fr.entry_index == m.entries.len-1) { - // NOTE(bill): No need to do anything else, just pop - } else { - old := __dynamic_map_get_entry(h, fr.entry_index); - end := __dynamic_map_get_entry(h, m.entries.len-1); - mem_copy(old, end, entry_size); - - if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 { - 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; - } - } - - // TODO(bill): Is this correct behaviour? - m.entries.len -= 1; -} diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin new file mode 100644 index 000000000..0d430345e --- /dev/null +++ b/core/runtime/core_builtin.odin @@ -0,0 +1,823 @@ +package runtime + +@builtin +Maybe :: union(T: typeid) #maybe {T}; + +@thread_local global_default_temp_allocator_data: Default_Temp_Allocator; + +@builtin +init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { + default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator); +} + + +@builtin +copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int { + n := max(0, min(len(dst), len(src))); + if n > 0 { + mem_copy(raw_data(dst), raw_data(src), n*size_of(E)); + } + return n; +} +@builtin +copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int { + n := max(0, min(len(dst), len(src))); + if n > 0 { + mem_copy(raw_data(dst), raw_data(src), n); + } + return n; +} +@builtin +copy :: proc{copy_slice, copy_from_string}; + + + +@builtin +unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { + bounds_check_error_loc(loc, index, len(array)); + n := len(array)-1; + if index != n { + array[index] = array[n]; + } + pop(array); +} + +@builtin +ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { + bounds_check_error_loc(loc, index, len(array)); + if index+1 < len(array) { + copy(array[index:], array[index+1:]); + } + pop(array); +} + +@builtin +remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) { + slice_expr_error_lo_hi_loc(loc, lo, hi, len(array)); + n := max(hi-lo, 0); + if n > 0 { + if hi != len(array) { + copy(array[lo:], array[hi:]); + } + (^Raw_Dynamic_Array)(array).len -= n; + } +} + + +@builtin +pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { + assert(len(array) > 0, "", loc); + res = array[len(array)-1]; + (^Raw_Dynamic_Array)(array).len -= 1; + return res; +} + + +@builtin +pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { + if len(array) == 0 { + return; + } + res, ok = array[len(array)-1], true; + (^Raw_Dynamic_Array)(array).len -= 1; + return; +} + +@builtin +pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { + assert(len(array) > 0, "", loc); + res = array[0]; + if len(array) > 1 { + copy(array[0:], array[1:]); + } + (^Raw_Dynamic_Array)(array).len -= 1; + return res; +} + +@builtin +pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { + if len(array) == 0 { + return; + } + res, ok = array[0], true; + if len(array) > 1 { + copy(array[0:], array[1:]); + } + (^Raw_Dynamic_Array)(array).len -= 1; + return; +} + + +@builtin +clear :: proc{clear_dynamic_array, clear_map}; + +@builtin +reserve :: proc{reserve_dynamic_array, reserve_map}; + +@builtin +resize :: proc{resize_dynamic_array}; + + +@builtin +free :: proc{mem_free}; + +@builtin +free_all :: proc{mem_free_all}; + + + +@builtin +delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) { + mem_free(raw_data(str), allocator, loc); +} +@builtin +delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) { + mem_free((^byte)(str), allocator, loc); +} +@builtin +delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) { + mem_free(raw_data(array), array.allocator, loc); +} +@builtin +delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) { + mem_free(raw_data(array), allocator, loc); +} +@builtin +delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) { + raw := transmute(Raw_Map)m; + delete_slice(raw.hashes); + mem_free(raw.entries.data, raw.entries.allocator, loc); +} + + +@builtin +delete :: proc{ + delete_string, + delete_cstring, + delete_dynamic_array, + delete_slice, + delete_map, +}; + + +@builtin +new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T { + ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); + if ptr != nil { ptr^ = T{}; } + return ptr; +} + +@builtin +new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T { + ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); + if ptr != nil { ptr^ = data; } + return ptr; +} + +make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T { + make_slice_error_loc(loc, len); + data := mem_alloc(size_of(E)*len, alignment, allocator, loc); + if data == nil && size_of(E) != 0 { + return nil; + } + // mem_zero(data, size_of(E)*len); + s := Raw_Slice{data, len}; + return transmute(T)s; +} + +@builtin +make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { + return make_aligned(T, len, align_of(E), allocator, loc); +} + +@builtin +make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T { + return make_dynamic_array_len_cap(T, 0, 16, allocator, loc); +} + +@builtin +make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { + return make_dynamic_array_len_cap(T, len, len, allocator, loc); +} + +@builtin +make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T { + make_dynamic_array_error_loc(loc, len, cap); + data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc); + s := Raw_Dynamic_Array{data, len, cap, allocator}; + if data == nil && size_of(E) != 0 { + s.len, s.cap = 0, 0; + } + // mem_zero(data, size_of(E)*cap); + return transmute(T)s; +} + +@builtin +make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T { + make_map_expr_error_loc(loc, cap); + context.allocator = allocator; + + m: T; + reserve_map(&m, cap); + return m; +} + +@builtin +make :: proc{ + make_slice, + make_dynamic_array, + make_dynamic_array_len, + make_dynamic_array_len_cap, + make_map, +}; + + + +@builtin +clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) { + if m == nil { + return; + } + raw_map := (^Raw_Map)(m); + entries := (^Raw_Dynamic_Array)(&raw_map.entries); + entries.len = 0; + for _, i in raw_map.hashes { + raw_map.hashes[i] = -1; + } +} + +@builtin +reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { + if m != nil { + __dynamic_map_reserve(__get_map_header(m), capacity); + } +} + +@builtin +delete_key :: proc(m: ^$T/map[$K]$V, key: K) { + if m != nil { + __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key)); + } +} + + + +@builtin +append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { + if array == nil { + return; + } + + arg_len := 1; + + if cap(array) < len(array)+arg_len { + cap := 2 * cap(array) + max(8, arg_len); + _ = reserve(array, cap, loc); + } + arg_len = min(cap(array)-len(array), arg_len); + if arg_len > 0 { + a := (^Raw_Dynamic_Array)(array); + if size_of(E) != 0 { + data := (^E)(a.data); + assert(data != nil); + val := arg; + mem_copy(ptr_offset(data, a.len), &val, size_of(E)); + } + a.len += arg_len; + } +} +@builtin +append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) { + if array == nil { + return; + } + + arg_len := len(args); + if arg_len <= 0 { + return; + } + + + if cap(array) < len(array)+arg_len { + cap := 2 * cap(array) + max(8, arg_len); + _ = reserve(array, cap, loc); + } + arg_len = min(cap(array)-len(array), arg_len); + if arg_len > 0 { + a := (^Raw_Dynamic_Array)(array); + if size_of(E) != 0 { + data := (^E)(a.data); + assert(data != nil); + mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len); + } + a.len += arg_len; + } +} +@builtin +append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) { + args := transmute([]E)arg; + append_elems(array=array, args=args, loc=loc); +} + +@builtin +reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + + old_cap := cap(array); + if capacity <= old_cap { + return true; + } + + if array.allocator.procedure == nil { + array.allocator = context.allocator; + } + assert(array.allocator.procedure != nil); + + + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return true; + } + + cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr)); + assert(cap_ptr^ == old_cap); + + + old_size := 0; + new_size := 0; + + max_align := 0; + for i in 0.. 0 { + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return; + } + + data := (^rawptr)(array)^; + + len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); + + + soa_offset := 0; + item_offset := 0; + + arg_copy := arg; + arg_ptr := &arg_copy; + + max_align := 0; + for i in 0.. 0 { + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return; + } + + data := (^rawptr)(array)^; + + len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); + + + soa_offset := 0; + item_offset := 0; + + args_ptr := &args[0]; + + max_align := 0; + for i in 0.. (ok: bool) #no_bounds_check { + if array == nil { + return; + } + n := len(array); + m :: 1; + resize(array, n+m, loc); + if n+m <= len(array) { + when size_of(E) != 0 { + copy(array[index+m:], array[index:]); + array[index] = arg; + } + ok = true; + } + return; +} + +@builtin +insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if array == nil { + return; + } + if len(args) == 0 { + ok = true; + return; + } + + n := len(array); + m := len(args); + resize(array, n+m, loc); + if n+m <= len(array) { + when size_of(E) != 0 { + copy(array[index+m:], array[index:]); + copy(array[index:], args); + } + ok = true; + } + return; +} + +@builtin +insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if array == nil { + return; + } + if len(args) == 0 { + ok = true; + return; + } + + n := len(array); + m := len(args); + resize(array, n+m, loc); + if n+m <= len(array) { + copy(array[index+m:], array[index:]); + copy(array[index:], args); + ok = true; + } + return; +} + +@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}; + + + + +@builtin +clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) { + if array != nil { + (^Raw_Dynamic_Array)(array).len = 0; + } +} + +@builtin +reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + a := (^Raw_Dynamic_Array)(array); + + if capacity <= a.cap { + return true; + } + + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } + assert(a.allocator.procedure != nil); + + old_size := a.cap * size_of(E); + new_size := capacity * size_of(E); + allocator := a.allocator; + + new_data := allocator.procedure( + allocator.data, .Resize, new_size, align_of(E), + a.data, old_size, 0, loc, + ); + if new_data == nil { + return false; + } + + a.data = new_data; + a.cap = capacity; + return true; +} + +@builtin +resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + a := (^Raw_Dynamic_Array)(array); + + if length <= a.cap { + a.len = max(length, 0); + return true; + } + + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } + assert(a.allocator.procedure != nil); + + old_size := a.cap * size_of(E); + new_size := length * size_of(E); + allocator := a.allocator; + + new_data := allocator.procedure( + allocator.data, .Resize, new_size, align_of(E), + a.data, old_size, 0, loc, + ); + if new_data == nil { + return false; + } + + a.data = new_data; + a.len = length; + a.cap = length; + return true; +} + + + +@builtin +incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { + s^ |= {elem}; + return s^; +} +@builtin +incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems { + s^ |= {elem}; + } + return s^; +} +@builtin +incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ |= other; + return s^; +} +@builtin +excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { + s^ &~= {elem}; + return s^; +} +@builtin +excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems { + s^ &~= {elem}; + } + return s^; +} +@builtin +excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ &~= other; + return s^; +} + +@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}; +@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}; + + +@builtin +card :: proc(s: $S/bit_set[$E; $U]) -> int { + when size_of(S) == 1 { + foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- } + return int(count_ones(transmute(u8)s)); + } else when size_of(S) == 2 { + foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- } + return int(count_ones(transmute(u16)s)); + } else when size_of(S) == 4 { + foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- } + return int(count_ones(transmute(u32)s)); + } else when size_of(S) == 8 { + foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- } + return int(count_ones(transmute(u64)s)); + } else when size_of(S) == 16 { + foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- } + return int(count_ones(transmute(u128)s)); + } else { + #panic("Unhandled card bit_set size"); + } +} + + + +@builtin +raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { + return (^E)(a); +} +@builtin +raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E { + ptr := (transmute(Raw_Slice)s).data; + return (^E)(ptr); +} +@builtin +raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E { + ptr := (transmute(Raw_Dynamic_Array)s).data; + return (^E)(ptr); +} +@builtin +raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 { + return (transmute(Raw_String)s).data; +} + +@builtin +raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}; + + + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +assert :: proc(condition: bool, message := "", loc := #caller_location) { + if !condition { + proc(message: string, loc: Source_Code_Location) { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("runtime assertion", message, loc); + }(message, loc); + } +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +panic :: proc(message: string, loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("panic", message, loc); +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +unimplemented :: proc(message := "", loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("not yet implemented", message, loc); +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +unreachable :: proc(message := "", loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + if message != "" { + p("internal error", message, loc); + } else { + p("internal error", "entered unreachable code", loc); + } +} diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin new file mode 100644 index 000000000..55289bbe4 --- /dev/null +++ b/core/runtime/dynamic_array_internal.odin @@ -0,0 +1,100 @@ +package runtime + +__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) { + array := (^Raw_Dynamic_Array)(array_); + array.allocator = context.allocator; + assert(array.allocator.procedure != nil); + + if cap > 0 { + __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc); + array.len = len; + } +} + +__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool { + array := (^Raw_Dynamic_Array)(array_); + + // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written + // assuming that appending/reserving will set the allocator, if it is not already set. + if array.allocator.procedure == nil { + array.allocator = context.allocator; + } + assert(array.allocator.procedure != nil); + + if cap <= array.cap { + return true; + } + + old_size := array.cap * elem_size; + new_size := cap * elem_size; + allocator := array.allocator; + + new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc); + if new_data != nil || elem_size == 0 { + array.data = new_data; + array.cap = cap; + return true; + } + return false; +} + +__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { + array := (^Raw_Dynamic_Array)(array_); + + ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc); + if ok { + array.len = len; + } + return ok; +} + + +__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, + items: rawptr, item_count: int, loc := #caller_location) -> int { + array := (^Raw_Dynamic_Array)(array_); + + if items == nil { + return 0; + } + if item_count <= 0 { + return 0; + } + + + ok := true; + if array.cap <= array.len+item_count { + cap := 2 * array.cap + max(8, item_count); + ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); + } + // TODO(bill): Better error handling for failed reservation + if !ok { + return array.len; + } + + assert(array.data != nil); + data := uintptr(array.data) + uintptr(elem_size*array.len); + + mem_copy(rawptr(data), items, elem_size * item_count); + array.len += item_count; + return array.len; +} + +__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int { + array := (^Raw_Dynamic_Array)(array_); + + ok := true; + if array.cap <= array.len+1 { + cap := 2 * array.cap + max(8, 1); + ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); + } + // TODO(bill): Better error handling for failed reservation + if !ok { + return array.len; + } + + assert(array.data != nil); + data := uintptr(array.data) + uintptr(elem_size*array.len); + mem_zero(rawptr(data), elem_size); + array.len += 1; + return array.len; +} diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin new file mode 100644 index 000000000..b4148e74e --- /dev/null +++ b/core/runtime/dynamic_map_internal.odin @@ -0,0 +1,303 @@ +package runtime + +import "intrinsics" +_ :: intrinsics; + +__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { + header := Map_Header{m = (^Raw_Map)(m)}; + Entry :: struct { + key: Map_Key, + next: int, + value: V, + }; + + header.is_key_string = intrinsics.type_is_string(K); + header.entry_size = int(size_of(Entry)); + header.entry_align = int(align_of(Entry)); + header.value_offset = uintptr(offset_of(Entry, value)); + header.value_size = int(size_of(V)); + return header; +} + +__get_map_key :: proc "contextless" (k: $K) -> Map_Key { + key := k; + map_key: Map_Key; + + T :: intrinsics.type_core_type(K); + + when intrinsics.type_is_integer(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + + sz :: 8*size_of(T); + when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); } + else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); } + else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } + else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } + else { #panic("Unhandled integer size"); } + } else when intrinsics.type_is_rune(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + map_key.key.val = u64((^rune)(&key)^); + } else when intrinsics.type_is_pointer(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + map_key.key.val = u64(uintptr((^rawptr)(&key)^)); + } else when intrinsics.type_is_float(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + + sz :: 8*size_of(T); + when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } + else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } + else { #panic("Unhandled float size"); } + } else when intrinsics.type_is_string(T) { + #assert(T == string); + str := (^string)(&key)^; + map_key.hash = default_hash_string(str); + map_key.key.str = str; + } else { + #panic("Unhandled map key type"); + } + + return map_key; +} + +_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { + h: u64 = seed; + for b in data { + h = (h ~ u64(b)) * 0x100000001b3; + } + return h; +} + + +default_hash :: inline proc "contextless" (data: []byte) -> u64 { + return _fnv64a(data); +} +default_hash_string :: inline proc "contextless" (s: string) -> u64 { + return default_hash(transmute([]byte)(s)); +} +default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 { + s := Raw_Slice{data, size}; + return default_hash(transmute([]byte)(s)); +} + + +source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { + hash := _fnv64a(transmute([]byte)s.file_path); + hash = hash ~ (u64(s.line) * 0x100000001b3); + hash = hash ~ (u64(s.column) * 0x100000001b3); + return hash; +} + + + +__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { + array := (^Raw_Slice)(array_); + + if new_count < array.len { + return true; + } + + assert(allocator.procedure != nil); + + old_size := array.len*size_of(T); + new_size := new_count*size_of(T); + + new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc); + if new_data == nil { + return false; + } + array.data = new_data; + array.len = new_count; + return true; +} + +__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) { + __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc); + + old_len := len(m.hashes); + __slice_resize(&m.hashes, cap, m.entries.allocator, loc); + for i in old_len.. rawptr { + index := __dynamic_map_find(h, key).entry_index; + if index >= 0 { + data := uintptr(__dynamic_map_get_entry(h, index)); + return rawptr(data + h.value_offset); + } + return nil; +} + +__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check { + index: int; + assert(value != nil); + + if len(h.m.hashes) == 0 { + __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc); + __dynamic_map_grow(h, loc); + } + + fr := __dynamic_map_find(h, key); + if fr.entry_index >= 0 { + index = fr.entry_index; + } else { + index = __dynamic_map_add_entry(h, key, loc); + if fr.entry_prev >= 0 { + entry := __dynamic_map_get_entry(h, fr.entry_prev); + entry.next = index; + } else { + h.m.hashes[fr.hash_index] = index; + } + } + { + e := __dynamic_map_get_entry(h, index); + e.key = key; + val := (^byte)(uintptr(e) + h.value_offset); + mem_copy(val, value, h.value_size); + } + + if __dynamic_map_full(h) { + __dynamic_map_grow(h, loc); + } +} + + +__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { + // TODO(bill): Determine an efficient growing rate + new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP); + __dynamic_map_rehash(h, new_count, loc); +} + +__dynamic_map_full :: inline proc(using h: Map_Header) -> bool { + return int(0.75 * f64(len(m.hashes))) <= m.entries.cap; +} + + +__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool { + if a.hash == b.hash { + if h.is_key_string { + return a.key.str == b.key.str; + } else { + return a.key.val == b.key.val; + } + return true; + } + return false; +} + +__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check { + fr := Map_Find_Result{-1, -1, -1}; + if n := u64(len(m.hashes)); n > 0 { + fr.hash_index = int(key.hash % n); + fr.entry_index = m.hashes[fr.hash_index]; + for fr.entry_index >= 0 { + entry := __dynamic_map_get_entry(h, fr.entry_index); + if __dynamic_map_hash_equal(h, entry.key, key) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = entry.next; + } + } + return fr; +} + +__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int { + prev := m.entries.len; + c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); + if c != prev { + end := __dynamic_map_get_entry(h, c-1); + end.key = key; + end.next = -1; + } + return prev; +} + +__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) { + fr := __dynamic_map_find(h, key); + if fr.entry_index >= 0 { + __dynamic_map_erase(h, fr); + } +} + +__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { + assert(0 <= index && index < m.entries.len); + return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)); +} + +__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { + if fr.entry_prev < 0 { + m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next; + } else { + prev := __dynamic_map_get_entry(h, fr.entry_prev); + curr := __dynamic_map_get_entry(h, fr.entry_index); + prev.next = curr.next; + } + if (fr.entry_index == m.entries.len-1) { + // NOTE(bill): No need to do anything else, just pop + } else { + old := __dynamic_map_get_entry(h, fr.entry_index); + end := __dynamic_map_get_entry(h, m.entries.len-1); + mem_copy(old, end, entry_size); + + if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 { + 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; + } + } + + // TODO(bill): Is this correct behaviour? + m.entries.len -= 1; +} From 87956676f52a56db98e263d1074b490b71141f0b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 20 Nov 2020 16:01:16 +0000 Subject: [PATCH 075/170] Add %s support for arrays and slices of bytes --- core/fmt/fmt.odin | 25 ++++++++++++++++--------- core/reflect/types.odin | 8 ++++++++ core/runtime/core_builtin.odin | 16 +++++++++++++++- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 7eca5f503..fae380de5 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1599,13 +1599,18 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Array: - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); - for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if verb == 's' && reflect.is_byte(info.elem) { + s := strings.string_from_ptr((^byte)(v.data), info.count); + fmt_string(fi, s, verb); + } else { + strings.write_byte(fi.buf, '['); + defer strings.write_byte(fi.buf, ']'); + for i in 0.. 0 { strings.write_string(fi.buf, ", "); } - data := uintptr(v.data) + uintptr(i*info.elem_size); - fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); + data := uintptr(v.data) + uintptr(i*info.elem_size); + fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); + } } case runtime.Type_Info_Enumerated_Array: @@ -1658,13 +1663,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Slice: - if verb == 'p' { - slice := cast(^mem.Raw_Slice)v.data; + slice := cast(^mem.Raw_Slice)v.data; + if verb == 's' && reflect.is_byte(info.elem) { + s := strings.string_from_ptr((^byte)(slice.data), slice.len); + fmt_string(fi, s, verb); + } else if verb == 'p' { fmt_pointer(fi, slice.data, 'p'); } else { strings.write_byte(fi.buf, '['); defer strings.write_byte(fi.buf, ']'); - slice := cast(^mem.Raw_Slice)v.data; for i in 0.. 0 { strings.write_string(fi.buf, ", "); } diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 91de8ee0b..348d516b4 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -218,6 +218,14 @@ is_unsigned :: proc(info: ^Type_Info) -> bool { return false; } +is_byte :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + #partial switch i in type_info_base(info).variant { + case Type_Info_Integer: return info.size == 1; + } + return false; +} + is_integer :: proc(info: ^Type_Info) -> bool { if info == nil { return false; } diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 0d430345e..35d082ac9 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -160,6 +160,8 @@ delete :: proc{ }; +// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value +// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator @builtin new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T { ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); @@ -222,6 +224,10 @@ make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := con return m; } +// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only) +// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the +// type of its argument, not a pointer to it. +// Make uses the specified allocator, default is context.allocator, default is context.allocator @builtin make :: proc{ make_slice, @@ -253,6 +259,8 @@ reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { } } +// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map. +// If m is nil, or there is no such element, this procedure is a no-op @builtin delete_key :: proc(m: ^$T/map[$K]$V, key: K) { if m != nil { @@ -286,6 +294,7 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { a.len += arg_len; } } + @builtin append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) { if array == nil { @@ -313,6 +322,8 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) a.len += arg_len; } } + +// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type @builtin append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) { args := transmute([]E)arg; @@ -523,6 +534,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l } } +// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type @builtin append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) { for arg in args { @@ -530,8 +542,10 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_ } } - +// The append built-in procedure appends elements to the end of a dynamic array @builtin append :: proc{append_elem, append_elems, append_elem_string}; + +// The append_soa built-in procedure appends elements to the end of an #soa dynamic array @builtin append_soa :: proc{append_soa_elem, append_soa_elems}; @builtin From 6416a6f39cce4c65c80e29bb4ff4b93a3e463947 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 20 Nov 2020 16:01:59 +0000 Subject: [PATCH 076/170] Allow string literals for `[N]byte` --- src/check_expr.cpp | 6 ++++++ src/ir.cpp | 4 +++- src/ir_print.cpp | 10 +++++++--- src/llvm_backend.cpp | 10 +++++++++- src/parser.hpp | 4 ++-- src/types.cpp | 7 +++++++ 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index aa3d67dae..f258253f5 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2955,6 +2955,12 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { if (check_is_assignable_to(c, operand, elem)) { operand->mode = Addressing_Value; } else { + if (operand->value.kind == ExactValue_String && is_type_u8_array(t)) { + String s = operand->value.value_string; + if (s.len == t->Array.count) { + break; + } + } operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); return; diff --git a/src/ir.cpp b/src/ir.cpp index 819442030..e8378bb97 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7801,7 +7801,9 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { if (tv.value.kind != ExactValue_Invalid) { // NOTE(bill): Edge case - if (tv.value.kind != ExactValue_Compound && + if (is_type_u8_array(tv.type) && tv.value.kind == ExactValue_String) { + return ir_add_module_constant(proc->module, tv.type, tv.value); + } else if (tv.value.kind != ExactValue_Compound && is_type_array(tv.type)) { Type *elem = core_array_type(tv.type); ExactValue value = convert_exact_value_for_type(tv.value, elem); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 0605058ee..3ca9954ef 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -745,7 +745,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, ']'); return; - } else if (is_type_array(type) && + } else if (is_type_array(type) && value.kind != ExactValue_Invalid && value.kind != ExactValue_String && value.kind != ExactValue_Compound) { @@ -796,7 +796,11 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * GB_ASSERT(is_type_array(type)); ir_write_str_lit(f, "c\""); ir_print_escape_string(f, str, false, false); - ir_write_str_lit(f, "\\00\""); + if (type->Array.count == str.len) { + ir_write_str_lit(f, "\""); + } else { + ir_write_str_lit(f, "\\00\""); + } } else if (is_type_cstring(t)) { // HACK NOTE(bill): This is a hack but it works because strings are created at the very end // of the .ll file @@ -810,7 +814,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_str_lit(f, ", "); ir_print_type(f, m, t_i32); ir_write_str_lit(f, " 0, i32 0)"); - }else { + } else { // HACK NOTE(bill): This is a hack but it works because strings are created at the very end // of the .ll file irValue *str_array = ir_add_global_string_array(m, str); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ea5081be4..d47c45401 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5126,7 +5126,15 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)value.value_string.text, cast(unsigned)value.value_string.len, - false); + false /*DontNullTerminate*/); + res.value = data; + return res; + } else if (is_type_u8_array(type) && value.kind == ExactValue_String) { + GB_ASSERT(type->Array.count == value.value_string.len); + LLVMValueRef data = LLVMConstStringInContext(ctx, + cast(char const *)value.value_string.text, + cast(unsigned)value.value_string.len, + true /*DontNullTerminate*/); res.value = data; return res; } else if (is_type_array(type) && diff --git a/src/parser.hpp b/src/parser.hpp index aa288304e..cd0d59a48 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -646,7 +646,7 @@ struct AstCommonStuff { u16 viral_state_flags; AstFile * file; Scope * scope; - TypeAndValue tav; + TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size }; struct Ast { @@ -655,7 +655,7 @@ struct Ast { u16 viral_state_flags; AstFile * file; Scope * scope; - TypeAndValue tav; + TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size // IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant union { diff --git a/src/types.cpp b/src/types.cpp index 1147beb33..8c39b9979 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1221,6 +1221,13 @@ bool is_type_u8_slice(Type *t) { } return false; } +bool is_type_u8_array(Type *t) { + t = base_type(t); + if (t->kind == Type_Array) { + return is_type_u8(t->Array.elem); + } + return false; +} bool is_type_u8_ptr(Type *t) { t = base_type(t); if (t->kind == Type_Pointer) { From 63e4a2341f1409eec1f2e58036cd01b24b66b8f0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 20 Nov 2020 16:24:23 +0000 Subject: [PATCH 077/170] Support string literals for fixed arrays of runes; Add %q support for arrays/slices of bytes --- core/fmt/fmt.odin | 4 ++-- src/check_expr.cpp | 13 ++++++++--- src/ir.cpp | 8 ++++--- src/ir_print.cpp | 22 +++++++++++++++++++ src/llvm_backend.cpp | 26 ++++++++++++++++++++++ src/types.cpp | 52 ++++++++++++++++++++++++++------------------ 6 files changed, 96 insertions(+), 29 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index fae380de5..79c62cc17 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1599,7 +1599,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Array: - if verb == 's' && reflect.is_byte(info.elem) { + if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) { s := strings.string_from_ptr((^byte)(v.data), info.count); fmt_string(fi, s, verb); } else { @@ -1664,7 +1664,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Slice: slice := cast(^mem.Raw_Slice)v.data; - if verb == 's' && reflect.is_byte(info.elem) { + if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) { s := strings.string_from_ptr((^byte)(slice.data), slice.len); fmt_string(fi, s, verb); } else if verb == 'p' { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f258253f5..c67ad22b8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2955,10 +2955,17 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { if (check_is_assignable_to(c, operand, elem)) { operand->mode = Addressing_Value; } else { - if (operand->value.kind == ExactValue_String && is_type_u8_array(t)) { + if (operand->value.kind == ExactValue_String) { String s = operand->value.value_string; - if (s.len == t->Array.count) { - break; + if (is_type_u8_array(t)) { + if (s.len == t->Array.count) { + break; + } + } else if (is_type_rune_array(t)) { + isize rune_count = s.len; + if (rune_count == t->Array.count) { + break; + } } } operand->mode = Addressing_Invalid; diff --git a/src/ir.cpp b/src/ir.cpp index e8378bb97..24fe15afb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7801,9 +7801,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { if (tv.value.kind != ExactValue_Invalid) { // NOTE(bill): Edge case - if (is_type_u8_array(tv.type) && tv.value.kind == ExactValue_String) { - return ir_add_module_constant(proc->module, tv.type, tv.value); - } else if (tv.value.kind != ExactValue_Compound && + if (is_type_u8_array(tv.type) && tv.value.kind == ExactValue_String) { + return ir_add_module_constant(proc->module, tv.type, tv.value); + } else if (is_type_rune_array(tv.type) && tv.value.kind == ExactValue_String) { + return ir_add_module_constant(proc->module, tv.type, tv.value); + } else if (tv.value.kind != ExactValue_Compound && is_type_array(tv.type)) { Type *elem = core_array_type(tv.type); ExactValue value = convert_exact_value_for_type(tv.value, elem); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 3ca9954ef..a6bfc75d3 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -734,6 +734,28 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) { i64 count = type->Array.count; Type *elem = type->Array.elem; + + if (is_type_rune_array(type)) { + Rune rune; + isize offset = 0; + isize width = 1; + String s = value.value_string; + ir_write_byte(f, '['); + for (i64 i = 0; i < count && offset < s.len; i++) { + width = gb_utf8_decode(s.text+offset, s.len-offset, &rune); + if (i > 0) ir_write_str_lit(f, ", "); + ir_print_type(f, m, elem); + ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_i64(rune), elem); + offset += width; + } + GB_ASSERT(offset == s.len); + + ir_write_byte(f, ']'); + return; + } + + ir_write_byte(f, '['); for (i64 i = 0; i < count; i++) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d47c45401..62a0013bc 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5123,6 +5123,32 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } } else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) { + if (is_type_rune_array(type) && value.kind == ExactValue_String) { + i64 count = type->Array.count; + Type *elem = type->Array.elem; + LLVMTypeRef et = lb_type(m, elem); + + Rune rune; + isize offset = 0; + isize width = 1; + String s = value.value_string; + + LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); + + for (i64 i = 0; i < count && offset < s.len; i++) { + width = gb_utf8_decode(s.text+offset, s.len-offset, &rune); + offset += width; + + elems[i] = LLVMConstInt(et, rune, true); + + } + GB_ASSERT(offset == s.len); + + res.value = LLVMConstArray(et, elems, cast(unsigned)count); + return res; + } + GB_PANIC("HERE!\n"); + LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)value.value_string.text, cast(unsigned)value.value_string.len, diff --git a/src/types.cpp b/src/types.cpp index 8c39b9979..b381ba9c9 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1214,27 +1214,6 @@ bool is_type_slice(Type *t) { t = base_type(t); return t->kind == Type_Slice; } -bool is_type_u8_slice(Type *t) { - t = base_type(t); - if (t->kind == Type_Slice) { - return is_type_u8(t->Slice.elem); - } - return false; -} -bool is_type_u8_array(Type *t) { - t = base_type(t); - if (t->kind == Type_Array) { - return is_type_u8(t->Array.elem); - } - return false; -} -bool is_type_u8_ptr(Type *t) { - t = base_type(t); - if (t->kind == Type_Pointer) { - return is_type_u8(t->Slice.elem); - } - return false; -} bool is_type_proc(Type *t) { t = base_type(t); return t->kind == Type_Proc; @@ -1278,6 +1257,37 @@ bool is_type_relative_slice(Type *t) { return t->kind == Type_RelativeSlice; } +bool is_type_u8_slice(Type *t) { + t = base_type(t); + if (t->kind == Type_Slice) { + return is_type_u8(t->Slice.elem); + } + return false; +} +bool is_type_u8_array(Type *t) { + t = base_type(t); + if (t->kind == Type_Array) { + return is_type_u8(t->Array.elem); + } + return false; +} +bool is_type_u8_ptr(Type *t) { + t = base_type(t); + if (t->kind == Type_Pointer) { + return is_type_u8(t->Slice.elem); + } + return false; +} +bool is_type_rune_array(Type *t) { + t = base_type(t); + if (t->kind == Type_Array) { + return is_type_rune(t->Array.elem); + } + return false; +} + + + Type *core_array_type(Type *t) { for (;;) { From a14ea5b2b5068e679a961d0fd43e84eea593e2da Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 20 Nov 2020 16:25:11 +0000 Subject: [PATCH 078/170] Add support for %s and %q for [dynamic]byte --- core/fmt/fmt.odin | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 79c62cc17..ec664a2a9 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1633,13 +1633,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Dynamic_Array: - if verb == 'p' { - slice := cast(^mem.Raw_Dynamic_Array)v.data; - fmt_pointer(fi, slice.data, 'p'); + array := cast(^mem.Raw_Dynamic_Array)v.data; + if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) { + s := strings.string_from_ptr((^byte)(array.data), array.len); + fmt_string(fi, s, verb); + } else if verb == 'p' { + fmt_pointer(fi, array.data, 'p'); } else { strings.write_byte(fi.buf, '['); defer strings.write_byte(fi.buf, ']'); - array := cast(^mem.Raw_Dynamic_Array)v.data; for i in 0.. 0 { strings.write_string(fi.buf, ", "); } From 260e28c0af2130a78862a5e253b98178fb480f02 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 20 Nov 2020 16:30:34 +0000 Subject: [PATCH 079/170] Fix casting of untyped strings --- src/check_expr.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c67ad22b8..24676b3ce 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2161,6 +2161,17 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_constant && is_type_untyped(src) && is_type_string(src)) { + if (is_type_u8_array(dst)) { + String s = operand->value.value_string; + return s.len == dst->Array.count; + } + if (is_type_rune_array(dst)) { + String s = operand->value.value_string; + return gb_utf8_strnlen(s.text, s.len) == dst->Array.count; + } + } + if (dst->kind == Type_Array && src->kind == Type_Array) { if (are_types_identical(dst->Array.elem, src->Array.elem)) { @@ -2962,7 +2973,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { break; } } else if (is_type_rune_array(t)) { - isize rune_count = s.len; + isize rune_count = gb_utf8_strnlen(s.text, s.len); if (rune_count == t->Array.count) { break; } From 58f768cb4f065a59e8d409ea99c212a58305204d Mon Sep 17 00:00:00 2001 From: Dan Bechard Date: Fri, 20 Nov 2020 15:39:08 -0800 Subject: [PATCH 080/170] Update kernel32.odin Fix typo in CreateProcessA/W mappings (https://github.com/odin-lang/Odin/issues/785) --- core/sys/win32/kernel32.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin index d5cefad94..ba3898730 100644 --- a/core/sys/win32/kernel32.odin +++ b/core/sys/win32/kernel32.odin @@ -8,12 +8,12 @@ foreign kernel32 { @(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring, process_attributes, thread_attributes: ^Security_Attributes, inherit_handle: Bool, creation_flags: u32, environment: rawptr, - current_direcotry: cstring, startup_info: ^Startup_Info, + current_directory: cstring, startup_info: ^Startup_Info, process_information: ^Process_Information) -> Bool ---; @(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring, process_attributes, thread_attributes: ^Security_Attributes, inherit_handle: Bool, creation_flags: u32, environment: rawptr, - current_direcotry: Wstring, startup_info: ^Startup_Info, + current_directory: Wstring, startup_info: ^Startup_Info, process_information: ^Process_Information) -> Bool ---; @(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---; @(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---; From 2d878de84d99e3129d51150d24020c6366f4403d Mon Sep 17 00:00:00 2001 From: Dan Bechard Date: Fri, 20 Nov 2020 15:43:51 -0800 Subject: [PATCH 081/170] Update check_expr.cpp Fixed typo in error message `procedure all` -> `procedure call` --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 24676b3ce..4b17b4f27 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7659,7 +7659,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr mix = arg->kind == Ast_FieldValue; } if (mix) { - error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed"); + error(arg, "Mixture of 'field = value' and value elements in a procedure call is not allowed"); fail = true; } } From fa50c8d7d3dc83642d10df255deabc73b75a17cf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 21 Nov 2020 11:40:15 +0000 Subject: [PATCH 082/170] Add `ODIN_TEST` constant for checking if `odin test` is being run --- src/checker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/checker.cpp b/src/checker.cpp index 3339b6bfc..0559fd300 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -744,6 +744,7 @@ void init_universal(void) { add_global_constant(str_lit("ODIN_DEFAULT_TO_NIL_ALLOCATOR"), t_untyped_bool, exact_value_bool(bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR)); add_global_constant(str_lit("ODIN_USE_LLVM_API"), t_untyped_bool, exact_value_bool(bc->use_llvm_api)); add_global_constant(str_lit("ODIN_NO_DYNAMIC_LITERALS"), t_untyped_bool, exact_value_bool(bc->no_dynamic_literals)); + add_global_constant(str_lit("ODIN_TEST"), t_untyped_bool, exact_value_bool(bc->command_kind == Command_test)); // Builtin Procedures From 740411f20713d2533073db4bc6a14f63a5f2a3db Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 21 Nov 2020 23:14:07 +0000 Subject: [PATCH 083/170] Minor change to unicode/letter.odin --- core/unicode/letter.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index c31d34dab..b498e4272 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -60,7 +60,7 @@ to_title :: proc(r: rune) -> rune { is_lower :: proc(r: rune) -> bool { if r <= MAX_ASCII { - return u8(r)-'a' < 26; + return u32(r)-'a' < 26; } c := i32(r); p := binary_search(c, to_upper_ranges[:], len(to_upper_ranges)/3, 3); @@ -76,7 +76,7 @@ is_lower :: proc(r: rune) -> bool { is_upper :: proc(r: rune) -> bool { if r <= MAX_ASCII { - return u8(r)-'A' < 26; + return u32(r)-'A' < 26; } c := i32(r); p := binary_search(c, to_lower_ranges[:], len(to_lower_ranges)/3, 3); From 5cc9ddd40b9fc669c19613e0c35a95b7bc75b96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Sun, 22 Nov 2020 02:49:19 +0100 Subject: [PATCH 084/170] Update nightly.yml --- .github/workflows/nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1648169e1..1b6d82eaf 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -51,7 +51,7 @@ jobs: - name: (Linux) Download LLVM run: sudo apt-get install llvm - name: build odin - run: make release + run: make nightly - name: Odin run run: ./odin run examples/demo/demo.odin - name: Copy artifacts @@ -77,7 +77,7 @@ jobs: TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - name: build odin - run: make release + run: make nightly - name: Odin run run: ./odin run examples/demo/demo.odin - name: Copy artifacts From a2461bdf6b2e5ab9db5ded4206857df74ba4d1cd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 22 Nov 2020 21:38:45 +0000 Subject: [PATCH 085/170] Modify llvm_abi.cpp to work correctly for win64 abi of `i128` types. (it's a pain) --- src/check_type.cpp | 6 +- src/llvm_abi.cpp | 62 +++++---- src/llvm_backend.cpp | 307 +++++++++++++++++++++++-------------------- src/llvm_backend.hpp | 1 + 4 files changed, 209 insertions(+), 167 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 6ea17eca6..dfd0f093d 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2279,7 +2279,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall return new_type; } if (build_context.ODIN_ARCH == "amd64") { - if (is_type_integer_128bit(original_type)) { + bool is_128 = is_type_integer_128bit(original_type); + if (!is_128 && is_type_bit_set(original_type) && type_size_of(original_type) == 16) { + // is_128 = true; + } + if (is_128) { if (build_context.ODIN_OS == "windows") { return alloc_type_simd_vector(2, t_u64); } else { diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 9722bf302..258b90eb5 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -40,6 +40,9 @@ i64 llvm_align_formula(i64 off, i64 a) { bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) { + if (type == nullptr) { + return false; + } return LLVMGetTypeKind(type) == kind; } @@ -124,6 +127,7 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa if (offset != 0 && ft->ret.kind == lbArg_Indirect && ft->ret.attribute != nullptr) { LLVMAddAttributeAtIndex(fn, offset, ft->ret.attribute); + LLVMAddAttributeAtIndex(fn, offset, noalias_attr); } lbCallingConventionKind cc_kind = lbCallingConvention_C; @@ -313,7 +317,14 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type return t; } -Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { +#if 0 +Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) { + Type **found = map_get(&m->llvm_types, hash_pointer(type)); + if (found) { + return *found; + } + GB_ASSERT_MSG(level < 64, "%s %d", LLVMPrintTypeToString(type), is_return); + LLVMTypeKind kind = LLVMGetTypeKind(type); switch (kind) { case LLVMVoidTypeKind: @@ -351,16 +362,16 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { defer (gb_free(heap_allocator(), param_types)); for (unsigned i = 0; i < param_count; i++) { - param_types[i] = lb_abi_to_odin_type(params[i], false, /*level*/0); + param_types[i] = lb_abi_to_odin_type(m, params[i], false, level+1); } LLVMTypeRef ret = LLVMGetReturnType(elem); - Type *ret_type = lb_abi_to_odin_type(ret, true, /*level*/0); + Type *ret_type = lb_abi_to_odin_type(m, ret, true, level+1); bool is_c_vararg = !!LLVMIsFunctionVarArg(elem); return alloc_type_proc_from_types(param_types, param_count, ret_type, is_c_vararg); } - return alloc_type_pointer(lb_abi_to_odin_type(elem, false, level)); + return alloc_type_pointer(lb_abi_to_odin_type(m, elem, false, level+1)); } case LLVMFunctionTypeKind: GB_PANIC("LLVMFunctionTypeKind should not be seen on its own"); @@ -371,7 +382,12 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { unsigned field_count = LLVMCountStructElementTypes(type); Type **fields = gb_alloc_array(heap_allocator(), Type *, field_count); for (unsigned i = 0; i < field_count; i++) { - fields[i] = lb_abi_to_odin_type(LLVMStructGetTypeAtIndex(type, i), false, level+1); + LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(type, i); + if (lb_is_type_kind(field_type, LLVMPointerTypeKind) && level > 0) { + fields[i] = t_rawptr; + } else { + fields[i] = lb_abi_to_odin_type(m, field_type, false, level+1); + } } if (is_return) { return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type), false); @@ -384,7 +400,7 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { { i64 count = LLVMGetArrayLength(type); - Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type), false, level+1); + Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1); return alloc_type_array(elem, count); } break; @@ -394,7 +410,7 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { case LLVMVectorTypeKind: { i64 count = LLVMGetVectorSize(type); - Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type), false, level+1); + Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1); return alloc_type_simd_vector(count, elem); } @@ -403,7 +419,7 @@ Type *lb_abi_to_odin_type(LLVMTypeRef type, bool is_return, u32 level = 0) { return 0; } - +#endif #define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention) @@ -424,22 +440,24 @@ namespace lbAbi386 { return ft; } - lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) { - if (build_context.metrics.os == TargetOs_windows && - build_context.word_size == 8 && - lb_is_type_kind(type, LLVMIntegerTypeKind) && - lb_sizeof(type) == 16) { + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) { + if (!is_return && lb_sizeof(type) > 8) { + return lb_arg_type_indirect(type, nullptr); + } + if (build_context.metrics.os == TargetOs_windows && + build_context.word_size == 8 && + lb_is_type_kind(type, LLVMIntegerTypeKind) && + type == LLVMIntTypeInContext(c, 128)) { + // NOTE(bill): Because Windows AMD64 is weird LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2); return lb_arg_type_direct(type, cast_type, nullptr, nullptr); } - - LLVMAttributeRef attr = nullptr; LLVMTypeRef i1 = LLVMInt1TypeInContext(c); if (type == i1) { - attr = lb_create_enum_attribute(c, "zeroext", true); + // attr = lb_create_enum_attribute(c, "zeroext", true); // return lb_arg_type_direct(type, i1, nullptr, attr); } return lb_arg_type_direct(type, nullptr, nullptr, attr); @@ -451,15 +469,15 @@ namespace lbAbi386 { for (unsigned i = 0; i < arg_count; i++) { LLVMTypeRef t = arg_types[i]; LLVMTypeKind kind = LLVMGetTypeKind(t); + i64 sz = lb_sizeof(t); if (kind == LLVMStructTypeKind) { - i64 sz = lb_sizeof(t); if (sz == 0) { args[i] = lb_arg_type_ignore(t); } else { args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true)); } } else { - args[i] = non_struct(c, t); + args[i] = non_struct(c, t, false); } } return args; @@ -478,7 +496,7 @@ namespace lbAbi386 { } return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true)); } - return non_struct(c, return_type); + return non_struct(c, return_type, true); } }; @@ -515,7 +533,7 @@ namespace lbAbiAmd64Win64 { break; } } else { - args[i] = lbAbi386::non_struct(c, t); + args[i] = lbAbi386::non_struct(c, t, false); } } return args; @@ -623,7 +641,7 @@ namespace lbAbiAmd64SysV { if (is_register(type)) { LLVMAttributeRef attribute = nullptr; if (type == LLVMInt1TypeInContext(c)) { - attribute = lb_create_enum_attribute(c, "zeroext", true); + // attribute = lb_create_enum_attribute(c, "zeroext", true); } return lb_arg_type_direct(type, nullptr, nullptr, attribute); } @@ -646,7 +664,7 @@ namespace lbAbiAmd64SysV { LLVMAttributeRef attr = nullptr; LLVMTypeRef i1 = LLVMInt1TypeInContext(c); if (type == i1) { - attr = lb_create_enum_attribute(c, "zeroext", true); + // attr = lb_create_enum_attribute(c, "zeroext", true); } return lb_arg_type_direct(type, nullptr, nullptr, attr); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 62a0013bc..89a0ff667 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1114,7 +1114,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return type; } - case Basic_typeid: return LLVMIntType(8*cast(unsigned)build_context.word_size); + case Basic_typeid: return LLVMIntTypeInContext(m->ctx, 8*cast(unsigned)build_context.word_size); // Endian Specific Types case Basic_i16le: return LLVMInt16TypeInContext(ctx); @@ -1353,130 +1353,144 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } case Type_Proc: - if (USE_LLVM_ABI) { - if (m->internal_type_level > 1) { - return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0); - } else { - unsigned param_count = 0; - if (type->Proc.calling_convention == ProcCC_Odin) { - param_count += 1; - } - - if (type->Proc.param_count != 0) { - GB_ASSERT(type->Proc.params->kind == Type_Tuple); - for_array(i, type->Proc.params->Tuple.variables) { - Entity *e = type->Proc.params->Tuple.variables[i]; - if (e->kind != Entity_Variable) { - continue; - } + { + if (USE_LLVM_ABI) { + if (m->internal_type_level > 5) { // TODO HACK(bill): is this really enough? + return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0); + } else { + unsigned param_count = 0; + if (type->Proc.calling_convention == ProcCC_Odin) { param_count += 1; } - } - LLVMTypeRef ret = nullptr; - LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, 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); - if (ret != nullptr) { - if (is_calling_convention_none(type->Proc.calling_convention) && - is_type_boolean(single_ret) && - type_size_of(single_ret) <= 1) { - ret = LLVMInt1TypeInContext(m->ctx); + if (type->Proc.param_count != 0) { + GB_ASSERT(type->Proc.params->kind == Type_Tuple); + for_array(i, type->Proc.params->Tuple.variables) { + Entity *e = type->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + param_count += 1; } } - } + m->internal_type_level += 1; + defer (m->internal_type_level -= 1); - isize param_index = 0; - if (type->Proc.param_count != 0) { - GB_ASSERT(type->Proc.params->kind == Type_Tuple); - for_array(i, type->Proc.params->Tuple.variables) { - Entity *e = type->Proc.params->Tuple.variables[i]; - if (e->kind != Entity_Variable) { + LLVMTypeRef ret = nullptr; + LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, 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); + if (ret != nullptr) { + if (is_type_boolean(single_ret) && + is_calling_convention_none(type->Proc.calling_convention) && + type_size_of(single_ret) <= 1) { + ret = LLVMInt1TypeInContext(m->ctx); + } + } + } + + isize param_index = 0; + if (type->Proc.param_count != 0) { + GB_ASSERT(type->Proc.params->kind == Type_Tuple); + for_array(i, type->Proc.params->Tuple.variables) { + Entity *e = type->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + + Type *e_type = reduce_tuple_to_single_type(e->type); + + LLVMTypeRef param_type = nullptr; + if (is_type_boolean(e_type) && + type_size_of(e_type) <= 1) { + param_type = LLVMInt1TypeInContext(m->ctx); + } else { + param_type = lb_type(m, e_type); + } + params[param_index++] = param_type; + } + } + if (param_index < param_count) { + params[param_index++] = lb_type(m, t_context_ptr); + } + GB_ASSERT(param_index == param_count); + + + lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention); + map_set(&m->function_type_map, hash_type(type), ft); + LLVMTypeRef new_abi_fn_ptr_type = lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg); + LLVMTypeRef new_abi_fn_type = LLVMGetElementType(new_abi_fn_ptr_type); + + // LLVMTypeRef new_ret = LLVMGetReturnType(new_abi_fn_type); + // LLVMTypeRef old_ret = LLVMGetReturnType(old_abi_fn_type); + // unsigned new_count = LLVMCountParamTypes(new_abi_fn_type); + // unsigned old_count = LLVMCountParamTypes(old_abi_fn_type); + // GB_ASSERT_MSG(new_count == old_count, "%u %u, %s %s", new_count, old_count, LLVMPrintTypeToString(new_abi_fn_type), LLVMPrintTypeToString(old_abi_fn_type)); + return new_abi_fn_ptr_type; + } + } else { + LLVMTypeRef old_abi_fn_type = nullptr; + + set_procedure_abi_types(type); + LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx); + if (type->Proc.return_by_pointer) { + // Void + } else if (type->Proc.abi_compat_result_type != nullptr) { + return_type = lb_type(m, type->Proc.abi_compat_result_type); + } + + isize extra_param_count = 0; + if (type->Proc.return_by_pointer) { + extra_param_count += 1; + } + if (type->Proc.calling_convention == ProcCC_Odin) { + extra_param_count += 1; + } + + isize param_count = type->Proc.abi_compat_params.count + extra_param_count; + auto param_types = array_make(temporary_allocator(), 0, param_count); + + if (type->Proc.return_by_pointer) { + array_add(¶m_types, LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0)); + } + + for_array(i, type->Proc.abi_compat_params) { + Type *param = type->Proc.abi_compat_params[i]; + if (param == nullptr) { continue; } - - Type *e_type = reduce_tuple_to_single_type(e->type); - - LLVMTypeRef param_type = nullptr; - if (is_calling_convention_none(type->Proc.calling_convention) && - is_type_boolean(e_type) && - type_size_of(e_type) <= 1) { - param_type = LLVMInt1TypeInContext(m->ctx); + if (type->Proc.params->Tuple.variables[i]->flags & EntityFlag_CVarArg) { + GB_ASSERT(i+1 == type->Proc.abi_compat_params.count); + break; + } + if (is_type_tuple(param)) { + param = base_type(param); + for_array(j, param->Tuple.variables) { + Entity *v = param->Tuple.variables[j]; + if (v->kind != Entity_Variable) { + // Sanity check + continue; + } + LLVMTypeRef t = lb_type(m, v->type); + array_add(¶m_types, t); + } } else { - param_type = lb_type(m, e_type); + array_add(¶m_types, lb_type(m, param)); } - params[param_index++] = param_type; } - } - if (param_index < param_count) { - params[param_index++] = lb_type(m, t_context_ptr); - } - GB_ASSERT(param_index == param_count); - - - lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention); - map_set(&m->function_type_map, hash_type(type), ft); - return lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg); - } - } else { - set_procedure_abi_types(type); - LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx); - if (type->Proc.return_by_pointer) { - // Void - } else if (type->Proc.abi_compat_result_type != nullptr) { - return_type = lb_type(m, type->Proc.abi_compat_result_type); - } - - isize extra_param_count = 0; - if (type->Proc.return_by_pointer) { - extra_param_count += 1; - } - if (type->Proc.calling_convention == ProcCC_Odin) { - extra_param_count += 1; - } - - isize param_count = type->Proc.abi_compat_params.count + extra_param_count; - auto param_types = array_make(temporary_allocator(), 0, param_count); - - if (type->Proc.return_by_pointer) { - array_add(¶m_types, LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0)); - } - - for_array(i, type->Proc.abi_compat_params) { - Type *param = type->Proc.abi_compat_params[i]; - if (param == nullptr) { - continue; - } - if (type->Proc.params->Tuple.variables[i]->flags & EntityFlag_CVarArg) { - GB_ASSERT(i+1 == type->Proc.abi_compat_params.count); - break; - } - if (is_type_tuple(param)) { - param = base_type(param); - for_array(j, param->Tuple.variables) { - Entity *v = param->Tuple.variables[j]; - if (v->kind != Entity_Variable) { - // Sanity check - continue; - } - LLVMTypeRef t = lb_type(m, v->type); - array_add(¶m_types, t); + if (type->Proc.calling_convention == ProcCC_Odin) { + array_add(¶m_types, lb_type(m, t_context_ptr)); } - } else { - array_add(¶m_types, lb_type(m, param)); - } - } - if (type->Proc.calling_convention == ProcCC_Odin) { - array_add(¶m_types, lb_type(m, t_context_ptr)); - } - LLVMTypeRef t = LLVMFunctionType(return_type, param_types.data, cast(unsigned)param_types.count, type->Proc.c_vararg); - return LLVMPointerType(t, 0); + old_abi_fn_type = LLVMFunctionType(return_type, param_types.data, cast(unsigned)param_types.count, type->Proc.c_vararg); + return LLVMPointerType(old_abi_fn_type, 0); + } } + break; case Type_BitFieldValue: - return LLVMIntType(type->BitFieldValue.bits); + return LLVMIntTypeInContext(m->ctx, type->BitFieldValue.bits); case Type_BitField: { @@ -1488,7 +1502,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { for_array(i, type->BitField.sizes) { u32 size = type->BitField.sizes[i]; - fields[i] = LLVMIntType(size); + fields[i] = LLVMIntTypeInContext(m->ctx, size); } internal_type = LLVMStructTypeInContext(ctx, fields, field_count, true); @@ -1507,7 +1521,11 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } break; case Type_BitSet: - return LLVMIntType(8*cast(unsigned)type_size_of(type)); + { + Type *ut = bit_set_to_int(type); + return lb_type(m, ut); + } + case Type_SimdVector: if (type->SimdVector.is_x86_mmx) { return LLVMX86MMXTypeInContext(ctx); @@ -1548,6 +1566,9 @@ LLVMTypeRef lb_type(lbModule *m, Type *type) { m->internal_type_level -= 1; if (USE_LLVM_ABI && m->internal_type_level == 0) { map_set(&m->types, hash_type(type), llvm_type); + if (is_type_named(type)) { + map_set(&m->llvm_types, hash_pointer(llvm_type), type); + } } return llvm_type; } @@ -1989,7 +2010,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { break; case Type_BitFieldValue: return nullptr; - // return LLVMIntType(type->BitFieldValue.bits); + // return LLVMIntTypeInContext(m->ctx, type->BitFieldValue.bits); case Type_BitField: { @@ -2003,7 +2024,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { // for_array(i, type->BitField.sizes) { // u32 size = type->BitField.sizes[i]; - // fields[i] = LLVMIntType(size); + // fields[i] = LLVMIntTypeInContext(m->ctx, size); // } // internal_type = LLVMStructTypeInContext(ctx, fields, field_count, true); @@ -2023,7 +2044,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { break; case Type_BitSet: return nullptr; - // return LLVMIntType(8*cast(unsigned)type_size_of(type)); + // return LLVMIntTypeInContext(m->ctx, 8*cast(unsigned)type_size_of(type)); case Type_SimdVector: return nullptr; // if (type->SimdVector.is_x86_mmx) { @@ -2507,9 +2528,25 @@ void lb_start_block(lbProcedure *p, lbBlock *b) { } LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRef dst_type) { + LLVMContextRef ctx = p->module->ctx; + LLVMTypeRef src_type = LLVMTypeOf(val); + + if (src_type == dst_type) { + return val; + } + i64 src_size = lb_sizeof(src_type); i64 dst_size = lb_sizeof(dst_type); + + if (dst_type == LLVMInt1TypeInContext(ctx)) { + GB_ASSERT(lb_is_type_kind(src_type, LLVMIntegerTypeKind)); + return LLVMBuildICmp(p->builder, LLVMIntNE, val, LLVMConstNull(src_type), ""); + } else if (src_type == LLVMInt1TypeInContext(ctx)) { + GB_ASSERT(lb_is_type_kind(src_type, LLVMIntegerTypeKind)); + return LLVMBuildZExtOrBitCast(p->builder, val, dst_type, ""); + } + if (src_size != dst_size && (lb_is_type_kind(src_type, LLVMVectorTypeKind) ^ lb_is_type_kind(dst_type, LLVMVectorTypeKind))) { // Okay } else { @@ -2627,11 +2664,7 @@ void lb_begin_procedure_body(lbProcedure *p) { } LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index); - if (USE_LLVM_ABI && LLVMTypeOf(value) == LLVMInt1TypeInContext(p->module->ctx)) { - value = LLVMBuildZExtOrBitCast(p->builder, value, param_type, ""); - } else { - value = OdinLLVMBuildTransmute(p, value, param_type); - } + value = OdinLLVMBuildTransmute(p, value, param_type); lbValue param = {}; param.value = value; @@ -4554,9 +4587,11 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { LLVMBuildRetVoid(p->builder); } else { LLVMValueRef ret_val = res.value; + ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.type); if (p->abi_function_type->ret.cast_type != nullptr) { ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.cast_type); } + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); LLVMBuildRet(p->builder, ret_val); } @@ -7458,18 +7493,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, if (xt == abi_type) { array_add(&processed_args, x); } else { - Type *at = lb_abi_to_odin_type(abi_type, false); - if (at == t_llvm_bool) { - x = lb_emit_conv(p, x, at); - } else if (is_type_simd_vector(at) && lb_sizeof(abi_type) > lb_sizeof(xt)) { - lbAddr v = lb_add_local_generated(p, at, false); - lbValue ptr = lb_addr_get_ptr(p, v); - ptr = lb_emit_conv(p, ptr, alloc_type_pointer(x.type)); - lb_emit_store(p, ptr, x); - x = lb_addr_load(p, v); - } else { - x = lb_emit_transmute(p, x, at); - } + x.value = OdinLLVMBuildTransmute(p, x.value, abi_type); array_add(&processed_args, x); } @@ -7509,20 +7533,14 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); result = lb_emit_load(p, return_ptr); } else if (rt != nullptr) { - LLVMTypeRef ret_type = ft->ret.cast_type; - if (!ret_type) { - ret_type = ft->ret.type; + result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining); + if (ft->ret.cast_type) { + result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.cast_type); } - Type *abi_rt = lb_abi_to_odin_type(ret_type, true); - result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); - if (ret_type != lb_type(m, rt)) { - if (is_type_simd_vector(abi_rt) && lb_sizeof(ret_type) > type_size_of(rt)) { - lbValue ptr = lb_address_from_load_or_generate_local(p, result); - ptr = lb_emit_conv(p, ptr, alloc_type_pointer(rt)); - result = lb_emit_load(p, ptr); - } else { - result = lb_emit_transmute(p, result, rt); - } + result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.type); + result.type = rt; + if (LLVMTypeOf(result.value) == LLVMInt1TypeInContext(p->module->ctx)) { + result.type = t_llvm_bool; } if (!is_type_tuple(rt)) { result = lb_emit_conv(p, result, rt); @@ -11416,6 +11434,7 @@ void lb_init_module(lbModule *m, Checker *c) { gb_mutex_init(&m->mutex); gbAllocator a = heap_allocator(); map_init(&m->types, a); + map_init(&m->llvm_types, a); map_init(&m->values, a); string_map_init(&m->members, a); map_init(&m->procedure_values, a); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index fc5968c93..289f9d324 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -74,6 +74,7 @@ struct lbModule { gbMutex mutex; Map types; // Key: Type * + Map llvm_types; // Key: LLVMTypeRef i32 internal_type_level; Map values; // Key: Entity * From 4379917c7d37e9af3aebe239d8efa33fd575324a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 22 Nov 2020 21:52:39 +0000 Subject: [PATCH 086/170] Re-enable `zeroext` for `i1` --- src/llvm_abi.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 258b90eb5..7521ba2f7 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -457,8 +457,7 @@ namespace lbAbi386 { LLVMAttributeRef attr = nullptr; LLVMTypeRef i1 = LLVMInt1TypeInContext(c); if (type == i1) { - // attr = lb_create_enum_attribute(c, "zeroext", true); - // return lb_arg_type_direct(type, i1, nullptr, attr); + attr = lb_create_enum_attribute(c, "zeroext", true); } return lb_arg_type_direct(type, nullptr, nullptr, attr); } @@ -641,7 +640,7 @@ namespace lbAbiAmd64SysV { if (is_register(type)) { LLVMAttributeRef attribute = nullptr; if (type == LLVMInt1TypeInContext(c)) { - // attribute = lb_create_enum_attribute(c, "zeroext", true); + attribute = lb_create_enum_attribute(c, "zeroext", true); } return lb_arg_type_direct(type, nullptr, nullptr, attribute); } @@ -664,7 +663,7 @@ namespace lbAbiAmd64SysV { LLVMAttributeRef attr = nullptr; LLVMTypeRef i1 = LLVMInt1TypeInContext(c); if (type == i1) { - // attr = lb_create_enum_attribute(c, "zeroext", true); + attr = lb_create_enum_attribute(c, "zeroext", true); } return lb_arg_type_direct(type, nullptr, nullptr, attr); } From 9e42cb159543546ca549eeba6c943cf0f4c8410b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 12:20:04 +0000 Subject: [PATCH 087/170] Add comparisons to structs where all fields are comparable `==` and `!=` --- core/runtime/internal.odin | 5 +- src/checker.cpp | 1 + src/ir.cpp | 115 ++++++++++++++++++++++++++++++++++++- src/llvm_abi.cpp | 60 ++++--------------- src/llvm_backend.cpp | 98 +++++++++++++++++++++++++++++++ src/llvm_backend.hpp | 2 + src/types.cpp | 64 +++++++++++++++++++++ 7 files changed, 292 insertions(+), 53 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index cd0c681b3..c313070ac 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -180,8 +180,9 @@ mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = } return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc); } - - +memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool { + return memory_compare(a, b, n) == 0; +} memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check { x := uintptr(a); y := uintptr(b); diff --git a/src/checker.cpp b/src/checker.cpp index 0559fd300..1a5a75152 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1768,6 +1768,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("memcpy"), str_lit("memmove"), + str_lit("memory_equal"), str_lit("memory_compare"), str_lit("memory_compare_zero"), diff --git a/src/ir.cpp b/src/ir.cpp index 24fe15afb..2d244f62c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -24,6 +24,7 @@ struct irModule { Map entity_names; // Key: Entity * of the typename Map debug_info; // Key: Unique pointer Map anonymous_proc_lits; // Key: Ast * + Map compare_procs; // Key: Type * irDebugInfo * debug_compile_unit; Array debug_location_stack; @@ -161,6 +162,7 @@ struct irProcedure { Ast * return_ptr_hint_ast; bool return_ptr_hint_used; + bool ignore_dead_instr; Array branch_blocks; @@ -525,6 +527,8 @@ struct irAddr { Type *ir_type(irValue *value); irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr); +void ir_begin_procedure_body(irProcedure *proc); +void ir_end_procedure_body(irProcedure *proc); irAddr ir_addr(irValue *addr) { irAddr v = {irAddr_Default, addr}; @@ -4859,6 +4863,87 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue return nullptr; } +irValue *ir_get_compare_proc_for_type(irModule *m, Type *type) { + Type *original_type = type; + type = base_type(type); + GB_ASSERT(type->kind == Type_Struct); + type_set_offsets(type); + Type *pt = alloc_type_pointer(type); + + auto key = hash_type(type); + irValue **found = map_get(&m->compare_procs, key); + if (found) { + return *found; + } + static Type *proc_type = nullptr; + if (proc_type == nullptr) { + Type *args[2] = {t_rawptr, t_rawptr}; + proc_type = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless); + set_procedure_abi_types(proc_type); + } + + static u32 proc_index = 0; + + char buf[16] = {}; + isize n = gb_snprintf(buf, 16, "__$cmp%u", ++proc_index); + char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); + String proc_name = make_string_c(str); + + + Ast *body = alloc_ast_node(nullptr, Ast_Invalid); + Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), proc_type, 0); + e->Procedure.link_name = proc_name; + irValue *p = ir_value_procedure(m, e, proc_type, nullptr, body, proc_name); + map_set(&m->values, hash_entity(e), p); + string_map_set(&m->members, proc_name, p); + + irProcedure *proc = &p->Proc; + proc->is_startup = true; + proc->ignore_dead_instr = true; + ir_begin_procedure_body(proc); + // ir_start_block(proc, proc->decl_block); + GB_ASSERT(proc->curr_block != nullptr); + + irBlock *done = ir_new_block(proc, nullptr, "done"); // NOTE(bill): Append later + + irValue *x = proc->params[0]; + irValue *y = proc->params[1]; + irValue *lhs = ir_emit_conv(proc, x, pt); + irValue *rhs = ir_emit_conv(proc, y, pt); + + irBlock *block_false = ir_new_block(proc, nullptr, "bfalse"); + + for_array(i, type->Struct.fields) { + irBlock *next_block = ir_new_block(proc, nullptr, "btrue"); + + irValue *pleft = ir_emit_struct_ep(proc, lhs, cast(i32)i); + irValue *pright = ir_emit_struct_ep(proc, rhs, cast(i32)i); + irValue *left = ir_emit_load(proc, pleft); + irValue *right = ir_emit_load(proc, pright); + irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right); + + ir_emit_if(proc, ok, next_block, block_false); + + ir_emit_jump(proc, next_block); + ir_start_block(proc, next_block); + } + + ir_emit_jump(proc, done); + ir_start_block(proc, block_false); + + ir_emit(proc, ir_instr_return(proc, ir_const_bool(false))); + + ir_emit_jump(proc, done); + ir_start_block(proc, done); + ir_emit(proc, ir_instr_return(proc, ir_const_bool(true))); + + ir_end_procedure_body(proc); + + map_set(&m->compare_procs, key, p); + + return p; +} + irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) { Type *a = base_type(ir_type(left)); Type *b = base_type(ir_type(right)); @@ -4992,6 +5077,30 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal } } + if (is_type_struct(a) && is_type_comparable(a)) { + irValue *left_ptr = ir_address_from_load_or_generate_local(proc, left); + irValue *right_ptr = ir_address_from_load_or_generate_local(proc, right); + irValue *res = {}; + if (is_type_simple_compare(a)) { + // TODO(bill): Test to see if this is actually faster!!!! + auto args = array_make(permanent_allocator(), 3); + args[0] = ir_emit_conv(proc, left_ptr, t_rawptr); + args[1] = ir_emit_conv(proc, right_ptr, t_rawptr); + args[2] = ir_const_int(type_size_of(a)); + res = ir_emit_runtime_call(proc, "memory_equal", args); + } else { + irValue *value = ir_get_compare_proc_for_type(proc->module, a); + auto args = array_make(permanent_allocator(), 2); + args[0] = ir_emit_conv(proc, left_ptr, t_rawptr); + args[1] = ir_emit_conv(proc, right_ptr, t_rawptr); + res = ir_emit_call(proc, value, args); + } + if (op_kind == Token_NotEq) { + res = ir_emit_unary_arith(proc, Token_Not, res, ir_type(res)); + } + return res; + } + if (is_type_string(a)) { if (is_type_cstring(a)) { left = ir_emit_conv(proc, left, t_string); @@ -11350,6 +11459,9 @@ void ir_begin_procedure_body(irProcedure *proc) { bool ir_remove_dead_instr(irProcedure *proc) { + if (proc->ignore_dead_instr) { + return false; + } isize elimination_count = 0; retry: #if 1 @@ -11476,7 +11588,7 @@ void ir_build_proc(irValue *value, irProcedure *parent) { proc->parent = parent; - if (proc->body != nullptr) { + if (proc->body != nullptr && proc->body->kind != Ast_Invalid) { u64 prev_state_flags = proc->module->state_flags; if (proc->tags != 0) { @@ -11578,6 +11690,7 @@ void ir_init_module(irModule *m, Checker *c) { map_init(&m->debug_info, heap_allocator()); map_init(&m->entity_names, heap_allocator()); map_init(&m->anonymous_proc_lits, heap_allocator()); + map_init(&m->compare_procs, heap_allocator()); array_init(&m->procs, heap_allocator()); array_init(&m->procs_to_generate, heap_allocator()); array_init(&m->foreign_library_paths, heap_allocator()); diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 7521ba2f7..77d4b42b0 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -267,56 +267,6 @@ i64 lb_alignof(LLVMTypeRef type) { return 1; } -Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) { - Type *t = alloc_type_struct(); - t->Struct.fields = array_make(heap_allocator(), field_count); - - Scope *scope = nullptr; - for_array(i, t->Struct.fields) { - t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved); - } - t->Struct.is_packed = is_packed; - - return t; -} - -Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) { - if (field_count == 0) { - return nullptr; - } - if (!must_be_tuple && field_count == 1) { - return field_types[0]; - } - - Type *t = alloc_type_tuple(); - t->Tuple.variables = array_make(heap_allocator(), field_count); - - Scope *scope = nullptr; - for_array(i, t->Tuple.variables) { - t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false); - } - t->Tuple.is_packed = is_packed; - - return t; -} - -Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg) { - - Type *params = alloc_type_tuple_from_field_types(param_types, param_count, false, true); - isize results_count = 0; - if (results != nullptr) { - if (results->kind != Type_Tuple) { - results = alloc_type_tuple_from_field_types(&results, 1, false, true); - } - results_count = results->Tuple.variables.count; - } - - Scope *scope = nullptr; - Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, /*not sure what to put here*/ProcCC_CDecl); - t->Proc.c_vararg = is_c_vararg; - return t; -} - #if 0 Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) { Type **found = map_get(&m->llvm_types, hash_pointer(type)); @@ -959,6 +909,16 @@ namespace lbAbiAmd64SysV { }; +namespace lbAbiAarch64 { + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + // ft->args = compute_arg_types(c, arg_types, arg_count); + // ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined); + // ft->calling_convention = calling_convention; + return ft; + } +} LB_ABI_INFO(lb_get_abi_info) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 89a0ff667..665f9570a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9143,6 +9143,78 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) { return {}; } +lbValue lb_get_compare_proc_for_type(lbModule *m, Type *type) { + Type *original_type = type; + type = base_type(type); + GB_ASSERT(type->kind == Type_Struct); + type_set_offsets(type); + Type *pt = alloc_type_pointer(type); + LLVMTypeRef ptr_type = lb_type(m, pt); + + auto key = hash_type(type); + lbProcedure **found = map_get(&m->compare_procs, key); + lbProcedure *compare_proc = nullptr; + if (found) { + compare_proc = *found; + } else { + static Type *proc_type = nullptr; + if (proc_type == nullptr) { + Type *args[2] = {t_rawptr, t_rawptr}; + proc_type = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless); + set_procedure_abi_types(proc_type); + } + + static u32 proc_index = 0; + + char buf[16] = {}; + isize n = gb_snprintf(buf, 16, "__$cmp%u", ++proc_index); + char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); + String proc_name = make_string_c(str); + + lbProcedure *p = lb_create_dummy_procedure(m, proc_name, proc_type); + lb_begin_procedure_body(p); + + LLVMValueRef x = LLVMGetParam(p->value, 0); + LLVMValueRef y = LLVMGetParam(p->value, 1); + x = LLVMBuildPointerCast(p->builder, x, ptr_type, ""); + y = LLVMBuildPointerCast(p->builder, y, ptr_type, ""); + lbValue lhs = {x, pt}; + lbValue rhs = {y, pt}; + + lbBlock *block_false = lb_create_block(p, "bfalse"); + + lbValue res = lb_const_bool(m, t_bool, true); + for_array(i, type->Struct.fields) { + lbBlock *next_block = lb_create_block(p, "btrue"); + + lbValue pleft = lb_emit_struct_ep(p, lhs, cast(i32)i); + lbValue pright = lb_emit_struct_ep(p, rhs, cast(i32)i); + lbValue left = lb_emit_load(p, pleft); + lbValue right = lb_emit_load(p, pright); + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + + lb_emit_if(p, ok, next_block, block_false); + + lb_emit_jump(p, next_block); + lb_start_block(p, next_block); + } + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); + + lb_start_block(p, block_false); + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + + lb_end_procedure_body(p); + + map_set(&m->compare_procs, key, p); + + compare_proc = p; + } + GB_ASSERT(compare_proc != nullptr); + + return {compare_proc->value, compare_proc->type}; +} lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) { Type *a = core_type(left.type); @@ -9272,6 +9344,31 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } } + + if (is_type_struct(a) && is_type_comparable(a)) { + lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); + lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); + lbValue res = {}; + if (is_type_simple_compare(a)) { + // TODO(bill): Test to see if this is actually faster!!!! + auto args = array_make(permanent_allocator(), 3); + args[0] = lb_emit_conv(p, left_ptr, t_rawptr); + args[1] = lb_emit_conv(p, right_ptr, t_rawptr); + args[2] = lb_const_int(p->module, t_int, type_size_of(a)); + res = lb_emit_runtime_call(p, "memory_equal", args); + } else { + lbValue value = lb_get_compare_proc_for_type(p->module, a); + auto args = array_make(permanent_allocator(), 2); + args[0] = lb_emit_conv(p, left_ptr, t_rawptr); + args[1] = lb_emit_conv(p, right_ptr, t_rawptr); + res = lb_emit_call(p, value, args); + } + if (op_kind == Token_NotEq) { + res = lb_emit_unary_arith(p, Token_Not, res, res.type); + } + return res; + } + if (is_type_string(a)) { if (is_type_cstring(a)) { left = lb_emit_conv(p, left, t_string); @@ -11442,6 +11539,7 @@ void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->const_strings, a); map_init(&m->anonymous_proc_lits, a); map_init(&m->function_type_map, a); + map_init(&m->compare_procs, a); array_init(&m->procedures_to_generate, a); array_init(&m->foreign_library_paths, a); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 289f9d324..388176e9a 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -87,6 +87,8 @@ struct lbModule { Map anonymous_proc_lits; // Key: Ast * Map function_type_map; // Key: Type * + Map compare_procs; // Key: Type * + u32 global_array_index; u32 global_generated_index; u32 nested_type_name_guid; diff --git a/src/types.cpp b/src/types.cpp index b381ba9c9..80812e94b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1942,6 +1942,15 @@ bool is_type_comparable(Type *t) { case Type_Opaque: return is_type_comparable(t->Opaque.elem); + + case Type_Struct: + for_array(i, t->Struct.fields) { + Entity *f = t->Struct.fields[i]; + if (!is_type_comparable(f->type)) { + return false; + } + } + return true; } return false; } @@ -3401,6 +3410,58 @@ Type *reduce_tuple_to_single_type(Type *original_type) { } +Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) { + Type *t = alloc_type_struct(); + t->Struct.fields = array_make(heap_allocator(), field_count); + + Scope *scope = nullptr; + for_array(i, t->Struct.fields) { + t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved); + } + t->Struct.is_packed = is_packed; + + return t; +} + +Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) { + if (field_count == 0) { + return nullptr; + } + if (!must_be_tuple && field_count == 1) { + return field_types[0]; + } + + Type *t = alloc_type_tuple(); + t->Tuple.variables = array_make(heap_allocator(), field_count); + + Scope *scope = nullptr; + for_array(i, t->Tuple.variables) { + t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false); + } + t->Tuple.is_packed = is_packed; + + return t; +} + +Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg, ProcCallingConvention calling_convention) { + + Type *params = alloc_type_tuple_from_field_types(param_types, param_count, false, true); + isize results_count = 0; + if (results != nullptr) { + if (results->kind != Type_Tuple) { + results = alloc_type_tuple_from_field_types(&results, 1, false, true); + } + results_count = results->Tuple.variables.count; + } + + Scope *scope = nullptr; + Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, calling_convention); + t->Proc.c_vararg = is_c_vararg; + return t; +} + + + gbString write_type_to_string(gbString str, Type *type) { if (type == nullptr) { return gb_string_appendc(str, ""); @@ -3719,3 +3780,6 @@ gbString type_to_string(Type *type) { return write_type_to_string(gb_string_make(heap_allocator(), ""), type); } + + + From 0b30c3dc5ad3f3908719af19e9f7e61daae37706 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 14:35:31 +0000 Subject: [PATCH 088/170] Add `flags: Type_Info_Flags,` to `runtime.Type_Info` --- core/runtime/core.odin | 7 +++++++ src/checker.cpp | 5 +++-- src/checker.hpp | 4 ++++ src/ir.cpp | 9 +++++++-- src/llvm_backend.cpp | 10 ++++++++-- src/types.cpp | 23 +++++++++++++++++++++++ 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index dfbe0d053..2989c4700 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -149,9 +149,16 @@ Type_Info_Relative_Slice :: struct { base_integer: ^Type_Info, }; +Type_Info_Flag :: enum u8 { + Comparable = 0, + Simple_Compare = 1, +}; +Type_Info_Flags :: distinct bit_set[Type_Info_Flag; u32]; + Type_Info :: struct { size: int, align: int, + flags: Type_Info_Flags, id: typeid, variant: union { diff --git a/src/checker.cpp b/src/checker.cpp index 1a5a75152..bba790cb2 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -899,6 +899,7 @@ bool init_checker(Checker *c, Parser *parser) { gbAllocator a = heap_allocator(); init_checker_info(&c->info); + c->info.checker = c; array_init(&c->procs_to_check, a); array_init(&c->procs_with_deferred_to_check, a); @@ -2179,9 +2180,9 @@ void init_core_type_info(Checker *c) { t_type_info_enum_value = type_info_enum_value->type; t_type_info_enum_value_ptr = alloc_type_pointer(t_type_info_enum_value); - GB_ASSERT(tis->fields.count == 4); + GB_ASSERT(tis->fields.count == 5); - Entity *type_info_variant = tis->fields[3]; + Entity *type_info_variant = tis->fields[4]; Type *tiv_type = type_info_variant->type; GB_ASSERT(is_type_union(tiv_type)); diff --git a/src/checker.hpp b/src/checker.hpp index 72c4b148c..168c00d33 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -252,8 +252,12 @@ struct AtomOpMapEntry { }; +struct CheckerContext; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { + Checker *checker; + Map untyped; // Key: Ast * | Expression -> ExprInfo // NOTE(bill): This needs to be a map and not on the Ast // as it needs to be iterated across diff --git a/src/ir.cpp b/src/ir.cpp index 2d244f62c..ce5dea86d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11974,6 +11974,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info // Useful types Type *t_i64_slice_ptr = alloc_type_pointer(alloc_type_slice(t_i64)); Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string)); + Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags")); + Type *t_type_info_flags = type_info_flags_entity->type; i32 type_info_member_types_index = 0; i32 type_info_member_names_index = 0; @@ -11993,11 +11995,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *tag = nullptr; irValue *ti_ptr = ir_emit_array_epi(proc, ir_global_type_info_data, cast(i32)entry_index); - irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 3); + irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 4); + + irValue *type_info_flags = ir_value_constant(t_type_info_flags, exact_value_i64(type_info_flags_of_type(t))); ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 0), ir_const_int(type_size_of(t))); ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 1), ir_const_int(type_align_of(t))); - ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), ir_typeid(proc->module, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), type_info_flags); + ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 3), ir_typeid(proc->module, t)); switch (t->kind) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 665f9570a..a2727b012 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11732,6 +11732,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da // Useful types Type *t_i64_slice_ptr = alloc_type_pointer(alloc_type_slice(t_i64)); Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string)); + Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags")); + Type *t_type_info_flags = type_info_flags_entity->type; + i32 type_info_member_types_index = 0; i32 type_info_member_names_index = 0; @@ -11751,11 +11754,14 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da lbValue tag = {}; lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data.addr, cast(i32)entry_index); - lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 3); + lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4); + + lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, type_info_flags_of_type(t)); lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 0), lb_const_int(m, t_int, type_size_of(t))); lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 1), lb_const_int(m, t_int, type_align_of(t))); - lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 2), lb_typeid(m, t)); + lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 2), type_info_flags); + lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 3), lb_typeid(m, t)); switch (t->kind) { diff --git a/src/types.cpp b/src/types.cpp index 80812e94b..09888f878 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -373,7 +373,28 @@ enum Typeid_Kind : u8 { Typeid_Relative_Slice, }; +// IMPORTANT NOTE(bill): This must match the same as the in core.odin +enum TypeInfoFlag : u32 { + TypeInfoFlag_Comparable = 1<<0, + TypeInfoFlag_Simple_Compare = 1<<1, +}; +bool is_type_comparable(Type *t); +bool is_type_simple_compare(Type *t); + +u32 type_info_flags_of_type(Type *type) { + if (type == nullptr) { + return 0; + } + u32 flags = 0; + if (is_type_comparable(type)) { + flags |= TypeInfoFlag_Comparable; + } + if (is_type_simple_compare(type)) { + flags |= TypeInfoFlag_Comparable; + } + return flags; +} // TODO(bill): Should I add extra information here specifying the kind of selection? @@ -1348,6 +1369,8 @@ bool is_type_simple_compare(Type *t) { return false; } + + Type *base_complex_elem_type(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { From 4e370e6ed8d6cfe3dee306dfbc298ba68722f12e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 15:53:49 +0000 Subject: [PATCH 089/170] Add `equal` procedure field to `runtime.Type_Info_Struct` --- core/reflect/reflect.odin | 78 +++++++++++++++++++++++++++++++ core/runtime/core.odin | 5 ++ src/ir.cpp | 11 +++-- src/llvm_backend.cpp | 14 ++++-- src/types.cpp | 97 ++++++++++++++++++++------------------- 5 files changed, 149 insertions(+), 56 deletions(-) diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 947a10771..df183e2fd 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -1206,3 +1206,81 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) { return; } + + +not_equal :: proc(a, b: any) -> bool { + return !equal(a, b); +} +equal :: proc(a, b: any) -> bool { + if a == nil && b == nil { + return true; + } + + if a.id != b.id { + return false; + } + + if a.data == b.data { + return true; + } + + t := type_info_of(a.id); + if .Comparable not_in t.flags { + return false; + } + + if t.size == 0 { + return true; + } + + if .Simple_Compare in t.flags { + return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0; + } + + t = runtime.type_info_core(t); + + #partial switch v in t.variant { + case Type_Info_String: + if v.is_cstring { + x := string((^cstring)(a.data)^); + y := string((^cstring)(b.data)^); + return x == y; + } else { + x := (^string)(a.data)^; + y := (^string)(b.data)^; + return x == y; + } + + case Type_Info_Array: + for i in 0.. bool; Type_Info_Struct :: struct { types: []^Type_Info, names: []string, @@ -97,6 +99,9 @@ Type_Info_Struct :: struct { is_packed: bool, is_raw_union: bool, custom_align: bool, + + equal: Type_Struct_Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set + // These are only set iff this structure is an SOA structure soa_kind: Type_Info_Struct_Soa_Kind, soa_base_type: ^Type_Info, diff --git a/src/ir.cpp b/src/ir.cpp index ce5dea86d..28edd5981 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12353,8 +12353,13 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align); + if (is_type_comparable(t) && !is_type_simple_compare(t)) { + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 8), ir_get_compare_proc_for_type(proc->module, t)); + } + + if (t->Struct.soa_kind != StructSoa_None) { - irValue *kind = ir_emit_struct_ep(proc, tag, 8); + irValue *kind = ir_emit_struct_ep(proc, tag, 9); Type *kind_type = type_deref(ir_type(kind)); irValue *soa_kind = ir_value_constant(kind_type, exact_value_i64(t->Struct.soa_kind)); @@ -12363,8 +12368,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info ir_emit_store(proc, kind, soa_kind); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 9), soa_type); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_len); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_type); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 11), soa_len); } } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a2727b012..0272d1dfa 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12178,7 +12178,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da case Type_Struct: { tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr); - LLVMValueRef vals[11] = {}; + LLVMValueRef vals[12] = {}; { @@ -12188,18 +12188,22 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da vals[5] = is_packed.value; vals[6] = is_raw_union.value; vals[7] = is_custom_align.value; + if (is_type_comparable(t) && !is_type_simple_compare(t)) { + vals[8] = lb_get_compare_proc_for_type(m, t).value; + } + if (t->Struct.soa_kind != StructSoa_None) { - lbValue kind = lb_emit_struct_ep(p, tag, 8); + lbValue kind = lb_emit_struct_ep(p, tag, 9); Type *kind_type = type_deref(kind.type); lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind)); lbValue soa_type = lb_type_info(m, t->Struct.soa_elem); lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count); - vals[8] = soa_kind.value; - vals[9] = soa_type.value; - vals[10] = soa_len.value; + vals[9] = soa_kind.value; + vals[1] = soa_type.value; + vals[11] = soa_len.value; } } diff --git a/src/types.cpp b/src/types.cpp index 09888f878..b8b4b32f5 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1321,54 +1321,6 @@ Type *core_array_type(Type *t) { return t; } -// NOTE(bill): type can be easily compared using memcmp -bool is_type_simple_compare(Type *t) { - t = core_type(t); - switch (t->kind) { - case Type_Array: - return is_type_simple_compare(t->Array.elem); - - case Type_EnumeratedArray: - return is_type_simple_compare(t->EnumeratedArray.elem); - - case Type_Basic: - if (t->Basic.flags & BasicFlag_SimpleCompare) { - return true; - } - return false; - - case Type_Pointer: - case Type_Proc: - case Type_BitSet: - case Type_BitField: - return true; - - case Type_Struct: - for_array(i, t->Struct.fields) { - Entity *f = t->Struct.fields[i]; - if (!is_type_simple_compare(f->type)) { - return false; - } - } - return true; - - case Type_Union: - for_array(i, t->Union.variants) { - Type *v = t->Union.variants[i]; - if (!is_type_simple_compare(v)) { - return false; - } - } - return true; - - case Type_SimdVector: - return is_type_simple_compare(t->SimdVector.elem); - - } - - return false; -} - Type *base_complex_elem_type(Type *t) { @@ -1978,6 +1930,55 @@ bool is_type_comparable(Type *t) { return false; } +// NOTE(bill): type can be easily compared using memcmp +bool is_type_simple_compare(Type *t) { + t = core_type(t); + switch (t->kind) { + case Type_Array: + return is_type_simple_compare(t->Array.elem); + + case Type_EnumeratedArray: + return is_type_simple_compare(t->EnumeratedArray.elem); + + case Type_Basic: + if (t->Basic.flags & BasicFlag_SimpleCompare) { + return true; + } + return false; + + case Type_Pointer: + case Type_Proc: + case Type_BitSet: + case Type_BitField: + return true; + + case Type_Struct: + for_array(i, t->Struct.fields) { + Entity *f = t->Struct.fields[i]; + if (!is_type_simple_compare(f->type)) { + return false; + } + } + return true; + + case Type_Union: + for_array(i, t->Union.variants) { + Type *v = t->Union.variants[i]; + if (!is_type_simple_compare(v)) { + return false; + } + } + return true; + + case Type_SimdVector: + return is_type_simple_compare(t->SimdVector.elem); + + } + + return false; +} + + Type *strip_type_aliasing(Type *x) { if (x == nullptr) { return x; From 67bc35e882800164c2a7a30cadc237d4051a4b46 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 16:19:26 +0000 Subject: [PATCH 090/170] Fix logic for comparisons of struct #raw_union types --- core/reflect/reflect.odin | 3 ++- src/types.cpp | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index df183e2fd..ed50d658b 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -1207,7 +1207,7 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) { return; } - +/* not_equal :: proc(a, b: any) -> bool { return !equal(a, b); } @@ -1284,3 +1284,4 @@ equal :: proc(a, b: any) -> bool { return true; } +*/ diff --git a/src/types.cpp b/src/types.cpp index b8b4b32f5..0fc529213 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1919,6 +1919,9 @@ bool is_type_comparable(Type *t) { return is_type_comparable(t->Opaque.elem); case Type_Struct: + if (t->Struct.is_raw_union) { + return is_type_simple_compare(t); + } for_array(i, t->Struct.fields) { Entity *f = t->Struct.fields[i]; if (!is_type_comparable(f->type)) { From 4762d2f2d15d64e012e2de876f1fd78c3ebd7c3e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 16:56:31 +0000 Subject: [PATCH 091/170] map type internal reorganization --- core/runtime/core.odin | 42 ------------------ core/runtime/dynamic_map_internal.odin | 60 ++++++++++++++++++++++---- src/check_type.cpp | 17 ++++---- src/checker.cpp | 8 ++-- src/ir.cpp | 2 +- src/llvm_backend.cpp | 2 +- src/types.cpp | 2 +- 7 files changed, 66 insertions(+), 67 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 14d5c99ce..76454159c 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -351,48 +351,6 @@ Raw_Map :: struct { entries: Raw_Dynamic_Array, } -INITIAL_MAP_CAP :: 16; - -Map_Key :: struct { - hash: u64, - /* NOTE(bill) - size_of(Map_Key) == 16 Bytes on 32-bit systems - size_of(Map_Key) == 24 Bytes on 64-bit systems - - This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems - however, this is probably not a huge problem in terms of memory usage - */ - key: struct #raw_union { - str: string, - val: u64, - }, -} - -Map_Find_Result :: struct { - hash_index: int, - entry_prev: int, - entry_index: int, -} - -Map_Entry_Header :: struct { - key: Map_Key, - next: int, -/* - value: Value_Type, -*/ -} - -Map_Header :: struct { - m: ^Raw_Map, - is_key_string: bool, - - entry_size: int, - entry_align: int, - - value_offset: uintptr, - value_size: int, -} - ///////////////////////////// // Init Startup Procedures // ///////////////////////////// diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index b4148e74e..71d3ba8f7 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -3,10 +3,52 @@ package runtime import "intrinsics" _ :: intrinsics; +INITIAL_MAP_CAP :: 16; + +Map_Hash :: struct { + hash: u64, + /* NOTE(bill) + size_of(Map_Hash) == 16 Bytes on 32-bit systems + size_of(Map_Hash) == 24 Bytes on 64-bit systems + + This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems + however, this is probably not a huge problem in terms of memory usage + */ + key: struct #raw_union { + str: string, + val: u64, + }, +} + +Map_Find_Result :: struct { + hash_index: int, + entry_prev: int, + entry_index: int, +} + +Map_Entry_Header :: struct { + key: Map_Hash, + next: int, +/* + value: Value_Type, +*/ +} + +Map_Header :: struct { + m: ^Raw_Map, + is_key_string: bool, + + entry_size: int, + entry_align: int, + + value_offset: uintptr, + value_size: int, +} + __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { header := Map_Header{m = (^Raw_Map)(m)}; Entry :: struct { - key: Map_Key, + key: Map_Hash, next: int, value: V, }; @@ -19,9 +61,9 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { return header; } -__get_map_key :: proc "contextless" (k: $K) -> Map_Key { +__get_map_key :: proc "contextless" (k: $K) -> Map_Hash { key := k; - map_key: Map_Key; + map_key: Map_Hash; T :: intrinsics.type_core_type(K); @@ -169,7 +211,7 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c header.m^ = nm; } -__dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> rawptr { +__dynamic_map_get :: proc(h: Map_Header, key: Map_Hash) -> rawptr { index := __dynamic_map_find(h, key).entry_index; if index >= 0 { data := uintptr(__dynamic_map_get_entry(h, index)); @@ -178,7 +220,7 @@ __dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> rawptr { return nil; } -__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check { +__dynamic_map_set :: proc(h: Map_Header, key: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check { index: int; assert(value != nil); @@ -223,7 +265,7 @@ __dynamic_map_full :: inline proc(using h: Map_Header) -> bool { } -__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool { +__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Hash) -> bool { if a.hash == b.hash { if h.is_key_string { return a.key.str == b.key.str; @@ -235,7 +277,7 @@ __dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool { return false; } -__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check { +__dynamic_map_find :: proc(using h: Map_Header, key: Map_Hash) -> Map_Find_Result #no_bounds_check { fr := Map_Find_Result{-1, -1, -1}; if n := u64(len(m.hashes)); n > 0 { fr.hash_index = int(key.hash % n); @@ -252,7 +294,7 @@ __dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result return fr; } -__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int { +__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Hash, loc := #caller_location) -> int { prev := m.entries.len; c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); if c != prev { @@ -263,7 +305,7 @@ __dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #calle return prev; } -__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) { +__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Hash) { fr := __dynamic_map_find(h, key); if fr.entry_index >= 0 { __dynamic_map_erase(h, fr); diff --git a/src/check_type.cpp b/src/check_type.cpp index dfd0f093d..a3b71a034 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2784,24 +2784,23 @@ void init_map_entry_type(Type *type) { if (type->Map.entry_type != nullptr) return; // NOTE(bill): The preload types may have not been set yet - GB_ASSERT(t_map_key != nullptr); + GB_ASSERT(t_map_hash != nullptr); Type *entry_type = alloc_type_struct(); /* struct { - hash: __MapKey; - next: int; - key: Key; - value: Value; + key: runtime.Map_Key, + next: int, + value: Value, } */ Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid); Scope *s = create_scope(builtin_pkg->scope); - auto fields = array_make(permanent_allocator(), 0, 3); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), t_map_key, false, 0, EntityState_Resolved)); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved)); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 2, EntityState_Resolved)); + auto fields = array_make(permanent_allocator(), 0, 4); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), t_map_hash, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, cast(i32)fields.count, EntityState_Resolved)); entry_type->Struct.fields = fields; diff --git a/src/checker.cpp b/src/checker.cpp index bba790cb2..efa7d76f7 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2278,14 +2278,14 @@ void init_core_source_code_location(Checker *c) { } void init_core_map_type(Checker *c) { - if (t_map_key == nullptr) { - Entity *e = find_core_entity(c, str_lit("Map_Key")); + if (t_map_hash == nullptr) { + Entity *e = find_core_entity(c, str_lit("Map_Hash")); if (e->state == EntityState_Unresolved) { auto ctx = c->init_ctx; check_entity_decl(&ctx, e, nullptr, nullptr); } - t_map_key = e->type; - GB_ASSERT(t_map_key != nullptr); + t_map_hash = e->type; + GB_ASSERT(t_map_hash != nullptr); } if (t_map_header == nullptr) { diff --git a/src/ir.cpp b/src/ir.cpp index 28edd5981..634e0a698 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3608,7 +3608,7 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) { Type *hash_type = t_u64; - irValue *v = ir_add_local_generated(proc, t_map_key, true); + irValue *v = ir_add_local_generated(proc, t_map_hash, true); Type *t = base_type(ir_type(key)); key = ir_emit_conv(proc, key, key_type); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 0272d1dfa..1fb0891b5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10259,7 +10259,7 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { Type *hash_type = t_u64; - lbAddr v = lb_add_local_generated(p, t_map_key, true); + lbAddr v = lb_add_local_generated(p, t_map_hash, true); lbValue vp = lb_addr_get_ptr(p, v); Type *t = base_type(key.type); key = lb_emit_conv(p, key, key_type); diff --git a/src/types.cpp b/src/types.cpp index 0fc529213..7ca9a3cc2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -684,7 +684,7 @@ gb_global Type *t_context_ptr = nullptr; gb_global Type *t_source_code_location = nullptr; gb_global Type *t_source_code_location_ptr = nullptr; -gb_global Type *t_map_key = nullptr; +gb_global Type *t_map_hash = nullptr; gb_global Type *t_map_header = nullptr; gb_global Type *t_vector_x86_mmx = nullptr; From 91758656f6027668d9a5c713812c012ac1b38008 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 18:25:01 +0000 Subject: [PATCH 092/170] Change internal layout of `map[K]V` --- core/fmt/fmt.odin | 10 +-- core/runtime/core_builtin.odin | 3 +- core/runtime/dynamic_map_internal.odin | 120 ++++++++++++------------- src/check_type.cpp | 14 +-- src/ir.cpp | 44 +++++---- src/llvm_backend.cpp | 39 ++++---- 6 files changed, 109 insertions(+), 121 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index ec664a2a9..0af7d2a8f 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1707,16 +1707,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { data := uintptr(entries.data) + uintptr(i*entry_size); header := cast(^runtime.Map_Entry_Header)data; - if reflect.is_string(info.key) { - strings.write_string(fi.buf, header.key.key.str); - } else { - fi := Info{buf = fi.buf}; - fmt_arg(&fi, any{rawptr(&header.key.key.val), info.key.id}, 'v'); - } + key := data + entry_type.offsets[2]; + fmt_arg(&Info{buf = fi.buf}, any{rawptr(key), info.key.id}, 'v'); strings.write_string(fi.buf, "="); - value := data + entry_type.offsets[2]; + value := data + entry_type.offsets[3]; fmt_arg(fi, any{rawptr(value), info.value.id}, 'v'); } } diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 35d082ac9..fd6c7c9af 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -264,7 +264,8 @@ reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { @builtin delete_key :: proc(m: ^$T/map[$K]$V, key: K) { if m != nil { - __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key)); + key := key; + __dynamic_map_delete_key(__get_map_header(m), __get_map_key(&key)); } } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 71d3ba8f7..7d93bb918 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -7,17 +7,7 @@ INITIAL_MAP_CAP :: 16; Map_Hash :: struct { hash: u64, - /* NOTE(bill) - size_of(Map_Hash) == 16 Bytes on 32-bit systems - size_of(Map_Hash) == 24 Bytes on 64-bit systems - - This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems - however, this is probably not a huge problem in terms of memory usage - */ - key: struct #raw_union { - str: string, - val: u64, - }, + key_ptr: rawptr, // address of Map_Entry_Header.key } Map_Find_Result :: struct { @@ -27,9 +17,10 @@ Map_Find_Result :: struct { } Map_Entry_Header :: struct { - key: Map_Hash, + hash: Map_Hash, next: int, /* + key: Key_Value, value: Value_Type, */ } @@ -41,6 +32,9 @@ Map_Header :: struct { entry_size: int, entry_align: int, + key_offset: uintptr, + key_size: int, + value_offset: uintptr, value_size: int, } @@ -48,57 +42,50 @@ Map_Header :: struct { __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { header := Map_Header{m = (^Raw_Map)(m)}; Entry :: struct { - key: Map_Hash, + hash: Map_Hash, next: int, + key: K, value: V, }; header.is_key_string = intrinsics.type_is_string(K); + header.entry_size = int(size_of(Entry)); header.entry_align = int(align_of(Entry)); + + header.key_offset = uintptr(offset_of(Entry, key)); + header.key_size = int(size_of(K)); + header.value_offset = uintptr(offset_of(Entry, value)); header.value_size = int(size_of(V)); return header; } -__get_map_key :: proc "contextless" (k: $K) -> Map_Hash { +__get_map_key :: proc "contextless" (k: ^$K) -> Map_Hash { key := k; - map_key: Map_Hash; + map_hash: Map_Hash; T :: intrinsics.type_core_type(K); + map_hash.key_ptr = k; + when intrinsics.type_is_integer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); } - else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); } - else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled integer size"); } + map_hash.hash = default_hash_ptr(key, size_of(T)); } else when intrinsics.type_is_rune(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64((^rune)(&key)^); + map_hash.hash = default_hash_ptr(key, size_of(T)); } else when intrinsics.type_is_pointer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64(uintptr((^rawptr)(&key)^)); + map_hash.hash = default_hash_ptr(key, size_of(T)); } else when intrinsics.type_is_float(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled float size"); } + map_hash.hash = default_hash_ptr(key, size_of(T)); } else when intrinsics.type_is_string(T) { #assert(T == string); - str := (^string)(&key)^; - map_key.hash = default_hash_string(str); - map_key.key.str = str; + str := (^string)(key)^; + map_hash.hash = default_hash_string(str); } else { #panic("Unhandled map key type"); } - return map_key; + return map_hash; } _fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { @@ -188,8 +175,8 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c entry_header := __dynamic_map_get_entry(header, i); data := uintptr(entry_header); - fr := __dynamic_map_find(new_header, entry_header.key); - j := __dynamic_map_add_entry(new_header, entry_header.key, loc); + fr := __dynamic_map_find(new_header, entry_header.hash); + j := __dynamic_map_add_entry(new_header, entry_header.hash, loc); if fr.entry_prev < 0 { nm.hashes[fr.hash_index] = j; } else { @@ -199,8 +186,7 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c e := __dynamic_map_get_entry(new_header, j); e.next = fr.entry_index; - ndata := uintptr(e); - mem_copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size); + mem_copy(rawptr(uintptr(e)+value_offset), rawptr(data+value_offset), value_size); if __dynamic_map_full(new_header) { __dynamic_map_grow(new_header, loc); @@ -211,8 +197,8 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c header.m^ = nm; } -__dynamic_map_get :: proc(h: Map_Header, key: Map_Hash) -> rawptr { - index := __dynamic_map_find(h, key).entry_index; +__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { + index := __dynamic_map_find(h, hash).entry_index; if index >= 0 { data := uintptr(__dynamic_map_get_entry(h, index)); return rawptr(data + h.value_offset); @@ -220,7 +206,7 @@ __dynamic_map_get :: proc(h: Map_Header, key: Map_Hash) -> rawptr { return nil; } -__dynamic_map_set :: proc(h: Map_Header, key: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check { +__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check { index: int; assert(value != nil); @@ -229,11 +215,11 @@ __dynamic_map_set :: proc(h: Map_Header, key: Map_Hash, value: rawptr, loc := #c __dynamic_map_grow(h, loc); } - fr := __dynamic_map_find(h, key); + fr := __dynamic_map_find(h, hash); if fr.entry_index >= 0 { index = fr.entry_index; } else { - index = __dynamic_map_add_entry(h, key, loc); + index = __dynamic_map_add_entry(h, hash, loc); if fr.entry_prev >= 0 { entry := __dynamic_map_get_entry(h, fr.entry_prev); entry.next = index; @@ -243,9 +229,15 @@ __dynamic_map_set :: proc(h: Map_Header, key: Map_Hash, value: rawptr, loc := #c } { e := __dynamic_map_get_entry(h, index); - e.key = key; - val := (^byte)(uintptr(e) + h.value_offset); + + key := rawptr(uintptr(e) + h.key_offset); + mem_copy(key, hash.key_ptr, h.key_size); + + val := rawptr(uintptr(e) + h.value_offset); mem_copy(val, value, h.value_size); + + e.hash.hash = hash.hash; + e.hash.key_ptr = key; } if __dynamic_map_full(h) { @@ -267,24 +259,28 @@ __dynamic_map_full :: inline proc(using h: Map_Header) -> bool { __dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Hash) -> bool { if a.hash == b.hash { - if h.is_key_string { - return a.key.str == b.key.str; - } else { - return a.key.val == b.key.val; + if a.key_ptr == b.key_ptr { + return true; } - return true; + if a.key_ptr == nil || b.key_ptr == nil { + return false; + } + if h.is_key_string { + return (^string)(a.key_ptr)^ == (^string)(b.key_ptr)^; + } + return memory_equal(a.key_ptr, b.key_ptr, h.key_size); } return false; } -__dynamic_map_find :: proc(using h: Map_Header, key: Map_Hash) -> Map_Find_Result #no_bounds_check { +__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 := u64(len(m.hashes)); n > 0 { - fr.hash_index = int(key.hash % n); + fr.hash_index = int(hash.hash % n); fr.entry_index = m.hashes[fr.hash_index]; for fr.entry_index >= 0 { entry := __dynamic_map_get_entry(h, fr.entry_index); - if __dynamic_map_hash_equal(h, entry.key, key) { + if __dynamic_map_hash_equal(h, entry.hash, hash) { return fr; } fr.entry_prev = fr.entry_index; @@ -294,19 +290,21 @@ __dynamic_map_find :: proc(using h: Map_Header, key: Map_Hash) -> Map_Find_Resul return fr; } -__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Hash, loc := #caller_location) -> int { +__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int { prev := m.entries.len; c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); if c != prev { end := __dynamic_map_get_entry(h, c-1); - end.key = key; + end.hash.hash = hash.hash; + end.hash.key_ptr = rawptr(uintptr(end) + key_offset); + mem_copy(end.hash.key_ptr, hash.key_ptr, key_size); end.next = -1; } return prev; } -__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Hash) { - fr := __dynamic_map_find(h, key); +__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) { + fr := __dynamic_map_find(h, hash); if fr.entry_index >= 0 { __dynamic_map_erase(h, fr); } @@ -332,7 +330,7 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds end := __dynamic_map_get_entry(h, m.entries.len-1); mem_copy(old, end, entry_size); - if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 { + if last := __dynamic_map_find(h, old.hash); last.entry_prev >= 0 { last_entry := __dynamic_map_get_entry(h, last.entry_prev); last_entry.next = fr.entry_index; } else { diff --git a/src/check_type.cpp b/src/check_type.cpp index a3b71a034..bcc6b60db 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2789,18 +2789,20 @@ void init_map_entry_type(Type *type) { /* struct { - key: runtime.Map_Key, - next: int, - value: Value, + hash: runtime.Map_Hash, + next: int, + key: Key, + value: Value, } */ Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid); Scope *s = create_scope(builtin_pkg->scope); auto fields = array_make(permanent_allocator(), 0, 4); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), t_map_hash, false, cast(i32)fields.count, EntityState_Resolved)); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, cast(i32)fields.count, EntityState_Resolved)); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_map_hash, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, cast(i32)fields.count, EntityState_Resolved)); entry_type->Struct.fields = fields; diff --git a/src/ir.cpp b/src/ir.cpp index 634e0a698..e9f0b5647 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3595,18 +3595,22 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty i64 entry_size = type_size_of (map_type->Map.entry_type); i64 entry_align = type_align_of (map_type->Map.entry_type); - i64 value_offset = type_offset_of(map_type->Map.entry_type, 2); + 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); ir_emit_store(proc, ir_emit_struct_ep(proc, h, 2), ir_const_int(entry_size)); ir_emit_store(proc, ir_emit_struct_ep(proc, h, 3), ir_const_int(entry_align)); - ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(value_offset)); - ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(value_size)); + ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(key_offset)); + ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(key_size)); + ir_emit_store(proc, ir_emit_struct_ep(proc, h, 6), ir_const_uintptr(value_offset)); + ir_emit_store(proc, ir_emit_struct_ep(proc, h, 7), ir_const_int(value_size)); return ir_emit_load(proc, h); } -irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) { +irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) { Type *hash_type = t_u64; irValue *v = ir_add_local_generated(proc, t_map_hash, true); Type *t = base_type(ir_type(key)); @@ -3627,10 +3631,6 @@ irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) { hashed_str = ir_emit_runtime_call(proc, "default_hash_string", args); } ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_str); - - irValue *key_data = ir_emit_struct_ep(proc, v, 1); - key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type)); - ir_emit_store(proc, key_data, str); } else { i64 sz = type_size_of(t); GB_ASSERT(sz <= 8); @@ -3640,16 +3640,17 @@ irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) { args[1] = ir_const_int(sz); irValue *hash = ir_emit_runtime_call(proc, "default_hash_ptr", args); - irValue *hash_ptr = ir_emit_struct_ep(proc, v, 0); - irValue *key_data = ir_emit_struct_ep(proc, v, 1); - key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type)); - ir_emit_store(proc, hash_ptr, hash); - ir_emit_store(proc, key_data, key); } } + irValue *key_ptr = ir_address_from_load_or_generate_local(proc, key); + key_ptr = ir_emit_conv(proc, key_ptr, t_rawptr); + + irValue *key_data = ir_emit_struct_ep(proc, v, 1); + ir_emit_store(proc, key_data, key_ptr); + return ir_emit_load(proc, v); } @@ -3705,7 +3706,7 @@ irValue *ir_insert_dynamic_map_key_and_value(irProcedure *proc, irValue *addr, T map_type = base_type(map_type); irValue *h = ir_gen_map_header(proc, addr, map_type); - irValue *key = ir_gen_map_key(proc, map_key, map_type->Map.key); + irValue *key = ir_gen_map_hash(proc, map_key, map_type->Map.key); irValue *v = ir_emit_conv(proc, map_value, map_type->Map.value); irValue *ptr = ir_add_local_generated(proc, ir_type(v), false); @@ -4062,7 +4063,7 @@ irValue *ir_addr_load(irProcedure *proc, irAddr const &addr) { Type *map_type = base_type(addr.map_type); irValue *v = ir_add_local_generated(proc, map_type->Map.lookup_result_type, true); irValue *h = ir_gen_map_header(proc, addr.addr, map_type); - irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key); + irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key); auto args = array_make(ir_allocator(), 2); args[0] = h; @@ -4230,7 +4231,7 @@ irValue *ir_addr_get_ptr(irProcedure *proc, irAddr const &addr, bool allow_refer if (allow_reference) { Type *map_type = base_type(addr.map_type); irValue *h = ir_gen_map_header(proc, addr.addr, map_type); - irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key); + irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key); auto args = array_make(ir_allocator(), 2); args[0] = h; @@ -8320,7 +8321,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { irValue *addr = ir_address_from_load_or_generate_local(proc, right); irValue *h = ir_gen_map_header(proc, addr, rt); - irValue *key = ir_gen_map_key(proc, left, rt->Map.key); + irValue *key = ir_gen_map_hash(proc, left, rt->Map.key); auto args = array_make(ir_allocator(), 2); args[0] = h; @@ -9954,13 +9955,8 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, ir elem = ir_emit_load(proc, elem); irValue *entry = ir_emit_ptr_offset(proc, elem, idx); - val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2)); - - irValue *key_raw = ir_emit_struct_ep(proc, entry, 0); - key_raw = ir_emit_struct_ep(proc, key_raw, 1); - irValue *key = ir_emit_conv(proc, key_raw, alloc_type_pointer(expr_type->Map.key)); - - idx = ir_emit_load(proc, key); + idx = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2)); + val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 3)); break; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1fb0891b5..f41f206b9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3361,13 +3361,8 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu elem = lb_emit_load(p, elem); lbValue entry = lb_emit_ptr_offset(p, elem, idx); - val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2)); - - lbValue key_raw = lb_emit_struct_ep(p, entry, 0); - key_raw = lb_emit_struct_ep(p, key_raw, 1); - lbValue key = lb_emit_conv(p, key_raw, alloc_type_pointer(expr_type->Map.key)); - - idx = lb_emit_load(p, key); + idx = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2)); + val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 3)); break; } @@ -10246,13 +10241,19 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { i64 entry_size = type_size_of (map_type->Map.entry_type); i64 entry_align = type_align_of (map_type->Map.entry_type); - i64 value_offset = type_offset_of(map_type->Map.entry_type, 2); + + 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); lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 2), lb_const_int(p->module, t_int, entry_size)); lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 3), lb_const_int(p->module, t_int, entry_align)); - lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 4), lb_const_int(p->module, t_uintptr, value_offset)); - lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 5), lb_const_int(p->module, t_int, value_size)); + lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 4), lb_const_int(p->module, t_uintptr, key_offset)); + lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 5), lb_const_int(p->module, t_int, key_size)); + lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 6), lb_const_int(p->module, t_uintptr, value_offset)); + lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 7), lb_const_int(p->module, t_int, value_size)); return lb_addr_load(p, h); } @@ -10278,10 +10279,6 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { hashed_str = lb_emit_runtime_call(p, "default_hash_string", args); } lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_str); - - lbValue key_data = lb_emit_struct_ep(p, vp, 1); - key_data = lb_emit_conv(p, key_data, alloc_type_pointer(key_type)); - lb_emit_store(p, key_data, str); } else { i64 sz = type_size_of(t); GB_ASSERT(sz <= 8); @@ -10291,16 +10288,14 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { args[1] = lb_const_int(p->module, t_int, sz); lbValue hash = lb_emit_runtime_call(p, "default_hash_ptr", args); - - lbValue hash_ptr = lb_emit_struct_ep(p, vp, 0); - lbValue key_data = lb_emit_struct_ep(p, vp, 1); - key_data = lb_emit_conv(p, key_data, alloc_type_pointer(key_type)); - - lb_emit_store(p, hash_ptr, hash); - lb_emit_store(p, key_data, key); + lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hash); } } + lbValue key_ptr = lb_address_from_load_or_generate_local(p, key); + key_ptr = lb_emit_conv(p, key_ptr, t_rawptr); + lb_emit_store(p, lb_emit_struct_ep(p, vp, 1), key_ptr); + return lb_addr_load(p, v); } @@ -12202,7 +12197,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count); vals[9] = soa_kind.value; - vals[1] = soa_type.value; + vals[10] = soa_type.value; vals[11] = soa_len.value; } } From b08ec005b29c28917cfe6ae664dcdb7b97f8f6b6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 18:33:49 +0000 Subject: [PATCH 093/170] Keep -vet happy --- core/fmt/fmt.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 0af7d2a8f..27e0de2ba 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1705,7 +1705,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if i > 0 { strings.write_string(fi.buf, ", "); } data := uintptr(entries.data) + uintptr(i*entry_size); - header := cast(^runtime.Map_Entry_Header)data; key := data + entry_type.offsets[2]; fmt_arg(&Info{buf = fi.buf}, any{rawptr(key), info.key.id}, 'v'); From a55568b0c480b8a4ba7cf2f24ff2bb41cfc759ff Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 Nov 2020 19:14:36 +0000 Subject: [PATCH 094/170] Make hash internal key be `uintptr` rather than `u64` to reduce entry size --- core/runtime/core_builtin.odin | 2 +- core/runtime/dynamic_map_internal.odin | 68 +++++++++++++------------- src/ir.cpp | 5 +- src/llvm_backend.cpp | 6 ++- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index fd6c7c9af..8a1be60d9 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -265,7 +265,7 @@ reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { delete_key :: proc(m: ^$T/map[$K]$V, key: K) { if m != nil { key := key; - __dynamic_map_delete_key(__get_map_header(m), __get_map_key(&key)); + __dynamic_map_delete_key(__get_map_header(m), __get_map_hash(&key)); } } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 7d93bb918..051d4073d 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -6,7 +6,7 @@ _ :: intrinsics; INITIAL_MAP_CAP :: 16; Map_Hash :: struct { - hash: u64, + hash: uintptr, key_ptr: rawptr, // address of Map_Entry_Header.key } @@ -39,6 +39,37 @@ Map_Header :: struct { value_size: int, } +INITIAL_HASH_SEED :: 0xcbf29ce484222325; + +_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 { + h: u64 = seed; + for b in data { + h = (h ~ u64(b)) * 0x100000001b3; + } + return h; +} + +default_hash :: inline proc "contextless" (data: []byte) -> uintptr { + return uintptr(_fnv64a(data)); +} +default_hash_string :: inline proc "contextless" (s: string) -> uintptr { + return default_hash(transmute([]byte)(s)); +} +default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintptr { + s := Raw_Slice{data, size}; + return default_hash(transmute([]byte)(s)); +} + + +source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr { + hash := _fnv64a(transmute([]byte)s.file_path); + hash = hash ~ (u64(s.line) * 0x100000001b3); + hash = hash ~ (u64(s.column) * 0x100000001b3); + return uintptr(hash); +} + + + __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { header := Map_Header{m = (^Raw_Map)(m)}; Entry :: struct { @@ -58,10 +89,11 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { header.value_offset = uintptr(offset_of(Entry, value)); header.value_size = int(size_of(V)); + return header; } -__get_map_key :: proc "contextless" (k: ^$K) -> Map_Hash { +__get_map_hash :: proc "contextless" (k: ^$K) -> Map_Hash { key := k; map_hash: Map_Hash; @@ -88,36 +120,6 @@ __get_map_key :: proc "contextless" (k: ^$K) -> Map_Hash { return map_hash; } -_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { - h: u64 = seed; - for b in data { - h = (h ~ u64(b)) * 0x100000001b3; - } - return h; -} - - -default_hash :: inline proc "contextless" (data: []byte) -> u64 { - return _fnv64a(data); -} -default_hash_string :: inline proc "contextless" (s: string) -> u64 { - return default_hash(transmute([]byte)(s)); -} -default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 { - s := Raw_Slice{data, size}; - return default_hash(transmute([]byte)(s)); -} - - -source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { - hash := _fnv64a(transmute([]byte)s.file_path); - hash = hash ~ (u64(s.line) * 0x100000001b3); - hash = hash ~ (u64(s.column) * 0x100000001b3); - return hash; -} - - - __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { array := (^Raw_Slice)(array_); @@ -275,7 +277,7 @@ __dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Hash) -> bool { __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 := u64(len(m.hashes)); n > 0 { + if n := uintptr(len(m.hashes)); n > 0 { fr.hash_index = int(hash.hash % n); fr.entry_index = m.hashes[fr.hash_index]; for fr.entry_index >= 0 { diff --git a/src/ir.cpp b/src/ir.cpp index e9f0b5647..b545e7ce3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3624,7 +3624,10 @@ irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) { ExactValue ev = str->Constant.value; GB_ASSERT(ev.kind == ExactValue_String); u64 hs = fnv64a(ev.value_string.text, ev.value_string.len); - hashed_str = ir_value_constant(t_u64, exact_value_u64(hs)); + if (build_context.word_size == 4) { + hs &= 0xffffffff; + } + hashed_str = ir_value_constant(t_uintptr, exact_value_u64(hs)); } else { auto args = array_make(ir_allocator(), 1); args[0] = str; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f41f206b9..5bbccc18a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10272,7 +10272,10 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { if (lb_is_const(str)) { String v = lb_get_const_string(p->module, str); u64 hs = fnv64a(v.text, v.len); - hashed_str = lb_const_int(p->module, t_u64, hs); + if (build_context.word_size == 4) { + hs &= 0xffffffff; + } + hashed_str = lb_const_int(p->module, t_uintptr, hs); } else { auto args = array_make(permanent_allocator(), 1); args[0] = str; @@ -10287,7 +10290,6 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { args[0] = lb_address_from_load_or_generate_local(p, key); args[1] = lb_const_int(p->module, t_int, sz); lbValue hash = lb_emit_runtime_call(p, "default_hash_ptr", args); - lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hash); } } From 776c3f4e90f5a8c2240ea084ffe94cce9f5525aa Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 24 Nov 2020 12:20:48 +0000 Subject: [PATCH 095/170] Prepare for M1 Mac --- build-m1.sh | 31 +++++++++++++++++ src/gb/gb.h | 80 ++++++++++++++++++++++++++++++++++++++++++-- src/llvm_backend.cpp | 4 +-- 3 files changed, 111 insertions(+), 4 deletions(-) create mode 100755 build-m1.sh diff --git a/build-m1.sh b/build-m1.sh new file mode 100755 index 000000000..502a340ad --- /dev/null +++ b/build-m1.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +release_mode=$1 + +warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined" +libraries="-pthread -ldl -lm -lstdc++" +other_args="-DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM" +compiler="clang" + +if [ -z "$release_mode" ]; then release_mode="0"; fi + +if [ "$release_mode" -eq "0" ]; then + other_args="${other_args} -g" +fi +if [ "$release_mode" -eq "1" ]; then + other_args="${other_args} -O3 -march=native" +fi + +if [[ "$(uname)" == "Darwin" ]]; then + + # Set compiler to clang on MacOS + # MacOS provides a symlink to clang called gcc, but it's nice to be explicit here. + compiler="clang" + + other_args="${other_args} -liconv" +elif [[ "$(uname)" == "FreeBSD" ]]; then + compiler="clang" +fi + +${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin \ + && ./odin run examples/demo/demo.odin -llvm-api diff --git a/src/gb/gb.h b/src/gb/gb.h index f13693000..b2718561b 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -157,7 +157,7 @@ extern "C" { #endif #endif -#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) +#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__) #ifndef GB_ARCH_64_BIT #define GB_ARCH_64_BIT 1 #endif @@ -230,7 +230,7 @@ extern "C" { #define GB_CACHE_LINE_SIZE 128 #endif -#elif defined(__arm__) +#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) #ifndef GB_CPU_ARM #define GB_CPU_ARM 1 #endif @@ -3702,6 +3702,12 @@ gb_inline void *gb_memcopy(void *dest, void const *source, isize n) { void *dest_copy = dest; __asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory"); +#elif defined(GB_CPU_ARM) + u8 *s = cast(u8 *)source; + u8 *d = cast(u8 *)dest; + for (isize i = 0; i < n; i++) { + *d++ = *s++; + } #else u8 *d = cast(u8 *)dest; u8 const *s = cast(u8 const *)source; @@ -4438,6 +4444,76 @@ gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) { #endif } +#elif defined(GB_CPU_ARM) + +gb_inline i32 gb_atomic32_load (gbAtomic32 const volatile *a) { + return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); +} +gb_inline void gb_atomic32_store(gbAtomic32 volatile *a, i32 value) { + __atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST); +} + +gb_inline i32 gb_atomic32_compare_exchange(gbAtomic32 volatile *a, i32 expected, i32 desired) { + i32 expected_copy = expected; + auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + if (result) { + return expected; + } else { + return expected_copy; + } +} + +gb_inline i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) { + return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST); +} + +gb_inline i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) { + return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST); +} + +gb_inline i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) { + return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST); +} + +gb_inline i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) { + return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST); +} + +gb_inline i64 gb_atomic64_load(gbAtomic64 const volatile *a) { + return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); +} + +gb_inline void gb_atomic64_store(gbAtomic64 volatile *a, i64 value) { + __atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST); +} + +gb_inline i64 gb_atomic64_compare_exchange(gbAtomic64 volatile *a, i64 expected, i64 desired) { + i64 expected_copy = expected; + auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + if (result) { + return expected; + } else { + return expected_copy; + } +} + +gb_inline i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) { + return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST); +} + +gb_inline i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) { + return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST); +} + +gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) { + return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST); +} + +gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) { + return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST); +} + + #else #error TODO(bill): Implement Atomics for this CPU #endif diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 5bbccc18a..f8c6b44e1 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8312,8 +8312,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, { LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); LLVMValueRef the_asm = LLVMGetInlineAsm(func_type, - "pause", 5, - "", 0, + cast(char *)"pause", 5, + cast(char *)"", 0, /*HasSideEffects*/true, /*IsAlignStack*/false, LLVMInlineAsmDialectATT ); From 9c1c9693f26114499d0fb37bb692291ede8fa7ea Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 24 Nov 2020 15:18:20 +0000 Subject: [PATCH 096/170] Patch up gb.h --- src/gb/gb.h | 23 ++++++++++++++++++++++- src/ir_print.cpp | 6 +++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/gb/gb.h b/src/gb/gb.h index b2718561b..9981b9e34 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -4639,7 +4639,11 @@ gb_inline void gb_yield_thread(void) { #if defined(GB_SYSTEM_WINDOWS) _mm_pause(); #elif defined(GB_SYSTEM_OSX) + #if defined(GB_CPU_X86) __asm__ volatile ("" : : : "memory"); + #elif defined(GB_CPU_ARM) + __asm__ volatile ("yield" : : : "memory"); + #endif #elif defined(GB_CPU_X86) _mm_pause(); #else @@ -4651,7 +4655,11 @@ gb_inline void gb_mfence(void) { #if defined(GB_SYSTEM_WINDOWS) _ReadWriteBarrier(); #elif defined(GB_SYSTEM_OSX) + #if defined(GB_CPU_X86) __sync_synchronize(); + #elif defined(GB_CPU_ARM) + __atomic_thread_fence(__ATOMIC_SEQ_CST); + #endif #elif defined(GB_CPU_X86) _mm_mfence(); #else @@ -4663,7 +4671,12 @@ gb_inline void gb_sfence(void) { #if defined(GB_SYSTEM_WINDOWS) _WriteBarrier(); #elif defined(GB_SYSTEM_OSX) + #if defined(GB_CPU_X86) __asm__ volatile ("" : : : "memory"); + #elif defined(GB_CPU_ARM) + // TODO(bill): is this correct? + __atomic_thread_fence(__ATOMIC_SEQ_CST); + #endif #elif defined(GB_CPU_X86) _mm_sfence(); #else @@ -5278,7 +5291,7 @@ void gb_affinity_init(gbAffinity *a) { for (;;) { // The 'temporary char'. Everything goes into this char, // so that we can check against EOF at the end of this loop. - char c; + int c; #define AF__CHECK(letter) ((c = getc(cpu_info)) == letter) if (AF__CHECK('c') && AF__CHECK('p') && AF__CHECK('u') && AF__CHECK(' ') && @@ -8884,6 +8897,14 @@ gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name) return result; } +#elif defined(__aarch64__) + gb_inline u64 gb_rdtsc(void) { + int64_t virtual_timer_value; + asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value)); + return virtual_timer_value; + } +#else +#error "gb_rdtsc not supported" #endif #if defined(GB_SYSTEM_WINDOWS) diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a6bfc75d3..cb71589c5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1575,7 +1575,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { break; case BuiltinProc_cpu_relax: - ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()"); + if (build_context.metrics.arch == TargetArch_amd64) { + ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()"); + } else { + // ir_write_str_lit(f, "call void asm sideeffect \"yield\", \"\"()"); + } break; default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break; } From dbaf4d24f6e67ec05e8640b037cd934121f125c5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Nov 2020 16:19:56 +0000 Subject: [PATCH 097/170] Update package json for new map layout; Correct llvm-api includes for *nix --- core/encoding/json/marshal.odin | 8 ++++---- src/llvm_backend.cpp | 4 ++++ src/llvm_backend.hpp | 19 +++++++++++++++++++ src/main.cpp | 4 ++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 92b18c9e3..4df827ad4 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -210,12 +210,12 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { data := uintptr(entries.data) + uintptr(i*entry_size); header := cast(^Map_Entry_Header)data; - marshal_arg(b, any{rawptr(&header.key.key.val), info.key.id}); + key := rawptr(data + entry_type.offsets[2]); + value := rawptr(data + entry_type.offsets[3]); + marshal_arg(b, any{key, info.key.id}); write_string(b, ": "); - - value := data + entry_type.offsets[2]; - marshal_arg(b, any{rawptr(value), info.value.id}); + marshal_arg(b, any{value, info.value.id}); } } write_byte(b, '}'); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f8c6b44e1..2d5e14c68 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12522,6 +12522,10 @@ void lb_generate_code(lbGenerator *gen) { 1, "", 0, LLVMDWARFEmissionFull, 0, true, true + #if LLVM_VERSION_MAJOR > 10 + , "", 0, + "", 0 + #endif ); } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 388176e9a..09929c8cd 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -1,3 +1,5 @@ +#if defined(LLVM_BACKEND_SUPPORT) +#if defined(GB_SYSTEM_WINDOWS) #include "llvm-c/Core.h" #include "llvm-c/ExecutionEngine.h" #include "llvm-c/Target.h" @@ -12,6 +14,23 @@ #include "llvm-c/Transforms/Scalar.h" #include "llvm-c/Transforms/Utils.h" #include "llvm-c/Transforms/Vectorize.h" +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#endif struct lbProcedure; diff --git a/src/main.cpp b/src/main.cpp index dbade085d..fbcc03dae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,7 +11,11 @@ gb_global Timings global_timings = {0}; #if defined(LLVM_BACKEND_SUPPORT) +#if defined(GB_SYSTEM_WINDOWS) #include "llvm-c/Types.h" +#else +#include +#endif #endif #include "parser.hpp" From 1acd5acd70d08b6e22b346b3b59a5ff55fb5553b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Nov 2020 16:28:13 +0000 Subject: [PATCH 098/170] Remove unused variable --- core/encoding/json/marshal.odin | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 4df827ad4..52854fc80 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -208,8 +208,6 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { if i > 0 { write_string(b, ", "); } data := uintptr(entries.data) + uintptr(i*entry_size); - header := cast(^Map_Entry_Header)data; - key := rawptr(data + entry_type.offsets[2]); value := rawptr(data + entry_type.offsets[3]); From 70f5d7a1c99680f93eb81c6c67fb7d1a0b5cef5e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Nov 2020 19:50:48 +0000 Subject: [PATCH 099/170] Enforce zeroing through memset to ensure padding is zeroed with llvm api --- src/llvm_backend.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2d5e14c68..2f84ce263 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2961,11 +2961,29 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p LLVMTypeRef llvm_type = lb_type(p->module, type); LLVMValueRef ptr = LLVMBuildAlloca(p->builder, llvm_type, name); - LLVMSetAlignment(ptr, 16); // TODO(bill): Make this configurable + + // unsigned alignment = 16; // TODO(bill): Make this configurable + unsigned alignment = cast(unsigned)lb_alignof(llvm_type); + LLVMSetAlignment(ptr, alignment); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); if (zero_init) { - LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr); + LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); + + switch (kind) { + case LLVMStructTypeKind: + case LLVMArrayTypeKind: + { + // NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too + LLVMTypeRef type_i8 = LLVMInt8TypeInContext(p->module->ctx); + LLVMTypeRef type_i32 = LLVMInt32TypeInContext(p->module->ctx); + i32 sz = cast(i32)type_size_of(type); + LLVMBuildMemSet(p->builder, ptr, LLVMConstNull(type_i8), LLVMConstInt(type_i32, sz, false), alignment); + } + break; + default: + LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr); + } } lbValue val = {}; From 8591655334fbeda0aacd02b3beb2cfca9ede0870 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Nov 2020 21:03:20 +0000 Subject: [PATCH 100/170] Clean up `__dynamic_map_rehash` behaviour --- 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 051d4073d..cb6be60f9 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -175,7 +175,6 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c } entry_header := __dynamic_map_get_entry(header, i); - data := uintptr(entry_header); fr := __dynamic_map_find(new_header, entry_header.hash); j := __dynamic_map_add_entry(new_header, entry_header.hash, loc); @@ -187,8 +186,9 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c } e := __dynamic_map_get_entry(new_header, j); + mem_copy(e, entry_header, entry_size); e.next = fr.entry_index; - mem_copy(rawptr(uintptr(e)+value_offset), rawptr(data+value_offset), value_size); + e.hash.key_ptr = rawptr(uintptr(entry_header) + key_offset); if __dynamic_map_full(new_header) { __dynamic_map_grow(new_header, loc); From aa859c2187e4c725302030827d4ef63e692f851a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Nov 2020 22:36:34 +0000 Subject: [PATCH 101/170] Clean up and fix `__dynamic_map_erase` --- core/runtime/dynamic_map_internal.odin | 15 +++++++++------ core/runtime/internal.odin | 6 ++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index cb6be60f9..d9713dd67 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -186,9 +186,8 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c } e := __dynamic_map_get_entry(new_header, j); - mem_copy(e, entry_header, entry_size); + __dynamic_map_copy_entry(header, e, entry_header); e.next = fr.entry_index; - e.hash.key_ptr = rawptr(uintptr(entry_header) + key_offset); if __dynamic_map_full(new_header) { __dynamic_map_grow(new_header, loc); @@ -264,9 +263,8 @@ __dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Hash) -> bool { if a.key_ptr == b.key_ptr { return true; } - if a.key_ptr == nil || b.key_ptr == nil { - return false; - } + assert(a.key_ptr != nil && b.key_ptr != nil); + if h.is_key_string { return (^string)(a.key_ptr)^ == (^string)(b.key_ptr)^; } @@ -317,6 +315,11 @@ __dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_H return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)); } +__dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { + mem_copy(new, old, h.entry_size); + new.hash.key_ptr = rawptr(uintptr(new) + h.key_offset); +} + __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { if fr.entry_prev < 0 { m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next; @@ -330,7 +333,7 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds } else { old := __dynamic_map_get_entry(h, fr.entry_index); end := __dynamic_map_get_entry(h, m.entries.len-1); - mem_copy(old, end, entry_size); + __dynamic_map_copy_entry(h, old, end); if last := __dynamic_map_find(h, old.hash); last.entry_prev >= 0 { last_entry := __dynamic_map_get_entry(h, last.entry_prev); diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index c313070ac..27cd3e767 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -184,6 +184,12 @@ memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool { return memory_compare(a, b, n) == 0; } memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check { + switch { + case a == b: return 0; + case a == nil: return -1; + case b == nil: return +1; + } + x := uintptr(a); y := uintptr(b); n := uintptr(n); From ca102487404eef1d14a33252fb989d1e89c43ffe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Nov 2020 23:50:25 +0000 Subject: [PATCH 102/170] Add `__dynamic_map_fix_keys` --- core/runtime/dynamic_map_internal.odin | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index d9713dd67..5795ddcb6 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -141,8 +141,17 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l return true; } +__dynamic_map_fix_keys :: proc(h: Map_Header) { + e := (^Map_Entry_Header)(m.entries.data); + for i in 0.. Map_Find_Resu __dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int { prev := m.entries.len; + prev_data := m.entries.data; c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); + if m.entries.data != prev_data { + __dynamic_map_fix_keys(h); + } if c != prev { end := __dynamic_map_get_entry(h, c-1); end.hash.hash = hash.hash; From f36c5de746a0e7da20a371c0cf44b304487eadd1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 Nov 2020 00:05:15 +0000 Subject: [PATCH 103/170] Fix typo --- 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 5795ddcb6..41c86db8b 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -142,8 +142,8 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l } __dynamic_map_fix_keys :: proc(h: Map_Header) { - e := (^Map_Entry_Header)(m.entries.data); - for i in 0.. Date: Thu, 26 Nov 2020 00:09:38 +0000 Subject: [PATCH 104/170] Keep -vet happy --- 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 41c86db8b..be52ff285 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -143,7 +143,7 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l __dynamic_map_fix_keys :: proc(h: Map_Header) { e := (^Map_Entry_Header)(h.m.entries.data); - for i in 0.. Date: Thu, 26 Nov 2020 10:19:45 +0000 Subject: [PATCH 105/170] Fix for in enum type --- src/ir.cpp | 2 +- src/llvm_backend.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index b545e7ce3..9607f8ce7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10104,7 +10104,7 @@ void ir_build_range_enum(irProcedure *proc, Type *enum_type, Type *val_type, irV irValue *max_count = ir_const_int(enum_count); irValue *ti = ir_type_info(proc, t); - irValue *variant = ir_emit_struct_ep(proc, ti, 3); + irValue *variant = ir_emit_struct_ep(proc, ti, 4); irValue *eti_ptr = ir_emit_conv(proc, variant, t_type_info_enum_ptr); irValue *values = ir_emit_load(proc, ir_emit_struct_ep(proc, eti_ptr, 2)); irValue *values_data = ir_slice_elem(proc, values); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2f84ce263..57385a9d6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3528,7 +3528,7 @@ void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValu lbValue max_count = lb_const_int(m, t_int, enum_count); lbValue ti = lb_type_info(m, t); - lbValue variant = lb_emit_struct_ep(p, ti, 3); + lbValue variant = lb_emit_struct_ep(p, ti, 4); lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr); lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2)); lbValue values_data = lb_slice_elem(p, values); From 9959a069fcc016aa9d01f90cc33bf9dd57f9b1ae Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 12:48:33 +0000 Subject: [PATCH 106/170] Simplify internals of `map[K]V` --- core/runtime/dynamic_map_internal.odin | 101 ++++++++++++------------- src/check_type.cpp | 4 +- 2 files changed, 51 insertions(+), 54 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index be52ff285..cef33cbeb 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -5,11 +5,47 @@ _ :: intrinsics; INITIAL_MAP_CAP :: 16; +// Temporary data structure for comparing hashes and keys Map_Hash :: struct { hash: uintptr, key_ptr: rawptr, // address of Map_Entry_Header.key } +__get_map_hash :: proc "contextless" (k: ^$K) -> Map_Hash { + key := k; + map_hash: Map_Hash; + + T :: intrinsics.type_core_type(K); + + map_hash.key_ptr = k; + + when intrinsics.type_is_integer(T) { + map_hash.hash = default_hash_ptr(key, size_of(T)); + } else when intrinsics.type_is_rune(T) { + map_hash.hash = default_hash_ptr(key, size_of(T)); + } else when intrinsics.type_is_pointer(T) { + map_hash.hash = default_hash_ptr(key, size_of(T)); + } else when intrinsics.type_is_float(T) { + map_hash.hash = default_hash_ptr(key, size_of(T)); + } else when intrinsics.type_is_string(T) { + #assert(T == string); + str := (^string)(key)^; + map_hash.hash = default_hash_string(str); + } else { + #panic("Unhandled map key type"); + } + + return 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_Find_Result :: struct { hash_index: int, entry_prev: int, @@ -17,7 +53,7 @@ Map_Find_Result :: struct { } Map_Entry_Header :: struct { - hash: Map_Hash, + hash: uintptr, next: int, /* key: Key_Value, @@ -73,7 +109,7 @@ source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr { __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { header := Map_Header{m = (^Raw_Map)(m)}; Entry :: struct { - hash: Map_Hash, + hash: uintptr, next: int, key: K, value: V, @@ -93,33 +129,6 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { return header; } -__get_map_hash :: proc "contextless" (k: ^$K) -> Map_Hash { - key := k; - map_hash: Map_Hash; - - T :: intrinsics.type_core_type(K); - - map_hash.key_ptr = k; - - when intrinsics.type_is_integer(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_rune(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_pointer(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_float(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_string(T) { - #assert(T == string); - str := (^string)(key)^; - map_hash.hash = default_hash_string(str); - } else { - #panic("Unhandled map key type"); - } - - return map_hash; -} - __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { array := (^Raw_Slice)(array_); @@ -141,17 +150,8 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l return true; } -__dynamic_map_fix_keys :: proc(h: Map_Header) { - e := (^Map_Entry_Header)(h.m.entries.data); - for _ in 0.. Map_Find_Resu fr.entry_index = m.hashes[fr.hash_index]; for fr.entry_index >= 0 { entry := __dynamic_map_get_entry(h, fr.entry_index); - if __dynamic_map_hash_equal(h, entry.hash, hash) { + entry_hash := __get_map_hash_from_entry(h, entry); + if __dynamic_map_hash_equal(h, entry_hash, hash) { return fr; } fr.entry_prev = fr.entry_index; @@ -307,14 +307,10 @@ __dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #cal prev := m.entries.len; prev_data := m.entries.data; c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); - if m.entries.data != prev_data { - __dynamic_map_fix_keys(h); - } if c != prev { end := __dynamic_map_get_entry(h, c-1); - end.hash.hash = hash.hash; - end.hash.key_ptr = rawptr(uintptr(end) + key_offset); - mem_copy(end.hash.key_ptr, hash.key_ptr, key_size); + end.hash = hash.hash; + mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size); end.next = -1; } return prev; @@ -334,7 +330,6 @@ __dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_H __dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { mem_copy(new, old, h.entry_size); - new.hash.key_ptr = rawptr(uintptr(new) + h.key_offset); } __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { @@ -352,7 +347,9 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds end := __dynamic_map_get_entry(h, m.entries.len-1); __dynamic_map_copy_entry(h, old, end); - if last := __dynamic_map_find(h, old.hash); last.entry_prev >= 0 { + old_hash := __get_map_hash_from_entry(h, old); + + if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 { last_entry := __dynamic_map_get_entry(h, last.entry_prev); last_entry.next = fr.entry_index; } else { diff --git a/src/check_type.cpp b/src/check_type.cpp index bcc6b60db..2d2a5b3f0 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2799,9 +2799,9 @@ void init_map_entry_type(Type *type) { Scope *s = create_scope(builtin_pkg->scope); auto fields = array_make(permanent_allocator(), 0, 4); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_map_hash, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, cast(i32)fields.count, EntityState_Resolved)); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, cast(i32)fields.count, EntityState_Resolved)); - array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, cast(i32)fields.count, EntityState_Resolved)); + array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, cast(i32)fields.count, EntityState_Resolved)); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, cast(i32)fields.count, EntityState_Resolved)); From 2e0fd34e59218ea942e4a378b689dc7cd3f844fa Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 12:52:30 +0000 Subject: [PATCH 107/170] Keep -vet happy --- core/runtime/dynamic_map_internal.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index cef33cbeb..38618ea34 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -305,7 +305,6 @@ __dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Resu __dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int { prev := m.entries.len; - prev_data := m.entries.data; c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); if c != prev { end := __dynamic_map_get_entry(h, c-1); From 39bed567b34a4bd74f7dfa99dd1ddb4621f67190 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 14:22:42 +0000 Subject: [PATCH 108/170] Add intrinsics.type_equal_proc; Make `map` use an internal equal procedure to compare keys --- core/intrinsics/intrinsics.odin | 3 + core/runtime/core.odin | 6 +- core/runtime/dynamic_map_internal.odin | 15 ++-- src/check_expr.cpp | 21 ++++++ src/checker.cpp | 8 +++ src/checker_builtin_procs.hpp | 4 ++ src/ir.cpp | 94 +++++++++++++++----------- src/llvm_backend.cpp | 83 ++++++++++++++--------- src/llvm_backend.hpp | 2 + src/types.cpp | 2 + 10 files changed, 154 insertions(+), 84 deletions(-) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 3e20baecd..3738b57dd 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -1,4 +1,5 @@ // This is purely for documentation +//+ignore package intrinsics // Types @@ -152,3 +153,5 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V -- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- + +type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) --- diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 76454159c..1cc564ff2 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -42,6 +42,8 @@ Platform_Endianness :: enum u8 { Big = 2, } +Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool; + Type_Info_Struct_Soa_Kind :: enum u8 { None = 0, Fixed = 1, @@ -89,7 +91,6 @@ Type_Info_Tuple :: struct { // Only used for procedures parameters and results names: []string, }; -Type_Struct_Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool; Type_Info_Struct :: struct { types: []^Type_Info, names: []string, @@ -100,7 +101,7 @@ Type_Info_Struct :: struct { is_raw_union: bool, custom_align: bool, - equal: Type_Struct_Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set + equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set // These are only set iff this structure is an SOA structure soa_kind: Type_Info_Struct_Soa_Kind, @@ -351,6 +352,7 @@ Raw_Map :: struct { entries: Raw_Dynamic_Array, } + ///////////////////////////// // Init Startup Procedures // ///////////////////////////// diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 38618ea34..0c7146e25 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -63,13 +63,13 @@ Map_Entry_Header :: struct { Map_Header :: struct { m: ^Raw_Map, - is_key_string: bool, + equal: Equal_Proc, entry_size: int, entry_align: int, - key_offset: uintptr, - key_size: int, + key_offset: uintptr, + key_size: int, value_offset: uintptr, value_size: int, @@ -115,7 +115,7 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { value: V, }; - header.is_key_string = intrinsics.type_is_string(K); + header.equal = intrinsics.type_equal_proc(K); header.entry_size = int(size_of(Entry)); header.entry_align = int(align_of(Entry)); @@ -275,12 +275,7 @@ __dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Hash) -> bool { if a.key_ptr == b.key_ptr { return true; } - assert(a.key_ptr != nil && b.key_ptr != nil); - - if h.is_key_string { - return (^string)(a.key_ptr)^ == (^string)(b.key_ptr)^; - } - return memory_equal(a.key_ptr, b.key_ptr, h.key_size); + return h.equal(a.key_ptr, b.key_ptr); } return false; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4b17b4f27..e2a6089b9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6060,6 +6060,27 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } break; + + case BuiltinProc_type_equal_proc: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_comparable(type)) { + gbString t = type_to_string(type); + error(ce->args[0], "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_equal_proc; + break; + } } return true; diff --git a/src/checker.cpp b/src/checker.cpp index efa7d76f7..ee3496fbf 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -726,6 +726,14 @@ void init_universal(void) { } add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]); + { + void set_procedure_abi_types(Type *type); + + Type *args[2] = {t_rawptr, t_rawptr}; + t_equal_proc = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless); + set_procedure_abi_types(t_equal_proc); + } + // Constants add_global_constant(str_lit("true"), t_untyped_bool, exact_value_bool(true)); add_global_constant(str_lit("false"), t_untyped_bool, exact_value_bool(false)); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index d0e009cc0..9a5cdb1b8 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -183,6 +183,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_field_index_of, + BuiltinProc_type_equal_proc, + BuiltinProc__type_end, @@ -367,5 +369,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, }; diff --git a/src/ir.cpp b/src/ir.cpp index 9607f8ce7..b1deda0b9 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -529,6 +529,8 @@ Type *ir_type(irValue *value); irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr); void ir_begin_procedure_body(irProcedure *proc); void ir_end_procedure_body(irProcedure *proc); +irValue *ir_get_equal_proc_for_type(irModule *m, Type *type); + irAddr ir_addr(irValue *addr) { irAddr v = {irAddr_Default, addr}; @@ -3591,7 +3593,6 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty irValue *m = ir_emit_conv(proc, map_val_ptr, type_deref(ir_type(gep0))); ir_emit_store(proc, gep0, m); - ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_const_bool(is_type_string(key_type))); i64 entry_size = type_size_of (map_type->Map.entry_type); i64 entry_align = type_align_of (map_type->Map.entry_type); @@ -3600,6 +3601,7 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty i64 value_offset = type_offset_of(map_type->Map.entry_type, 3); i64 value_size = type_size_of (map_type->Map.value); + ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_get_equal_proc_for_type(proc->module, key_type)); ir_emit_store(proc, ir_emit_struct_ep(proc, h, 2), ir_const_int(entry_size)); ir_emit_store(proc, ir_emit_struct_ep(proc, h, 3), ir_const_int(entry_align)); ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(key_offset)); @@ -4867,11 +4869,9 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue return nullptr; } -irValue *ir_get_compare_proc_for_type(irModule *m, Type *type) { +irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) { Type *original_type = type; type = base_type(type); - GB_ASSERT(type->kind == Type_Struct); - type_set_offsets(type); Type *pt = alloc_type_pointer(type); auto key = hash_type(type); @@ -4879,12 +4879,6 @@ irValue *ir_get_compare_proc_for_type(irModule *m, Type *type) { if (found) { return *found; } - static Type *proc_type = nullptr; - if (proc_type == nullptr) { - Type *args[2] = {t_rawptr, t_rawptr}; - proc_type = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless); - set_procedure_abi_types(proc_type); - } static u32 proc_index = 0; @@ -4895,9 +4889,9 @@ irValue *ir_get_compare_proc_for_type(irModule *m, Type *type) { Ast *body = alloc_ast_node(nullptr, Ast_Invalid); - Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), proc_type, 0); + Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_equal_proc, 0); e->Procedure.link_name = proc_name; - irValue *p = ir_value_procedure(m, e, proc_type, nullptr, body, proc_name); + irValue *p = ir_value_procedure(m, e, t_equal_proc, nullptr, body, proc_name); map_set(&m->values, hash_entity(e), p); string_map_set(&m->members, proc_name, p); @@ -4908,39 +4902,59 @@ irValue *ir_get_compare_proc_for_type(irModule *m, Type *type) { // ir_start_block(proc, proc->decl_block); GB_ASSERT(proc->curr_block != nullptr); - irBlock *done = ir_new_block(proc, nullptr, "done"); // NOTE(bill): Append later - irValue *x = proc->params[0]; irValue *y = proc->params[1]; irValue *lhs = ir_emit_conv(proc, x, pt); irValue *rhs = ir_emit_conv(proc, y, pt); - irBlock *block_false = ir_new_block(proc, nullptr, "bfalse"); + irBlock *block_same_ptr = ir_new_block(proc, nullptr, "same_ptr"); + irBlock *block_diff_ptr = ir_new_block(proc, nullptr, "diff_ptr"); - for_array(i, type->Struct.fields) { - irBlock *next_block = ir_new_block(proc, nullptr, "btrue"); - - irValue *pleft = ir_emit_struct_ep(proc, lhs, cast(i32)i); - irValue *pright = ir_emit_struct_ep(proc, rhs, cast(i32)i); - irValue *left = ir_emit_load(proc, pleft); - irValue *right = ir_emit_load(proc, pright); - irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right); - - ir_emit_if(proc, ok, next_block, block_false); - - ir_emit_jump(proc, next_block); - ir_start_block(proc, next_block); - } - - ir_emit_jump(proc, done); - ir_start_block(proc, block_false); - - ir_emit(proc, ir_instr_return(proc, ir_const_bool(false))); - - ir_emit_jump(proc, done); - ir_start_block(proc, done); + irValue *same_ptr = ir_emit_comp(proc, Token_CmpEq, lhs, rhs); + ir_emit_if(proc, same_ptr, block_same_ptr, block_diff_ptr); + ir_start_block(proc, block_same_ptr); ir_emit(proc, ir_instr_return(proc, ir_const_bool(true))); + ir_start_block(proc, block_diff_ptr); + + if (type->kind == Type_Struct) { + type_set_offsets(type); + + irBlock *done = ir_new_block(proc, nullptr, "done"); // NOTE(bill): Append later + + irBlock *block_false = ir_new_block(proc, nullptr, "bfalse"); + + for_array(i, type->Struct.fields) { + irBlock *next_block = ir_new_block(proc, nullptr, "btrue"); + + irValue *pleft = ir_emit_struct_ep(proc, lhs, cast(i32)i); + irValue *pright = ir_emit_struct_ep(proc, rhs, cast(i32)i); + irValue *left = ir_emit_load(proc, pleft); + irValue *right = ir_emit_load(proc, pright); + irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right); + + ir_emit_if(proc, ok, next_block, block_false); + + ir_emit_jump(proc, next_block); + ir_start_block(proc, next_block); + } + + ir_emit_jump(proc, done); + ir_start_block(proc, block_false); + + ir_emit(proc, ir_instr_return(proc, ir_const_bool(false))); + + ir_emit_jump(proc, done); + ir_start_block(proc, done); + ir_emit(proc, ir_instr_return(proc, ir_const_bool(true))); + } else { + irValue *left = ir_emit_load(proc, lhs); + irValue *right = ir_emit_load(proc, rhs); + irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right); + ok = ir_emit_conv(proc, ok, t_bool); + ir_emit(proc, ir_instr_return(proc, ok)); + } + ir_end_procedure_body(proc); map_set(&m->compare_procs, key, p); @@ -5093,7 +5107,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal args[2] = ir_const_int(type_size_of(a)); res = ir_emit_runtime_call(proc, "memory_equal", args); } else { - irValue *value = ir_get_compare_proc_for_type(proc->module, a); + irValue *value = ir_get_equal_proc_for_type(proc->module, a); auto args = array_make(permanent_allocator(), 2); args[0] = ir_emit_conv(proc, left_ptr, t_rawptr); args[1] = ir_emit_conv(proc, right_ptr, t_rawptr); @@ -7572,6 +7586,8 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return ir_emit(proc, ir_instr_atomic_cxchg(proc, type, address, old_value, new_value, id)); } + case BuiltinProc_type_equal_proc: + return ir_get_equal_proc_for_type(proc->module, ce->args[0]->tav.type); } @@ -12353,7 +12369,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align); if (is_type_comparable(t) && !is_type_simple_compare(t)) { - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 8), ir_get_compare_proc_for_type(proc->module, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 8), ir_get_equal_proc_for_type(proc->module, t)); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 57385a9d6..39b4030af 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8555,6 +8555,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = fix_typed; return res; } + + + case BuiltinProc_type_equal_proc: + return lb_get_equal_proc_for_type(p->module, ce->args[0]->tav.type); } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); @@ -9156,11 +9160,11 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) { return {}; } -lbValue lb_get_compare_proc_for_type(lbModule *m, Type *type) { +lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { Type *original_type = type; type = base_type(type); - GB_ASSERT(type->kind == Type_Struct); - type_set_offsets(type); + GB_ASSERT(is_type_comparable(type)); + Type *pt = alloc_type_pointer(type); LLVMTypeRef ptr_type = lb_type(m, pt); @@ -9170,13 +9174,6 @@ lbValue lb_get_compare_proc_for_type(lbModule *m, Type *type) { if (found) { compare_proc = *found; } else { - static Type *proc_type = nullptr; - if (proc_type == nullptr) { - Type *args[2] = {t_rawptr, t_rawptr}; - proc_type = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless); - set_procedure_abi_types(proc_type); - } - static u32 proc_index = 0; char buf[16] = {}; @@ -9184,7 +9181,7 @@ lbValue lb_get_compare_proc_for_type(lbModule *m, Type *type) { char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); String proc_name = make_string_c(str); - lbProcedure *p = lb_create_dummy_procedure(m, proc_name, proc_type); + lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc); lb_begin_procedure_body(p); LLVMValueRef x = LLVMGetParam(p->value, 0); @@ -9194,29 +9191,50 @@ lbValue lb_get_compare_proc_for_type(lbModule *m, Type *type) { lbValue lhs = {x, pt}; lbValue rhs = {y, pt}; - lbBlock *block_false = lb_create_block(p, "bfalse"); - lbValue res = lb_const_bool(m, t_bool, true); - for_array(i, type->Struct.fields) { - lbBlock *next_block = lb_create_block(p, "btrue"); - - lbValue pleft = lb_emit_struct_ep(p, lhs, cast(i32)i); - lbValue pright = lb_emit_struct_ep(p, rhs, cast(i32)i); - lbValue left = lb_emit_load(p, pleft); - lbValue right = lb_emit_load(p, pright); - lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); - - lb_emit_if(p, ok, next_block, block_false); - - lb_emit_jump(p, next_block); - lb_start_block(p, next_block); - } + lbBlock *block_same_ptr = lb_create_block(p, "same_ptr"); + lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr"); + lbValue same_ptr = lb_emit_comp(p, Token_CmpEq, lhs, rhs); + lb_emit_if(p, same_ptr, block_same_ptr, block_diff_ptr); + lb_start_block(p, block_same_ptr); LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); - lb_start_block(p, block_false); + lb_start_block(p, block_diff_ptr); - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + if (type->kind == Type_Struct) { + type_set_offsets(type); + + lbBlock *block_false = lb_create_block(p, "bfalse"); + lbValue res = lb_const_bool(m, t_bool, true); + + for_array(i, type->Struct.fields) { + lbBlock *next_block = lb_create_block(p, "btrue"); + + lbValue pleft = lb_emit_struct_ep(p, lhs, cast(i32)i); + lbValue pright = lb_emit_struct_ep(p, rhs, cast(i32)i); + lbValue left = lb_emit_load(p, pleft); + lbValue right = lb_emit_load(p, pright); + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + + lb_emit_if(p, ok, next_block, block_false); + + lb_emit_jump(p, next_block); + lb_start_block(p, next_block); + } + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); + + lb_start_block(p, block_false); + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + } else { + lbValue left = lb_emit_load(p, lhs); + lbValue right = lb_emit_load(p, rhs); + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + ok = lb_emit_conv(p, ok, t_bool); + LLVMBuildRet(p->builder, ok.value); + } lb_end_procedure_body(p); @@ -9370,7 +9388,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri args[2] = lb_const_int(p->module, t_int, type_size_of(a)); res = lb_emit_runtime_call(p, "memory_equal", args); } else { - lbValue value = lb_get_compare_proc_for_type(p->module, a); + lbValue value = lb_get_equal_proc_for_type(p->module, a); auto args = array_make(permanent_allocator(), 2); args[0] = lb_emit_conv(p, left_ptr, t_rawptr); args[1] = lb_emit_conv(p, right_ptr, t_rawptr); @@ -10255,8 +10273,6 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { lbValue m = lb_emit_conv(p, map_val_ptr, type_deref(gep0.type)); lb_emit_store(p, gep0, m); - lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 1), lb_const_bool(p->module, t_bool, is_type_string(key_type))); - i64 entry_size = type_size_of (map_type->Map.entry_type); i64 entry_align = type_align_of (map_type->Map.entry_type); @@ -10266,6 +10282,7 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, 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); + lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 1), lb_get_equal_proc_for_type(p->module, key_type)); lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 2), lb_const_int(p->module, t_int, entry_size)); lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 3), lb_const_int(p->module, t_int, entry_align)); lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 4), lb_const_int(p->module, t_uintptr, key_offset)); @@ -12204,7 +12221,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da vals[6] = is_raw_union.value; vals[7] = is_custom_align.value; if (is_type_comparable(t) && !is_type_simple_compare(t)) { - vals[8] = lb_get_compare_proc_for_type(m, t).value; + vals[8] = lb_get_equal_proc_for_type(m, t).value; } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 09929c8cd..48ffd27a0 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -379,6 +379,8 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos); +lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type); +lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info" diff --git a/src/types.cpp b/src/types.cpp index 7ca9a3cc2..75783f948 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -690,6 +690,8 @@ gb_global Type *t_map_header = nullptr; gb_global Type *t_vector_x86_mmx = nullptr; +gb_global Type *t_equal_proc = nullptr; + i64 type_size_of (Type *t); i64 type_align_of (Type *t); From 085972bb2ccde65d148b7fdc07b7ea8329e46293 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 14:31:42 +0000 Subject: [PATCH 109/170] Minor clean up --- core/runtime/dynamic_map_internal.odin | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 0c7146e25..59661668f 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -265,16 +265,13 @@ __dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { __dynamic_map_rehash(h, new_count, loc); } -__dynamic_map_full :: inline proc(using h: Map_Header) -> bool { +__dynamic_map_full :: inline proc "contextless" (using h: Map_Header) -> bool { return int(0.75 * f64(len(m.hashes))) <= m.entries.cap; } -__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Hash) -> bool { +__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool { if a.hash == b.hash { - if a.key_ptr == b.key_ptr { - return true; - } return h.equal(a.key_ptr, b.key_ptr); } return false; @@ -322,7 +319,7 @@ __dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_H return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)); } -__dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { +__dynamic_map_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) { mem_copy(new, old, h.entry_size); } @@ -351,6 +348,5 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds } } - // TODO(bill): Is this correct behaviour? m.entries.len -= 1; } From 97c66c9c732af1e0735ee3f48a8af08b199bddf9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 15:27:53 +0000 Subject: [PATCH 110/170] Add `intrinsics.type_hasher_proc`; Make `map` work with generic hasher procedure --- core/intrinsics/intrinsics.odin | 3 +- core/runtime/core.odin | 5 +- core/runtime/dynamic_map_internal.odin | 64 +++++---- src/check_expr.cpp | 25 +++- src/check_type.cpp | 32 ++++- src/checker.cpp | 8 +- src/checker_builtin_procs.hpp | 4 +- src/ir.cpp | 96 ++++++++++++- src/llvm_backend.cpp | 190 +++++++++++++++++-------- src/llvm_backend.hpp | 4 +- src/types.cpp | 6 +- 11 files changed, 338 insertions(+), 99 deletions(-) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 3738b57dd..f817116f0 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -154,4 +154,5 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V -- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- -type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) --- +type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) --- +type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) --- diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 1cc564ff2..74b1338c7 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -42,7 +42,8 @@ Platform_Endianness :: enum u8 { Big = 2, } -Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool; +Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool; +Hasher_Proc :: distinct proc "contextless" (data: rawptr, seed: uintptr) -> uintptr; Type_Info_Struct_Soa_Kind :: enum u8 { None = 0, @@ -125,6 +126,8 @@ Type_Info_Map :: struct { key: ^Type_Info, value: ^Type_Info, generated_struct: ^Type_Info, + key_equal: Equal_Proc, + key_hasher: Hasher_Proc, }; Type_Info_Bit_Field :: struct { names: []string, diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 59661668f..99c2e92ee 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -11,31 +11,11 @@ Map_Hash :: struct { key_ptr: rawptr, // address of Map_Entry_Header.key } -__get_map_hash :: proc "contextless" (k: ^$K) -> Map_Hash { - key := k; - map_hash: Map_Hash; - - T :: intrinsics.type_core_type(K); - +__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) { + hasher := intrinsics.type_hasher_proc(K); map_hash.key_ptr = k; - - when intrinsics.type_is_integer(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_rune(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_pointer(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_float(T) { - map_hash.hash = default_hash_ptr(key, size_of(T)); - } else when intrinsics.type_is_string(T) { - #assert(T == string); - str := (^string)(key)^; - map_hash.hash = default_hash_string(str); - } else { - #panic("Unhandled map key type"); - } - - return map_hash; + map_hash.hash = hasher(k, 0); + return; } __get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) { @@ -96,6 +76,42 @@ default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintp return default_hash(transmute([]byte)(s)); } +_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr { + h := u64(seed) + 0xcbf29ce484222325; + p := uintptr(data); + inline for _ in 0.. uintptr { + h := u64(seed) + 0xcbf29ce484222325; + p := uintptr(data); + for _ in 0.. uintptr { return inline _default_hasher_const(data, seed, 1); } +default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); } +default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); } +default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); } +default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 16); } +default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { + h := u64(seed) + 0xcbf29ce484222325; + str := (^[]byte)(data)^; + for b in str { + h = (h ~ u64(b)) * 0x100000001b3; + } + return uintptr(h); +} + + source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr { hash := _fnv64a(transmute([]byte)s.file_path); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e2a6089b9..6ba25619f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -91,7 +91,7 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); void set_procedure_abi_types(Type *type); void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); - +void add_map_key_type_dependencies(CheckerContext *ctx, Type *key); Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem); Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem); @@ -6081,6 +6081,29 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_equal_proc; break; } + + case BuiltinProc_type_hasher_proc: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_valid_for_keys(type)) { + gbString t = type_to_string(type); + error(ce->args[0], "Expected a valid type for map keys for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + add_map_key_type_dependencies(c, type); + + operand->mode = Addressing_Value; + operand->type = t_hasher_proc; + break; + } } return true; diff --git a/src/check_type.cpp b/src/check_type.cpp index 2d2a5b3f0..758d1969b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2848,6 +2848,26 @@ void init_map_internal_types(Type *type) { type->Map.lookup_result_type = make_optional_ok_type(value); } +void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) { + if (is_type_string(key)) { + add_package_dependency(ctx, "runtime", "default_hash_string"); + add_package_dependency(ctx, "runtime", "default_hasher_string"); + } else if (!is_type_polymorphic(key)) { + add_package_dependency(ctx, "runtime", "default_hash_ptr"); + GB_ASSERT_MSG(is_type_simple_compare(key), "%s", type_to_string(key)); + + i64 sz = type_size_of(key); + switch (sz) { + case 1: add_package_dependency(ctx, "runtime", "default_hasher1"); break; + case 2: add_package_dependency(ctx, "runtime", "default_hasher2"); break; + case 4: add_package_dependency(ctx, "runtime", "default_hasher4"); break; + case 8: add_package_dependency(ctx, "runtime", "default_hasher8"); break; + case 16: add_package_dependency(ctx, "runtime", "default_hasher16"); break; + default: GB_PANIC("unhandled hasher for key type: %s", type_to_string(key)); + } + } +} + void check_map_type(CheckerContext *ctx, Type *type, Ast *node) { GB_ASSERT(type->kind == Type_Map); ast_node(mt, MapType, node); @@ -2864,16 +2884,16 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) { gb_string_free(str); } } + if (type_size_of(key) == 0) { + gbString str = type_to_string(key); + error(node, "Invalid type of a key for a map of size 0, got '%s'", str); + gb_string_free(str); + } type->Map.key = key; type->Map.value = value; - if (is_type_string(key)) { - add_package_dependency(ctx, "runtime", "default_hash_string"); - } else { - add_package_dependency(ctx, "runtime", "default_hash_ptr"); - } - + add_map_key_type_dependencies(ctx, key); init_core_map_type(ctx->checker); init_map_internal_types(type); diff --git a/src/checker.cpp b/src/checker.cpp index ee3496fbf..901f5439c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -729,9 +729,13 @@ void init_universal(void) { { void set_procedure_abi_types(Type *type); - Type *args[2] = {t_rawptr, t_rawptr}; - t_equal_proc = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless); + Type *equal_args[2] = {t_rawptr, t_rawptr}; + t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless); set_procedure_abi_types(t_equal_proc); + + Type *hasher_args[2] = {t_rawptr, t_uintptr}; + t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless); + set_procedure_abi_types(t_hasher_proc); } // Constants diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 9a5cdb1b8..b2157b3c1 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -184,6 +184,7 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_field_index_of, BuiltinProc_type_equal_proc, + BuiltinProc_type_hasher_proc, BuiltinProc__type_end, @@ -369,7 +370,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, }; diff --git a/src/ir.cpp b/src/ir.cpp index b1deda0b9..0c0a92667 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -24,7 +24,9 @@ struct irModule { Map entity_names; // Key: Entity * of the typename Map debug_info; // Key: Unique pointer Map anonymous_proc_lits; // Key: Ast * - Map compare_procs; // Key: Type * + + Map equal_procs; // Key: Type * + Map hasher_procs; // Key: Type * irDebugInfo * debug_compile_unit; Array debug_location_stack; @@ -4875,7 +4877,7 @@ irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) { Type *pt = alloc_type_pointer(type); auto key = hash_type(type); - irValue **found = map_get(&m->compare_procs, key); + irValue **found = map_get(&m->equal_procs, key); if (found) { return *found; } @@ -4883,7 +4885,7 @@ irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) { static u32 proc_index = 0; char buf[16] = {}; - isize n = gb_snprintf(buf, 16, "__$cmp%u", ++proc_index); + isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index); char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); String proc_name = make_string_c(str); @@ -4894,6 +4896,7 @@ irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) { irValue *p = ir_value_procedure(m, e, t_equal_proc, nullptr, body, proc_name); map_set(&m->values, hash_entity(e), p); string_map_set(&m->members, proc_name, p); + map_set(&m->equal_procs, key, p); irProcedure *proc = &p->Proc; proc->is_startup = true; @@ -4957,7 +4960,83 @@ irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) { ir_end_procedure_body(proc); - map_set(&m->compare_procs, key, p); + return p; +} + + +irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { + Type *original_type = type; + type = base_type(type); + Type *pt = alloc_type_pointer(type); + + GB_ASSERT(is_type_valid_for_keys(type)); + + auto key = hash_type(type); + irValue **found = map_get(&m->hasher_procs, key); + if (found) { + return *found; + } + + static u32 proc_index = 0; + + char buf[16] = {}; + isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index); + char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); + String proc_name = make_string_c(str); + + + Ast *body = alloc_ast_node(nullptr, Ast_Invalid); + Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_hasher_proc, 0); + e->Procedure.link_name = proc_name; + irValue *p = ir_value_procedure(m, e, t_hasher_proc, nullptr, body, proc_name); + map_set(&m->values, hash_entity(e), p); + string_map_set(&m->members, proc_name, p); + map_set(&m->hasher_procs, key, p); + + irProcedure *proc = &p->Proc; + proc->is_startup = true; + proc->ignore_dead_instr = true; + ir_begin_procedure_body(proc); + // ir_start_block(proc, proc->decl_block); + GB_ASSERT(proc->curr_block != nullptr); + + irValue *data = proc->params[0]; + irValue *seed = proc->params[1]; + + if (type->kind == Type_Struct) { + type_set_offsets(type); + + GB_PANIC("Type_Struct"); + } else if (is_type_string(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else { + GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); + + i64 sz = type_size_of(type); + char const *name = nullptr; + switch (sz) { + case 1: name = "default_hasher1"; break; + case 2: name = "default_hasher2"; break; + case 4: name = "default_hasher4"; break; + case 8: name = "default_hasher8"; break; + case 16: name = "default_hasher16"; break; + default: GB_PANIC("unhandled hasher for key type: %s", type_to_string(type)); + } + GB_ASSERT(name != nullptr); + + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + irValue *res = ir_emit_runtime_call(proc, name, args); + ir_emit(proc, ir_instr_return(proc, res)); + } + + + ir_end_procedure_body(proc); return p; } @@ -7589,6 +7668,8 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu case BuiltinProc_type_equal_proc: return ir_get_equal_proc_for_type(proc->module, ce->args[0]->tav.type); + case BuiltinProc_type_hasher_proc: + return ir_get_hasher_proc_for_type(proc->module, ce->args[0]->tav.type); } GB_PANIC("Unhandled built-in procedure"); @@ -11705,7 +11786,8 @@ void ir_init_module(irModule *m, Checker *c) { map_init(&m->debug_info, heap_allocator()); map_init(&m->entity_names, heap_allocator()); map_init(&m->anonymous_proc_lits, heap_allocator()); - map_init(&m->compare_procs, heap_allocator()); + map_init(&m->equal_procs, heap_allocator()); + map_init(&m->hasher_procs, heap_allocator()); array_init(&m->procs, heap_allocator()); array_init(&m->procs_to_generate, heap_allocator()); array_init(&m->foreign_library_paths, heap_allocator()); @@ -12447,10 +12529,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *key = ir_emit_struct_ep(proc, tag, 0); irValue *value = ir_emit_struct_ep(proc, tag, 1); irValue *generated_struct = ir_emit_struct_ep(proc, tag, 2); + irValue *key_equal = ir_emit_struct_ep(proc, tag, 3); + irValue *key_hasher = ir_emit_struct_ep(proc, tag, 4); ir_emit_store(proc, key, ir_get_type_info_ptr(proc, t->Map.key)); ir_emit_store(proc, value, ir_get_type_info_ptr(proc, t->Map.value)); ir_emit_store(proc, generated_struct, ir_get_type_info_ptr(proc, t->Map.generated_struct_type)); + ir_emit_store(proc, key_equal, ir_get_equal_proc_for_type(proc->module, t->Map.key)); + ir_emit_store(proc, key_hasher, ir_get_hasher_proc_for_type(proc->module, t->Map.key)); break; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 39b4030af..bc024ab8b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8559,6 +8559,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_type_equal_proc: return lb_get_equal_proc_for_type(p->module, ce->args[0]->tav.type); + + case BuiltinProc_type_hasher_proc: + return lb_get_hasher_proc_for_type(p->module, ce->args[0]->tav.type); } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); @@ -9169,84 +9172,156 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { LLVMTypeRef ptr_type = lb_type(m, pt); auto key = hash_type(type); - lbProcedure **found = map_get(&m->compare_procs, key); + lbProcedure **found = map_get(&m->equal_procs, key); lbProcedure *compare_proc = nullptr; if (found) { compare_proc = *found; - } else { - static u32 proc_index = 0; + GB_ASSERT(compare_proc != nullptr); + return {compare_proc->value, compare_proc->type}; + } - char buf[16] = {}; - isize n = gb_snprintf(buf, 16, "__$cmp%u", ++proc_index); - char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); - String proc_name = make_string_c(str); + static u32 proc_index = 0; - lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc); - lb_begin_procedure_body(p); + char buf[16] = {}; + isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index); + char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); + String proc_name = make_string_c(str); - LLVMValueRef x = LLVMGetParam(p->value, 0); - LLVMValueRef y = LLVMGetParam(p->value, 1); - x = LLVMBuildPointerCast(p->builder, x, ptr_type, ""); - y = LLVMBuildPointerCast(p->builder, y, ptr_type, ""); - lbValue lhs = {x, pt}; - lbValue rhs = {y, pt}; + lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc); + map_set(&m->equal_procs, key, p); + lb_begin_procedure_body(p); + + LLVMValueRef x = LLVMGetParam(p->value, 0); + LLVMValueRef y = LLVMGetParam(p->value, 1); + x = LLVMBuildPointerCast(p->builder, x, ptr_type, ""); + y = LLVMBuildPointerCast(p->builder, y, ptr_type, ""); + lbValue lhs = {x, pt}; + lbValue rhs = {y, pt}; - lbBlock *block_same_ptr = lb_create_block(p, "same_ptr"); - lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr"); + lbBlock *block_same_ptr = lb_create_block(p, "same_ptr"); + lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr"); - lbValue same_ptr = lb_emit_comp(p, Token_CmpEq, lhs, rhs); - lb_emit_if(p, same_ptr, block_same_ptr, block_diff_ptr); - lb_start_block(p, block_same_ptr); - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); + lbValue same_ptr = lb_emit_comp(p, Token_CmpEq, lhs, rhs); + lb_emit_if(p, same_ptr, block_same_ptr, block_diff_ptr); + lb_start_block(p, block_same_ptr); + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); - lb_start_block(p, block_diff_ptr); + lb_start_block(p, block_diff_ptr); - if (type->kind == Type_Struct) { - type_set_offsets(type); + if (type->kind == Type_Struct) { + type_set_offsets(type); - lbBlock *block_false = lb_create_block(p, "bfalse"); - lbValue res = lb_const_bool(m, t_bool, true); + lbBlock *block_false = lb_create_block(p, "bfalse"); + lbValue res = lb_const_bool(m, t_bool, true); - for_array(i, type->Struct.fields) { - lbBlock *next_block = lb_create_block(p, "btrue"); + for_array(i, type->Struct.fields) { + lbBlock *next_block = lb_create_block(p, "btrue"); - lbValue pleft = lb_emit_struct_ep(p, lhs, cast(i32)i); - lbValue pright = lb_emit_struct_ep(p, rhs, cast(i32)i); - lbValue left = lb_emit_load(p, pleft); - lbValue right = lb_emit_load(p, pright); - lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); - - lb_emit_if(p, ok, next_block, block_false); - - lb_emit_jump(p, next_block); - lb_start_block(p, next_block); - } - - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); - - lb_start_block(p, block_false); - - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); - } else { - lbValue left = lb_emit_load(p, lhs); - lbValue right = lb_emit_load(p, rhs); + lbValue pleft = lb_emit_struct_ep(p, lhs, cast(i32)i); + lbValue pright = lb_emit_struct_ep(p, rhs, cast(i32)i); + lbValue left = lb_emit_load(p, pleft); + lbValue right = lb_emit_load(p, pright); lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); - ok = lb_emit_conv(p, ok, t_bool); - LLVMBuildRet(p->builder, ok.value); + + lb_emit_if(p, ok, next_block, block_false); + + lb_emit_jump(p, next_block); + lb_start_block(p, next_block); } - lb_end_procedure_body(p); + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); - map_set(&m->compare_procs, key, p); + lb_start_block(p, block_false); - compare_proc = p; + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + } else { + lbValue left = lb_emit_load(p, lhs); + lbValue right = lb_emit_load(p, rhs); + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + ok = lb_emit_conv(p, ok, t_bool); + LLVMBuildRet(p->builder, ok.value); } - GB_ASSERT(compare_proc != nullptr); + lb_end_procedure_body(p); + + compare_proc = p; return {compare_proc->value, compare_proc->type}; } +lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { + Type *original_type = type; + type = base_type(type); + GB_ASSERT(is_type_valid_for_keys(type)); + + Type *pt = alloc_type_pointer(type); + LLVMTypeRef ptr_type = lb_type(m, pt); + + auto key = hash_type(type); + lbProcedure **found = map_get(&m->hasher_procs, key); + lbProcedure *hasher_proc = nullptr; + if (found) { + hasher_proc = *found; + GB_ASSERT(hasher_proc != nullptr); + return {hasher_proc->value, hasher_proc->type}; + } + + static u32 proc_index = 0; + + char buf[16] = {}; + isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index); + char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); + String proc_name = make_string_c(str); + + lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_hasher_proc); + map_set(&m->hasher_procs, key, p); + lb_begin_procedure_body(p); + + LLVMValueRef x = LLVMGetParam(p->value, 0); + LLVMValueRef y = LLVMGetParam(p->value, 1); + lbValue data = {x, t_rawptr}; + lbValue seed = {y, t_uintptr}; + + if (type->kind == Type_Struct) { + type_set_offsets(type); + + GB_PANIC("Type_Struct"); + } else if (is_type_string(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args); + LLVMBuildRet(p->builder, res.value); + } else { + GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); + + i64 sz = type_size_of(type); + char const *name = nullptr; + switch (sz) { + case 1: name = "default_hasher1"; break; + case 2: name = "default_hasher2"; break; + case 4: name = "default_hasher4"; break; + case 8: name = "default_hasher8"; break; + case 16: name = "default_hasher16"; break; + default: GB_PANIC("unhandled hasher for key type: %s", type_to_string(type)); + } + GB_ASSERT(name != nullptr); + + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_runtime_call(p, name, args); + LLVMBuildRet(p->builder, res.value); + } + + lb_end_procedure_body(p); + + hasher_proc = p; + return {hasher_proc->value, hasher_proc->type}; +} + + + lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) { Type *a = core_type(left.type); Type *b = core_type(right.type); @@ -11571,7 +11646,8 @@ void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->const_strings, a); map_init(&m->anonymous_proc_lits, a); map_init(&m->function_type_map, a); - map_init(&m->compare_procs, a); + map_init(&m->equal_procs, a); + map_init(&m->hasher_procs, a); array_init(&m->procedures_to_generate, a); array_init(&m->foreign_library_paths, a); @@ -12307,10 +12383,12 @@ 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); - LLVMValueRef vals[3] = { + LLVMValueRef vals[5] = { lb_get_type_info_ptr(m, t->Map.key).value, lb_get_type_info_ptr(m, t->Map.value).value, lb_get_type_info_ptr(m, t->Map.generated_struct_type).value, + lb_get_equal_proc_for_type(m, t->Map.key).value, + lb_get_hasher_proc_for_type(m, t->Map.key).value }; lbValue res = {}; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 48ffd27a0..52b2b1e0f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -106,7 +106,8 @@ struct lbModule { Map anonymous_proc_lits; // Key: Ast * Map function_type_map; // Key: Type * - Map compare_procs; // Key: Type * + Map equal_procs; // Key: Type * + Map hasher_procs; // Key: Type * u32 global_array_index; u32 global_generated_index; @@ -380,6 +381,7 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos); lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type); +lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type); lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" diff --git a/src/types.cpp b/src/types.cpp index 75783f948..5c8db71e1 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -690,7 +690,8 @@ gb_global Type *t_map_header = nullptr; gb_global Type *t_vector_x86_mmx = nullptr; -gb_global Type *t_equal_proc = nullptr; +gb_global Type *t_equal_proc = nullptr; +gb_global Type *t_hasher_proc = nullptr; i64 type_size_of (Type *t); @@ -1949,6 +1950,9 @@ bool is_type_simple_compare(Type *t) { if (t->Basic.flags & BasicFlag_SimpleCompare) { return true; } + if (t->Basic.kind == Basic_typeid) { + return true; + } return false; case Type_Pointer: From 1dfe0cdd1de44d6c8b94c4e902a731655f8972c9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 15:50:29 +0000 Subject: [PATCH 111/170] Simplify hashing approach `map` --- core/runtime/core.odin | 6 ++-- core/runtime/dynamic_map_internal.odin | 10 ++++++ src/check_type.cpp | 6 ++-- src/ir.cpp | 50 +++++++++----------------- src/llvm_backend.cpp | 44 ++++++++--------------- src/types.cpp | 3 ++ 6 files changed, 51 insertions(+), 68 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 74b1338c7..b0c76720a 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -42,8 +42,10 @@ Platform_Endianness :: enum u8 { Big = 2, } -Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool; -Hasher_Proc :: distinct proc "contextless" (data: rawptr, seed: uintptr) -> uintptr; +// Procedure type to test whether two values of the same type are equal +Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool; +// Procedure type to hash a value, default seed value is 0 +Hasher_Proc :: distinct proc "contextless" (data: rawptr, seed: uintptr = 0) -> uintptr; Type_Info_Struct_Soa_Kind :: enum u8 { None = 0, diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 99c2e92ee..8ae366db3 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -110,6 +110,16 @@ default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uin } return uintptr(h); } +default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { + h := u64(seed) + 0xcbf29ce484222325; + ptr := (^uintptr)(data)^; + for (^byte)(ptr)^ != 0 { + b := (^byte)(ptr)^; + h = (h ~ u64(b)) * 0x100000001b3; + ptr += 1; + } + return uintptr(h); +} diff --git a/src/check_type.cpp b/src/check_type.cpp index 758d1969b..8da410fa9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2849,11 +2849,11 @@ void init_map_internal_types(Type *type) { } void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) { - if (is_type_string(key)) { - add_package_dependency(ctx, "runtime", "default_hash_string"); + if (is_type_cstring(key)) { + add_package_dependency(ctx, "runtime", "default_hasher_cstring"); + } else if (is_type_string(key)) { add_package_dependency(ctx, "runtime", "default_hasher_string"); } else if (!is_type_polymorphic(key)) { - add_package_dependency(ctx, "runtime", "default_hash_ptr"); GB_ASSERT_MSG(is_type_simple_compare(key), "%s", type_to_string(key)); i64 sz = type_size_of(key); diff --git a/src/ir.cpp b/src/ir.cpp index 0c0a92667..90b0eda2a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -532,6 +532,7 @@ irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, i void ir_begin_procedure_body(irProcedure *proc); void ir_end_procedure_body(irProcedure *proc); irValue *ir_get_equal_proc_for_type(irModule *m, Type *type); +irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type); irAddr ir_addr(irValue *addr) { @@ -3620,43 +3621,18 @@ irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) { Type *t = base_type(ir_type(key)); key = ir_emit_conv(proc, key, key_type); - if (is_type_string(t)) { - irValue *str = ir_emit_conv(proc, key, t_string); - irValue *hashed_str = nullptr; - - if (str->kind == irValue_Constant) { - ExactValue ev = str->Constant.value; - GB_ASSERT(ev.kind == ExactValue_String); - u64 hs = fnv64a(ev.value_string.text, ev.value_string.len); - if (build_context.word_size == 4) { - hs &= 0xffffffff; - } - hashed_str = ir_value_constant(t_uintptr, exact_value_u64(hs)); - } else { - auto args = array_make(ir_allocator(), 1); - args[0] = str; - hashed_str = ir_emit_runtime_call(proc, "default_hash_string", args); - } - ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_str); - } else { - i64 sz = type_size_of(t); - GB_ASSERT(sz <= 8); - if (sz != 0) { - auto args = array_make(ir_allocator(), 2); - args[0] = ir_address_from_load_or_generate_local(proc, key); - args[1] = ir_const_int(sz); - irValue *hash = ir_emit_runtime_call(proc, "default_hash_ptr", args); - - irValue *hash_ptr = ir_emit_struct_ep(proc, v, 0); - ir_emit_store(proc, hash_ptr, hash); - } - } - irValue *key_ptr = ir_address_from_load_or_generate_local(proc, key); key_ptr = ir_emit_conv(proc, key_ptr, t_rawptr); - irValue *key_data = ir_emit_struct_ep(proc, v, 1); - ir_emit_store(proc, key_data, key_ptr); + irValue *hasher = ir_get_hasher_proc_for_type(proc->module, key_type); + + auto args = array_make(permanent_allocator(), 2); + args[0] = key_ptr; + args[1] = ir_value_constant(t_uintptr, exact_value_i64(0)); + irValue *hashed_key = ir_emit_call(proc, hasher, args); + + ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_key); + ir_emit_store(proc, ir_emit_struct_ep(proc, v, 1), key_ptr); return ir_emit_load(proc, v); } @@ -5007,6 +4983,12 @@ irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { type_set_offsets(type); GB_PANIC("Type_Struct"); + } else if (is_type_cstring(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + irValue *res = ir_emit_runtime_call(proc, "default_hasher_cstring", args); + ir_emit(proc, ir_instr_return(proc, res)); } else if (is_type_string(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index bc024ab8b..bea0beab9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9286,6 +9286,12 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { type_set_offsets(type); GB_PANIC("Type_Struct"); + } else if (is_type_cstring(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args); + LLVMBuildRet(p->builder, res.value); } else if (is_type_string(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; @@ -10375,37 +10381,17 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { Type *t = base_type(key.type); key = lb_emit_conv(p, key, key_type); - if (is_type_string(t)) { - lbValue str = lb_emit_conv(p, key, t_string); - lbValue hashed_str = {}; - - if (lb_is_const(str)) { - String v = lb_get_const_string(p->module, str); - u64 hs = fnv64a(v.text, v.len); - if (build_context.word_size == 4) { - hs &= 0xffffffff; - } - hashed_str = lb_const_int(p->module, t_uintptr, hs); - } else { - auto args = array_make(permanent_allocator(), 1); - args[0] = str; - hashed_str = lb_emit_runtime_call(p, "default_hash_string", args); - } - lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_str); - } else { - i64 sz = type_size_of(t); - GB_ASSERT(sz <= 8); - if (sz != 0) { - auto args = array_make(permanent_allocator(), 2); - args[0] = lb_address_from_load_or_generate_local(p, key); - args[1] = lb_const_int(p->module, t_int, sz); - lbValue hash = lb_emit_runtime_call(p, "default_hash_ptr", args); - lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hash); - } - } - lbValue key_ptr = lb_address_from_load_or_generate_local(p, key); key_ptr = lb_emit_conv(p, key_ptr, t_rawptr); + + lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type); + + auto args = array_make(permanent_allocator(), 2); + args[0] = key_ptr; + args[1] = lb_const_int(p->module, t_uintptr, 0); + lbValue 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); diff --git a/src/types.cpp b/src/types.cpp index 5c8db71e1..26f187a62 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1922,6 +1922,9 @@ bool is_type_comparable(Type *t) { return is_type_comparable(t->Opaque.elem); case Type_Struct: + if (type_size_of(t) == 0) { + return false; + } if (t->Struct.is_raw_union) { return is_type_simple_compare(t); } From 7fbc081119c57bf378a317fe6618eacd0465834d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 16:03:21 +0000 Subject: [PATCH 112/170] Improve const hash --- src/ir.cpp | 37 +++++++++++++++++++++++++++++------ src/llvm_backend.cpp | 46 ++++++++++++++++++++++++++++++++++---------- src/llvm_backend.hpp | 2 +- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 90b0eda2a..cb60b9e3b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3615,6 +3615,29 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty return ir_emit_load(proc, h); } +irValue *ir_const_hash(irModule *m, irValue *key, Type *key_type) { + irValue *hashed_key = nullptr; + + if (key->kind == irValue_Constant) { + u64 hash = 0xcbf29ce484222325; + if (is_type_string(key_type)) { + GB_ASSERT(key->Constant.value.kind == ExactValue_String); + String s = key->Constant.value.value_string; + hash = fnv64a(s.text, s.len); + } else { + return nullptr; + } + // TODO(bill): other const hash types + + if (build_context.word_size == 4) { + hash &= 0xffffffffull; + } + hashed_key = ir_const_uintptr(hash); + } + + return hashed_key; +} + irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) { Type *hash_type = t_u64; irValue *v = ir_add_local_generated(proc, t_map_hash, true); @@ -3624,12 +3647,14 @@ irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) { irValue *key_ptr = ir_address_from_load_or_generate_local(proc, key); key_ptr = ir_emit_conv(proc, key_ptr, t_rawptr); - irValue *hasher = ir_get_hasher_proc_for_type(proc->module, key_type); - - auto args = array_make(permanent_allocator(), 2); - args[0] = key_ptr; - args[1] = ir_value_constant(t_uintptr, exact_value_i64(0)); - irValue *hashed_key = ir_emit_call(proc, hasher, args); + irValue *hashed_key = ir_const_hash(proc->module, key, key_type); + if (hashed_key == nullptr) { + irValue *hasher = ir_get_hasher_proc_for_type(proc->module, key_type); + auto args = array_make(permanent_allocator(), 2); + args[0] = key_ptr; + args[1] = ir_value_constant(t_uintptr, exact_value_i64(0)); + hashed_key = ir_emit_call(proc, hasher, args); + } ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_key); ir_emit_store(proc, ir_emit_struct_ep(proc, v, 1), key_ptr); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index bea0beab9..a4cd32efa 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -142,7 +142,7 @@ lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { 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_key(p, addr.map.key, map_type->Map.key); + lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key); auto args = array_make(permanent_allocator(), 2); args[0] = h; @@ -592,7 +592,7 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { Type *map_type = base_type(addr.map.type); 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_key(p, addr.map.key, map_type->Map.key); + lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key); auto args = array_make(permanent_allocator(), 2); args[0] = h; @@ -6190,7 +6190,7 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { { 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_key(p, left, rt->Map.key); + lbValue key = lb_gen_map_hash(p, left, rt->Map.key); auto args = array_make(permanent_allocator(), 2); args[0] = h; @@ -10374,7 +10374,30 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { return lb_addr_load(p, h); } -lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { +lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { + lbValue hashed_key = {}; + + if (lb_is_const(key)) { + u64 hash = 0xcbf29ce484222325; + if (is_type_string(key_type)) { + size_t length = 0; + char const *text = LLVMGetAsString(key.value, &length); + hash = fnv64a(text, cast(isize)length); + } else { + return {}; + } + // TODO(bill): other const hash types + + if (build_context.word_size == 4) { + hash &= 0xffffffffull; + } + hashed_key = lb_const_int(m, t_uintptr, hash); + } + + return hashed_key; +} + +lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type) { Type *hash_type = t_u64; lbAddr v = lb_add_local_generated(p, t_map_hash, true); lbValue vp = lb_addr_get_ptr(p, v); @@ -10384,12 +10407,15 @@ lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) { lbValue key_ptr = lb_address_from_load_or_generate_local(p, key); key_ptr = lb_emit_conv(p, key_ptr, t_rawptr); - lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type); + 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); - auto args = array_make(permanent_allocator(), 2); - args[0] = key_ptr; - args[1] = lb_const_int(p->module, t_uintptr, 0); - lbValue hashed_key = lb_emit_call(p, hasher, args); + auto args = array_make(permanent_allocator(), 2); + args[0] = key_ptr; + args[1] = lb_const_int(p->module, t_uintptr, 0); + 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); @@ -10403,7 +10429,7 @@ 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_key(p, map_key, map_type->Map.key); + lbValue key = lb_gen_map_hash(p, map_key, map_type->Map.key); lbValue v = lb_emit_conv(p, map_value, map_type->Map.value); lbAddr value_addr = lb_add_local_generated(p, v.type, false); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 52b2b1e0f..34a5dfacf 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -370,7 +370,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_key(lbProcedure *p, lbValue key, Type *key_type); +lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type); void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node); From 57f5976ac1b5416093ef25d725c30ae5ff270809 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 16:12:21 +0000 Subject: [PATCH 113/170] Support map keys for simple compare types --- core/runtime/dynamic_map_internal.odin | 4 +++- src/check_type.cpp | 5 ++++- src/ir.cpp | 10 ++++++++-- src/llvm_backend.cpp | 10 ++++++++-- src/types.cpp | 4 +++- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 8ae366db3..7d5ff017b 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -76,6 +76,7 @@ default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintp return default_hash(transmute([]byte)(s)); } +@(private) _default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr { h := u64(seed) + 0xcbf29ce484222325; p := uintptr(data); @@ -86,7 +87,8 @@ _default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, } return uintptr(h); } -_default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr { + +default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr { h := u64(seed) + 0xcbf29ce484222325; p := uintptr(data); for _ in 0..kind == Type_Struct) { type_set_offsets(type); - - GB_PANIC("Type_Struct"); + GB_ASSERT(is_type_simple_compare(type)); + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = ir_const_int(sz); + irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); + ir_emit(proc, ir_instr_return(proc, res)); } else if (is_type_cstring(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a4cd32efa..4fe98bfcf 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9284,8 +9284,14 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { if (type->kind == Type_Struct) { type_set_offsets(type); - - GB_PANIC("Type_Struct"); + GB_ASSERT(is_type_simple_compare(type)); + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = lb_const_int(m, t_int, sz); + lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); + LLVMBuildRet(p->builder, res.value); } else if (is_type_cstring(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; diff --git a/src/types.cpp b/src/types.cpp index 26f187a62..f8f7dd66e 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -964,7 +964,6 @@ bool is_type_valid_for_keys(Type *t); Type *alloc_type_map(i64 count, Type *key, Type *value) { if (key != nullptr) { - GB_ASSERT(is_type_valid_for_keys(key)); GB_ASSERT(value != nullptr); } Type *t = alloc_type(Type_Map); @@ -1558,6 +1557,9 @@ bool is_type_valid_for_keys(Type *t) { if (is_type_typeid(t)) { return true; } + if (is_type_simple_compare(t)) { + return true; + } return false; } From b922398a962734b97d65f93db2784939bce36288 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 16:13:16 +0000 Subject: [PATCH 114/170] Sanity check for map key --- src/check_type.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 453348aa6..10ffe076c 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2854,7 +2854,9 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) { } else if (is_type_string(key)) { add_package_dependency(ctx, "runtime", "default_hasher_string"); } else if (!is_type_polymorphic(key)) { - GB_ASSERT_MSG(is_type_simple_compare(key), "%s", type_to_string(key)); + if (!is_type_simple_compare(key)) { + return; + } if (is_type_struct(key)) { add_package_dependency(ctx, "runtime", "default_hasher_n"); From 5ab7ec5b16b08584764b1a02441299e18328df1e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 16:37:19 +0000 Subject: [PATCH 115/170] Support any comparable type for map keys --- src/check_type.cpp | 42 ++++++++++++++----- src/ir.cpp | 96 +++++++++++++++++++++++++++++++++++++++---- src/llvm_backend.cpp | 98 ++++++++++++++++++++++++++++++++++++++++---- src/types.cpp | 6 +++ 4 files changed, 214 insertions(+), 28 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 10ffe076c..9c8308757 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2849,26 +2849,48 @@ void init_map_internal_types(Type *type) { } void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) { + key = core_type(key); + if (is_type_cstring(key)) { add_package_dependency(ctx, "runtime", "default_hasher_cstring"); } else if (is_type_string(key)) { add_package_dependency(ctx, "runtime", "default_hasher_string"); } else if (!is_type_polymorphic(key)) { - if (!is_type_simple_compare(key)) { + if (!is_type_comparable(key)) { return; } - if (is_type_struct(key)) { + if (key->kind == Type_Struct) { add_package_dependency(ctx, "runtime", "default_hasher_n"); - } + if (!is_type_simple_compare(key)) { + for_array(i, key->Struct.fields) { + Entity *field = key->Struct.fields[i]; + add_map_key_type_dependencies(ctx, field->type); + } + } + } else if (key->kind == Type_EnumeratedArray) { + add_package_dependency(ctx, "runtime", "default_hasher_n"); + if (!is_type_simple_compare(key->EnumeratedArray.elem)) { + add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem); + } + } else if (key->kind == Type_Array) { + add_package_dependency(ctx, "runtime", "default_hasher_n"); + if (!is_type_simple_compare(key->Array.elem)) { + add_map_key_type_dependencies(ctx, key->Array.elem); + } + } else { + if (!is_type_simple_compare(key)) { + GB_PANIC("HERE"); + } - i64 sz = type_size_of(key); - switch (sz) { - case 1: add_package_dependency(ctx, "runtime", "default_hasher1"); break; - case 2: add_package_dependency(ctx, "runtime", "default_hasher2"); break; - case 4: add_package_dependency(ctx, "runtime", "default_hasher4"); break; - case 8: add_package_dependency(ctx, "runtime", "default_hasher8"); break; - case 16: add_package_dependency(ctx, "runtime", "default_hasher16"); break; + i64 sz = type_size_of(key); + switch (sz) { + case 1: add_package_dependency(ctx, "runtime", "default_hasher1"); break; + case 2: add_package_dependency(ctx, "runtime", "default_hasher2"); break; + case 4: add_package_dependency(ctx, "runtime", "default_hasher4"); break; + case 8: add_package_dependency(ctx, "runtime", "default_hasher8"); break; + case 16: add_package_dependency(ctx, "runtime", "default_hasher16"); break; + } } } } diff --git a/src/ir.cpp b/src/ir.cpp index 84f943eda..7e3050825 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4967,7 +4967,7 @@ irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) { irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { Type *original_type = type; - type = base_type(type); + type = core_type(type); Type *pt = alloc_type_pointer(type); GB_ASSERT(is_type_valid_for_keys(type)); @@ -5006,14 +5006,92 @@ irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { if (type->kind == Type_Struct) { type_set_offsets(type); - GB_ASSERT(is_type_simple_compare(type)); - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; - args[1] = seed; - args[2] = ir_const_int(sz); - irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); - ir_emit(proc, ir_instr_return(proc, res)); + if (is_type_simple_compare(type)) { + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = ir_const_int(sz); + irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else { + data = ir_emit_conv(proc, data, t_u8_ptr); + + auto args = array_make(permanent_allocator(), 2); + for_array(i, type->Struct.fields) { + i64 offset = type->Struct.offsets[i]; + Entity *field = type->Struct.fields[i]; + irValue *field_hasher = ir_get_hasher_proc_for_type(m, field->type); + irValue *ptr = ir_emit_ptr_offset(proc, data, ir_const_uintptr(offset)); + + args[0] = ptr; + args[1] = seed; + seed = ir_emit_call(proc, field_hasher, args); + } + ir_emit(proc, ir_instr_return(proc, seed)); + } + } else if (type->kind == Type_Array) { + if (is_type_simple_compare(type)) { + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = ir_const_int(sz); + irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else { + irValue *pres = ir_add_local_generated(proc, t_uintptr, false); + ir_emit_store(proc, pres, seed); + + auto args = array_make(permanent_allocator(), 2); + irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); + + auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); + + data = ir_emit_conv(proc, data, pt); + + irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); + args[0] = ptr; + args[1] = ir_emit_load(proc, pres); + irValue *new_seed = ir_emit_call(proc, elem_hasher, args); + ir_emit_store(proc, pres, new_seed); + + ir_loop_end(proc, loop_data); + + irValue *res = ir_emit_load(proc, pres); + ir_emit(proc, ir_instr_return(proc, res)); + } + } else if (type->kind == Type_EnumeratedArray) { + if (is_type_simple_compare(type)) { + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = ir_const_int(sz); + irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else { + irValue *pres = ir_add_local_generated(proc, t_uintptr, false); + ir_emit_store(proc, pres, seed); + + auto args = array_make(permanent_allocator(), 2); + irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); + + auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); + + data = ir_emit_conv(proc, data, pt); + + irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); + args[0] = ptr; + args[1] = ir_emit_load(proc, pres); + irValue *new_seed = ir_emit_call(proc, elem_hasher, args); + ir_emit_store(proc, pres, new_seed); + + ir_loop_end(proc, loop_data); + + irValue *res = ir_emit_load(proc, pres); + ir_emit(proc, ir_instr_return(proc, res)); + } } else if (is_type_cstring(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4fe98bfcf..140d01292 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9251,7 +9251,7 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { Type *original_type = type; - type = base_type(type); + type = core_type(type); GB_ASSERT(is_type_valid_for_keys(type)); Type *pt = alloc_type_pointer(type); @@ -9284,14 +9284,94 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { if (type->kind == Type_Struct) { type_set_offsets(type); - GB_ASSERT(is_type_simple_compare(type)); - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; - args[1] = seed; - args[2] = lb_const_int(m, t_int, sz); - lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); - LLVMBuildRet(p->builder, res.value); + if (is_type_simple_compare(type)) { + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = lb_const_int(m, t_int, sz); + lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); + LLVMBuildRet(p->builder, res.value); + } else { + data = lb_emit_conv(p, data, t_u8_ptr); + + auto args = array_make(permanent_allocator(), 2); + for_array(i, type->Struct.fields) { + i64 offset = type->Struct.offsets[i]; + Entity *field = type->Struct.fields[i]; + lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type); + lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset)); + + args[0] = ptr; + args[1] = seed; + seed = lb_emit_call(p, field_hasher, args); + } + LLVMBuildRet(p->builder, seed.value); + } + } else if (type->kind == Type_Array) { + if (is_type_simple_compare(type)) { + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = lb_const_int(m, t_int, sz); + lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); + LLVMBuildRet(p->builder, res.value); + } else { + lbAddr pres = lb_add_local_generated(p, t_uintptr, false); + lb_addr_store(p, pres, seed); + + auto args = array_make(permanent_allocator(), 2); + lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem); + + auto loop_data = lb_loop_start(p, type->Array.count, t_i32); + + data = lb_emit_conv(p, data, pt); + + lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx); + args[0] = ptr; + args[1] = lb_addr_load(p, pres); + lbValue new_seed = lb_emit_call(p, elem_hasher, args); + lb_addr_store(p, pres, new_seed); + + lb_loop_end(p, loop_data); + + lbValue res = lb_addr_load(p, pres); + LLVMBuildRet(p->builder, res.value); + } + } else if (type->kind == Type_EnumeratedArray) { + if (is_type_simple_compare(type)) { + i64 sz = type_size_of(type); + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = lb_const_int(m, t_int, sz); + lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); + LLVMBuildRet(p->builder, res.value); + } else { + lbAddr res = lb_add_local_generated(p, t_uintptr, false); + lb_addr_store(p, res, seed); + + auto args = array_make(permanent_allocator(), 2); + lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem); + + auto loop_data = lb_loop_start(p, type->EnumeratedArray.count, t_i32); + + data = lb_emit_conv(p, data, pt); + + lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx); + args[0] = ptr; + args[1] = lb_addr_load(p, res); + lbValue new_seed = lb_emit_call(p, elem_hasher, args); + lb_addr_store(p, res, new_seed); + + lb_loop_end(p, loop_data); + + lbValue vres = lb_addr_load(p, res); + LLVMBuildRet(p->builder, vres.value); + } + + } else if (is_type_cstring(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; diff --git a/src/types.cpp b/src/types.cpp index f8f7dd66e..df87cb645 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1542,6 +1542,8 @@ bool is_type_valid_for_keys(Type *t) { if (is_type_untyped(t)) { return false; } + return is_type_comparable(t); +#if 0 if (is_type_integer(t)) { return true; } @@ -1560,8 +1562,12 @@ bool is_type_valid_for_keys(Type *t) { if (is_type_simple_compare(t)) { return true; } + if (is_type_comparable(t)) { + return true; + } return false; +#endif } bool is_type_valid_bit_set_elem(Type *t) { From 9e1341631246e752b7be703a6ef3e4398205b726 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 17:58:54 +0000 Subject: [PATCH 116/170] Simplify simple compare hasher code --- src/ir.cpp | 164 ++++++++++++++++++---------------------- src/llvm_backend.cpp | 176 +++++++++++++++++++------------------------ 2 files changed, 152 insertions(+), 188 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 7e3050825..06d88ae5b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4965,6 +4965,32 @@ irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) { } +irValue *ir_simple_compare_hash(irProcedure *p, Type *type, irValue *data, irValue *seed) { + GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); + + i64 sz = type_size_of(type); + char const *name = nullptr; + switch (sz) { + case 1: name = "default_hasher1"; break; + case 2: name = "default_hasher2"; break; + case 4: name = "default_hasher4"; break; + case 8: name = "default_hasher8"; break; + case 16: name = "default_hasher16"; break; + } + if (name != nullptr) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + return ir_emit_runtime_call(p, name, args); + } + + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = ir_const_int(type_size_of(type)); + return ir_emit_runtime_call(p, "default_hasher_n", args); +} + irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { Type *original_type = type; type = core_type(type); @@ -4998,100 +5024,78 @@ irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { proc->is_startup = true; proc->ignore_dead_instr = true; ir_begin_procedure_body(proc); + defer (ir_end_procedure_body(proc)); + // ir_start_block(proc, proc->decl_block); GB_ASSERT(proc->curr_block != nullptr); irValue *data = proc->params[0]; irValue *seed = proc->params[1]; + if (is_type_simple_compare(type)) { + irValue *res = ir_simple_compare_hash(proc, type, data, seed); + ir_emit(proc, ir_instr_return(proc, res)); + return p; + } + if (type->kind == Type_Struct) { type_set_offsets(type); - if (is_type_simple_compare(type)) { - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; + data = ir_emit_conv(proc, data, t_u8_ptr); + + auto args = array_make(permanent_allocator(), 2); + for_array(i, type->Struct.fields) { + i64 offset = type->Struct.offsets[i]; + Entity *field = type->Struct.fields[i]; + irValue *field_hasher = ir_get_hasher_proc_for_type(m, field->type); + irValue *ptr = ir_emit_ptr_offset(proc, data, ir_const_uintptr(offset)); + + args[0] = ptr; args[1] = seed; - args[2] = ir_const_int(sz); - irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); - ir_emit(proc, ir_instr_return(proc, res)); - } else { - data = ir_emit_conv(proc, data, t_u8_ptr); - - auto args = array_make(permanent_allocator(), 2); - for_array(i, type->Struct.fields) { - i64 offset = type->Struct.offsets[i]; - Entity *field = type->Struct.fields[i]; - irValue *field_hasher = ir_get_hasher_proc_for_type(m, field->type); - irValue *ptr = ir_emit_ptr_offset(proc, data, ir_const_uintptr(offset)); - - args[0] = ptr; - args[1] = seed; - seed = ir_emit_call(proc, field_hasher, args); - } - ir_emit(proc, ir_instr_return(proc, seed)); + seed = ir_emit_call(proc, field_hasher, args); } + ir_emit(proc, ir_instr_return(proc, seed)); } else if (type->kind == Type_Array) { - if (is_type_simple_compare(type)) { - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; - args[1] = seed; - args[2] = ir_const_int(sz); - irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); - ir_emit(proc, ir_instr_return(proc, res)); - } else { - irValue *pres = ir_add_local_generated(proc, t_uintptr, false); - ir_emit_store(proc, pres, seed); + irValue *pres = ir_add_local_generated(proc, t_uintptr, false); + ir_emit_store(proc, pres, seed); - auto args = array_make(permanent_allocator(), 2); - irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); + auto args = array_make(permanent_allocator(), 2); + irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); - auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); + auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); - data = ir_emit_conv(proc, data, pt); + data = ir_emit_conv(proc, data, pt); - irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); - args[0] = ptr; - args[1] = ir_emit_load(proc, pres); - irValue *new_seed = ir_emit_call(proc, elem_hasher, args); - ir_emit_store(proc, pres, new_seed); + irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); + args[0] = ptr; + args[1] = ir_emit_load(proc, pres); + irValue *new_seed = ir_emit_call(proc, elem_hasher, args); + ir_emit_store(proc, pres, new_seed); - ir_loop_end(proc, loop_data); + ir_loop_end(proc, loop_data); - irValue *res = ir_emit_load(proc, pres); - ir_emit(proc, ir_instr_return(proc, res)); - } + irValue *res = ir_emit_load(proc, pres); + ir_emit(proc, ir_instr_return(proc, res)); } else if (type->kind == Type_EnumeratedArray) { - if (is_type_simple_compare(type)) { - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; - args[1] = seed; - args[2] = ir_const_int(sz); - irValue *res = ir_emit_runtime_call(proc, "default_hasher_n", args); - ir_emit(proc, ir_instr_return(proc, res)); - } else { - irValue *pres = ir_add_local_generated(proc, t_uintptr, false); - ir_emit_store(proc, pres, seed); + irValue *pres = ir_add_local_generated(proc, t_uintptr, false); + ir_emit_store(proc, pres, seed); - auto args = array_make(permanent_allocator(), 2); - irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); + auto args = array_make(permanent_allocator(), 2); + irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); - auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); + auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); - data = ir_emit_conv(proc, data, pt); + data = ir_emit_conv(proc, data, pt); - irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); - args[0] = ptr; - args[1] = ir_emit_load(proc, pres); - irValue *new_seed = ir_emit_call(proc, elem_hasher, args); - ir_emit_store(proc, pres, new_seed); + irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); + args[0] = ptr; + args[1] = ir_emit_load(proc, pres); + irValue *new_seed = ir_emit_call(proc, elem_hasher, args); + ir_emit_store(proc, pres, new_seed); - ir_loop_end(proc, loop_data); + ir_loop_end(proc, loop_data); - irValue *res = ir_emit_load(proc, pres); - ir_emit(proc, ir_instr_return(proc, res)); - } + irValue *res = ir_emit_load(proc, pres); + ir_emit(proc, ir_instr_return(proc, res)); } else if (is_type_cstring(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; @@ -5105,25 +5109,7 @@ irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args); ir_emit(proc, ir_instr_return(proc, res)); } else { - GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); - - i64 sz = type_size_of(type); - char const *name = nullptr; - switch (sz) { - case 1: name = "default_hasher1"; break; - case 2: name = "default_hasher2"; break; - case 4: name = "default_hasher4"; break; - case 8: name = "default_hasher8"; break; - case 16: name = "default_hasher16"; break; - default: GB_PANIC("unhandled hasher for key type: %s", type_to_string(type)); - } - GB_ASSERT(name != nullptr); - - auto args = array_make(permanent_allocator(), 2); - args[0] = data; - args[1] = seed; - irValue *res = ir_emit_runtime_call(proc, name, args); - ir_emit(proc, ir_instr_return(proc, res)); + GB_PANIC("Unhandled type for hasher: %s", type_to_string(type)); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 140d01292..b37744b20 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9249,6 +9249,32 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { return {compare_proc->value, compare_proc->type}; } +lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) { + GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); + + i64 sz = type_size_of(type); + char const *name = nullptr; + switch (sz) { + case 1: name = "default_hasher1"; break; + case 2: name = "default_hasher2"; break; + case 4: name = "default_hasher4"; break; + case 8: name = "default_hasher8"; break; + case 16: name = "default_hasher16"; break; + } + if (name != nullptr) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + return lb_emit_runtime_call(p, name, args); + } + + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = lb_const_int(p->module, t_int, type_size_of(type)); + return lb_emit_runtime_call(p, "default_hasher_n", args); +} + lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { Type *original_type = type; type = core_type(type); @@ -9259,11 +9285,9 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { auto key = hash_type(type); lbProcedure **found = map_get(&m->hasher_procs, key); - lbProcedure *hasher_proc = nullptr; if (found) { - hasher_proc = *found; - GB_ASSERT(hasher_proc != nullptr); - return {hasher_proc->value, hasher_proc->type}; + GB_ASSERT(*found != nullptr); + return {(*found)->value, (*found)->type}; } static u32 proc_index = 0; @@ -9276,102 +9300,77 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_hasher_proc); map_set(&m->hasher_procs, key, p); lb_begin_procedure_body(p); + defer (lb_end_procedure_body(p)); LLVMValueRef x = LLVMGetParam(p->value, 0); LLVMValueRef y = LLVMGetParam(p->value, 1); lbValue data = {x, t_rawptr}; lbValue seed = {y, t_uintptr}; + if (is_type_simple_compare(type)) { + lbValue res = lb_simple_compare_hash(p, type, data, seed); + LLVMBuildRet(p->builder, res.value); + return {p->value, p->type}; + } + if (type->kind == Type_Struct) { type_set_offsets(type); - if (is_type_simple_compare(type)) { - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; + data = lb_emit_conv(p, data, t_u8_ptr); + + auto args = array_make(permanent_allocator(), 2); + for_array(i, type->Struct.fields) { + i64 offset = type->Struct.offsets[i]; + Entity *field = type->Struct.fields[i]; + lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type); + lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset)); + + args[0] = ptr; args[1] = seed; - args[2] = lb_const_int(m, t_int, sz); - lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); - LLVMBuildRet(p->builder, res.value); - } else { - data = lb_emit_conv(p, data, t_u8_ptr); - - auto args = array_make(permanent_allocator(), 2); - for_array(i, type->Struct.fields) { - i64 offset = type->Struct.offsets[i]; - Entity *field = type->Struct.fields[i]; - lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type); - lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset)); - - args[0] = ptr; - args[1] = seed; - seed = lb_emit_call(p, field_hasher, args); - } - LLVMBuildRet(p->builder, seed.value); + seed = lb_emit_call(p, field_hasher, args); } + LLVMBuildRet(p->builder, seed.value); } else if (type->kind == Type_Array) { - if (is_type_simple_compare(type)) { - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; - args[1] = seed; - args[2] = lb_const_int(m, t_int, sz); - lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); - LLVMBuildRet(p->builder, res.value); - } else { - lbAddr pres = lb_add_local_generated(p, t_uintptr, false); - lb_addr_store(p, pres, seed); + lbAddr pres = lb_add_local_generated(p, t_uintptr, false); + lb_addr_store(p, pres, seed); - auto args = array_make(permanent_allocator(), 2); - lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem); + auto args = array_make(permanent_allocator(), 2); + lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem); - auto loop_data = lb_loop_start(p, type->Array.count, t_i32); + auto loop_data = lb_loop_start(p, type->Array.count, t_i32); - data = lb_emit_conv(p, data, pt); + data = lb_emit_conv(p, data, pt); - lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx); - args[0] = ptr; - args[1] = lb_addr_load(p, pres); - lbValue new_seed = lb_emit_call(p, elem_hasher, args); - lb_addr_store(p, pres, new_seed); + lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx); + args[0] = ptr; + args[1] = lb_addr_load(p, pres); + lbValue new_seed = lb_emit_call(p, elem_hasher, args); + lb_addr_store(p, pres, new_seed); - lb_loop_end(p, loop_data); + lb_loop_end(p, loop_data); - lbValue res = lb_addr_load(p, pres); - LLVMBuildRet(p->builder, res.value); - } + lbValue res = lb_addr_load(p, pres); + LLVMBuildRet(p->builder, res.value); } else if (type->kind == Type_EnumeratedArray) { - if (is_type_simple_compare(type)) { - i64 sz = type_size_of(type); - auto args = array_make(permanent_allocator(), 3); - args[0] = data; - args[1] = seed; - args[2] = lb_const_int(m, t_int, sz); - lbValue res = lb_emit_runtime_call(p, "default_hasher_n", args); - LLVMBuildRet(p->builder, res.value); - } else { - lbAddr res = lb_add_local_generated(p, t_uintptr, false); - lb_addr_store(p, res, seed); + lbAddr res = lb_add_local_generated(p, t_uintptr, false); + lb_addr_store(p, res, seed); - auto args = array_make(permanent_allocator(), 2); - lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem); + auto args = array_make(permanent_allocator(), 2); + lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem); - auto loop_data = lb_loop_start(p, type->EnumeratedArray.count, t_i32); + auto loop_data = lb_loop_start(p, type->EnumeratedArray.count, t_i32); - data = lb_emit_conv(p, data, pt); + data = lb_emit_conv(p, data, pt); - lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx); - args[0] = ptr; - args[1] = lb_addr_load(p, res); - lbValue new_seed = lb_emit_call(p, elem_hasher, args); - lb_addr_store(p, res, new_seed); - - lb_loop_end(p, loop_data); - - lbValue vres = lb_addr_load(p, res); - LLVMBuildRet(p->builder, vres.value); - } + lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx); + args[0] = ptr; + args[1] = lb_addr_load(p, res); + lbValue new_seed = lb_emit_call(p, elem_hasher, args); + lb_addr_store(p, res, new_seed); + lb_loop_end(p, loop_data); + lbValue vres = lb_addr_load(p, res); + LLVMBuildRet(p->builder, vres.value); } else if (is_type_cstring(type)) { auto args = array_make(permanent_allocator(), 2); args[0] = data; @@ -9385,31 +9384,10 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args); LLVMBuildRet(p->builder, res.value); } else { - GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); - - i64 sz = type_size_of(type); - char const *name = nullptr; - switch (sz) { - case 1: name = "default_hasher1"; break; - case 2: name = "default_hasher2"; break; - case 4: name = "default_hasher4"; break; - case 8: name = "default_hasher8"; break; - case 16: name = "default_hasher16"; break; - default: GB_PANIC("unhandled hasher for key type: %s", type_to_string(type)); - } - GB_ASSERT(name != nullptr); - - auto args = array_make(permanent_allocator(), 2); - args[0] = data; - args[1] = seed; - lbValue res = lb_emit_runtime_call(p, name, args); - LLVMBuildRet(p->builder, res.value); + GB_PANIC("Unhandled type for hasher: %s", type_to_string(type)); } - lb_end_procedure_body(p); - - hasher_proc = p; - return {hasher_proc->value, hasher_proc->type}; + return {p->value, p->type}; } From f06f33872ca22ca9afd1787b130ee3fb5fb2d723 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 18:17:21 +0000 Subject: [PATCH 117/170] Make 16 simple hasher cases for small types --- core/runtime/dynamic_map_internal.odin | 16 +++++++++- src/check_type.cpp | 42 +++++++++++--------------- src/ir.cpp | 42 +++++++++++--------------- src/llvm_backend.cpp | 14 +++------ 4 files changed, 55 insertions(+), 59 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 7d5ff017b..667a56575 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -77,7 +77,7 @@ default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintp } @(private) -_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr { +_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr where N <= 16 { h := u64(seed) + 0xcbf29ce484222325; p := uintptr(data); inline for _ in 0.. uintptr { return inline _default_hasher_const(data, seed, 1); } default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); } +default_hasher3 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 3); } default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); } +default_hasher5 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 5); } +default_hasher6 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 6); } +default_hasher7 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 7); } default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); } +default_hasher9 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 9); } +default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 10); } +default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 11); } +default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 12); } +default_hasher13 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 13); } +default_hasher14 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 14); } +default_hasher15 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 15); } default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 16); } + default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { h := u64(seed) + 0xcbf29ce484222325; str := (^[]byte)(data)^; diff --git a/src/check_type.cpp b/src/check_type.cpp index 9c8308757..ab69c89bc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2860,37 +2860,31 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) { return; } + if (is_type_simple_compare(key)) { + i64 sz = type_size_of(key); + if (1 <= sz && sz <= 16) { + char buf[20] = {}; + gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz); + add_package_dependency(ctx, "runtime", buf); + return; + } else { + add_package_dependency(ctx, "runtime", "default_hasher_n"); + return; + } + } + if (key->kind == Type_Struct) { add_package_dependency(ctx, "runtime", "default_hasher_n"); - if (!is_type_simple_compare(key)) { - for_array(i, key->Struct.fields) { - Entity *field = key->Struct.fields[i]; - add_map_key_type_dependencies(ctx, field->type); - } + for_array(i, key->Struct.fields) { + Entity *field = key->Struct.fields[i]; + add_map_key_type_dependencies(ctx, field->type); } } else if (key->kind == Type_EnumeratedArray) { add_package_dependency(ctx, "runtime", "default_hasher_n"); - if (!is_type_simple_compare(key->EnumeratedArray.elem)) { - add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem); - } + add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem); } else if (key->kind == Type_Array) { add_package_dependency(ctx, "runtime", "default_hasher_n"); - if (!is_type_simple_compare(key->Array.elem)) { - add_map_key_type_dependencies(ctx, key->Array.elem); - } - } else { - if (!is_type_simple_compare(key)) { - GB_PANIC("HERE"); - } - - i64 sz = type_size_of(key); - switch (sz) { - case 1: add_package_dependency(ctx, "runtime", "default_hasher1"); break; - case 2: add_package_dependency(ctx, "runtime", "default_hasher2"); break; - case 4: add_package_dependency(ctx, "runtime", "default_hasher4"); break; - case 8: add_package_dependency(ctx, "runtime", "default_hasher8"); break; - case 16: add_package_dependency(ctx, "runtime", "default_hasher16"); break; - } + add_map_key_type_dependencies(ctx, key->Array.elem); } } } diff --git a/src/ir.cpp b/src/ir.cpp index 06d88ae5b..0d9292a50 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4969,15 +4969,10 @@ irValue *ir_simple_compare_hash(irProcedure *p, Type *type, irValue *data, irVal GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); i64 sz = type_size_of(type); - char const *name = nullptr; - switch (sz) { - case 1: name = "default_hasher1"; break; - case 2: name = "default_hasher2"; break; - case 4: name = "default_hasher4"; break; - case 8: name = "default_hasher8"; break; - case 16: name = "default_hasher16"; break; - } - if (name != nullptr) { + if (1 <= sz && sz <= 16) { + char name[20] = {}; + gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz); + auto args = array_make(permanent_allocator(), 2); args[0] = data; args[1] = seed; @@ -5038,7 +5033,19 @@ irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { return p; } - if (type->kind == Type_Struct) { + if (is_type_cstring(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + irValue *res = ir_emit_runtime_call(proc, "default_hasher_cstring", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else if (is_type_string(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else if (type->kind == Type_Struct) { type_set_offsets(type); data = ir_emit_conv(proc, data, t_u8_ptr); @@ -5096,25 +5103,10 @@ irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { irValue *res = ir_emit_load(proc, pres); ir_emit(proc, ir_instr_return(proc, res)); - } else if (is_type_cstring(type)) { - auto args = array_make(permanent_allocator(), 2); - args[0] = data; - args[1] = seed; - irValue *res = ir_emit_runtime_call(proc, "default_hasher_cstring", args); - ir_emit(proc, ir_instr_return(proc, res)); - } else if (is_type_string(type)) { - auto args = array_make(permanent_allocator(), 2); - args[0] = data; - args[1] = seed; - irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args); - ir_emit(proc, ir_instr_return(proc, res)); } else { GB_PANIC("Unhandled type for hasher: %s", type_to_string(type)); } - - ir_end_procedure_body(proc); - return p; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b37744b20..001a44895 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9253,15 +9253,11 @@ lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); i64 sz = type_size_of(type); - char const *name = nullptr; - switch (sz) { - case 1: name = "default_hasher1"; break; - case 2: name = "default_hasher2"; break; - case 4: name = "default_hasher4"; break; - case 8: name = "default_hasher8"; break; - case 16: name = "default_hasher16"; break; - } - if (name != nullptr) { + + if (1 <= sz && sz <= 16) { + char name[20] = {}; + gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz); + auto args = array_make(permanent_allocator(), 2); args[0] = data; args[1] = seed; From dd4f8e9747217cf8b9103603bfba5b199faf0c1f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 18:32:49 +0000 Subject: [PATCH 118/170] Improve default pass manager for -llvm-api --- src/llvm_backend.cpp | 82 ++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 57 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 001a44895..5b9603a13 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12980,39 +12980,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod); defer (LLVMDisposePassManager(default_function_pass_manager)); - /*{ - LLVMAddMemCpyOptPass(default_function_pass_manager); - LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager); - LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager); - LLVMAddAggressiveInstCombinerPass(default_function_pass_manager); - LLVMAddConstantPropagationPass(default_function_pass_manager); - LLVMAddAggressiveDCEPass(default_function_pass_manager); - LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager); - LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager); - LLVMAddCFGSimplificationPass(default_function_pass_manager); - // LLVMAddUnifyFunctionExitNodesPass(default_function_pass_manager); - - if (build_context.optimization_level >= 2) { - LLVMAddAggressiveInstCombinerPass(default_function_pass_manager); - LLVMAddEarlyCSEPass(default_function_pass_manager); - LLVMAddEarlyCSEMemSSAPass(default_function_pass_manager); - LLVMAddLowerExpectIntrinsicPass(default_function_pass_manager); - - LLVMAddAlignmentFromAssumptionsPass(default_function_pass_manager); - LLVMAddLoopRotatePass(default_function_pass_manager); - LLVMAddDeadStoreEliminationPass(default_function_pass_manager); - LLVMAddScalarizerPass(default_function_pass_manager); - LLVMAddReassociatePass(default_function_pass_manager); - LLVMAddAddDiscriminatorsPass(default_function_pass_manager); - LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager); - LLVMAddCorrelatedValuePropagationPass(default_function_pass_manager); - - LLVMAddSLPVectorizePass(default_function_pass_manager); - LLVMAddLoopVectorizePass(default_function_pass_manager); - - } - }*/ - if (build_context.optimization_level == 0 && false) { + { auto dfpm = default_function_pass_manager; LLVMAddMemCpyOptPass(dfpm); @@ -13024,42 +12992,33 @@ void lb_generate_code(lbGenerator *gen) { LLVMAddMergedLoadStoreMotionPass(dfpm); LLVMAddPromoteMemoryToRegisterPass(dfpm); LLVMAddCFGSimplificationPass(dfpm); + LLVMAddTailCallEliminationPass(dfpm); LLVMAddScalarizerPass(dfpm); - } else { - auto dfpm = default_function_pass_manager; - LLVMAddMemCpyOptPass(dfpm); - LLVMAddPromoteMemoryToRegisterPass(dfpm); - LLVMAddMergedLoadStoreMotionPass(dfpm); - LLVMAddAggressiveInstCombinerPass(dfpm); - LLVMAddConstantPropagationPass(dfpm); - LLVMAddAggressiveDCEPass(dfpm); - LLVMAddMergedLoadStoreMotionPass(dfpm); - LLVMAddPromoteMemoryToRegisterPass(dfpm); - LLVMAddCFGSimplificationPass(dfpm); - - - // LLVMAddInstructionCombiningPass(dfpm); LLVMAddSLPVectorizePass(dfpm); LLVMAddLoopVectorizePass(dfpm); LLVMAddEarlyCSEPass(dfpm); LLVMAddEarlyCSEMemSSAPass(dfpm); + LLVMAddConstantPropagationPass(dfpm); LLVMAddScalarizerPass(dfpm); LLVMAddLoopIdiomPass(dfpm); - // LLVMAddAggressiveInstCombinerPass(dfpm); - // LLVMAddLowerExpectIntrinsicPass(dfpm); - // LLVMAddPartiallyInlineLibCallsPass(dfpm); + if (build_context.optimization_level != 0) { + // LLVMAddAggressiveInstCombinerPass(dfpm); + // LLVMAddLowerExpectIntrinsicPass(dfpm); - // LLVMAddAlignmentFromAssumptionsPass(dfpm); - // LLVMAddDeadStoreEliminationPass(dfpm); - // LLVMAddReassociatePass(dfpm); - // LLVMAddAddDiscriminatorsPass(dfpm); - // LLVMAddPromoteMemoryToRegisterPass(dfpm); - // LLVMAddCorrelatedValuePropagationPass(dfpm); - // LLVMAddMemCpyOptPass(dfpm); + // LLVMAddPartiallyInlineLibCallsPass(dfpm); + + // LLVMAddAlignmentFromAssumptionsPass(dfpm); + // LLVMAddDeadStoreEliminationPass(dfpm); + // LLVMAddReassociatePass(dfpm); + // LLVMAddAddDiscriminatorsPass(dfpm); + // LLVMAddPromoteMemoryToRegisterPass(dfpm); + // LLVMAddCorrelatedValuePropagationPass(dfpm); + // LLVMAddMemCpyOptPass(dfpm); + } } @@ -13309,6 +13268,15 @@ void lb_generate_code(lbGenerator *gen) { } } + for_array(i, m->equal_procs.entries) { + lbProcedure *p = m->equal_procs.entries[i].value; + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + } + for_array(i, m->hasher_procs.entries) { + lbProcedure *p = m->hasher_procs.entries[i].value; + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + } + TIME_SECTION("LLVM Module Pass"); From f4f2b8f5ad6bc57e95681687618d4693356dfa0c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 18:42:13 +0000 Subject: [PATCH 119/170] Add `_internal` field to `context` --- core/runtime/core.odin | 3 +++ core/slice/sort.odin | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index b0c76720a..b23358b5c 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -332,6 +332,9 @@ Context :: struct { user_data: any, user_ptr: rawptr, user_index: int, + + // Internal use only + _internal: rawptr, } diff --git a/core/slice/sort.odin b/core/slice/sort.odin index 1e346b1f6..1e7051de5 100644 --- a/core/slice/sort.odin +++ b/core/slice/sort.odin @@ -54,17 +54,17 @@ reverse_sort :: proc(data: $T/[]$E) where ORD(E) { // TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough? sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) { - context.user_ptr = rawptr(key); + context._internal = rawptr(key); sort_by(data, proc(i, j: E) -> bool { - k := (proc(E) -> K)(context.user_ptr); + k := (proc(E) -> K)(context._internal); return k(i) < k(j); }); } reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) { - context.user_ptr = rawptr(key); + context._internal = rawptr(key); sort_by(data, proc(i, j: E) -> bool { - k := (proc(E) -> K)(context.user_ptr); + k := (proc(E) -> K)(context._internal); return k(j) < k(i); }); } From 5803fcc1587c75c15d0672a0917043a02ab2aebe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Nov 2020 20:41:01 +0000 Subject: [PATCH 120/170] Revert function passes --- core/runtime/dynamic_map_internal.odin | 18 +-- src/llvm_backend.cpp | 154 ++++++++++++++++--------- 2 files changed, 108 insertions(+), 64 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 667a56575..e880a043f 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -101,15 +101,15 @@ default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: i // NOTE(bill): There are loads of predefined ones to improve optimizations for small types -default_hasher1 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 1); } -default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); } -default_hasher3 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 3); } -default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); } -default_hasher5 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 5); } -default_hasher6 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 6); } -default_hasher7 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 7); } -default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); } -default_hasher9 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 9); } +default_hasher1 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 1); } +default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); } +default_hasher3 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 3); } +default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); } +default_hasher5 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 5); } +default_hasher6 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 6); } +default_hasher7 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 7); } +default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); } +default_hasher9 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 9); } default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 10); } default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 11); } default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 12); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 5b9603a13..d4e77ccc8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -658,12 +658,14 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { res.value = LLVMBuildZExtOrBitCast(p->builder, field, lb_type(p->module, int_type), ""); return res; } else if (addr.kind == lbAddr_Context) { + lbValue a = addr.addr; + a.value = LLVMBuildPointerCast(p->builder, a.value, lb_type(p->module, t_context_ptr), ""); + if (addr.ctx.sel.index.count > 0) { - lbValue a = addr.addr; lbValue b = lb_emit_deep_field_gep(p, a, addr.ctx.sel); return lb_emit_load(p, b); } else { - return lb_emit_load(p, addr.addr); + return lb_emit_load(p, a); } } else if (addr.kind == lbAddr_SoaVariable) { Type *t = type_deref(addr.addr.type); @@ -1355,7 +1357,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Type_Proc: { if (USE_LLVM_ABI) { - if (m->internal_type_level > 5) { // TODO HACK(bill): is this really enough? + if (m->internal_type_level > 256) { // TODO HACK(bill): is this really enough? return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0); } else { unsigned param_count = 0; @@ -1406,13 +1408,19 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { type_size_of(e_type) <= 1) { param_type = LLVMInt1TypeInContext(m->ctx); } else { - param_type = lb_type(m, e_type); + if (is_type_proc(e_type)) { + param_type = lb_type(m, t_rawptr); + } else { + param_type = lb_type(m, e_type); + } } + params[param_index++] = param_type; } } if (param_index < param_count) { - params[param_index++] = lb_type(m, t_context_ptr); + params[param_index++] = lb_type(m, t_rawptr); + // params[param_index++] = lb_type(m, t_context_ptr); } GB_ASSERT(param_index == param_count); @@ -1480,7 +1488,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } } if (type->Proc.calling_convention == ProcCC_Odin) { - array_add(¶m_types, lb_type(m, t_context_ptr)); + array_add(¶m_types, lb_type(m, t_rawptr)); + // array_add(¶m_types, lb_type(m, t_context_ptr)); } old_abi_fn_type = LLVMFunctionType(return_type, param_types.data, cast(unsigned)param_types.count, type->Proc.c_vararg); @@ -6960,7 +6969,7 @@ lbValue lb_copy_value_to_ptr(lbProcedure *p, lbValue val, Type *new_type, i64 al lbAddr ptr = lb_add_local_generated(p, new_type, false); LLVMSetAlignment(ptr.addr.value, cast(unsigned)alignment); lb_addr_store(p, ptr, val); - ptr.kind = lbAddr_Context; + // ptr.kind = lbAddr_Context; return ptr.addr; } @@ -7381,10 +7390,12 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, args[arg_index++] = arg.value; } if (context_ptr.addr.value != nullptr) { - args[arg_index++] = context_ptr.addr.value; + LLVMValueRef cp = context_ptr.addr.value; + cp = LLVMBuildPointerCast(p->builder, cp, lb_type(p->module, t_rawptr), ""); + args[arg_index++] = cp; } - LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder); - GB_ASSERT(curr_block != p->decl_block->block); + LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder); + GB_ASSERT(curr_block != p->decl_block->block); if (USE_LLVM_ABI) { @@ -12983,7 +12994,73 @@ void lb_generate_code(lbGenerator *gen) { { auto dfpm = default_function_pass_manager; - LLVMAddMemCpyOptPass(dfpm); + if (build_context.optimization_level == 0) { + LLVMAddMemCpyOptPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddMergedLoadStoreMotionPass(dfpm); + LLVMAddAggressiveInstCombinerPass(dfpm); + LLVMAddEarlyCSEPass(dfpm); + LLVMAddEarlyCSEMemSSAPass(dfpm); + LLVMAddConstantPropagationPass(dfpm); + LLVMAddAggressiveDCEPass(dfpm); + LLVMAddMergedLoadStoreMotionPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddCFGSimplificationPass(dfpm); + + + // LLVMAddInstructionCombiningPass(dfpm); + LLVMAddSLPVectorizePass(dfpm); + LLVMAddLoopVectorizePass(dfpm); + + LLVMAddScalarizerPass(dfpm); + LLVMAddLoopIdiomPass(dfpm); + } else { + bool do_extra_passes = true; + + int repeat_count = cast(int)build_context.optimization_level; + do { + LLVMAddMemCpyOptPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddMergedLoadStoreMotionPass(dfpm); + LLVMAddAlignmentFromAssumptionsPass(dfpm); + LLVMAddEarlyCSEPass(dfpm); + LLVMAddEarlyCSEMemSSAPass(dfpm); + LLVMAddConstantPropagationPass(dfpm); + if (do_extra_passes) { + LLVMAddAggressiveInstCombinerPass(dfpm); + LLVMAddAggressiveDCEPass(dfpm); + } + LLVMAddMergedLoadStoreMotionPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddCFGSimplificationPass(dfpm); + LLVMAddTailCallEliminationPass(dfpm); + + if (do_extra_passes) { + LLVMAddSLPVectorizePass(dfpm); + LLVMAddLoopVectorizePass(dfpm); + + LLVMAddConstantPropagationPass(dfpm); + LLVMAddScalarizerPass(dfpm); + LLVMAddLoopIdiomPass(dfpm); + + LLVMAddAggressiveInstCombinerPass(dfpm); + LLVMAddLowerExpectIntrinsicPass(dfpm); + + LLVMAddDeadStoreEliminationPass(dfpm); + LLVMAddReassociatePass(dfpm); + LLVMAddAddDiscriminatorsPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddCorrelatedValuePropagationPass(dfpm); + } + } while (repeat_count --> 0); + } + } + + + LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod); + defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); + { + auto dfpm = default_function_pass_manager_without_memcpy; LLVMAddPromoteMemoryToRegisterPass(dfpm); LLVMAddMergedLoadStoreMotionPass(dfpm); LLVMAddAggressiveInstCombinerPass(dfpm); @@ -12992,48 +13069,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMAddMergedLoadStoreMotionPass(dfpm); LLVMAddPromoteMemoryToRegisterPass(dfpm); LLVMAddCFGSimplificationPass(dfpm); - LLVMAddTailCallEliminationPass(dfpm); - LLVMAddScalarizerPass(dfpm); - - LLVMAddSLPVectorizePass(dfpm); - LLVMAddLoopVectorizePass(dfpm); - LLVMAddEarlyCSEPass(dfpm); - LLVMAddEarlyCSEMemSSAPass(dfpm); - - LLVMAddConstantPropagationPass(dfpm); - LLVMAddScalarizerPass(dfpm); - LLVMAddLoopIdiomPass(dfpm); - - - if (build_context.optimization_level != 0) { - // LLVMAddAggressiveInstCombinerPass(dfpm); - // LLVMAddLowerExpectIntrinsicPass(dfpm); - - // LLVMAddPartiallyInlineLibCallsPass(dfpm); - - // LLVMAddAlignmentFromAssumptionsPass(dfpm); - // LLVMAddDeadStoreEliminationPass(dfpm); - // LLVMAddReassociatePass(dfpm); - // LLVMAddAddDiscriminatorsPass(dfpm); - // LLVMAddPromoteMemoryToRegisterPass(dfpm); - // LLVMAddCorrelatedValuePropagationPass(dfpm); - // LLVMAddMemCpyOptPass(dfpm); - } - } - - - LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod); - defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); - { - LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy); - LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy); - LLVMAddAggressiveInstCombinerPass(default_function_pass_manager_without_memcpy); - LLVMAddConstantPropagationPass(default_function_pass_manager_without_memcpy); - LLVMAddAggressiveDCEPass(default_function_pass_manager_without_memcpy); - LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy); - LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy); - LLVMAddCFGSimplificationPass(default_function_pass_manager_without_memcpy); - // LLVMAddUnifyFunctionExitNodesPass(default_function_pass_manager_without_memcpy); + // LLVMAddUnifyFunctionExitNodesPass(dfpm); } TIME_SECTION("LLVM Runtime Creation"); @@ -13327,8 +13363,16 @@ void lb_generate_code(lbGenerator *gen) { LLVMDIBuilderFinalize(m->debug_builder); - if (LLVMVerifyModule(mod, LLVMAbortProcessAction, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); + if (LLVMVerifyModule(mod, LLVMReturnStatusAction, &llvm_error)) { + gb_printf_err("LLVM Error:\n%s\n", llvm_error); + if (build_context.keep_temp_files) { + TIME_SECTION("LLVM Print Module to File"); + if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return; + } + } gb_exit(1); return; } From 7c1c9d22b48bd84f0d0802c4c6a1c7577bd201e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 30 Nov 2020 19:44:43 +0000 Subject: [PATCH 121/170] Fix `thread.run_with_poly_data*` --- core/thread/thread.odin | 70 ++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/core/thread/thread.odin b/core/thread/thread.odin index b98f4c07c..ffa47faa7 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -57,75 +57,89 @@ run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe( } run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) - where intrinsics.type_is_pointer(T) || size_of(T) == size_of(rawptr) { + where size_of(T) <= size_of(rawptr) { thread_proc :: proc(t: ^Thread) { - fn := cast(proc(rawptr))t.data; + fn := cast(proc(T))t.data; assert(t.user_index >= 1); - data := t.user_args[0]; + data := (^T)(&t.user_args[0])^; fn(data); destroy(t); } t := create(thread_proc, priority); t.data = rawptr(fn); t.user_index = 1; - t.user_args[0] = transmute(rawptr)data; + data := data; + mem.copy(&t.user_args[0], &data, size_of(data)); t.init_context = init_context; start(t); } run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) - where intrinsics.type_is_pointer(T1) || size_of(T1) == size_of(rawptr), - intrinsics.type_is_pointer(T2) || size_of(T2) == size_of(rawptr) { + where size_of(T1) <= size_of(rawptr), + size_of(T2) <= size_of(rawptr) { thread_proc :: proc(t: ^Thread) { - fn := cast(proc(rawptr, rawptr))t.data; + fn := cast(proc(T1, T2))t.data; assert(t.user_index >= 2); - fn(t.user_args[0], t.user_args[1]); + arg1 := (^T1)(&t.user_args[0])^; + arg2 := (^T2)(&t.user_args[1])^; + fn(arg1, arg2); destroy(t); } t := create(thread_proc, priority); t.data = rawptr(fn); t.user_index = 2; - t.user_args[0] = transmute(rawptr)arg1; - t.user_args[1] = transmute(rawptr)arg2; + arg1, arg2 := arg1, arg2; + mem.copy(&t.user_args[0], &arg1, size_of(arg1)); + mem.copy(&t.user_args[1], &arg2, size_of(arg2)); t.init_context = init_context; start(t); } run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) - where intrinsics.type_is_pointer(T1) || size_of(T1) == size_of(rawptr), - intrinsics.type_is_pointer(T2) || size_of(T2) == size_of(rawptr), - intrinsics.type_is_pointer(T3) || size_of(T3) == size_of(rawptr) { + where size_of(T1) <= size_of(rawptr), + size_of(T2) <= size_of(rawptr), + size_of(T3) <= size_of(rawptr) { thread_proc :: proc(t: ^Thread) { - fn := cast(proc(rawptr, rawptr, rawptr))t.data; + fn := cast(proc(T1, T2, T3))t.data; assert(t.user_index >= 3); - fn(t.user_args[0], t.user_args[1], t.user_args[2]); + arg1 := (^T1)(&t.user_args[0])^; + arg2 := (^T2)(&t.user_args[1])^; + arg3 := (^T2)(&t.user_args[2])^; + fn(arg1, arg2, arg3); destroy(t); } t := create(thread_proc, priority); t.data = rawptr(fn); t.user_index = 3; - t.user_args[0] = transmute(rawptr)arg1; - t.user_args[1] = transmute(rawptr)arg2; - t.user_args[2] = transmute(rawptr)arg3; + arg1, arg2, arg3 := arg1, arg2, arg3; + mem.copy(&t.user_args[0], &arg1, size_of(arg1)); + mem.copy(&t.user_args[1], &arg2, size_of(arg2)); + mem.copy(&t.user_args[2], &arg3, size_of(arg3)); t.init_context = init_context; start(t); } run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) - where intrinsics.type_is_pointer(T1) || size_of(T1) == size_of(rawptr), - intrinsics.type_is_pointer(T2) || size_of(T2) == size_of(rawptr), - intrinsics.type_is_pointer(T3) || size_of(T3) == size_of(rawptr) { + where size_of(T1) <= size_of(rawptr), + size_of(T2) <= size_of(rawptr), + size_of(T3) <= size_of(rawptr) { thread_proc :: proc(t: ^Thread) { - fn := cast(proc(rawptr, rawptr, rawptr))t.data; - assert(t.user_index >= 3); - fn(t.user_args[0], t.user_args[1], t.user_args[2]); + fn := cast(proc(T1, T2, T3, T4))t.data; + assert(t.user_index >= 4); + arg1 := (^T1)(&t.user_args[0])^; + arg2 := (^T2)(&t.user_args[1])^; + arg3 := (^T2)(&t.user_args[2])^; + arg4 := (^T2)(&t.user_args[3])^; + fn(arg1, arg2, arg3, arg4); destroy(t); } t := create(thread_proc, priority); t.data = rawptr(fn); - t.user_index = 3; - t.user_args[0] = transmute(rawptr)arg1; - t.user_args[1] = transmute(rawptr)arg2; - t.user_args[2] = transmute(rawptr)arg3; + t.user_index = 4; + arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4; + mem.copy(&t.user_args[0], &arg1, size_of(arg1)); + mem.copy(&t.user_args[1], &arg2, size_of(arg2)); + mem.copy(&t.user_args[2], &arg3, size_of(arg3)); + mem.copy(&t.user_args[3], &arg4, size_of(arg4)); t.init_context = init_context; start(t); } From ef417017f0e6ae061844cc4e0f5e050c6a7a4fc4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 30 Nov 2020 19:46:41 +0000 Subject: [PATCH 122/170] Fix typo --- core/thread/thread.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/thread/thread.odin b/core/thread/thread.odin index ffa47faa7..51fb116e3 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -104,7 +104,7 @@ run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, assert(t.user_index >= 3); arg1 := (^T1)(&t.user_args[0])^; arg2 := (^T2)(&t.user_args[1])^; - arg3 := (^T2)(&t.user_args[2])^; + arg3 := (^T3)(&t.user_args[2])^; fn(arg1, arg2, arg3); destroy(t); } @@ -127,8 +127,8 @@ run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc assert(t.user_index >= 4); arg1 := (^T1)(&t.user_args[0])^; arg2 := (^T2)(&t.user_args[1])^; - arg3 := (^T2)(&t.user_args[2])^; - arg4 := (^T2)(&t.user_args[3])^; + arg3 := (^T3)(&t.user_args[2])^; + arg4 := (^T4)(&t.user_args[3])^; fn(arg1, arg2, arg3, arg4); destroy(t); } From 400816ebf71c94cbc1a4454e72c2d12ec93b847c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 1 Dec 2020 00:40:54 +0000 Subject: [PATCH 123/170] Fix Pointer store in LLVM backend --- src/llvm_backend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d4e77ccc8..37702a4b2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -493,7 +493,7 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); } - if (USE_LLVM_ABI && is_type_proc(a)) { + if (is_type_proc(a)) { // NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be // stored as regular pointer with no procedure information @@ -502,7 +502,7 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { LLVMBuildStore(p->builder, v, ptr.value); } else { Type *ca = core_type(a); - if (ca->kind == Type_Basic) { + if (ca->kind == Type_Basic && ca->kind == Type_Proc) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); } else { GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type)); From 2d0c0a7a83b4061c670dabc991c735ca2ea04e03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 1 Dec 2020 15:38:15 +0000 Subject: [PATCH 124/170] Fix typo --- src/llvm_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 37702a4b2..324473be3 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -502,7 +502,7 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { LLVMBuildStore(p->builder, v, ptr.value); } else { Type *ca = core_type(a); - if (ca->kind == Type_Basic && ca->kind == Type_Proc) { + if (ca->kind == Type_Basic || ca->kind == Type_Proc) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); } else { GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type)); From 32b1537aa3988e791a6e5dcdc858e345b0abb119 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 10:39:59 +0000 Subject: [PATCH 125/170] Fix xor for constant booleans --- src/exact_value.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 051b6d76a..da6ee3dab 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -685,6 +685,8 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool); case Token_And: return exact_value_bool(x.value_bool & y.value_bool); case Token_Or: return exact_value_bool(x.value_bool | y.value_bool); + case Token_Xor: return exact_value_bool(x.value_bool ^ y.value_bool); + case Token_AndNot: return exact_value_bool(x.value_bool & ~y.value_bool); default: goto error; } break; From a9c181102711a9e5437a1b3f103fce53dd40439c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 10:41:18 +0000 Subject: [PATCH 126/170] Fix typo --- src/exact_value.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exact_value.cpp b/src/exact_value.cpp index da6ee3dab..7199a28ac 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -685,8 +685,8 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool); case Token_And: return exact_value_bool(x.value_bool & y.value_bool); case Token_Or: return exact_value_bool(x.value_bool | y.value_bool); - case Token_Xor: return exact_value_bool(x.value_bool ^ y.value_bool); - case Token_AndNot: return exact_value_bool(x.value_bool & ~y.value_bool); + case Token_AndNot: return exact_value_bool(x.value_bool & !y.value_bool); + case Token_Xor: return exact_value_bool((x.value_bool && !y.value_bool) || (!x.value_bool && y.value_bool)); default: goto error; } break; From 996c8540719be980f98cf75640ca8b91278bfe3d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 11:48:50 +0000 Subject: [PATCH 127/170] Disable `lb_const_hash` for the time being --- src/check_expr.cpp | 1 + src/llvm_backend.cpp | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6ba25619f..d31874959 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9469,6 +9469,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } add_package_dependency(c, "runtime", "type_assertion_check"); + add_package_dependency(c, "runtime", "type_assertion_check2"); case_end; case_ast_node(tc, TypeCast, node); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 324473be3..7a3596070 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9871,7 +9871,7 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p Type *dst_type = tuple->Tuple.variables[0]->type; lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 6); + auto args = array_make(permanent_allocator(), 7); args[0] = ok; args[1] = lb_const_string(m, pos.file); @@ -9880,7 +9880,8 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p args[4] = lb_typeid(m, src_type); args[5] = lb_typeid(m, dst_type); - lb_emit_runtime_call(p, "type_assertion_check", args); + args[6] = lb_emit_conv(p, value_, t_rawptr); + lb_emit_runtime_call(p, "type_assertion_check2", args); } return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0)); @@ -9932,7 +9933,7 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos // NOTE(bill): Panic on invalid conversion lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 6); + auto args = array_make(permanent_allocator(), 7); args[0] = ok; args[1] = lb_const_string(m, pos.file); @@ -9941,7 +9942,8 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos args[4] = any_typeid; args[5] = dst_typeid; - lb_emit_runtime_call(p, "type_assertion_check", args); + args[6] = lb_emit_struct_ev(p, value, 0);; + lb_emit_runtime_call(p, "type_assertion_check2", args); return lb_addr(lb_emit_struct_ep(p, v.addr, 0)); } @@ -10446,14 +10448,38 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { } lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { + if (true) { + return {}; + } + lbValue hashed_key = {}; + if (lb_is_const(key)) { u64 hash = 0xcbf29ce484222325; - if (is_type_string(key_type)) { + if (is_type_cstring(key_type)) { size_t length = 0; char const *text = LLVMGetAsString(key.value, &length); hash = fnv64a(text, cast(isize)length); + } else if (is_type_string(key_type)) { + unsigned data_indices[] = {0}; + unsigned len_indices[] = {1}; + LLVMValueRef data = LLVMConstExtractValue(key.value, data_indices, gb_count_of(data_indices)); + LLVMValueRef len = LLVMConstExtractValue(key.value, len_indices, gb_count_of(len_indices)); + isize length = LLVMConstIntGetSExtValue(len); + char const *text = nullptr; + if (length != 0) { + if (LLVMGetConstOpcode(data) != LLVMGetElementPtr) { + return {}; + } + // TODO(bill): THIS IS BROKEN! THIS NEEDS FIXING :P + + size_t ulength = 0; + text = LLVMGetAsString(data, &ulength); + gb_printf_err("%td %td %s\n", length, ulength, text); + length = gb_min(length, cast(isize)ulength); + } + hash = fnv64a(text, cast(isize)length); } else { return {}; } From a6301ab58a1da0accaac3ea3b166bd859bdf455a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 11:50:57 +0000 Subject: [PATCH 128/170] Add type_assertion_check2 --- core/runtime/error_checks.odin | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index b1bc0b646..7c442510e 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -98,6 +98,59 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column handle_error(file, line, column, from, to); } +type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid, from_data: rawptr) { + if ok { + return; + } + + variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid { + if id == nil || data == nil { + return id; + } + ti := type_info_base(type_info_of(id)); + #partial switch v in ti.variant { + case Type_Info_Any: + return (^any)(data).id; + case Type_Info_Union: + tag_ptr := uintptr(data) + v.tag_offset; + idx := 0; + switch v.tag_type.size { + case 1: idx = int((^u8)(tag_ptr)^) - 1; + case 2: idx = int((^u16)(tag_ptr)^) - 1; + case 4: idx = int((^u32)(tag_ptr)^) - 1; + case 8: idx = int((^u64)(tag_ptr)^) - 1; + case 16: idx = int((^u128)(tag_ptr)^) - 1; + } + if idx < 0 { + return nil; + } else if idx < len(v.variants) { + return v.variants[idx].id; + } + } + return id; + } + + handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid, from_data: rawptr) { + context = default_context(); + + actual := variant_type(from, from_data); + + print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_string(" Invalid type assertion from "); + print_typeid(from); + print_string(" to "); + print_typeid(to); + if actual != from { + print_string(", actual type: "); + print_typeid(actual); + } + print_byte('\n'); + type_assertion_trap(); + } + handle_error(file, line, column, from, to, from_data); +} + + make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) { if 0 <= len { return; From 7389ffba6d346f24fb429903c5ddab5a2a560bdc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 21:07:15 +0000 Subject: [PATCH 129/170] Fix -llvm-api const initialization of `&T{}` --- src/llvm_backend.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7a3596070..11b301268 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -13148,6 +13148,8 @@ void lb_generate_code(lbGenerator *gen) { lbValue init = lb_build_expr(p, var->decl->init_expr); if (!lb_is_const(init)) { var->init = init; + } else { + LLVMSetInitializer(var->var.value, init.value); } } From 4d30b8892794c53e02156988a4b9bfe3e9d7e0d2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:11:46 +0000 Subject: [PATCH 130/170] Fix typos --- core/text/scanner/scanner.odin | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index fefa9f5a1..dad9c6d95 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -293,6 +293,9 @@ scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) { case 'z': ch = advance(s); base, prefix = 12, 'z'; + case 'h': + tok = Float; + fallthrough; case 'x': ch = advance(s); base, prefix = 16, 'x'; From d9343ae9973f3a8983df786b28b5c8a891d9f61d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:12:14 +0000 Subject: [PATCH 131/170] Add `package io` --- core/io/conv.odin | 185 ++++++++++++++++++ core/io/io.odin | 461 +++++++++++++++++++++++++++++++++++++++++++++ core/io/multi.odin | 115 +++++++++++ core/io/util.odin | 90 +++++++++ 4 files changed, 851 insertions(+) create mode 100644 core/io/conv.odin create mode 100644 core/io/io.odin create mode 100644 core/io/multi.odin create mode 100644 core/io/util.odin diff --git a/core/io/conv.odin b/core/io/conv.odin new file mode 100644 index 000000000..4afc3490d --- /dev/null +++ b/core/io/conv.odin @@ -0,0 +1,185 @@ +package io + +Conversion_Error :: enum { + None, + Missing_Procedure, + Fallback_Possible, +} + +to_reader :: proc(s: Stream) -> (r: Reader, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read == nil { + err = .Missing_Procedure; + } + return; +} +to_writer :: proc(s: Stream) -> (w: Writer, err: Conversion_Error) { + w.stream = s; + if s.vtable == nil || s.impl_write == nil { + err = .Missing_Procedure; + } + return; +} + +to_closer :: proc(s: Stream) -> (c: Closer, err: Conversion_Error) { + c.stream = s; + if s.vtable == nil || s.impl_close == nil { + err = .Missing_Procedure; + } + return; +} +to_flusher :: proc(s: Stream) -> (f: Flusher, err: Conversion_Error) { + f.stream = s; + if s.vtable == nil || s.impl_flush == nil { + err = .Missing_Procedure; + } + return; +} +to_seeker :: proc(s: Stream) -> (seeker: Seeker, err: Conversion_Error) { + seeker.stream = s; + if s.vtable == nil || s.impl_seek == nil { + err = .Missing_Procedure; + } + return; +} + +to_read_writer :: proc(s: Stream) -> (r: Read_Writer, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read == nil || s.impl_write == nil { + err = .Missing_Procedure; + } + return; +} +to_read_closer :: proc(s: Stream) -> (r: Read_Closer, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read == nil || s.impl_close == nil { + err = .Missing_Procedure; + } + return; +} +to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil { + err = .Missing_Procedure; + } + return; +} +to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil { + err = .Missing_Procedure; + } + return; +} +to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, err: Conversion_Error) { + w.stream = s; + if s.vtable == nil || s.impl_write == nil || s.impl_flush == nil { + err = .Missing_Procedure; + } + return; +} +to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, err: Conversion_Error) { + w.stream = s; + if s.vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil { + err = .Missing_Procedure; + } + return; +} + +to_reader_at :: proc(s: Stream) -> (r: Reader_At, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read_at == nil { + err = .Missing_Procedure; + } + return; +} +to_writer_at :: proc(s: Stream) -> (w: Writer_At, err: Conversion_Error) { + w.stream = s; + if s.vtable == nil || s.impl_write_at == nil { + err = .Missing_Procedure; + } + return; +} +to_reader_from :: proc(s: Stream) -> (r: Reader_From, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read_from == nil { + err = .Missing_Procedure; + } + return; +} +to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) { + w.stream = s; + if s.vtable == nil || s.impl_write_to == nil { + err = .Missing_Procedure; + } + return; +} + +to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) { + b.stream = s; + if s.vtable == nil || s.impl_read_byte == nil { + err = .Missing_Procedure; + if s.vtable != nil && s.impl_read != nil { + err = .Fallback_Possible; + } + } + return; +} +to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, err: Conversion_Error) { + b.stream = s; + if s.vtable != nil { + if s.impl_unread_byte == nil { + err = .Missing_Procedure; + return; + } + if s.impl_read_byte != nil { + err = .None; + } else if s.impl_read != nil { + err = .Fallback_Possible; + } else { + err = .Missing_Procedure; + } + } + return; +} +to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, err: Conversion_Error) { + b.stream = s; + if s.vtable == nil || s.impl_write_byte == nil { + err = .Missing_Procedure; + if s.vtable != nil && s.impl_write != nil { + err = .Fallback_Possible; + } + } + return; +} + +to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, err: Conversion_Error) { + r.stream = s; + if s.vtable == nil || s.impl_read_rune == nil { + err = .Missing_Procedure; + if s.vtable != nil && s.impl_read != nil { + err = .Fallback_Possible; + } + } + return; + +} +to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, err: Conversion_Error) { + r.stream = s; + if s.vtable != nil { + if s.impl_unread_rune == nil { + err = .Missing_Procedure; + return; + } + if s.impl_read_rune != nil { + err = .None; + } else if s.impl_read != nil { + err = .Fallback_Possible; + } else { + err = .Missing_Procedure; + } + } else { + err = .Missing_Procedure; + } + return; +} diff --git a/core/io/io.odin b/core/io/io.odin new file mode 100644 index 000000000..be3bca147 --- /dev/null +++ b/core/io/io.odin @@ -0,0 +1,461 @@ +package io + +import "intrinsics" +import "core:runtime" +import "core:unicode/utf8" + +Seek_From :: enum { + Start = 0, // seek relative to the origin of the file + Current = 1, // seek relative to the current offset + End = 2, // seek relative to the end +} + +Error :: enum i32 { + // No Error + None = 0, + + // EOF is the error returned by `read` when no more input is available + EOF, + + // Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data + Unexpected_EOF, + + // Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error + Short_Write, + + // Short_Buffer means that a read required a longer buffer than was provided + Short_Buffer, + + // No_Progress is returned by some implementations of `io.Reader` when many calls + // to `read` have failed to return any data or error. + // This is usually a signed of a broken `io.Reader` implementation + No_Progress, + + Invalid_Whence, + Invalid_Offset, + Invalid_Unread, + + // Empty is returned when a procedure has not been implemented for an io.Stream + Empty = -1, +} + +Close_Proc :: distinct proc(using s: Stream) -> Error; +Flush_Proc :: distinct proc(using s: Stream) -> Error; +Seek_Proc :: distinct proc(using s: Stream, offset: i64, whence: Seek_From) -> (i64, Error); +Size_Proc :: distinct proc(using s: Stream) -> i64; +Read_Proc :: distinct proc(using s: Stream, p: []byte) -> (n: int, err: Error); +Read_At_Proc :: distinct proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error); +Read_From_Proc :: distinct proc(using s: Stream, r: Reader) -> (n: i64, err: Error); +Read_Byte_Proc :: distinct proc(using s: Stream) -> (byte, Error); +Read_Rune_Proc :: distinct proc(using s: Stream) -> (ch: rune, size: int, err: Error); +Unread_Byte_Proc :: distinct proc(using s: Stream) -> Error; +Unread_Rune_Proc :: distinct proc(using s: Stream) -> Error; +Write_Proc :: distinct proc(using s: Stream, p: []byte) -> (n: int, err: Error); +Write_At_Proc :: distinct proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error); +Write_To_Proc :: distinct proc(using s: Stream, w: Writer) -> (n: i64, err: Error); +Write_Byte_Proc :: distinct proc(using s: Stream, c: byte) -> Error; +Destroy_Proc :: distinct proc(using s: Stream) -> Error; + + +Stream :: struct { + using vtable: ^Stream_VTable, + data: rawptr, +} +Stream_VTable :: struct { + impl_close: Close_Proc, + impl_flush: Flush_Proc, + + impl_seek: Seek_Proc, + impl_size: Size_Proc, + + impl_read: Read_Proc, + impl_read_at: Read_At_Proc, + impl_read_byte: Read_Byte_Proc, + impl_read_rune: Read_Rune_Proc, + impl_write_to: Write_To_Proc, + + impl_write: Write_Proc, + impl_write_at: Write_At_Proc, + impl_write_byte: Write_Byte_Proc, + impl_read_from: Read_From_Proc, + + impl_unread_byte: Unread_Byte_Proc, + impl_unread_rune: Unread_Rune_Proc, + + impl_destroy: Destroy_Proc, +} + + +Reader :: struct {using stream: Stream}; +Writer :: struct {using stream: Stream}; +Closer :: struct {using stream: Stream}; +Flusher :: struct {using stream: Stream}; +Seeker :: struct {using stream: Stream}; + +Read_Writer :: struct {using stream: Stream}; +Read_Closer :: struct {using stream: Stream}; +Read_Write_Closer :: struct {using stream: Stream}; +Read_Write_Seeker :: struct {using stream: Stream}; +Write_Flusher :: struct {using stream: Stream}; +Write_Flush_Closer :: struct {using stream: Stream}; + +Reader_At :: struct {using stream: Stream}; +Writer_At :: struct {using stream: Stream}; +Reader_From :: struct {using stream: Stream}; +Writer_To :: struct {using stream: Stream}; + +Byte_Reader :: struct {using stream: Stream}; +Byte_Scanner :: struct {using stream: Stream}; +Byte_Writer :: struct {using stream: Stream}; + +Rune_Reader :: struct {using stream: Stream}; +Rune_Scanner :: struct {using stream: Stream}; + + +destroy :: proc(s: Stream) -> Error { + if s.vtable != nil && s.impl_destroy != nil { + return s->impl_destroy(); + } + // Instead of .Empty, .None is fine in this case + return .None; +} + +read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) { + if s.vtable != nil && s.impl_read != nil { + return s->impl_read(p); + } + return 0, .Empty; +} + +write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) { + if s.vtable != nil && s.impl_write != nil { + return s->impl_write(p); + } + return 0, .Empty; +} + +seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { + if s.vtable != nil && s.impl_seek != nil { + return s->impl_seek(offset, whence); + } + return 0, .Empty; +} + +close :: proc(s: Closer, p: []byte) -> Error { + if s.vtable != nil && s.impl_close != nil { + return s->impl_close(); + } + // Instead of .Empty, .None is fine in this case + return .None; +} + +flush :: proc(s: Flusher, p: []byte) -> Error { + if s.vtable != nil && s.impl_flush != nil { + return s->impl_flush(); + } + // Instead of .Empty, .None is fine in this case + return .None; +} + +size :: proc(s: Stream, p: []byte) -> i64 { + if s.vtable == nil { + return 0; + } + if s.impl_size != nil { + return s->impl_size(); + } + if s.impl_seek == nil { + return 0; + } + + curr, end: i64; + err: Error; + if curr, err = s->impl_seek(0, .Current); err != nil { + return 0; + } + + if end, err = s->impl_seek(0, .End); err != nil { + return 0; + } + + if _, err = s->impl_seek(curr, .Start); err != nil { + return 0; + } + + return end; +} + + + + +read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) { + if r.vtable == nil { + return 0, .Empty; + } + if r.impl_read_at != nil { + return r->impl_read_at(p, offset); + } + if r.impl_seek == nil || r.impl_read == nil { + return 0, .Empty; + } + + current_offset: i64; + current_offset, err = r->impl_seek(offset, .Current); + if err != nil { + return 0, err; + } + defer r->impl_seek(current_offset, .Start); + + return r->impl_read(p); +} + +write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) { + if w.vtable == nil { + return 0, .Empty; + } + if w.impl_write_at != nil { + return w->impl_write_at(p, offset); + } + if w.impl_seek == nil || w.impl_write == nil { + return 0, .Empty; + } + + current_offset: i64; + current_offset, err = w->impl_seek(offset, .Current); + if err != nil { + return 0, err; + } + defer w->impl_seek(current_offset, .Start); + + return w->impl_write(p); +} + +write_to :: proc(r: Reader, w: Writer) -> (n: i64, err: Error) { + if r.vtable == nil || w.vtable == nil { + return 0, .Empty; + } + if r.impl_write_to != nil { + return r->impl_write_to(w); + } + return 0, .Empty; +} +read_from :: proc(w: Writer, r: Reader) -> (n: i64, err: Error) { + if r.vtable == nil || w.vtable == nil { + return 0, .Empty; + } + if r.impl_read_from != nil { + return w->impl_read_from(r); + } + return 0, .Empty; +} + + +read_byte :: proc(r: Byte_Reader) -> (byte, Error) { + if r.vtable == nil { + return 0, .Empty; + } + if r.impl_read_byte != nil { + return r->impl_read_byte(); + } + if r.impl_read == nil { + return 0, .Empty; + } + + b: [1]byte; + _, err := r->impl_read(b[:]); + return b[0], err; +} + +write_byte :: proc(w: Byte_Writer, c: byte) -> Error { + if w.vtable == nil { + return .Empty; + } + if w.impl_write_byte != nil { + return w->impl_write_byte(c); + } + if w.impl_write == nil { + return .Empty; + } + + b := [1]byte{c}; + _, err := w->impl_write(b[:]); + return err; +} + +read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) { + if br.vtable == nil { + return 0, 0, .Empty; + } + if br.impl_read_rune != nil { + return br->impl_read_rune(); + } + if br.impl_read == nil { + return 0, 0, .Empty; + } + + b: [utf8.UTF_MAX]byte; + _, err = br->impl_read(b[:1]); + + s0 := b[0]; + ch = rune(s0); + size = 1; + if err != nil { + return; + } + if ch < utf8.RUNE_SELF { + return; + } + x := utf8.accept_sizes[s0]; + if x >= 0xf0 { + mask := rune(x) << 31 >> 31; + ch = ch &~ mask | utf8.RUNE_ERROR&mask; + return; + } + sz := int(x&7); + n: int; + n, err = br->impl_read(b[1:sz]); + if err != nil || n+1 < sz { + ch = utf8.RUNE_ERROR; + return; + } + + ch, size = utf8.decode_rune(b[:sz]); + return; +} + +unread_byte :: proc(s: Byte_Scanner) -> Error { + if s.vtable != nil && s.impl_unread_byte != nil { + return s->impl_unread_byte(); + } + return .Empty; +} +unread_rune :: proc(s: Rune_Scanner) -> Error { + if s.vtable != nil && s.impl_unread_rune != nil { + return s->impl_unread_rune(); + } + return .Empty; +} + + +write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) { + return write(s, transmute([]byte)str); +} + +write_rune :: proc(s: Writer, r: rune) -> (n: int, err: Error) { + buf, w := utf8.encode_rune(r); + return write(s, buf[:w]); +} + + + +read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) { + return read_at_least(r, buf, len(buf)); +} + + +read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) { + if len(buf) < min { + return 0, .Short_Buffer; + } + for n < min && err == nil { + nn: int; + nn, err = read(r, buf[n:]); + n += n; + } + + if n >= min { + err = nil; + } else if n > 0 && err == .EOF { + err = .Unexpected_EOF; + } + return; +} + +// copy copies from src to dst till either EOF is reached on src or an error occurs +// It returns the number of bytes copied and the first error that occurred whilst copying, if any. +copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) { + return _copy_buffer(dst, src, nil); +} + +// copy_buffer is the same as copy except that it stages through the provided buffer (if one is required) +// rather than allocating a temporary one on the stack through `intrinsics.alloca` +// If buf is `nil`, it is allocate through `intrinsics.alloca`; otherwise if it has zero length, it will panic +copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) { + if buf != nil && len(buf) == 0 { + panic("empty buffer in io.copy_buffer"); + } + return _copy_buffer(dst, src, buf); +} + + + +// copy_n copies n bytes (or till an error) from src to dst. +// It returns the number of bytes copied and the first error that occurred whilst copying, if any. +// On return, written == n IFF err == nil +copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) { + nsrc := inline_limited_reader(&Limited_Reader{}, src, n); + written, err = copy(dst, nsrc); + if written == n { + return n, nil; + } + if written < n && err == nil { + // src stopped early and must have been an EOF + err = .EOF; + } + return; +} + + +@(private) +_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) { + if dst.vtable == nil || src.vtable == nil { + return 0, .Empty; + } + if src.impl_write_to != nil { + return src->impl_write_to(dst); + } + if src.impl_read_from != nil { + return dst->impl_read_from(src); + } + buf := buf; + if buf == nil { + DEFAULT_SIZE :: 4 * 1024; + size := DEFAULT_SIZE; + if src.vtable == _limited_reader_vtable { + l := (^Limited_Reader)(src.data); + if i64(size) > l.n { + if l.n < 1 { + size = 1; + } else { + size = int(l.n); + } + } + } + // NOTE(bill): alloca is fine here + buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size}; + } + for { + nr, er := read(src, buf); + if nr > 0 { + nw, ew := write(dst, buf[0:nr]); + if nw > 0 { + written += i64(nw); + } + if ew != nil { + err = ew; + break; + } + if nr != nw { + err = .Short_Write; + break; + } + } + if er != nil { + if er != .EOF { + err = er; + } + break; + } + } + return; +} diff --git a/core/io/multi.odin b/core/io/multi.odin new file mode 100644 index 000000000..453ee279a --- /dev/null +++ b/core/io/multi.odin @@ -0,0 +1,115 @@ +package io + +import "core:runtime" + +@(private) +Multi_Reader :: struct { + using stream: Stream, + readers: [dynamic]Reader, +} + +@(private) +_multi_reader_vtable := &Stream_VTable{ + impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { + mr := (^Multi_Reader)(s.data); + for len(mr.readers) > 0 { + r := mr.readers[0]; + n, err = read(r, p); + if err == .EOF { + ordered_remove(&mr.readers, 0); + } + if n > 0 || err != .EOF { + if err == .EOF && len(mr.readers) > 0 { + // Don't return EOF yet, more readers remain + err = nil; + } + return; + } + } + return 0, .EOF; + }, + impl_destroy = proc(s: Stream) -> Error { + mr := (^Multi_Reader)(s.data); + context.allocator = mr.readers.allocator; + delete(mr.readers); + free(mr); + return .None; + }, +}; + +mutlti_reader :: proc(readers: ..Reader, allocator := context.allocator) -> Reader { + context.allocator = allocator; + mr := new(Multi_Reader); + mr.vtable = _multi_reader_vtable; + mr.data = mr; + all_readers := make([dynamic]Reader, 0, len(readers)); + + for w in readers { + if w.vtable == _multi_reader_vtable { + other := (^Multi_Reader)(w.data); + append(&all_readers, ..other.readers[:]); + } else { + append(&all_readers, w); + } + } + + mr.readers = all_readers; + res, _ := to_reader(mr^); + return res; +} + + +@(private) +Multi_Writer :: struct { + using stream: Stream, + writers: []Writer, + allocator: runtime.Allocator, +} + +@(private) +_multi_writer_vtable := &Stream_VTable{ + impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) { + mw := (^Multi_Writer)(s.data); + for w in mw.writers { + n, err = write(w, p); + if err != nil { + return; + } + if n != len(p) { + err = .Short_Write; + return; + } + } + + return len(p), nil; + }, + impl_destroy = proc(s: Stream) -> Error { + mw := (^Multi_Writer)(s.data); + context.allocator = mw.allocator; + delete(mw.writers); + free(mw); + return .None; + }, +}; + +mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> Writer { + context.allocator = allocator; + mw := new(Multi_Writer); + mw.vtable = _multi_writer_vtable; + mw.data = mw; + mw.allocator = allocator; + all_writers := make([dynamic]Writer, 0, len(writers)); + + for w in writers { + if w.vtable == _multi_writer_vtable { + other := (^Multi_Writer)(w.data); + append(&all_writers, ..other.writers); + } else { + append(&all_writers, w); + } + } + + mw.writers = all_writers[:]; + res, _ := to_writer(mw^); + return res; +} diff --git a/core/io/util.odin b/core/io/util.odin new file mode 100644 index 000000000..9a2dc09af --- /dev/null +++ b/core/io/util.odin @@ -0,0 +1,90 @@ +package io + +import "core:runtime" + +@(private) +Tee_Reader :: struct { + using stream: Stream, + r: Reader, + w: Writer, + allocator: runtime.Allocator, +} + +@(private) +_tee_reader_vtable := &Stream_VTable{ + impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { + t := (^Tee_Reader)(s.data); + n, err = read(t.r, p); + if n > 0 { + if wn, werr := write(t.w, p[:n]); werr != nil { + return wn, werr; + } + } + return; + }, + impl_destroy = proc(s: Stream) -> Error { + t := (^Tee_Reader)(s.data); + allocator := t.allocator; + free(t, allocator); + return .None; + }, +}; + +// tee_reader +// tee_reader must call io.destroy when done with +tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> Reader { + t := new(Tee_Reader, allocator); + t.r, t.w = r, w; + t.allocator = allocator; + t.data = t; + t.vtable = _tee_reader_vtable; + res, _ := to_reader(t^); + return res; +} + + +// A Limited_Reader reads from r but limits the amount of +// data returned to just n bytes. Each call to read +// updates n to reflect the new amount remaining. +// read returns EOF when n <= 0 or when the underlying r returns EOF. +Limited_Reader :: struct { + using stream: Stream, + r: Reader, // underlying reader + n: i64, // max_bytes +} + +@(private) +_limited_reader_vtable := &Stream_VTable{ + impl_read = proc(using s: Stream, p: []byte) -> (n: int, err: Error) { + l := (^Limited_Reader)(s.data); + if l.n <= 0 { + return 0, .EOF; + } + p := p; + if i64(len(p)) > l.n { + p = p[0:l.n]; + } + n, err = read(l.r, p); + l.n -= i64(n); + return; + }, +}; + +new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader { + l := new(Limited_Reader); + l.vtable = _limited_reader_vtable; + l.data = l; + l.r = r; + l.n = n; + return l; +} + +@(private="package") +inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { + l.vtable = _limited_reader_vtable; + l.data = l; + l.r = r; + l.n = n; + res, _ := to_reader(l^); + return res; +} From 8e08ae47fbe038b6b8415b3da7715936f4fd852c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:12:43 +0000 Subject: [PATCH 132/170] Add `strings.Reader` --- core/strings/reader.odin | 143 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 core/strings/reader.odin diff --git a/core/strings/reader.odin b/core/strings/reader.odin new file mode 100644 index 000000000..1dbfe9451 --- /dev/null +++ b/core/strings/reader.odin @@ -0,0 +1,143 @@ +package strings + +import "core:io" +import "core:unicode/utf8" + +Reader :: struct { + using stream: io.Stream, + s: string, + i: i64, // current reading index + prev_rune: int, // previous reading index of rune or < 0 +} + +reader_reset :: proc(r: ^Reader, s: string) { + r.data = r; + r.s = s; + r.i = 0; + r.prev_rune = -1; +} + +new_reader :: proc(s: string, allocator := context.allocator) -> ^Reader { + r := new(Reader, allocator); + reader_reset(r, s); + r.vtable = _reader_vtable; + return r; +} + +@(private) +_reader_vtable := &io.Stream_VTable{ + impl_size = proc(s: io.Stream) -> i64 { + r := (^Reader)(s.data); + return i64(len(r.s)); + }, + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + r := (^Reader)(s.data); + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + r.prev_rune = -1; + n = copy(p, r.s[r.i:]); + r.i += i64(n); + return; + }, + impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) { + r := (^Reader)(s.data); + if off < 0 { + return 0, .Invalid_Offset; + } + if off >= i64(len(r.s)) { + return 0, .EOF; + } + n = copy(p, r.s[off:]); + if n < len(p) { + err = .EOF; + } + return; + }, + impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { + r := (^Reader)(s.data); + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + b := r.s[r.i]; + r.i += 1; + return b, nil; + }, + impl_unread_byte = proc(s: io.Stream) -> io.Error { + r := (^Reader)(s.data); + if r.i <= 0 { + return .Invalid_Unread; + } + r.prev_rune = -1; + r.i -= 1; + return nil; + }, + impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) { + r := (^Reader)(s.data); + if r.i >= i64(len(r.s)) { + r.prev_rune = -1; + return 0, 0, .EOF; + } + r.prev_rune = int(r.i); + if c := r.s[r.i]; c < utf8.RUNE_SELF { + r.i += 1; + return rune(c), 1, nil; + } + ch, size = utf8.decode_rune_in_string(r.s[r.i:]); + r.i += i64(size); + return; + }, + impl_unread_rune = proc(s: io.Stream) -> io.Error { + r := (^Reader)(s.data); + if r.i <= 0 { + return .Invalid_Unread; + } + if r.prev_rune < 0 { + return .Invalid_Unread; + } + r.i = i64(r.prev_rune); + r.prev_rune = -1; + return nil; + }, + impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { + r := (^Reader)(s.data); + r.prev_rune = -1; + abs: i64; + switch whence { + case .Start: + abs = offset; + case .Current: + abs = r.i + offset; + case .End: + abs = i64(len(r.s)) + offset; + case: + return 0, .Invalid_Whence; + } + + if abs < 0 { + return 0, .Invalid_Offset; + } + r.i = abs; + return abs, nil; + }, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + r := (^Reader)(s.data); + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, nil; + } + s := r.s[r.i:]; + m: int; + m, err = io.write_string(w, s); + if m > len(s) { + panic("strings.Reader.write_to: invalid io.write_string count"); + } + r.i += i64(m); + n = i64(m); + if m != len(s) && err == nil { + err = .Short_Write; + } + return; + }, +}; From c4172e391443a021e43ff55938b86637eec4e1d2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:17:21 +0000 Subject: [PATCH 133/170] Rename stream field names --- core/io/conv.odin | 46 +++++++++++++++++++-------------------- core/io/io.odin | 42 +++++++++++++++++------------------ core/io/multi.odin | 24 ++++++++++---------- core/io/util.odin | 18 +++++++-------- core/strings/builder.odin | 18 +++++++++++++++ core/strings/reader.odin | 22 +++++++++---------- 6 files changed, 94 insertions(+), 76 deletions(-) diff --git a/core/io/conv.odin b/core/io/conv.odin index 4afc3490d..07f3494fd 100644 --- a/core/io/conv.odin +++ b/core/io/conv.odin @@ -8,14 +8,14 @@ Conversion_Error :: enum { to_reader :: proc(s: Stream) -> (r: Reader, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read == nil { + if s.stream_vtable == nil || s.impl_read == nil { err = .Missing_Procedure; } return; } to_writer :: proc(s: Stream) -> (w: Writer, err: Conversion_Error) { w.stream = s; - if s.vtable == nil || s.impl_write == nil { + if s.stream_vtable == nil || s.impl_write == nil { err = .Missing_Procedure; } return; @@ -23,21 +23,21 @@ to_writer :: proc(s: Stream) -> (w: Writer, err: Conversion_Error) { to_closer :: proc(s: Stream) -> (c: Closer, err: Conversion_Error) { c.stream = s; - if s.vtable == nil || s.impl_close == nil { + if s.stream_vtable == nil || s.impl_close == nil { err = .Missing_Procedure; } return; } to_flusher :: proc(s: Stream) -> (f: Flusher, err: Conversion_Error) { f.stream = s; - if s.vtable == nil || s.impl_flush == nil { + if s.stream_vtable == nil || s.impl_flush == nil { err = .Missing_Procedure; } return; } to_seeker :: proc(s: Stream) -> (seeker: Seeker, err: Conversion_Error) { seeker.stream = s; - if s.vtable == nil || s.impl_seek == nil { + if s.stream_vtable == nil || s.impl_seek == nil { err = .Missing_Procedure; } return; @@ -45,42 +45,42 @@ to_seeker :: proc(s: Stream) -> (seeker: Seeker, err: Conversion_Error) { to_read_writer :: proc(s: Stream) -> (r: Read_Writer, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read == nil || s.impl_write == nil { + if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil { err = .Missing_Procedure; } return; } to_read_closer :: proc(s: Stream) -> (r: Read_Closer, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read == nil || s.impl_close == nil { + if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil { err = .Missing_Procedure; } return; } to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil { + if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil { err = .Missing_Procedure; } return; } to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil { + if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil { err = .Missing_Procedure; } return; } to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, err: Conversion_Error) { w.stream = s; - if s.vtable == nil || s.impl_write == nil || s.impl_flush == nil { + if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil { err = .Missing_Procedure; } return; } to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, err: Conversion_Error) { w.stream = s; - if s.vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil { + if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil { err = .Missing_Procedure; } return; @@ -88,28 +88,28 @@ to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, err: Convers to_reader_at :: proc(s: Stream) -> (r: Reader_At, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read_at == nil { + if s.stream_vtable == nil || s.impl_read_at == nil { err = .Missing_Procedure; } return; } to_writer_at :: proc(s: Stream) -> (w: Writer_At, err: Conversion_Error) { w.stream = s; - if s.vtable == nil || s.impl_write_at == nil { + if s.stream_vtable == nil || s.impl_write_at == nil { err = .Missing_Procedure; } return; } to_reader_from :: proc(s: Stream) -> (r: Reader_From, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read_from == nil { + if s.stream_vtable == nil || s.impl_read_from == nil { err = .Missing_Procedure; } return; } to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) { w.stream = s; - if s.vtable == nil || s.impl_write_to == nil { + if s.stream_vtable == nil || s.impl_write_to == nil { err = .Missing_Procedure; } return; @@ -117,9 +117,9 @@ to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) { to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) { b.stream = s; - if s.vtable == nil || s.impl_read_byte == nil { + if s.stream_vtable == nil || s.impl_read_byte == nil { err = .Missing_Procedure; - if s.vtable != nil && s.impl_read != nil { + if s.stream_vtable != nil && s.impl_read != nil { err = .Fallback_Possible; } } @@ -127,7 +127,7 @@ to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) { } to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, err: Conversion_Error) { b.stream = s; - if s.vtable != nil { + if s.stream_vtable != nil { if s.impl_unread_byte == nil { err = .Missing_Procedure; return; @@ -144,9 +144,9 @@ to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, err: Conversion_Error) { } to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, err: Conversion_Error) { b.stream = s; - if s.vtable == nil || s.impl_write_byte == nil { + if s.stream_vtable == nil || s.impl_write_byte == nil { err = .Missing_Procedure; - if s.vtable != nil && s.impl_write != nil { + if s.stream_vtable != nil && s.impl_write != nil { err = .Fallback_Possible; } } @@ -155,9 +155,9 @@ to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, err: Conversion_Error) { to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, err: Conversion_Error) { r.stream = s; - if s.vtable == nil || s.impl_read_rune == nil { + if s.stream_vtable == nil || s.impl_read_rune == nil { err = .Missing_Procedure; - if s.vtable != nil && s.impl_read != nil { + if s.stream_vtable != nil && s.impl_read != nil { err = .Fallback_Possible; } } @@ -166,7 +166,7 @@ to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, err: Conversion_Error) { } to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, err: Conversion_Error) { r.stream = s; - if s.vtable != nil { + if s.stream_vtable != nil { if s.impl_unread_rune == nil { err = .Missing_Procedure; return; diff --git a/core/io/io.odin b/core/io/io.odin index be3bca147..7aca6e25f 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -58,8 +58,8 @@ Destroy_Proc :: distinct proc(using s: Stream) -> Error; Stream :: struct { - using vtable: ^Stream_VTable, - data: rawptr, + using stream_vtable: ^Stream_VTable, + stream_data: rawptr, } Stream_VTable :: struct { impl_close: Close_Proc, @@ -113,7 +113,7 @@ Rune_Scanner :: struct {using stream: Stream}; destroy :: proc(s: Stream) -> Error { - if s.vtable != nil && s.impl_destroy != nil { + if s.stream_vtable != nil && s.impl_destroy != nil { return s->impl_destroy(); } // Instead of .Empty, .None is fine in this case @@ -121,28 +121,28 @@ destroy :: proc(s: Stream) -> Error { } read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) { - if s.vtable != nil && s.impl_read != nil { + if s.stream_vtable != nil && s.impl_read != nil { return s->impl_read(p); } return 0, .Empty; } write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) { - if s.vtable != nil && s.impl_write != nil { + if s.stream_vtable != nil && s.impl_write != nil { return s->impl_write(p); } return 0, .Empty; } seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { - if s.vtable != nil && s.impl_seek != nil { + if s.stream_vtable != nil && s.impl_seek != nil { return s->impl_seek(offset, whence); } return 0, .Empty; } close :: proc(s: Closer, p: []byte) -> Error { - if s.vtable != nil && s.impl_close != nil { + if s.stream_vtable != nil && s.impl_close != nil { return s->impl_close(); } // Instead of .Empty, .None is fine in this case @@ -150,7 +150,7 @@ close :: proc(s: Closer, p: []byte) -> Error { } flush :: proc(s: Flusher, p: []byte) -> Error { - if s.vtable != nil && s.impl_flush != nil { + if s.stream_vtable != nil && s.impl_flush != nil { return s->impl_flush(); } // Instead of .Empty, .None is fine in this case @@ -158,7 +158,7 @@ flush :: proc(s: Flusher, p: []byte) -> Error { } size :: proc(s: Stream, p: []byte) -> i64 { - if s.vtable == nil { + if s.stream_vtable == nil { return 0; } if s.impl_size != nil { @@ -189,7 +189,7 @@ size :: proc(s: Stream, p: []byte) -> i64 { read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) { - if r.vtable == nil { + if r.stream_vtable == nil { return 0, .Empty; } if r.impl_read_at != nil { @@ -210,7 +210,7 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) { } write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) { - if w.vtable == nil { + if w.stream_vtable == nil { return 0, .Empty; } if w.impl_write_at != nil { @@ -231,7 +231,7 @@ write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) { } write_to :: proc(r: Reader, w: Writer) -> (n: i64, err: Error) { - if r.vtable == nil || w.vtable == nil { + if r.stream_vtable == nil || w.stream_vtable == nil { return 0, .Empty; } if r.impl_write_to != nil { @@ -240,7 +240,7 @@ write_to :: proc(r: Reader, w: Writer) -> (n: i64, err: Error) { return 0, .Empty; } read_from :: proc(w: Writer, r: Reader) -> (n: i64, err: Error) { - if r.vtable == nil || w.vtable == nil { + if r.stream_vtable == nil || w.stream_vtable == nil { return 0, .Empty; } if r.impl_read_from != nil { @@ -251,7 +251,7 @@ read_from :: proc(w: Writer, r: Reader) -> (n: i64, err: Error) { read_byte :: proc(r: Byte_Reader) -> (byte, Error) { - if r.vtable == nil { + if r.stream_vtable == nil { return 0, .Empty; } if r.impl_read_byte != nil { @@ -267,7 +267,7 @@ read_byte :: proc(r: Byte_Reader) -> (byte, Error) { } write_byte :: proc(w: Byte_Writer, c: byte) -> Error { - if w.vtable == nil { + if w.stream_vtable == nil { return .Empty; } if w.impl_write_byte != nil { @@ -283,7 +283,7 @@ write_byte :: proc(w: Byte_Writer, c: byte) -> Error { } read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) { - if br.vtable == nil { + if br.stream_vtable == nil { return 0, 0, .Empty; } if br.impl_read_rune != nil { @@ -324,13 +324,13 @@ read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) { } unread_byte :: proc(s: Byte_Scanner) -> Error { - if s.vtable != nil && s.impl_unread_byte != nil { + if s.stream_vtable != nil && s.impl_unread_byte != nil { return s->impl_unread_byte(); } return .Empty; } unread_rune :: proc(s: Rune_Scanner) -> Error { - if s.vtable != nil && s.impl_unread_rune != nil { + if s.stream_vtable != nil && s.impl_unread_rune != nil { return s->impl_unread_rune(); } return .Empty; @@ -408,7 +408,7 @@ copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) { @(private) _copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) { - if dst.vtable == nil || src.vtable == nil { + if dst.stream_vtable == nil || src.stream_vtable == nil { return 0, .Empty; } if src.impl_write_to != nil { @@ -421,8 +421,8 @@ _copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, er if buf == nil { DEFAULT_SIZE :: 4 * 1024; size := DEFAULT_SIZE; - if src.vtable == _limited_reader_vtable { - l := (^Limited_Reader)(src.data); + if src.stream_vtable == _limited_reader_vtable { + l := (^Limited_Reader)(src.stream_data); if i64(size) > l.n { if l.n < 1 { size = 1; diff --git a/core/io/multi.odin b/core/io/multi.odin index 453ee279a..b6583ecf6 100644 --- a/core/io/multi.odin +++ b/core/io/multi.odin @@ -11,7 +11,7 @@ Multi_Reader :: struct { @(private) _multi_reader_vtable := &Stream_VTable{ impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { - mr := (^Multi_Reader)(s.data); + mr := (^Multi_Reader)(s.stream_data); for len(mr.readers) > 0 { r := mr.readers[0]; n, err = read(r, p); @@ -29,7 +29,7 @@ _multi_reader_vtable := &Stream_VTable{ return 0, .EOF; }, impl_destroy = proc(s: Stream) -> Error { - mr := (^Multi_Reader)(s.data); + mr := (^Multi_Reader)(s.stream_data); context.allocator = mr.readers.allocator; delete(mr.readers); free(mr); @@ -40,13 +40,13 @@ _multi_reader_vtable := &Stream_VTable{ mutlti_reader :: proc(readers: ..Reader, allocator := context.allocator) -> Reader { context.allocator = allocator; mr := new(Multi_Reader); - mr.vtable = _multi_reader_vtable; - mr.data = mr; + mr.stream_vtable = _multi_reader_vtable; + mr.stream_data = mr; all_readers := make([dynamic]Reader, 0, len(readers)); for w in readers { - if w.vtable == _multi_reader_vtable { - other := (^Multi_Reader)(w.data); + if w.stream_vtable == _multi_reader_vtable { + other := (^Multi_Reader)(w.stream_data); append(&all_readers, ..other.readers[:]); } else { append(&all_readers, w); @@ -69,7 +69,7 @@ Multi_Writer :: struct { @(private) _multi_writer_vtable := &Stream_VTable{ impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) { - mw := (^Multi_Writer)(s.data); + mw := (^Multi_Writer)(s.stream_data); for w in mw.writers { n, err = write(w, p); if err != nil { @@ -84,7 +84,7 @@ _multi_writer_vtable := &Stream_VTable{ return len(p), nil; }, impl_destroy = proc(s: Stream) -> Error { - mw := (^Multi_Writer)(s.data); + mw := (^Multi_Writer)(s.stream_data); context.allocator = mw.allocator; delete(mw.writers); free(mw); @@ -95,14 +95,14 @@ _multi_writer_vtable := &Stream_VTable{ mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> Writer { context.allocator = allocator; mw := new(Multi_Writer); - mw.vtable = _multi_writer_vtable; - mw.data = mw; + mw.stream_vtable = _multi_writer_vtable; + mw.stream_data = mw; mw.allocator = allocator; all_writers := make([dynamic]Writer, 0, len(writers)); for w in writers { - if w.vtable == _multi_writer_vtable { - other := (^Multi_Writer)(w.data); + if w.stream_vtable == _multi_writer_vtable { + other := (^Multi_Writer)(w.stream_data); append(&all_writers, ..other.writers); } else { append(&all_writers, w); diff --git a/core/io/util.odin b/core/io/util.odin index 9a2dc09af..bf1c223b8 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -13,7 +13,7 @@ Tee_Reader :: struct { @(private) _tee_reader_vtable := &Stream_VTable{ impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { - t := (^Tee_Reader)(s.data); + t := (^Tee_Reader)(s.stream_data); n, err = read(t.r, p); if n > 0 { if wn, werr := write(t.w, p[:n]); werr != nil { @@ -23,7 +23,7 @@ _tee_reader_vtable := &Stream_VTable{ return; }, impl_destroy = proc(s: Stream) -> Error { - t := (^Tee_Reader)(s.data); + t := (^Tee_Reader)(s.stream_data); allocator := t.allocator; free(t, allocator); return .None; @@ -36,8 +36,8 @@ tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> Read t := new(Tee_Reader, allocator); t.r, t.w = r, w; t.allocator = allocator; - t.data = t; - t.vtable = _tee_reader_vtable; + t.stream_data = t; + t.stream_vtable = _tee_reader_vtable; res, _ := to_reader(t^); return res; } @@ -56,7 +56,7 @@ Limited_Reader :: struct { @(private) _limited_reader_vtable := &Stream_VTable{ impl_read = proc(using s: Stream, p: []byte) -> (n: int, err: Error) { - l := (^Limited_Reader)(s.data); + l := (^Limited_Reader)(s.stream_data); if l.n <= 0 { return 0, .EOF; } @@ -72,8 +72,8 @@ _limited_reader_vtable := &Stream_VTable{ new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader { l := new(Limited_Reader); - l.vtable = _limited_reader_vtable; - l.data = l; + l.stream_vtable = _limited_reader_vtable; + l.stream_data = l; l.r = r; l.n = n; return l; @@ -81,8 +81,8 @@ new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader { @(private="package") inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { - l.vtable = _limited_reader_vtable; - l.data = l; + l.stream_vtable = _limited_reader_vtable; + l.stream_data = l; l.r = r; l.n = n; res, _ := to_reader(l^); diff --git a/core/strings/builder.odin b/core/strings/builder.odin index e5b93fb67..b31190215 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -32,6 +32,24 @@ make_builder :: proc{ make_builder_len_cap, }; +init_builder_none :: proc(b: ^Builder, allocator := context.allocator) { + b.buf = make([dynamic]byte, allocator); +} + +init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) { + b.buf = make([dynamic]byte, len, allocator); +} + +init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) { + b.buf = make([dynamic]byte, len, cap, allocator); +} + +init_builder :: proc{ + init_builder_none, + init_builder_len, + init_builder_len_cap, +}; + diff --git a/core/strings/reader.odin b/core/strings/reader.odin index 1dbfe9451..46256641f 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -11,7 +11,8 @@ Reader :: struct { } reader_reset :: proc(r: ^Reader, s: string) { - r.data = r; + r.stream_data = r; + r.stream_vtable = _reader_vtable; r.s = s; r.i = 0; r.prev_rune = -1; @@ -20,18 +21,17 @@ reader_reset :: proc(r: ^Reader, s: string) { new_reader :: proc(s: string, allocator := context.allocator) -> ^Reader { r := new(Reader, allocator); reader_reset(r, s); - r.vtable = _reader_vtable; return r; } @(private) _reader_vtable := &io.Stream_VTable{ impl_size = proc(s: io.Stream) -> i64 { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); return i64(len(r.s)); }, impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); if r.i >= i64(len(r.s)) { return 0, .EOF; } @@ -41,7 +41,7 @@ _reader_vtable := &io.Stream_VTable{ return; }, impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); if off < 0 { return 0, .Invalid_Offset; } @@ -55,7 +55,7 @@ _reader_vtable := &io.Stream_VTable{ return; }, impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); r.prev_rune = -1; if r.i >= i64(len(r.s)) { return 0, .EOF; @@ -65,7 +65,7 @@ _reader_vtable := &io.Stream_VTable{ return b, nil; }, impl_unread_byte = proc(s: io.Stream) -> io.Error { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); if r.i <= 0 { return .Invalid_Unread; } @@ -74,7 +74,7 @@ _reader_vtable := &io.Stream_VTable{ return nil; }, impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); if r.i >= i64(len(r.s)) { r.prev_rune = -1; return 0, 0, .EOF; @@ -89,7 +89,7 @@ _reader_vtable := &io.Stream_VTable{ return; }, impl_unread_rune = proc(s: io.Stream) -> io.Error { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); if r.i <= 0 { return .Invalid_Unread; } @@ -101,7 +101,7 @@ _reader_vtable := &io.Stream_VTable{ return nil; }, impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); r.prev_rune = -1; abs: i64; switch whence { @@ -122,7 +122,7 @@ _reader_vtable := &io.Stream_VTable{ return abs, nil; }, impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - r := (^Reader)(s.data); + r := (^Reader)(s.stream_data); r.prev_rune = -1; if r.i >= i64(len(r.s)) { return 0, nil; From 0cf3ae93c052e33984cc73a557a8a8cdd411ad0e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:39:33 +0000 Subject: [PATCH 134/170] Add `os.stream_from_handle`; fix `io.close` --- core/io/io.odin | 5 ++-- core/os/stream.odin | 62 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 core/os/stream.odin diff --git a/core/io/io.odin b/core/io/io.odin index 7aca6e25f..47363bd28 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -113,11 +113,12 @@ Rune_Scanner :: struct {using stream: Stream}; destroy :: proc(s: Stream) -> Error { + close_err := close({s}); if s.stream_vtable != nil && s.impl_destroy != nil { return s->impl_destroy(); } // Instead of .Empty, .None is fine in this case - return .None; + return close_err; } read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) { @@ -141,7 +142,7 @@ seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) return 0, .Empty; } -close :: proc(s: Closer, p: []byte) -> Error { +close :: proc(s: Closer) -> Error { if s.stream_vtable != nil && s.impl_close != nil { return s->impl_close(); } diff --git a/core/os/stream.odin b/core/os/stream.odin new file mode 100644 index 000000000..c82f38107 --- /dev/null +++ b/core/os/stream.odin @@ -0,0 +1,62 @@ +package os + +import "core:io" + +stream_from_handle :: proc(fd: Handle) -> io.Stream { + s: io.Stream; + s.stream_data = rawptr(uintptr(fd)); + s.stream_vtable = _file_stream_vtable; + return s; +} + + +_file_stream_vtable := &io.Stream_VTable{ + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + os_err: Errno; + n, os_err = read(fd, p); + return; + }, + impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + os_err: Errno; + n, os_err = read_at(fd, p, offset); + return; + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + os_err: Errno; + n, os_err = write(fd, p); + return; + }, + impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + os_err: Errno; + n, os_err = write_at(fd, p, offset); + return; + }, + impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { + fd := Handle(uintptr(s.stream_data)); + n, os_err := seek(fd, offset, int(whence)); + return n, nil; + }, + impl_size = proc(s: io.Stream) -> i64 { + fd := Handle(uintptr(s.stream_data)); + sz, _ := file_size(fd); + return sz; + }, + impl_flush = proc(s: io.Stream) -> io.Error { + fd := Handle(uintptr(s.stream_data)); + when ODIN_OS == "windows" { + flush(fd); + } else { + // TOOD(bill): other operating systems + } + return nil; + }, + impl_close = proc(s: io.Stream) -> io.Error { + fd := Handle(uintptr(s.stream_data)); + close(fd); + return nil; + }, +}; From 875415daa9e68ee25d7b99654d6620d665157427 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:40:23 +0000 Subject: [PATCH 135/170] Make `os._file_stream_vtable` private --- core/os/stream.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/os/stream.odin b/core/os/stream.odin index c82f38107..7d3749a32 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -10,6 +10,7 @@ stream_from_handle :: proc(fd: Handle) -> io.Stream { } +@(private) _file_stream_vtable := &io.Stream_VTable{ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { fd := Handle(uintptr(s.stream_data)); From bca28e94ec9dd16960aa0d2b7955ed2a435c5fc7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:43:28 +0000 Subject: [PATCH 136/170] Keep `fmt` happy --- core/os/stream.odin | 14 ++++++++++---- core/text/scanner/scanner.odin | 5 ++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/os/stream.odin b/core/os/stream.odin index 7d3749a32..1815262c7 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -20,8 +20,10 @@ _file_stream_vtable := &io.Stream_VTable{ }, impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { fd := Handle(uintptr(s.stream_data)); - os_err: Errno; - n, os_err = read_at(fd, p, offset); + when ODIN_OS == "windows" { + os_err: Errno; + n, os_err = read_at(fd, p, offset); + } return; }, impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { @@ -32,13 +34,17 @@ _file_stream_vtable := &io.Stream_VTable{ }, impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { fd := Handle(uintptr(s.stream_data)); - os_err: Errno; - n, os_err = write_at(fd, p, offset); + when ODIN_OS == "windows" { + os_err: Errno; + n, os_err = write_at(fd, p, offset); + _ = os_err; + } return; }, impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { fd := Handle(uintptr(s.stream_data)); n, os_err := seek(fd, offset, int(whence)); + _ = os_err; return n, nil; }, impl_size = proc(s: io.Stream) -> i64 { diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index dad9c6d95..bbd2090b6 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -262,13 +262,12 @@ scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) { if !seen_dot { tok = Int; - ch0 := ch; if ch == '0' { ch = advance(s); p := lower(ch); if .Scan_C_Int_Prefixes in s.flags { - switch lower(ch) { + switch p { case 'b': ch = advance(s); base, prefix = 2, 'b'; @@ -280,7 +279,7 @@ scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) { digsep = 1; // Leading zero } } else { - switch lower(ch) { + switch p { case 'b': ch = advance(s); base, prefix = 2, 'b'; From e0fb081cbd0185b9dfc865ac903ef68012d01c0d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Dec 2020 23:46:41 +0000 Subject: [PATCH 137/170] Keep -vet happy --- core/os/stream.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/os/stream.odin b/core/os/stream.odin index 1815262c7..db310e6ec 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -19,8 +19,8 @@ _file_stream_vtable := &io.Stream_VTable{ return; }, impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)); when ODIN_OS == "windows" { + fd := Handle(uintptr(s.stream_data)); os_err: Errno; n, os_err = read_at(fd, p, offset); } @@ -33,8 +33,8 @@ _file_stream_vtable := &io.Stream_VTable{ return; }, impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)); when ODIN_OS == "windows" { + fd := Handle(uintptr(s.stream_data)); os_err: Errno; n, os_err = write_at(fd, p, offset); _ = os_err; @@ -53,8 +53,8 @@ _file_stream_vtable := &io.Stream_VTable{ return sz; }, impl_flush = proc(s: io.Stream) -> io.Error { - fd := Handle(uintptr(s.stream_data)); when ODIN_OS == "windows" { + fd := Handle(uintptr(s.stream_data)); flush(fd); } else { // TOOD(bill): other operating systems From 334a8c46e8b969537b6c3304f19620309ab9e7f4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 00:27:21 +0000 Subject: [PATCH 138/170] Fix io typos --- core/io/io.odin | 20 +++++++++++++++++--- core/io/util.odin | 12 ++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/core/io/io.odin b/core/io/io.odin index 47363bd28..0e39a4ef8 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -150,7 +150,7 @@ close :: proc(s: Closer) -> Error { return .None; } -flush :: proc(s: Flusher, p: []byte) -> Error { +flush :: proc(s: Flusher) -> Error { if s.stream_vtable != nil && s.impl_flush != nil { return s->impl_flush(); } @@ -158,7 +158,7 @@ flush :: proc(s: Flusher, p: []byte) -> Error { return .None; } -size :: proc(s: Stream, p: []byte) -> i64 { +size :: proc(s: Stream) -> i64 { if s.stream_vtable == nil { return 0; } @@ -267,7 +267,21 @@ read_byte :: proc(r: Byte_Reader) -> (byte, Error) { return b[0], err; } -write_byte :: proc(w: Byte_Writer, c: byte) -> Error { +write_byte :: proc{ + write_byte_to_byte_writer, + write_byte_to_writer, +}; + +write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte) -> Error { + return _write_byte(auto_cast w, c); +} + +write_byte_to_writer :: proc(w: Writer, c: byte) -> Error { + return _write_byte(auto_cast w, c); +} + +@(private) +_write_byte :: proc(w: Byte_Writer, c: byte) -> Error { if w.stream_vtable == nil { return .Empty; } diff --git a/core/io/util.odin b/core/io/util.odin index bf1c223b8..e714ac7d0 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -1,6 +1,18 @@ package io import "core:runtime" +import "core:strconv" + +write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) { + buf: [32]byte; + s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil); + return write_string(w, s); +} +write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) { + buf: [32]byte; + s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil); + return write_string(w, s); +} @(private) Tee_Reader :: struct { From 18da0b3418511c013cc3a06df72f50d4abc00042 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 10:45:26 +0000 Subject: [PATCH 139/170] Integrate `package io` into core library --- core/encoding/json/marshal.odin | 5 +- core/fmt/fmt.odin | 473 +++++++++++++++++--------------- core/io/conv.odin | 15 + core/io/io.odin | 13 +- core/io/util.odin | 7 + core/reflect/types.odin | 278 ++++++++++--------- core/strings/builder.odin | 223 ++++++++++----- core/strings/conversion.odin | 21 +- core/strings/strings.odin | 15 +- 9 files changed, 621 insertions(+), 429 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 52854fc80..7bdc3abc3 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -14,7 +14,8 @@ Marshal_Error :: enum { } marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) { - b := strings.make_builder(allocator); + b: strings.Builder; + strings.init_builder(&b, allocator); err := marshal_arg(&b, v); @@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { case b32: val = bool(b); case b64: val = bool(b); } - write_string(b, val ? "true" : "false"); + write_string_builder(b, val ? "true" : "false"); case Type_Info_Any: return .Unsupported_Type; diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 27e0de2ba..ff627bdb2 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -3,6 +3,7 @@ package fmt import "core:math/bits" import "core:mem" import "core:os" +import "core:io" import "core:reflect" import "core:runtime" import "core:strconv" @@ -29,7 +30,7 @@ Info :: struct { reordered: bool, good_arg_index: bool, - buf: ^strings.Builder, + writer: io.Writer, arg: any, // Temporary record_level: int, } @@ -111,7 +112,6 @@ fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> int { strings.flush_builder(&buf); return flush_data.bytes_written; } - // print* procedures return the number of bytes written print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep); } println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep); } @@ -130,17 +130,20 @@ eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fm // aprint* procedures return a string that was allocated with the current context // They must be freed accordingly aprint :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(); + str: strings.Builder; + strings.init_builder(&str); sbprint(buf=&str, args=args, sep=sep); return strings.to_string(str); } aprintln :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(); + str: strings.Builder; + strings.init_builder(&str); sbprintln(buf=&str, args=args, sep=sep); return strings.to_string(str); } aprintf :: proc(fmt: string, args: ..any) -> string { - str := strings.make_builder(); + str: strings.Builder; + strings.init_builder(&str); sbprintf(&str, fmt, ..args); return strings.to_string(str); } @@ -148,17 +151,20 @@ aprintf :: proc(fmt: string, args: ..any) -> string { // tprint* procedures return a string that was allocated with the current context's temporary allocator tprint :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(context.temp_allocator); + str: strings.Builder; + strings.init_builder(&str, context.temp_allocator); sbprint(buf=&str, args=args, sep=sep); return strings.to_string(str); } tprintln :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(context.temp_allocator); + str: strings.Builder; + strings.init_builder(&str, context.temp_allocator); sbprintln(buf=&str, args=args, sep=sep); return strings.to_string(str); } tprintf :: proc(fmt: string, args: ..any) -> string { - str := strings.make_builder(context.temp_allocator); + str: strings.Builder; + strings.init_builder(&str, context.temp_allocator); sbprintf(&str, fmt, ..args); return strings.to_string(str); } @@ -205,15 +211,34 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { + wprint(w=strings.to_writer(buf), args=args, sep=sep); + strings.flush_builder(buf); + return strings.to_string(buf^); +} + +sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { + wprintln(w=strings.to_writer(buf), args=args, sep=sep); + strings.flush_builder(buf); + return strings.to_string(buf^); +} + +sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { + wprintf(w=strings.to_writer(buf), fmt=fmt, args=args); + strings.flush_builder(buf); + return strings.to_string(buf^); +} + + +wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info; - fi.buf = buf; + fi.writer = w; // NOTE(bill): Old approach // prev_string := false; // for arg, i in args { // is_string := arg != nil && reflect.is_string(type_info_of(arg.id)); // if i > 0 && !is_string && !prev_string { - // strings.write_byte(buf, ' '); + // io.write_byte(writer, ' '); // } // fmt_value(&fi, args[i], 'v'); // prev_string = is_string; @@ -223,47 +248,46 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // so I am going to keep the same behaviour as `*println` for `*print` for _, i in args { if i > 0 { - strings.write_string(buf, sep); + io.write_string(fi.writer, sep); } fmt_value(&fi, args[i], 'v'); } - strings.flush_builder(buf); - return strings.to_string(buf^); + io.flush(auto_cast w); + return int(io.size(auto_cast w)); } -sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { +wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info; - fi.buf = buf; + fi.writer = w; for _, i in args { if i > 0 { - strings.write_string(buf, sep); + io.write_string(fi.writer, sep); } fmt_value(&fi, args[i], 'v'); } - strings.write_byte(buf, '\n'); - strings.flush_builder(buf); - return strings.to_string(buf^); + io.write_byte(fi.writer, '\n'); + io.flush(auto_cast w); + return int(io.size(auto_cast w)); } -sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { +wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi: Info; arg_index: int = 0; end := len(fmt); was_prev_index := false; - loop: for i := 0; i < end; /**/ { - fi = Info{buf = b, good_arg_index = true, reordered = fi.reordered}; + fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}; prev_i := i; for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') { i += 1; } if i > prev_i { - strings.write_string(b, fmt[prev_i:i]); + io.write_string(fi.writer, fmt[prev_i:i]); } if i >= end { break loop; @@ -278,13 +302,13 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { // Skip extra one i += 1; } - strings.write_byte(b, char); + io.write_byte(fi.writer, char); continue loop; } else if char == '{' { if i < end && fmt[i] == char { // Skip extra one i += 1; - strings.write_byte(b, char); + io.write_byte(fi.writer, char); continue loop; } } @@ -315,7 +339,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { i += 1; fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index); if !fi.width_set { - strings.write_string(b, "%!(BAD WIDTH)"); + io.write_string(w, "%!(BAD WIDTH)"); } if fi.width < 0 { @@ -346,7 +370,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { fi.prec_set = false; } if !fi.prec_set { - strings.write_string(fi.buf, "%!(BAD PRECISION)"); + io.write_string(fi.writer, "%!(BAD PRECISION)"); } was_prev_index = false; } else { @@ -359,7 +383,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { } if i >= end { - strings.write_string(b, "%!(NO VERB)"); + io.write_string(fi.writer, "%!(NO VERB)"); break loop; } @@ -368,11 +392,11 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { switch { case verb == '%': - strings.write_byte(b, '%'); + io.write_byte(fi.writer, '%'); case !fi.good_arg_index: - strings.write_string(b, "%!(BAD ARGUMENT NUMBER)"); + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)"); case arg_index >= len(args): - strings.write_string(b, "%!(MISSING ARGUMENT)"); + io.write_string(fi.writer, "%!(MISSING ARGUMENT)"); case: fmt_arg(&fi, args[arg_index], verb); arg_index += 1; @@ -388,14 +412,14 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { arg_index = new_arg_index; i = new_i; } else { - strings.write_string(b, "%!(BAD ARGUMENT NUMBER "); + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER "); // Skip over the bad argument start_index := i; for i < end && fmt[i] != '}' && fmt[i] != ':' { i += 1; } fmt_arg(&fi, fmt[start_index:i], 'v'); - strings.write_string(b, ")"); + io.write_string(fi.writer, ")"); } } @@ -428,7 +452,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { i += 1; fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index); if !fi.width_set { - strings.write_string(b, "%!(BAD WIDTH)"); + io.write_string(fi.writer, "%!(BAD WIDTH)"); } if fi.width < 0 { @@ -459,7 +483,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { fi.prec_set = false; } if !fi.prec_set { - strings.write_string(fi.buf, "%!(BAD PRECISION)"); + io.write_string(fi.writer, "%!(BAD PRECISION)"); } was_prev_index = false; } else { @@ -473,7 +497,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { if i >= end { - strings.write_string(b, "%!(NO VERB)"); + io.write_string(fi.writer, "%!(NO VERB)"); break loop; } @@ -483,7 +507,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { } if i >= end { - strings.write_string(b, "%!(MISSING CLOSE BRACE)"); + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)"); break loop; } @@ -492,11 +516,11 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { switch { case brace != '}': - strings.write_string(b, "%!(MISSING CLOSE BRACE)"); + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)"); case !fi.good_arg_index: - strings.write_string(b, "%!(BAD ARGUMENT NUMBER)"); + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)"); case arg_index >= len(args): - strings.write_string(b, "%!(MISSING ARGUMENT)"); + io.write_string(fi.writer, "%!(MISSING ARGUMENT)"); case: fmt_arg(&fi, args[arg_index], verb); arg_index += 1; @@ -505,23 +529,23 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { } if !fi.reordered && arg_index < len(args) { - strings.write_string(b, "%!(EXTRA "); + io.write_string(fi.writer, "%!(EXTRA "); for arg, index in args[arg_index:] { if index > 0 { - strings.write_string(b, ", "); + io.write_string(fi.writer, ", "); } if arg == nil { - strings.write_string(b, ""); + io.write_string(fi.writer, ""); } else { fmt_arg(&fi, args[index], 'v'); } } - strings.write_string(b, ")"); + io.write_string(fi.writer, ")"); } - strings.flush_builder(b); - return strings.to_string(b^); + io.flush(auto_cast fi.writer); + return int(io.size(auto_cast fi.writer)); } @@ -599,23 +623,23 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { - strings.write_string(buf, "%!"); - strings.write_rune(buf, verb); - strings.write_byte(buf, '('); + io.write_string(writer, "%!"); + io.write_rune(writer, verb); + io.write_byte(writer, '('); if arg.id != nil { - reflect.write_typeid(buf, arg.id); - strings.write_byte(buf, '='); + reflect.write_typeid(writer, arg.id); + io.write_byte(writer, '='); fmt_value(fi, arg, 'v'); } else { - strings.write_string(buf, ""); + io.write_string(writer, ""); } - strings.write_byte(buf, ')'); + io.write_byte(writer, ')'); } fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { switch verb { case 't', 'v': - strings.write_string(buf, b ? "true" : "false"); + io.write_string(writer, b ? "true" : "false"); case: fmt_bad_verb(fi, verb); } @@ -633,7 +657,7 @@ fmt_write_padding :: proc(fi: ^Info, width: int) { } for i := 0; i < width; i += 1 { - strings.write_byte(fi.buf, pad_byte); + io.write_byte(fi.writer, pad_byte); } } @@ -692,8 +716,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d case 16: c = 'x'; } if c != 0 { - strings.write_byte(fi.buf, '0'); - strings.write_byte(fi.buf, c); + io.write_byte(fi.writer, '0'); + io.write_byte(fi.writer, c); } } @@ -758,8 +782,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i case 16: c = 'x'; } if c != 0 { - strings.write_byte(fi.buf, '0'); - strings.write_byte(fi.buf, c); + io.write_byte(fi.writer, '0'); + io.write_byte(fi.writer, c); } } @@ -775,9 +799,9 @@ __DIGITS_UPPER := "0123456789ABCDEFX"; fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': - strings.write_rune(fi.buf, r); + io.write_rune(fi.writer, r); case 'q': - strings.write_quoted_rune(fi.buf, r); + strings.write_quoted_rune(fi.writer, r); case: fmt_int(fi, u64(r), false, 32, verb); } @@ -799,7 +823,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb); } else { - strings.write_string(fi.buf, "U+"); + io.write_string(fi.writer, "U+"); _fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER); } @@ -824,7 +848,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb); } else { - strings.write_string(fi.buf, "U+"); + io.write_string(fi.writer, "U+"); _fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER); } @@ -835,18 +859,18 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru _pad :: proc(fi: ^Info, s: string) { if !fi.width_set { - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); return; } width := fi.width - utf8.rune_count_in_string(s); if fi.minus { // right pad - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); fmt_write_padding(fi, width); } else { // left pad fmt_write_padding(fi, width); - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); } } @@ -872,15 +896,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - strings.write_string(fi.buf, string(b)); + io.write_string(fi.writer, string(b)); return; } if fi.plus || b[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - strings.write_byte(fi.buf, b[0]); + io.write_byte(fi.writer, b[0]); fmt_write_padding(fi, fi.width - len(b)); - strings.write_string(fi.buf, string(b[1:])); + io.write_string(fi.writer, string(b[1:])); } else { _pad(fi, string(b)); } @@ -908,15 +932,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - strings.write_string(fi.buf, string(b)); + io.write_string(fi.writer, string(b)); return; } if fi.plus || str[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - strings.write_byte(fi.buf, b[0]); + io.write_byte(fi.writer, b[0]); fmt_write_padding(fi, fi.width - len(b)); - strings.write_string(fi.buf, string(b[1:])); + io.write_string(fi.writer, string(b[1:])); } else { _pad(fi, string(b)); } @@ -939,7 +963,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { case: panic("Unhandled float size"); } - strings.write_string(fi.buf, "0h"); + io.write_string(fi.writer, "0h"); _fmt_int(fi, u, 16, false, bit_size, __DIGITS_LOWER if verb == 'h' else __DIGITS_UPPER); @@ -952,15 +976,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_string :: proc(fi: ^Info, s: string, verb: rune) { switch verb { case 's', 'v': - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); if fi.width_set && len(s) < fi.width { for _ in 0.. 0 && space { - strings.write_byte(fi.buf, ' '); + io.write_byte(fi.writer, ' '); } char_set := __DIGITS_UPPER; if verb == 'x' { @@ -991,7 +1015,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { switch verb { case 'p', 'v': if !fi.hash || verb == 'v' { - strings.write_string(fi.buf, "0x"); + io.write_string(fi.writer, "0x"); } _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER); @@ -1052,7 +1076,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if v.id == nil || v.data == nil { - strings.write_string(fi.buf, ""); + io.write_string(fi.writer, ""); return; } @@ -1069,7 +1093,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if !ok { str = "!%(BAD ENUM VALUE)"; } - strings.write_string(fi.buf, str); + io.write_string(fi.writer, str); } } } @@ -1101,6 +1125,17 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T return "", false; } +fmt_write_i64 :: proc(w: io.Writer, i: i64, base: int = 10) { + buf: [32]byte; + s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil); + io.write_string(w, s); +} +fmt_write_u64 :: proc(w: io.Writer, i: u64, base: int) { + buf: [32]byte; + s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil); + io.write_string(w, s); +} + fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { @@ -1161,12 +1196,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { et := runtime.type_info_base(info.elem); if name != "" { - strings.write_string(fi.buf, name); + io.write_string(fi.writer, name); } else { - reflect.write_type(fi.buf, type_info); + reflect.write_type(fi.writer, type_info); } - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); e, is_enum := et.variant.(runtime.Type_Info_Enum); commas := 0; @@ -1176,21 +1211,21 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } if commas > 0 { - strings.write_string(fi.buf, ", "); + io.write_string(fi.writer, ", "); } if is_enum { for ev, evi in e.values { v := u64(ev); if v == u64(i) { - strings.write_string(fi.buf, e.names[evi]); + io.write_string(fi.writer, e.names[evi]); commas += 1; continue loop; } } } v := i64(i) + info.lower; - strings.write_i64(fi.buf, v, 10); + fmt_write_i64(fi.writer, v, 10); commas += 1; } } @@ -1212,19 +1247,19 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") { } if bit_field_name != "" { - strings.write_string(fi.buf, bit_field_name); - strings.write_byte(fi.buf, '{'); + io.write_string(fi.writer, bit_field_name); + io.write_byte(fi.writer, '{'); } else { - strings.write_string(fi.buf, "bit_field{"); + io.write_string(fi.writer, "bit_field{"); } for name, i in info.names { if i > 0 { - strings.write_string(fi.buf, ", "); + io.write_string(fi.writer, ", "); } bits := u64(info.bits[i]); offset := u64(info.offsets[i]); - strings.write_string(fi.buf, name); - strings.write_string(fi.buf, " = "); + io.write_string(fi.writer, name); + io.write_string(fi.writer, " = "); n := 8*u64(size_of(u64)); sa := n - bits; @@ -1232,10 +1267,10 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") { u <<= sa; u >>= sa; - strings.write_u64(fi.buf, u, 10); + fmt_write_u64(fi.writer, u, 10); } - strings.write_byte(fi.buf, '}'); + io.write_byte(fi.writer, '}'); } } @@ -1258,16 +1293,16 @@ fmt_opaque :: proc(fi: ^Info, v: any) { type_info := type_info_of(v.id); if is_nil(v.data, type_info.size) { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); return; } if ot, ok := rt.type_info_base(type_info).variant.(rt.Type_Info_Opaque); ok { elem := rt.type_info_base(ot.elem); if elem == nil { return; } - reflect.write_type(fi.buf, type_info); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + reflect.write_type(fi.writer, type_info); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); #partial switch in elem.variant { case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float: @@ -1276,9 +1311,9 @@ fmt_opaque :: proc(fi: ^Info, v: any) { // Okay } } else { - reflect.write_type(fi.buf, type_info); - strings.write_byte(fi.buf, '{'); - strings.write_byte(fi.buf, '}'); + reflect.write_type(fi.writer, type_info); + io.write_byte(fi.writer, '{'); + io.write_byte(fi.writer, '}'); } } @@ -1289,14 +1324,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { n -= 1; } for in 0.."); + io.write_string(fi.writer, ""); return; } @@ -1369,7 +1404,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { w -= 1; switch { case u == 0: - strings.write_string(fi.buf, "0s"); + io.write_string(fi.writer, "0s"); return; case u < u64(time.Microsecond): prec = 0; @@ -1408,7 +1443,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { w -= 1; buf[w] = '-'; } - strings.write_string(fi.buf, string(buf[w:])); + io.write_string(fi.writer, string(buf[w:])); return; case time.Time: @@ -1417,20 +1452,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { h, min, s := time.clock(t); ns := i64(t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9; write_padded_number(fi, i64(y), 4); - strings.write_byte(fi.buf, '-'); + io.write_byte(fi.writer, '-'); write_padded_number(fi, i64(mon), 2); - strings.write_byte(fi.buf, '-'); + io.write_byte(fi.writer, '-'); write_padded_number(fi, i64(d), 2); - strings.write_byte(fi.buf, ' '); + io.write_byte(fi.writer, ' '); write_padded_number(fi, i64(h), 2); - strings.write_byte(fi.buf, ':'); + io.write_byte(fi.writer, ':'); write_padded_number(fi, i64(min), 2); - strings.write_byte(fi.buf, ':'); + io.write_byte(fi.writer, ':'); write_padded_number(fi, i64(s), 2); - strings.write_byte(fi.buf, '.'); + io.write_byte(fi.writer, '.'); write_padded_number(fi, i64(ns), 9); - strings.write_string(fi.buf, " +0000 UTC"); + io.write_string(fi.writer, " +0000 UTC"); return; } @@ -1441,15 +1476,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { return; } if b.is_raw_union { - strings.write_string(fi.buf, info.name); - strings.write_string(fi.buf, "{}"); + io.write_string(fi.writer, info.name); + io.write_string(fi.writer, "{}"); return; }; is_soa := b.soa_kind != .None; - strings.write_string(fi.buf, info.name); - strings.write_byte(fi.buf, '[' if is_soa else '{'); + io.write_string(fi.writer, info.name); + io.write_byte(fi.writer, '[' if is_soa else '{'); hash := fi.hash; defer fi.hash = hash; indent := fi.indent; defer fi.indent -= 1; @@ -1458,13 +1493,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fi.indent += 1; if hash { - strings.write_byte(fi.buf, '\n'); + io.write_byte(fi.writer, '\n'); } defer { if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && index > 0 { io.write_string(fi.writer, ", "); } field_count := -1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } - strings.write_string(fi.buf, base_type_name); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_string(fi.writer, base_type_name); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); for name, i in b.names { field_count += 1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.."); + io.write_string(fi.writer, ""); return; } if fi.record_level < 1 { fi.record_level += 1; defer fi.record_level -= 1; - strings.write_byte(fi.buf, '&'); + io.write_byte(fi.writer, '&'); fmt_value(fi, a, verb); return; } @@ -1582,13 +1617,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct, runtime.Type_Info_Union: if ptr == nil { - strings.write_string(fi.buf, ""); + io.write_string(fi.writer, ""); return; } if fi.record_level < 1 { fi.record_level += 1; defer fi.record_level -= 1; - strings.write_byte(fi.buf, '&'); + io.write_byte(fi.writer, '&'); fmt_value(fi, a, verb); return; } @@ -1603,10 +1638,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { s := strings.string_from_ptr((^byte)(v.data), info.count); fmt_string(fi, s, verb); } else { - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(v.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1614,19 +1649,19 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Enumerated_Array: - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } idx, ok := stored_enum_value_to_string(info.index, info.min_value, i); if ok { - strings.write_byte(fi.buf, '.'); - strings.write_string(fi.buf, idx); + io.write_byte(fi.writer, '.'); + io.write_string(fi.writer, idx); } else { - strings.write_i64(fi.buf, i64(info.min_value)+i64(i)); + fmt_write_i64(fi.writer, i64(info.min_value)+i64(i)); } - strings.write_string(fi.buf, " = "); + io.write_string(fi.writer, " = "); data := uintptr(v.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1640,10 +1675,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } else if verb == 'p' { fmt_pointer(fi, array.data, 'p'); } else { - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(array.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1652,12 +1687,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Simd_Vector: if info.is_x86_mmx { - strings.write_string(fi.buf, "intrinsics.x86_mmx<>"); + io.write_string(fi.writer, "intrinsics.x86_mmx<>"); } - strings.write_byte(fi.buf, '<'); - defer strings.write_byte(fi.buf, '>'); + io.write_byte(fi.writer, '<'); + defer io.write_byte(fi.writer, '>'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(v.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1672,10 +1707,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } else if verb == 'p' { fmt_pointer(fi, slice.data, 'p'); } else { - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(slice.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1687,8 +1722,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { return; } - strings.write_string(fi.buf, "map["); - defer strings.write_byte(fi.buf, ']'); + io.write_string(fi.writer, "map["); + defer io.write_byte(fi.writer, ']'); m := (^mem.Raw_Map)(v.data); if m != nil { @@ -1702,14 +1737,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { entry_size := ed.elem_size; for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(entries.data) + uintptr(i*entry_size); key := data + entry_type.offsets[2]; - fmt_arg(&Info{buf = fi.buf}, any{rawptr(key), info.key.id}, 'v'); + fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v'); - strings.write_string(fi.buf, "="); + io.write_string(fi.writer, "="); value := data + entry_type.offsets[3]; fmt_arg(fi, any{rawptr(value), info.value.id}, 'v'); @@ -1718,21 +1753,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct: if info.is_raw_union { - strings.write_string(fi.buf, "(raw_union)"); + io.write_string(fi.writer, "(raw_union)"); return; } is_soa := info.soa_kind != .None; - strings.write_byte(fi.buf, '[' if is_soa else '{'); - defer strings.write_byte(fi.buf, ']' if is_soa else '}'); + io.write_byte(fi.writer, '[' if is_soa else '{'); + defer io.write_byte(fi.writer, ']' if is_soa else '}'); fi.indent += 1; defer fi.indent -= 1; hash := fi.hash; defer fi.hash = hash; fi.hash = false; - if hash { strings.write_byte(fi.buf, '\n'); } + if hash { io.write_byte(fi.writer, '\n'); } if is_soa { fi.indent += 1; @@ -1761,33 +1796,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { for index in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && index > 0 { io.write_string(fi.writer, ", "); } field_count := -1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } - strings.write_string(fi.buf, base_type_name); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_string(fi.writer, base_type_name); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0..= 0); if v.data == nil { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else if info.no_nil { id := info.variants[tag].id; fmt_arg(fi, any{v.data, id}, verb); } else if tag == 0 { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else { id := info.variants[tag-1].id; fmt_arg(fi, any{v.data, id}, verb); @@ -1886,16 +1921,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Procedure: ptr := (^rawptr)(v.data)^; if ptr == nil { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else { - reflect.write_typeid(fi.buf, v.id); - strings.write_string(fi.buf, " @ "); + reflect.write_typeid(fi.writer, v.id); + io.write_string(fi.writer, " @ "); fmt_pointer(fi, ptr, 'p'); } case runtime.Type_Info_Type_Id: id := (^typeid)(v.data)^; - reflect.write_typeid(fi.buf, id); + reflect.write_typeid(fi.writer, id); case runtime.Type_Info_Bit_Field: fmt_bit_field(fi, v); @@ -1918,18 +1953,18 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if verb == 'p' { fmt_pointer(fi, ptr, 'p'); } else if ptr == nil { - strings.write_string(fi.buf, "[]"); + io.write_string(fi.writer, "[]"); } else { len_ptr := uintptr(v.data) + uintptr(info.base_integer.size); len_any := any{rawptr(len_ptr), info.base_integer.id}; len, _ := reflect.as_int(len_any); slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice); - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(ptr) + uintptr(i*slice_type.elem_size); fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb); @@ -1956,10 +1991,10 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { r, i := real(c), imag(c); fmt_float(fi, r, bits/2, verb); if !fi.plus && i >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, i, bits/2, verb); - strings.write_rune(fi.buf, 'i'); + io.write_rune(fi.writer, 'i'); case: fmt_bad_verb(fi, verb); @@ -1975,22 +2010,22 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { fmt_float(fi, r, bits/4, verb); if !fi.plus && i >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, i, bits/4, verb); - strings.write_rune(fi.buf, 'i'); + io.write_rune(fi.writer, 'i'); if !fi.plus && j >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, j, bits/4, verb); - strings.write_rune(fi.buf, 'j'); + io.write_rune(fi.writer, 'j'); if !fi.plus && k >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, k, bits/4, verb); - strings.write_rune(fi.buf, 'k'); + io.write_rune(fi.writer, 'k'); case: fmt_bad_verb(fi, verb); @@ -2000,7 +2035,7 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { if arg == nil { - strings.write_string(fi.buf, ""); + io.write_string(fi.writer, ""); return; } fi.arg = arg; @@ -2010,7 +2045,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { switch a in arg { case ^runtime.Type_Info: ti = a; } - reflect.write_type(fi.buf, ti); + reflect.write_type(fi.writer, ti); return; } @@ -2028,12 +2063,12 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { custom_types: switch a in arg { case runtime.Source_Code_Location: if fi.hash && verb == 'v' { - strings.write_string(fi.buf, a.file_path); - strings.write_byte(fi.buf, '('); - strings.write_i64(fi.buf, i64(a.line), 10); - strings.write_byte(fi.buf, ':'); - strings.write_i64(fi.buf, i64(a.column), 10); - strings.write_byte(fi.buf, ')'); + io.write_string(fi.writer, a.file_path); + io.write_byte(fi.writer, '('); + fmt_write_i64(fi.writer, i64(a.line), 10); + io.write_byte(fi.writer, ':'); + fmt_write_i64(fi.writer, i64(a.column), 10); + io.write_byte(fi.writer, ')'); return; } } @@ -2080,7 +2115,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case string: fmt_string(fi, a, verb); case cstring: fmt_cstring(fi, a, verb); - case typeid: reflect.write_typeid(fi.buf, a); + case typeid: reflect.write_typeid(fi.writer, a); case i16le: fmt_int(fi, u64(a), true, 16, verb); case u16le: fmt_int(fi, u64(a), false, 16, verb); diff --git a/core/io/conv.odin b/core/io/conv.odin index 07f3494fd..e976bb7b7 100644 --- a/core/io/conv.odin +++ b/core/io/conv.odin @@ -114,6 +114,21 @@ to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) { } return; } +to_Write_Closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) { + w.stream = s; + if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil { + err = .Missing_Procedure; + } + return; +} +to_Write_Seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) { + w.stream = s; + if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil { + err = .Missing_Procedure; + } + return; +} + to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) { b.stream = s; diff --git a/core/io/io.odin b/core/io/io.odin index 0e39a4ef8..bc23c4c86 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -96,6 +96,9 @@ Read_Writer :: struct {using stream: Stream}; Read_Closer :: struct {using stream: Stream}; Read_Write_Closer :: struct {using stream: Stream}; Read_Write_Seeker :: struct {using stream: Stream}; + +Write_Closer :: struct {using stream: Stream}; +Write_Seeker :: struct {using stream: Stream}; Write_Flusher :: struct {using stream: Stream}; Write_Flush_Closer :: struct {using stream: Stream}; @@ -117,7 +120,6 @@ destroy :: proc(s: Stream) -> Error { if s.stream_vtable != nil && s.impl_destroy != nil { return s->impl_destroy(); } - // Instead of .Empty, .None is fine in this case return close_err; } @@ -205,9 +207,14 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) { if err != nil { return 0, err; } - defer r->impl_seek(current_offset, .Start); - return r->impl_read(p); + n, err = r->impl_read(p); + if err != nil { + return; + } + _, err = r->impl_seek(current_offset, .Start); + return; + } write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) { diff --git a/core/io/util.odin b/core/io/util.odin index e714ac7d0..06e95a8b3 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -14,6 +14,13 @@ write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) { return write_string(w, s); } +write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) { + return write_u64(w, u64(i), base); +} +write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) { + return write_i64(w, i64(i), base); +} + @(private) Tee_Reader :: struct { using stream: Stream, diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 348d516b4..274625193 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -1,5 +1,6 @@ package reflect +import "core:io" import "core:strings" are_types_identical :: proc(a, b: ^Type_Info) -> bool { @@ -360,258 +361,273 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool { -write_typeid :: proc(buf: ^strings.Builder, id: typeid) { +write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) { write_type(buf, type_info_of(id)); } +write_typeid_writer :: proc(writer: io.Writer, id: typeid) { + write_type(writer, type_info_of(id)); +} -write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) { +write_typeid :: proc{ + write_typeid_builder, + write_typeid_writer, +}; + +write_type :: proc{ + write_type_builder, + write_type_writer, +}; + +write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) { + write_type_writer(strings.to_writer(buf), ti); +} +write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) { using strings; if ti == nil { - write_string(buf, "nil"); + write_string(w, "nil"); return; } switch info in ti.variant { case Type_Info_Named: - write_string(buf, info.name); + write_string(w, info.name); case Type_Info_Integer: switch ti.id { - case int: write_string(buf, "int"); - case uint: write_string(buf, "uint"); - case uintptr: write_string(buf, "uintptr"); + case int: write_string(w, "int"); + case uint: write_string(w, "uint"); + case uintptr: write_string(w, "uintptr"); case: - write_byte(buf, 'i' if info.signed else 'u'); - write_i64(buf, i64(8*ti.size), 10); + io.write_byte(w, 'i' if info.signed else 'u'); + io.write_i64(w, i64(8*ti.size), 10); switch info.endianness { case .Platform: // Okay - case .Little: write_string(buf, "le"); - case .Big: write_string(buf, "be"); + case .Little: write_string(w, "le"); + case .Big: write_string(w, "be"); } } case Type_Info_Rune: - write_string(buf, "rune"); + io.write_string(w, "rune"); case Type_Info_Float: - write_byte(buf, 'f'); - write_i64(buf, i64(8*ti.size), 10); + io.write_byte(w, 'f'); + io.write_i64(w, i64(8*ti.size), 10); switch info.endianness { case .Platform: // Okay - case .Little: write_string(buf, "le"); - case .Big: write_string(buf, "be"); + case .Little: write_string(w, "le"); + case .Big: write_string(w, "be"); } case Type_Info_Complex: - write_string(buf, "complex"); - write_i64(buf, i64(8*ti.size), 10); + io.write_string(w, "complex"); + io.write_i64(w, i64(8*ti.size), 10); case Type_Info_Quaternion: - write_string(buf, "quaternion"); - write_i64(buf, i64(8*ti.size), 10); + io.write_string(w, "quaternion"); + io.write_i64(w, i64(8*ti.size), 10); case Type_Info_String: if info.is_cstring { - write_string(buf, "cstring"); + write_string(w, "cstring"); } else { - write_string(buf, "string"); + write_string(w, "string"); } case Type_Info_Boolean: switch ti.id { - case bool: write_string(buf, "bool"); + case bool: write_string(w, "bool"); case: - write_byte(buf, 'b'); - write_i64(buf, i64(8*ti.size), 10); + io.write_byte(w, 'b'); + io.write_i64(w, i64(8*ti.size), 10); } case Type_Info_Any: - write_string(buf, "any"); + write_string(w, "any"); case Type_Info_Type_Id: - write_string(buf, "typeid"); + write_string(w, "typeid"); case Type_Info_Pointer: if info.elem == nil { - write_string(buf, "rawptr"); + write_string(w, "rawptr"); } else { - write_string(buf, "^"); - write_type(buf, info.elem); + write_string(w, "^"); + write_type(w, info.elem); } case Type_Info_Procedure: - write_string(buf, "proc"); + write_string(w, "proc"); if info.params == nil { - write_string(buf, "()"); + write_string(w, "()"); } else { t := info.params.variant.(Type_Info_Tuple); - write_string(buf, "("); + write_string(w, "("); for t, i in t.types { if i > 0 { - write_string(buf, ", "); + write_string(w, ", "); } - write_type(buf, t); + write_type(w, t); } - write_string(buf, ")"); + write_string(w, ")"); } if info.results != nil { - write_string(buf, " -> "); - write_type(buf, info.results); + write_string(w, " -> "); + write_type(w, info.results); } case Type_Info_Tuple: count := len(info.names); - if count != 1 { write_string(buf, "("); } + if count != 1 { write_string(w, "("); } for name, i in info.names { - if i > 0 { write_string(buf, ", "); } + if i > 0 { write_string(w, ", "); } t := info.types[i]; if len(name) > 0 { - write_string(buf, name); - write_string(buf, ": "); + write_string(w, name); + write_string(w, ": "); } - write_type(buf, t); + write_type(w, t); } - if count != 1 { write_string(buf, ")"); } + if count != 1 { write_string(w, ")"); } case Type_Info_Array: - write_string(buf, "["); - write_i64(buf, i64(info.count), 10); - write_string(buf, "]"); - write_type(buf, info.elem); + io.write_string(w, "["); + io.write_i64(w, i64(info.count), 10); + io.write_string(w, "]"); + write_type(w, info.elem); case Type_Info_Enumerated_Array: - write_string(buf, "["); - write_type(buf, info.index); - write_string(buf, "]"); - write_type(buf, info.elem); + write_string(w, "["); + write_type(w, info.index); + write_string(w, "]"); + write_type(w, info.elem); case Type_Info_Dynamic_Array: - write_string(buf, "[dynamic]"); - write_type(buf, info.elem); + io.write_string(w, "[dynamic]"); + write_type(w, info.elem); case Type_Info_Slice: - write_string(buf, "[]"); - write_type(buf, info.elem); + io.write_string(w, "[]"); + write_type(w, info.elem); case Type_Info_Map: - write_string(buf, "map["); - write_type(buf, info.key); - write_byte(buf, ']'); - write_type(buf, info.value); + io.write_string(w, "map["); + write_type(w, info.key); + io.write_byte(w, ']'); + write_type(w, info.value); case Type_Info_Struct: switch info.soa_kind { case .None: // Ignore case .Fixed: - write_string(buf, "#soa["); - write_i64(buf, i64(info.soa_len)); - write_byte(buf, ']'); - write_type(buf, info.soa_base_type); + io.write_string(w, "#soa["); + io.write_i64(w, i64(info.soa_len)); + io.write_byte(w, ']'); + write_type(w, info.soa_base_type); return; case .Slice: - write_string(buf, "#soa[]"); - write_type(buf, info.soa_base_type); + io.write_string(w, "#soa[]"); + write_type(w, info.soa_base_type); return; case .Dynamic: - write_string(buf, "#soa[dynamic]"); - write_type(buf, info.soa_base_type); + io.write_string(w, "#soa[dynamic]"); + write_type(w, info.soa_base_type); return; } - write_string(buf, "struct "); - if info.is_packed { write_string(buf, "#packed "); } - if info.is_raw_union { write_string(buf, "#raw_union "); } + write_string(w, "struct "); + if info.is_packed { write_string(w, "#packed "); } + if info.is_raw_union { write_string(w, "#raw_union "); } if info.custom_align { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); + io.write_string(w, "#align "); + io.write_i64(w, i64(ti.align), 10); + io.write_byte(w, ' '); } - write_byte(buf, '{'); + io.write_byte(w, '{'); for name, i in info.names { - if i > 0 { write_string(buf, ", "); } - write_string(buf, name); - write_string(buf, ": "); - write_type(buf, info.types[i]); + if i > 0 { write_string(w, ", "); } + io.write_string(w, name); + io.write_string(w, ": "); + write_type(w, info.types[i]); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Union: - write_string(buf, "union "); + write_string(w, "union "); if info.custom_align { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); + write_string(w, "#align "); + io.write_i64(w, i64(ti.align), 10); + io.write_byte(w, ' '); } - write_byte(buf, '{'); + io.write_byte(w, '{'); for variant, i in info.variants { - if i > 0 { write_string(buf, ", "); } - write_type(buf, variant); + if i > 0 { write_string(w, ", "); } + write_type(w, variant); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Enum: - write_string(buf, "enum "); - write_type(buf, info.base); - write_string(buf, " {"); + write_string(w, "enum "); + write_type(w, info.base); + write_string(w, " {"); for name, i in info.names { - if i > 0 { write_string(buf, ", "); } - write_string(buf, name); + if i > 0 { write_string(w, ", "); } + write_string(w, name); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Bit_Field: - write_string(buf, "bit_field "); + write_string(w, "bit_field "); if ti.align != 1 { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); + write_string(w, "#align "); + io.write_i64(w, i64(ti.align), 10); + io.write_byte(w, ' '); } - write_string(buf, " {"); + write_string(w, " {"); for name, i in info.names { - if i > 0 { write_string(buf, ", "); } - write_string(buf, name); - write_string(buf, ": "); - write_i64(buf, i64(info.bits[i]), 10); + if i > 0 { write_string(w, ", "); } + write_string(w, name); + write_string(w, ": "); + io.write_i64(w, i64(info.bits[i]), 10); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Bit_Set: - write_string(buf, "bit_set["); + write_string(w, "bit_set["); switch { case is_enum(info.elem): - write_type(buf, info.elem); + write_type(w, info.elem); case is_rune(info.elem): - write_encoded_rune(buf, rune(info.lower)); - write_string(buf, ".."); - write_encoded_rune(buf, rune(info.upper)); + write_encoded_rune(w, rune(info.lower)); + write_string(w, ".."); + write_encoded_rune(w, rune(info.upper)); case: - write_i64(buf, info.lower, 10); - write_string(buf, ".."); - write_i64(buf, info.upper, 10); + io.write_i64(w, info.lower, 10); + write_string(w, ".."); + io.write_i64(w, info.upper, 10); } if info.underlying != nil { - write_string(buf, "; "); - write_type(buf, info.underlying); + write_string(w, "; "); + write_type(w, info.underlying); } - write_byte(buf, ']'); + io.write_byte(w, ']'); case Type_Info_Opaque: - write_string(buf, "opaque "); - write_type(buf, info.elem); + write_string(w, "opaque "); + write_type(w, info.elem); case Type_Info_Simd_Vector: if info.is_x86_mmx { - write_string(buf, "intrinsics.x86_mmx"); + write_string(w, "intrinsics.x86_mmx"); } else { - write_string(buf, "#simd["); - write_i64(buf, i64(info.count)); - write_byte(buf, ']'); - write_type(buf, info.elem); + write_string(w, "#simd["); + io.write_i64(w, i64(info.count)); + io.write_byte(w, ']'); + write_type(w, info.elem); } case Type_Info_Relative_Pointer: - write_string(buf, "#relative("); - write_type(buf, info.base_integer); - write_string(buf, ") "); - write_type(buf, info.pointer); + write_string(w, "#relative("); + write_type(w, info.base_integer); + write_string(w, ") "); + write_type(w, info.pointer); case Type_Info_Relative_Slice: - write_string(buf, "#relative("); - write_type(buf, info.base_integer); - write_string(buf, ") "); - write_type(buf, info.slice); - + write_string(w, "#relative("); + write_type(w, info.base_integer); + write_string(w, ") "); + write_type(w, info.slice); } } diff --git a/core/strings/builder.odin b/core/strings/builder.odin index b31190215..3506b4bc8 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -3,6 +3,7 @@ package strings import "core:mem" import "core:unicode/utf8" import "core:strconv" +import "core:io" Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool); @@ -50,6 +51,39 @@ init_builder :: proc{ init_builder_len_cap, }; +@(private) +_builder_stream_vtable := &io.Stream_VTable{ + impl_flush = proc(s: io.Stream) -> io.Error { + b := (^Builder)(s.stream_data); + flush_builder(b); + return nil; + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Builder)(s.stream_data); + n = write_bytes(b, p); + if len(b.buf) == cap(b.buf) { + err = .EOF; + } + return; + }, + impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { + b := (^Builder)(s.stream_data); + _ = write_byte(b, c); + if len(b.buf) == cap(b.buf) { + return .EOF; + } + return nil; + }, +}; + +to_stream :: proc(b: ^Builder) -> io.Stream { + return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}; +} +to_writer :: proc(b: ^Builder) -> io.Writer { + w, _ := io.to_writer(to_stream(b)); + return w; +} + @@ -139,36 +173,75 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { return; } -write_rune :: proc(b: ^Builder, r: rune) -> int { +write_rune :: proc{ + write_rune_builder, + write_rune_writer, +}; +write_rune_builder :: proc(b: ^Builder, r: rune) -> int { + return write_rune_writer(to_writer(b), r); +} +write_rune_writer :: proc(w: io.Writer, r: rune) -> int { if r < utf8.RUNE_SELF { - return write_byte(b, byte(r)); + return _write_byte(w, byte(r)); } s, n := utf8.encode_rune(r); - write_bytes(b, s[:n]); + n, _ = io.write(w, s[:n]); return n; } -write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { + + +write_quoted_rune :: proc{ + write_quoted_rune_builder, + write_quoted_rune_writer, +}; + +write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) { + return write_quoted_rune_writer(to_writer(b), r); +} + +@(private) +_write_byte :: proc(w: io.Writer, r: byte) -> int { + err := io.write_byte(w, r); + return 1 if err == nil else 0; +} + +write_quoted_rune_writer :: proc(w: io.Writer, r: rune) -> (n: int) { + quote := byte('\''); - n += write_byte(b, quote); + n += _write_byte(w, quote); buf, width := utf8.encode_rune(r); if width == 1 && r == utf8.RUNE_ERROR { - n += write_byte(b, '\\'); - n += write_byte(b, 'x'); - n += write_byte(b, DIGITS_LOWER[buf[0]>>4]); - n += write_byte(b, DIGITS_LOWER[buf[0]&0xf]); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'x'); + n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]); + n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]); } else { - n += write_escaped_rune(b, r, quote); + n += write_escaped_rune(w, r, quote); } - n += write_byte(b, quote); + n += _write_byte(w, quote); return; } -write_string :: proc(b: ^Builder, s: string) -> (n: int) { - return write_bytes(b, transmute([]byte)s); + +write_string :: proc{ + write_string_builder, + write_string_writer, +}; + +write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) { + return write_string_writer(to_writer(b), s); } +write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) { + n, _ = io.write(w, transmute([]byte)s); + return; +} + + + + pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { return 0; @@ -190,8 +263,17 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { @(private, static) DIGITS_LOWER := "0123456789abcdefx"; -write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { - n += write_byte(b, quote); +write_quoted_string :: proc{ + write_quoted_string_builder, + write_quoted_string_writer, +}; + +write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { + return write_quoted_string_writer(to_writer(b), str, quote); +} + +write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) { + n += _write_byte(w, quote); for width, s := 0, str; len(s) > 0; s = s[width:] { r := rune(s[0]); width = 1; @@ -199,57 +281,74 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: r, width = utf8.decode_rune_in_string(s); } if width == 1 && r == utf8.RUNE_ERROR { - n += write_byte(b, '\\'); - n += write_byte(b, 'x'); - n += write_byte(b, DIGITS_LOWER[s[0]>>4]); - n += write_byte(b, DIGITS_LOWER[s[0]&0xf]); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'x'); + n += _write_byte(w, DIGITS_LOWER[s[0]>>4]); + n += _write_byte(w, DIGITS_LOWER[s[0]&0xf]); continue; } - n += write_escaped_rune(b, r, quote); + n += write_escaped_rune(w, r, quote); } - n += write_byte(b, quote); + n += _write_byte(w, quote); return; } +write_encoded_rune :: proc{ + write_encoded_rune_builder, + write_encoded_rune_writer, +}; -write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { +write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { + return write_encoded_rune_writer(to_writer(b), r, write_quote); + +} +write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) { if write_quote { - n += write_byte(b, '\''); + n += _write_byte(w, '\''); } switch r { - case '\a': n += write_string(b, `\a"`); - case '\b': n += write_string(b, `\b"`); - case '\e': n += write_string(b, `\e"`); - case '\f': n += write_string(b, `\f"`); - case '\n': n += write_string(b, `\n"`); - case '\r': n += write_string(b, `\r"`); - case '\t': n += write_string(b, `\t"`); - case '\v': n += write_string(b, `\v"`); + case '\a': n += write_string(w, `\a"`); + case '\b': n += write_string(w, `\b"`); + case '\e': n += write_string(w, `\e"`); + case '\f': n += write_string(w, `\f"`); + case '\n': n += write_string(w, `\n"`); + case '\r': n += write_string(w, `\r"`); + case '\t': n += write_string(w, `\t"`); + case '\v': n += write_string(w, `\v"`); case: if r < 32 { - n += write_string(b, `\x`); + n += write_string(w, `\x`); buf: [2]byte; s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil); switch len(s) { - case 0: n += write_string(b, "00"); - case 1: n += write_byte(b, '0'); - case 2: n += write_string(b, s); + case 0: n += write_string(w, "00"); + case 1: n += _write_byte(w, '0'); + case 2: n += write_string(w, s); } } else { - n += write_rune(b, r); + n += write_rune(w, r); } } if write_quote { - n += write_byte(b, '\''); + n += _write_byte(w, '\''); } return; } -write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { +write_escaped_rune :: proc{ + write_escaped_rune_builder, + write_escaped_rune_writer, +}; + +write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { + return write_escaped_rune_writer(to_writer(b), r, quote, html_safe); +} + +write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) { is_printable :: proc(r: rune) -> bool { if r <= 0xff { switch r { @@ -267,54 +366,54 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false if html_safe { switch r { case '<', '>', '&': - n += write_byte(b, '\\'); - n += write_byte(b, 'u'); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'u'); for s := 12; s >= 0; s -= 4 { - n += write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]); + n += _write_byte(w, DIGITS_LOWER[r>>uint(s) & 0xf]); } return; } } if r == rune(quote) || r == '\\' { - n += write_byte(b, '\\'); - n += write_byte(b, byte(r)); + n += _write_byte(w, '\\'); + n += _write_byte(w, byte(r)); return; } else if is_printable(r) { - n += write_encoded_rune(b, r, false); + n += write_encoded_rune(w, r, false); return; } switch r { - case '\a': n += write_string(b, `\a`); - case '\b': n += write_string(b, `\b`); - case '\e': n += write_string(b, `\e`); - case '\f': n += write_string(b, `\f`); - case '\n': n += write_string(b, `\n`); - case '\r': n += write_string(b, `\r`); - case '\t': n += write_string(b, `\t`); - case '\v': n += write_string(b, `\v`); + case '\a': n += write_string(w, `\a`); + case '\b': n += write_string(w, `\b`); + case '\e': n += write_string(w, `\e`); + case '\f': n += write_string(w, `\f`); + case '\n': n += write_string(w, `\n`); + case '\r': n += write_string(w, `\r`); + case '\t': n += write_string(w, `\t`); + case '\v': n += write_string(w, `\v`); case: switch c := r; { case c < ' ': - n += write_byte(b, '\\'); - n += write_byte(b, 'x'); - n += write_byte(b, DIGITS_LOWER[byte(c)>>4]); - n += write_byte(b, DIGITS_LOWER[byte(c)&0xf]); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'x'); + n += _write_byte(w, DIGITS_LOWER[byte(c)>>4]); + n += _write_byte(w, DIGITS_LOWER[byte(c)&0xf]); case c > utf8.MAX_RUNE: c = 0xfffd; fallthrough; case c < 0x10000: - n += write_byte(b, '\\'); - n += write_byte(b, 'u'); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'u'); for s := 12; s >= 0; s -= 4 { - n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]); + n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]); } case: - n += write_byte(b, '\\'); - n += write_byte(b, 'U'); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'U'); for s := 28; s >= 0; s -= 4 { - n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]); + n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]); } } } diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index 674fd9206..41add2778 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -8,7 +8,8 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> return ""; } - b := make_builder_len_cap(0, 0, allocator); + b: Builder; + init_builder(&b, 0, 0, allocator); s := s; for c, i in s { @@ -57,14 +58,16 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> } to_lower :: proc(s: string, allocator := context.allocator) -> string { - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); for r in s { write_rune(&b, unicode.to_lower(r)); } return to_string(b); } to_upper :: proc(s: string, allocator := context.allocator) -> string { - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); for r in s { write_rune(&b, unicode.to_upper(r)); } @@ -123,7 +126,8 @@ to_lower_camel_case :: to_camel_case; to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { if !is_delimiter(curr) { @@ -144,7 +148,8 @@ to_upper_camel_case :: to_pascal_case; to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { if !is_delimiter(curr) { @@ -164,7 +169,8 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower; @@ -221,7 +227,8 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); prev, curr: rune; diff --git a/core/strings/strings.odin b/core/strings/strings.odin index fd9e7299b..0306defc8 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -756,7 +756,8 @@ split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator // Adjacent invalid bytes are only replaced once scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { str := s; - b := make_builder(0, len(str), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); has_error := false; cursor := 0; @@ -811,7 +812,8 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return ""; } - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); str := s; column: int; @@ -868,7 +870,8 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte remains := length-1; pad_len := rune_count(pad); - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); write_pad_string(&b, pad, pad_len, remains/2); @@ -888,7 +891,8 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context remains := length-1; pad_len := rune_count(pad); - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); write_string(&b, str); @@ -907,7 +911,8 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex remains := length-1; pad_len := rune_count(pad); - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); write_pad_string(&b, pad, pad_len, remains); From 828fe2ce56402a667ac7f5d79bf4575763c7c0d0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 12:25:45 +0000 Subject: [PATCH 140/170] Fix #795 --- src/check_stmt.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ad72256c3..8a41dd12b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1636,8 +1636,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } else if (operands.count != result_count) { error(node, "Expected %td return values, got %td", result_count, operands.count); } else { - isize max_count = rs->results.count; - for (isize i = 0; i < max_count; i++) { + for (isize i = 0; i < result_count; i++) { Entity *e = pt->results->Tuple.variables[i]; check_assignment(ctx, &operands[i], e->type, str_lit("return statement")); } From 6e04b1c42938030a82c1f1384b40b6940a1bd273 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 13:23:36 +0000 Subject: [PATCH 141/170] Remove `using stream` on custom stream types in package io --- core/io/multi.odin | 22 ++++++++++------------ core/io/util.odin | 25 ++++++++++++------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/core/io/multi.odin b/core/io/multi.odin index b6583ecf6..817d4a1cb 100644 --- a/core/io/multi.odin +++ b/core/io/multi.odin @@ -4,7 +4,6 @@ import "core:runtime" @(private) Multi_Reader :: struct { - using stream: Stream, readers: [dynamic]Reader, } @@ -37,11 +36,9 @@ _multi_reader_vtable := &Stream_VTable{ }, }; -mutlti_reader :: proc(readers: ..Reader, allocator := context.allocator) -> Reader { +mutlti_reader :: proc(readers: ..Reader, allocator := context.allocator) -> (r: Reader) { context.allocator = allocator; mr := new(Multi_Reader); - mr.stream_vtable = _multi_reader_vtable; - mr.stream_data = mr; all_readers := make([dynamic]Reader, 0, len(readers)); for w in readers { @@ -54,14 +51,15 @@ mutlti_reader :: proc(readers: ..Reader, allocator := context.allocator) -> Read } mr.readers = all_readers; - res, _ := to_reader(mr^); - return res; + + r.stream_vtable = _multi_reader_vtable; + r.stream_data = mr; + return; } @(private) Multi_Writer :: struct { - using stream: Stream, writers: []Writer, allocator: runtime.Allocator, } @@ -92,11 +90,9 @@ _multi_writer_vtable := &Stream_VTable{ }, }; -mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> Writer { +mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (w: Writer) { context.allocator = allocator; mw := new(Multi_Writer); - mw.stream_vtable = _multi_writer_vtable; - mw.stream_data = mw; mw.allocator = allocator; all_writers := make([dynamic]Writer, 0, len(writers)); @@ -110,6 +106,8 @@ mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> Writ } mw.writers = all_writers[:]; - res, _ := to_writer(mw^); - return res; + + w.stream_vtable = _multi_writer_vtable; + w.stream_data = mw; + return; } diff --git a/core/io/util.odin b/core/io/util.odin index 06e95a8b3..b618a6c23 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -23,7 +23,6 @@ write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) { @(private) Tee_Reader :: struct { - using stream: Stream, r: Reader, w: Writer, allocator: runtime.Allocator, @@ -51,14 +50,14 @@ _tee_reader_vtable := &Stream_VTable{ // tee_reader // tee_reader must call io.destroy when done with -tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> Reader { +tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) { t := new(Tee_Reader, allocator); t.r, t.w = r, w; t.allocator = allocator; - t.stream_data = t; - t.stream_vtable = _tee_reader_vtable; - res, _ := to_reader(t^); - return res; + + out.stream_data = t; + out.stream_vtable = _tee_reader_vtable; + return; } @@ -67,7 +66,6 @@ tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> Read // updates n to reflect the new amount remaining. // read returns EOF when n <= 0 or when the underlying r returns EOF. Limited_Reader :: struct { - using stream: Stream, r: Reader, // underlying reader n: i64, // max_bytes } @@ -91,19 +89,20 @@ _limited_reader_vtable := &Stream_VTable{ new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader { l := new(Limited_Reader); - l.stream_vtable = _limited_reader_vtable; - l.stream_data = l; l.r = r; l.n = n; return l; } +limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) { + r.stream_vtable = _limited_reader_vtable; + r.stream_data = l; + return; +} + @(private="package") inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { - l.stream_vtable = _limited_reader_vtable; - l.stream_data = l; l.r = r; l.n = n; - res, _ := to_reader(l^); - return res; + return limited_reader_to_reader(l); } From 5acdcfb57c612e25c9e8a49497aeff9881e6fe60 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 13:26:33 +0000 Subject: [PATCH 142/170] Keep -vet happy --- core/io/multi.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/io/multi.odin b/core/io/multi.odin index 817d4a1cb..fa26b0052 100644 --- a/core/io/multi.odin +++ b/core/io/multi.odin @@ -90,7 +90,7 @@ _multi_writer_vtable := &Stream_VTable{ }, }; -mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (w: Writer) { +mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (out: Writer) { context.allocator = allocator; mw := new(Multi_Writer); mw.allocator = allocator; @@ -107,7 +107,7 @@ mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (w: mw.writers = all_writers[:]; - w.stream_vtable = _multi_writer_vtable; - w.stream_data = mw; + out.stream_vtable = _multi_writer_vtable; + out.stream_data = mw; return; } From 047586afc69977164567daff0927afea31284519 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 14:21:33 +0000 Subject: [PATCH 143/170] Change ExactValue layout for complex/quaternion types to minimize its size --- src/check_expr.cpp | 20 ++++----- src/exact_value.cpp | 102 +++++++++++++++++++++++-------------------- src/ir_print.cpp | 12 ++--- src/llvm_backend.cpp | 24 +++++----- 4 files changed, 82 insertions(+), 76 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d31874959..8e09c0320 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3407,7 +3407,7 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti return value; } else if (value.kind == ExactValue_Quaternion) { // @QuaternionLayout - Quaternion256 q = value.value_quaternion; + Quaternion256 q = *value.value_quaternion; GB_ASSERT(sel.index.count == 1); switch (sel.index[0]) { @@ -3432,7 +3432,7 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti return empty_exact_value; } else if (value.kind == ExactValue_Complex) { // @QuaternionLayout - Complex128 c = value.value_complex; + Complex128 c = *value.value_complex; GB_ASSERT(sel.index.count == 1); switch (sel.index[0]) { @@ -4727,8 +4727,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_type_complex(x->type)) { if (x->mode == Addressing_Constant) { ExactValue v = exact_value_to_complex(x->value); - f64 r = v.value_complex.real; - f64 i = -v.value_complex.imag; + f64 r = v.value_complex->real; + f64 i = -v.value_complex->imag; x->value = exact_value_complex(r, i); x->mode = Addressing_Constant; } else { @@ -4737,10 +4737,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } else if (is_type_quaternion(x->type)) { if (x->mode == Addressing_Constant) { ExactValue v = exact_value_to_quaternion(x->value); - f64 r = v.value_quaternion.real; - f64 i = -v.value_quaternion.imag; - f64 j = -v.value_quaternion.jmag; - f64 k = -v.value_quaternion.kmag; + f64 r = +v.value_quaternion->real; + f64 i = -v.value_quaternion->imag; + f64 j = -v.value_quaternion->jmag; + f64 k = -v.value_quaternion->kmag; x->value = exact_value_quaternion(r, i, j, k); x->mode = Addressing_Constant; } else { @@ -5157,8 +5157,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->value.value_float = gb_abs(operand->value.value_float); break; case ExactValue_Complex: { - f64 r = operand->value.value_complex.real; - f64 i = operand->value.value_complex.imag; + f64 r = operand->value.value_complex->real; + f64 i = operand->value.value_complex->imag; operand->value = exact_value_float(gb_sqrt(r*r + i*i)); break; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 7199a28ac..326f4d587 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -46,16 +46,16 @@ enum ExactValueKind { struct ExactValue { ExactValueKind kind; union { - bool value_bool; - String value_string; - BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer - f64 value_float; - i64 value_pointer; - Complex128 value_complex; - Quaternion256 value_quaternion; - Ast * value_compound; - Ast * value_procedure; - Type * value_typeid; + bool value_bool; + String value_string; + BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer + f64 value_float; + i64 value_pointer; + Complex128 *value_complex; + Quaternion256 *value_quaternion; + Ast * value_compound; + Ast * value_procedure; + Type * value_typeid; }; }; @@ -85,9 +85,9 @@ HashKey hash_exact_value(ExactValue v) { case ExactValue_Pointer: return hash_integer(v.value_pointer); case ExactValue_Complex: - return hashing_proc(&v.value_complex, gb_size_of(Complex128)); + return hashing_proc(v.value_complex, gb_size_of(Complex128)); case ExactValue_Quaternion: - return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256)); + return hashing_proc(v.value_quaternion, gb_size_of(Quaternion256)); case ExactValue_Compound: return hash_pointer(v.value_compound); case ExactValue_Procedure: @@ -139,17 +139,19 @@ ExactValue exact_value_float(f64 f) { ExactValue exact_value_complex(f64 real, f64 imag) { ExactValue result = {ExactValue_Complex}; - result.value_complex.real = real; - result.value_complex.imag = imag; + result.value_complex = gb_alloc_item(permanent_allocator(), Complex128); + result.value_complex->real = real; + result.value_complex->imag = imag; return result; } ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { ExactValue result = {ExactValue_Quaternion}; - result.value_quaternion.real = real; - result.value_quaternion.imag = imag; - result.value_quaternion.jmag = jmag; - result.value_quaternion.kmag = kmag; + result.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256); + result.value_quaternion->real = real; + result.value_quaternion->imag = imag; + result.value_quaternion->jmag = jmag; + result.value_quaternion->kmag = kmag; return result; } @@ -373,6 +375,7 @@ ExactValue exact_value_to_complex(ExactValue v) { // return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag); } ExactValue r = {ExactValue_Invalid}; + v.value_complex = gb_alloc_item(permanent_allocator(), Complex128); return r; } ExactValue exact_value_to_quaternion(ExactValue v) { @@ -382,11 +385,12 @@ ExactValue exact_value_to_quaternion(ExactValue v) { case ExactValue_Float: return exact_value_quaternion(v.value_float, 0, 0, 0); case ExactValue_Complex: - return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0); + return exact_value_quaternion(v.value_complex->real, v.value_complex->imag, 0, 0); case ExactValue_Quaternion: return v; } ExactValue r = {ExactValue_Invalid}; + v.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256); return r; } @@ -396,9 +400,9 @@ ExactValue exact_value_real(ExactValue v) { case ExactValue_Float: return v; case ExactValue_Complex: - return exact_value_float(v.value_complex.real); + return exact_value_float(v.value_complex->real); case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion.real); + return exact_value_float(v.value_quaternion->real); } ExactValue r = {ExactValue_Invalid}; return r; @@ -410,9 +414,9 @@ ExactValue exact_value_imag(ExactValue v) { case ExactValue_Float: return exact_value_i64(0); case ExactValue_Complex: - return exact_value_float(v.value_complex.imag); + return exact_value_float(v.value_complex->imag); case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion.imag); + return exact_value_float(v.value_quaternion->imag); } ExactValue r = {ExactValue_Invalid}; return r; @@ -425,7 +429,7 @@ ExactValue exact_value_jmag(ExactValue v) { case ExactValue_Complex: return exact_value_i64(0); case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion.jmag); + return exact_value_float(v.value_quaternion->jmag); } ExactValue r = {ExactValue_Invalid}; return r; @@ -438,7 +442,7 @@ ExactValue exact_value_kmag(ExactValue v) { case ExactValue_Complex: return exact_value_i64(0); case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion.kmag); + return exact_value_float(v.value_quaternion->kmag); } ExactValue r = {ExactValue_Invalid}; return r; @@ -532,15 +536,15 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, return i; } case ExactValue_Complex: { - f64 real = v.value_complex.real; - f64 imag = v.value_complex.imag; + f64 real = v.value_complex->real; + f64 imag = v.value_complex->imag; return exact_value_complex(-real, -imag); } case ExactValue_Quaternion: { - f64 real = v.value_quaternion.real; - f64 imag = v.value_quaternion.imag; - f64 jmag = v.value_quaternion.jmag; - f64 kmag = v.value_quaternion.kmag; + f64 real = v.value_quaternion->real; + f64 imag = v.value_quaternion->imag; + f64 jmag = v.value_quaternion->jmag; + f64 kmag = v.value_quaternion->kmag; return exact_value_quaternion(-real, -imag, -jmag, -kmag); } } @@ -732,10 +736,10 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) case ExactValue_Complex: { y = exact_value_to_complex(y); - f64 a = x.value_complex.real; - f64 b = x.value_complex.imag; - f64 c = y.value_complex.real; - f64 d = y.value_complex.imag; + f64 a = x.value_complex->real; + f64 b = x.value_complex->imag; + f64 c = y.value_complex->real; + f64 d = y.value_complex->imag; f64 real = 0; f64 imag = 0; switch (op) { @@ -765,14 +769,14 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) case ExactValue_Quaternion: { y = exact_value_to_quaternion(y); - f64 xr = x.value_quaternion.real; - f64 xi = x.value_quaternion.imag; - f64 xj = x.value_quaternion.jmag; - f64 xk = x.value_quaternion.kmag; - f64 yr = y.value_quaternion.real; - f64 yi = y.value_quaternion.imag; - f64 yj = y.value_quaternion.jmag; - f64 yk = y.value_quaternion.kmag; + f64 xr = x.value_quaternion->real; + f64 xi = x.value_quaternion->imag; + f64 xj = x.value_quaternion->jmag; + f64 xk = x.value_quaternion->kmag; + f64 yr = y.value_quaternion->real; + f64 yi = y.value_quaternion->imag; + f64 yj = y.value_quaternion->jmag; + f64 yk = y.value_quaternion->kmag; f64 real = 0; @@ -899,10 +903,10 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { } case ExactValue_Complex: { - f64 a = x.value_complex.real; - f64 b = x.value_complex.imag; - f64 c = y.value_complex.real; - f64 d = y.value_complex.imag; + f64 a = x.value_complex->real; + f64 b = x.value_complex->imag; + f64 c = y.value_complex->real; + f64 d = y.value_complex->imag; switch (op) { case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0; case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0; @@ -978,7 +982,9 @@ gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize st case ExactValue_Float: return gb_string_append_fmt(str, "%f", v.value_float); case ExactValue_Complex: - return gb_string_append_fmt(str, "%f+%fi", v.value_complex.real, v.value_complex.imag); + return gb_string_append_fmt(str, "%f+%fi", v.value_complex->real, v.value_complex->imag); + case ExactValue_Quaternion: + return gb_string_append_fmt(str, "%f+%fi+%fj+%fk", v.value_quaternion->real, v.value_quaternion->imag, v.value_quaternion->jmag, v.value_quaternion->kmag); case ExactValue_Pointer: return str; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index cb71589c5..a04f7b657 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -953,9 +953,9 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, ' '); ir_write_byte(f, '{'); ir_print_type(f, m, ft); ir_write_byte(f, ' '); - ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft); + ir_print_exact_value(f, m, exact_value_float(value.value_complex->real), ft); ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); - ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft); + ir_print_exact_value(f, m, exact_value_float(value.value_complex->imag), ft); ir_write_byte(f, '}'); break; } @@ -968,13 +968,13 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, ' '); ir_write_byte(f, '{'); ir_print_type(f, m, ft); ir_write_byte(f, ' '); - ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->imag), ft); ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); - ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->jmag), ft); ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); - ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->kmag), ft); ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); - ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->real), ft); ir_write_byte(f, '}'); break; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 11b301268..b4edc4914 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5316,12 +5316,12 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc LLVMValueRef values[2] = {}; switch (8*type_size_of(type)) { case 64: - values[0] = lb_const_f32(m, cast(f32)value.value_complex.real); - values[1] = lb_const_f32(m, cast(f32)value.value_complex.imag); + values[0] = lb_const_f32(m, cast(f32)value.value_complex->real); + values[1] = lb_const_f32(m, cast(f32)value.value_complex->imag); break; case 128: - values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_complex.real); - values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_complex.imag); + values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->real); + values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->imag); break; } @@ -5335,17 +5335,17 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc switch (8*type_size_of(type)) { case 128: // @QuaternionLayout - values[3] = lb_const_f32(m, cast(f32)value.value_quaternion.real); - values[0] = lb_const_f32(m, cast(f32)value.value_quaternion.imag); - values[1] = lb_const_f32(m, cast(f32)value.value_quaternion.jmag); - values[2] = lb_const_f32(m, cast(f32)value.value_quaternion.kmag); + values[3] = lb_const_f32(m, cast(f32)value.value_quaternion->real); + values[0] = lb_const_f32(m, cast(f32)value.value_quaternion->imag); + values[1] = lb_const_f32(m, cast(f32)value.value_quaternion->jmag); + values[2] = lb_const_f32(m, cast(f32)value.value_quaternion->kmag); break; case 256: // @QuaternionLayout - values[3] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.real); - values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.imag); - values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.jmag); - values[2] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.kmag); + values[3] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->real); + values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->imag); + values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->jmag); + values[2] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->kmag); break; } From 0ef02e6737ffc382a00fc549811e9fca49bd4089 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 15:57:46 +0000 Subject: [PATCH 144/170] Improve packages io and strings; add io.Section_Reader --- core/io/io.odin | 15 +++++- core/io/util.odin | 94 ++++++++++++++++++++++++++++++++++-- core/strings/builder.odin | 44 +++++++---------- core/strings/conversion.odin | 57 ++++++++++++---------- core/strings/strings.odin | 36 ++++++++------ 5 files changed, 173 insertions(+), 73 deletions(-) diff --git a/core/io/io.odin b/core/io/io.odin index bc23c4c86..bfa81d417 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -35,13 +35,16 @@ Error :: enum i32 { Invalid_Offset, Invalid_Unread, + Negative_Read, + Negative_Write, + // Empty is returned when a procedure has not been implemented for an io.Stream Empty = -1, } Close_Proc :: distinct proc(using s: Stream) -> Error; Flush_Proc :: distinct proc(using s: Stream) -> Error; -Seek_Proc :: distinct proc(using s: Stream, offset: i64, whence: Seek_From) -> (i64, Error); +Seek_Proc :: distinct proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error); Size_Proc :: distinct proc(using s: Stream) -> i64; Read_Proc :: distinct proc(using s: Stream, p: []byte) -> (n: int, err: Error); Read_At_Proc :: distinct proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error); @@ -120,7 +123,10 @@ destroy :: proc(s: Stream) -> Error { if s.stream_vtable != nil && s.impl_destroy != nil { return s->impl_destroy(); } - return close_err; + if close_err != .None { + return close_err; + } + return .Empty; } read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) { @@ -364,6 +370,11 @@ write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) { } write_rune :: proc(s: Writer, r: rune) -> (n: int, err: Error) { + if r < utf8.RUNE_SELF { + err = write_byte(s, byte(r)); + n = 1 if err == nil else 0; + return; + } buf, w := utf8.encode_rune(r); return write(s, buf[:w]); } diff --git a/core/io/util.odin b/core/io/util.odin index b618a6c23..2036aa570 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -48,7 +48,11 @@ _tee_reader_vtable := &Stream_VTable{ }, }; -// tee_reader +// tee_reader returns a Reader that writes to 'w' what it reads from 'r' +// All reads from 'r' performed through it are matched with a corresponding write to 'w' +// There is no internal buffering done +// The write must complete before th read completes +// Any error encountered whilst writing is reported as a 'read' error // tee_reader must call io.destroy when done with tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) { t := new(Tee_Reader, allocator); @@ -61,9 +65,8 @@ tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out } -// A Limited_Reader reads from r but limits the amount of -// data returned to just n bytes. Each call to read -// updates n to reflect the new amount remaining. +// A Limited_Reader reads from r but limits the amount of data returned to just n bytes. +// Each call to read updates n to reflect the new amount remaining. // read returns EOF when n <= 0 or when the underlying r returns EOF. Limited_Reader :: struct { r: Reader, // underlying reader @@ -72,7 +75,7 @@ Limited_Reader :: struct { @(private) _limited_reader_vtable := &Stream_VTable{ - impl_read = proc(using s: Stream, p: []byte) -> (n: int, err: Error) { + impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { l := (^Limited_Reader)(s.stream_data); if l.n <= 0 { return 0, .EOF; @@ -106,3 +109,84 @@ inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { l.n = n; return limited_reader_to_reader(l); } + +// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At +Section_Reader :: struct { + r: Reader_At, + base: i64, + off: i64, + limit: i64, +} + +init_section_reader :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) { + s.r = r; + s.off = off; + s.limit = off + n; + return; +} +section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) { + out.stream_data = s; + out.stream_vtable = _section_reader_vtable; + return; +} + +@(private) +_section_reader_vtable := &Stream_VTable{ + impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) { + s := (^Section_Reader)(stream.stream_data); + if s.off >= s.limit { + return 0, .EOF; + } + p := p; + if max := s.limit - s.off; i64(len(p)) > max { + p = p[0:max]; + } + n, err = read_at(s.r, p, s.off); + s.off += i64(n); + return; + }, + impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) { + s := (^Section_Reader)(stream.stream_data); + p, off := p, off; + + if off < 0 || off >= s.limit - s.base { + return 0, .EOF; + } + off += s.base; + if max := s.limit - off; i64(len(p)) > max { + p = p[0:max]; + n, err = read_at(s.r, p, off); + if err == nil { + err = .EOF; + } + return; + } + return read_at(s.r, p, off); + }, + impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { + s := (^Section_Reader)(stream.stream_data); + + offset := offset; + switch whence { + case: + return 0, .Invalid_Whence; + case .Start: + offset += s.base; + case .Current: + offset += s.off; + case .End: + offset += s.limit; + } + if offset < s.base { + return 0, .Invalid_Offset; + } + s.off = offset; + n = offset - s.base; + return; + }, + impl_size = proc(stream: Stream) -> i64 { + s := (^Section_Reader)(stream.stream_data); + return s.limit - s.base; + }, +}; + diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 3506b4bc8..6ca5be27d 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -74,6 +74,16 @@ _builder_stream_vtable := &io.Stream_VTable{ } return nil; }, + impl_size = proc(s: io.Stream) -> i64 { + b := (^Builder)(s.stream_data); + return i64(len(b.buf)); + }, + impl_destroy = proc(s: io.Stream) -> io.Error { + b := (^Builder)(s.stream_data); + flush_builder(b); + delete(b.buf); + return .None; + }, }; to_stream :: proc(b: ^Builder) -> io.Stream { @@ -173,42 +183,23 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { return; } -write_rune :: proc{ - write_rune_builder, - write_rune_writer, -}; -write_rune_builder :: proc(b: ^Builder, r: rune) -> int { - return write_rune_writer(to_writer(b), r); -} -write_rune_writer :: proc(w: io.Writer, r: rune) -> int { - if r < utf8.RUNE_SELF { - return _write_byte(w, byte(r)); - } - - s, n := utf8.encode_rune(r); - n, _ = io.write(w, s[:n]); - return n; +write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) { + return io.write_rune(to_writer(b), r); } - -write_quoted_rune :: proc{ - write_quoted_rune_builder, - write_quoted_rune_writer, -}; - write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) { - return write_quoted_rune_writer(to_writer(b), r); + return write_quoted_rune(to_writer(b), r); } @(private) -_write_byte :: proc(w: io.Writer, r: byte) -> int { - err := io.write_byte(w, r); +_write_byte :: proc(w: io.Writer, c: byte) -> int { + err := io.write_byte(w, c); return 1 if err == nil else 0; } -write_quoted_rune_writer :: proc(w: io.Writer, r: rune) -> (n: int) { +write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) { quote := byte('\''); n += _write_byte(w, quote); buf, width := utf8.encode_rune(r); @@ -328,7 +319,8 @@ write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> case 2: n += write_string(w, s); } } else { - n += write_rune(w, r); + rn, _ := io.write_rune(w, r); + n += rn; } } diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index 41add2778..c03bed86a 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -1,5 +1,6 @@ package strings +import "core:io" import "core:unicode" import "core:unicode/utf8" @@ -61,7 +62,7 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string { b: Builder; init_builder(&b, 0, len(s), allocator); for r in s { - write_rune(&b, unicode.to_lower(r)); + write_rune_builder(&b, unicode.to_lower(r)); } return to_string(b); } @@ -69,7 +70,7 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string { b: Builder; init_builder(&b, 0, len(s), allocator); for r in s { - write_rune(&b, unicode.to_upper(r)); + write_rune_builder(&b, unicode.to_upper(r)); } return to_string(b); } @@ -101,7 +102,7 @@ is_separator :: proc(r: rune) -> bool { } -string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, prev, curr, next: rune)) { +string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) { prev, curr: rune; for next in s { if curr == 0 { @@ -110,14 +111,14 @@ string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, continue; } - callback(b, prev, curr, next); + callback(w, prev, curr, next); prev = curr; curr = next; } if len(s) > 0 { - callback(b, prev, curr, 0); + callback(w, prev, curr, 0); } } @@ -128,15 +129,16 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s = trim_space(s); b: Builder; init_builder(&b, 0, len(s), allocator); + w := to_writer(&b); - string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { + string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { if !is_delimiter(curr) { if is_delimiter(prev) { - write_rune(b, unicode.to_upper(curr)); + io.write_rune(w, unicode.to_upper(curr)); } else if unicode.is_lower(prev) { - write_rune(b, curr); + io.write_rune(w, curr); } else { - write_rune(b, unicode.to_lower(curr)); + io.write_rune(w, unicode.to_lower(curr)); } } }); @@ -150,15 +152,16 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s = trim_space(s); b: Builder; init_builder(&b, 0, len(s), allocator); + w := to_writer(&b); - string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { + string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { if !is_delimiter(curr) { if is_delimiter(prev) || prev == 0 { - write_rune(b, unicode.to_upper(curr)); + io.write_rune(w, unicode.to_upper(curr)); } else if unicode.is_lower(prev) { - write_rune(b, curr); + io.write_rune(w, curr); } else { - write_rune(b, unicode.to_lower(curr)); + io.write_rune(w, unicode.to_lower(curr)); } } }); @@ -171,6 +174,7 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo s = trim_space(s); b: Builder; init_builder(&b, 0, len(s), allocator); + w := to_writer(&b); adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower; @@ -179,15 +183,15 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo for next in s { if is_delimiter(curr) { if !is_delimiter(prev) { - write_rune(&b, delimiter); + io.write_rune(w, delimiter); } } else if unicode.is_upper(curr) { if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) { - write_rune(&b, delimiter); + io.write_rune(w, delimiter); } - write_rune(&b, adjust_case(curr)); + io.write_rune(w, adjust_case(curr)); } else if curr != 0 { - write_rune(&b, adjust_case(curr)); + io.write_rune(w, adjust_case(curr)); } prev = curr; @@ -196,9 +200,9 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo if len(s) > 0 { if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 { - write_rune(&b, delimiter); + io.write_rune(w, delimiter); } - write_rune(&b, adjust_case(curr)); + io.write_rune(w, adjust_case(curr)); } return to_string(b); @@ -229,21 +233,22 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { s = trim_space(s); b: Builder; init_builder(&b, 0, len(s), allocator); + w := to_writer(&b); prev, curr: rune; for next in s { if is_delimiter(curr) { if !is_delimiter(prev) { - write_rune(&b, delimiter); + io.write_rune(w, delimiter); } } else if unicode.is_upper(curr) { if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) { - write_rune(&b, delimiter); + io.write_rune(w, delimiter); } - write_rune(&b, unicode.to_upper(curr)); + io.write_rune(w, unicode.to_upper(curr)); } else if curr != 0 { - write_rune(&b, unicode.to_lower(curr)); + io.write_rune(w, unicode.to_lower(curr)); } prev = curr; @@ -252,10 +257,10 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { if len(s) > 0 { if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 { - write_rune(&b, delimiter); - write_rune(&b, unicode.to_upper(curr)); + io.write_rune(w, delimiter); + io.write_rune(w, unicode.to_upper(curr)); } else { - write_rune(&b, unicode.to_lower(curr)); + io.write_rune(w, unicode.to_lower(curr)); } } diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 0306defc8..4b0230f59 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1,5 +1,6 @@ package strings +import "core:io" import "core:mem" import "core:unicode/utf8" @@ -814,6 +815,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> b: Builder; init_builder(&b, allocator); + writer := to_writer(&b); str := s; column: int; @@ -824,7 +826,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> expand := tab_size - column%tab_size; for i := 0; i < expand; i += 1 { - write_byte(&b, ' '); + io.write_byte(writer, ' '); } column += expand; @@ -835,7 +837,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> column += w; } - write_rune(&b, r); + io.write_rune(writer, r); } str = str[w:]; @@ -874,9 +876,11 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); - write_pad_string(&b, pad, pad_len, remains/2); - write_string(&b, str); - write_pad_string(&b, pad, pad_len, (remains+1)/2); + w := to_writer(&b); + + write_pad_string(w, pad, pad_len, remains/2); + io.write_string(w, str); + write_pad_string(w, pad, pad_len, (remains+1)/2); return to_string(b); } @@ -895,8 +899,10 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); - write_string(&b, str); - write_pad_string(&b, pad, pad_len, remains); + w := to_writer(&b); + + io.write_string(w, str); + write_pad_string(w, pad, pad_len, remains); return to_string(b); } @@ -915,8 +921,10 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); - write_pad_string(&b, pad, pad_len, remains); - write_string(&b, str); + w := to_writer(&b); + + write_pad_string(w, pad, pad_len, remains); + io.write_string(w, str); return to_string(b); } @@ -925,19 +933,19 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex @private -write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) { +write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { repeats := remains / pad_len; for i := 0; i < repeats; i += 1 { - write_string(b, pad); + io.write_string(w, pad); } n := remains % pad_len; p := pad; for i := 0; i < n; i += 1 { - r, w := utf8.decode_rune_in_string(p); - write_rune(b, r); - p = p[w:]; + r, width := utf8.decode_rune_in_string(p); + io.write_rune(w, r); + p = p[width:]; } } From 05a3bdad588d7d1194d4705b4f60506208e36b39 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 11:28:14 +0000 Subject: [PATCH 145/170] Allow nested procedures to access `@(static)` and `@(thread_local)` variables --- src/check_expr.cpp | 6 ++++-- src/checker.cpp | 11 ++++++++--- src/llvm_backend.cpp | 32 +++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8e09c0320..ea460ce09 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1075,8 +1075,10 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ if (e->parent_proc_decl != nullptr && e->parent_proc_decl != c->curr_proc_decl) { if (e->kind == Entity_Variable) { - error(n, "Nested procedures do not capture its parent's variables: %.*s", LIT(name)); - return nullptr; + if ((e->flags & EntityFlag_Static) == 0) { + error(n, "Nested procedures do not capture its parent's variables: %.*s", LIT(name)); + return nullptr; + } } else if (e->kind == Entity_Label) { error(n, "Nested procedures do not capture its parent's labels: %.*s", LIT(name)); return nullptr; diff --git a/src/checker.cpp b/src/checker.cpp index 901f5439c..c1a107c15 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -368,9 +368,14 @@ void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entit if (e->kind == Entity_Label) { continue; } - if (e->kind == Entity_Variable && - !(e->scope->flags&ScopeFlag_File)) { - continue; + if (e->kind == Entity_Variable) { + if (e->scope->flags&ScopeFlag_File) { + // Global variables are file to access + } else if (e->flags&EntityFlag_Static) { + // Allow static/thread_local variables to be referenced + } else { + continue; + } } } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b4edc4914..8063bdc8b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12875,6 +12875,7 @@ void lb_generate_code(lbGenerator *gen) { lbValue var; lbValue init; DeclInfo *decl; + bool is_initialized; }; auto global_variables = array_make(permanent_allocator(), 0, global_variable_max_count); @@ -12937,15 +12938,22 @@ void lb_generate_code(lbGenerator *gen) { var.var = g; var.decl = decl; - if (decl->init_expr != nullptr && !is_type_any(e->type)) { + if (decl->init_expr != nullptr) { TypeAndValue tav = type_and_value_of_expr(decl->init_expr); - if (tav.mode != Addressing_Invalid) { - if (tav.value.kind != ExactValue_Invalid) { - ExactValue v = tav.value; - lbValue init = lb_const_value(m, tav.type, v); - LLVMSetInitializer(g.value, init.value); + if (!is_type_any(e->type)) { + if (tav.mode != Addressing_Invalid) { + if (tav.value.kind != ExactValue_Invalid) { + ExactValue v = tav.value; + lbValue init = lb_const_value(m, tav.type, v); + LLVMSetInitializer(g.value, init.value); + var.is_initialized = true; + } } } + if (!var.is_initialized && + (is_type_untyped_nil(tav.type) || is_type_untyped_undef(tav.type))) { + var.is_initialized = true; + } } array_add(&global_variables, var); @@ -13146,10 +13154,13 @@ void lb_generate_code(lbGenerator *gen) { auto *var = &global_variables[i]; if (var->decl->init_expr != nullptr) { lbValue init = lb_build_expr(p, var->decl->init_expr); - if (!lb_is_const(init)) { - var->init = init; + if (lb_is_const(init)) { + if (!var->is_initialized) { + LLVMSetInitializer(var->var.value, init.value); + var->is_initialized = true; + } } else { - LLVMSetInitializer(var->var.value, init.value); + var->init = init; } } @@ -13166,6 +13177,7 @@ void lb_generate_code(lbGenerator *gen) { } if (var->init.value != nullptr) { + GB_ASSERT(!var->is_initialized); Type *t = type_deref(var->var.type); if (is_type_any(t)) { @@ -13182,6 +13194,8 @@ void lb_generate_code(lbGenerator *gen) { } else { lb_emit_store(p, var->var, lb_emit_conv(p, var->init, t)); } + + var->is_initialized = true; } } From 5d0db4c63a9c3878ab588b3b6f5b0d1c7a2d2dca Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 15:04:54 +0000 Subject: [PATCH 146/170] Clean up `fmt` usage with `io.Writer` and `strings.Builder` --- core/fmt/fmt.odin | 84 +++++------- core/reflect/types.odin | 263 +++++++++++++++++++------------------- core/strings/builder.odin | 30 ----- 3 files changed, 169 insertions(+), 208 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index ff627bdb2..3241548a3 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -63,55 +63,28 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist } -Flush_Data :: struct { - handle: os.Handle, - bytes_written: int, -} - -fmt_file_builder :: proc(data: []byte, fd: ^Flush_Data) -> strings.Builder { - b := strings.builder_from_slice(data); - b.flush_data = rawptr(fd); - b.flush_proc = proc(b: ^strings.Builder) -> (do_reset: bool) { - fd := (^Flush_Data)(b.flush_data); - res := strings.to_string(b^); - n, _ := os.write_string(fd.handle, res); - fd.bytes_written += max(n, 0); - return true; - }; - - return b; -} - fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { - data: [DEFAULT_BUFFER_SIZE]byte; - flush_data := Flush_Data{handle=fd}; - buf := fmt_file_builder(data[:], &flush_data); - _ = sbprint(buf=&buf, args=args, sep=sep); - return flush_data.bytes_written; + w, _ := io.to_writer(os.stream_from_handle(fd)); + return wprint(w=w, args=args, sep=sep); } fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { - data: [DEFAULT_BUFFER_SIZE]byte; - flush_data := Flush_Data{handle=fd}; - buf := fmt_file_builder(data[:], &flush_data); - _ = sbprintln(buf=&buf, args=args, sep=sep); - return flush_data.bytes_written; + w, _ := io.to_writer(os.stream_from_handle(fd)); + return wprintln(w=w, args=args, sep=sep); } fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { - data: [DEFAULT_BUFFER_SIZE]byte; - flush_data := Flush_Data{handle=fd}; - buf := fmt_file_builder(data[:], &flush_data); - _ = sbprintf(&buf, fmt, ..args); - return flush_data.bytes_written; + w, _ := io.to_writer(os.stream_from_handle(fd)); + return wprintf(w, fmt, ..args); } fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> int { - data: [DEFAULT_BUFFER_SIZE]byte; - flush_data := Flush_Data{handle=fd}; - buf := fmt_file_builder(data[:], &flush_data); - reflect.write_type(&buf, info); - strings.flush_builder(&buf); - return flush_data.bytes_written; + w, _ := io.to_writer(os.stream_from_handle(fd)); + return wprint_type(w, info); } +fprint_typeid :: proc(fd: os.Handle, id: typeid) -> int { + w, _ := io.to_writer(os.stream_from_handle(fd)); + return wprint_typeid(w, id); +} + // print* procedures return the number of bytes written print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep); } println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep); } @@ -212,19 +185,16 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprint(w=strings.to_writer(buf), args=args, sep=sep); - strings.flush_builder(buf); return strings.to_string(buf^); } sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprintln(w=strings.to_writer(buf), args=args, sep=sep); - strings.flush_builder(buf); return strings.to_string(buf^); } sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { wprintf(w=strings.to_writer(buf), fmt=fmt, args=args); - strings.flush_builder(buf); return strings.to_string(buf^); } @@ -246,6 +216,10 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { // NOTE(bill, 2020-06-19): I have found that the previous approach was not what people were expecting // and were expecting `*print` to be the same `*println` except for the added newline // so I am going to keep the same behaviour as `*println` for `*print` + + + size0 := io.size(auto_cast w); + for _, i in args { if i > 0 { io.write_string(fi.writer, sep); @@ -254,13 +228,15 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fmt_value(&fi, args[i], 'v'); } io.flush(auto_cast w); - return int(io.size(auto_cast w)); + return int(io.size(auto_cast w) - size0); } wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info; fi.writer = w; + size0 := io.size(auto_cast w); + for _, i in args { if i > 0 { io.write_string(fi.writer, sep); @@ -270,7 +246,7 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { } io.write_byte(fi.writer, '\n'); io.flush(auto_cast w); - return int(io.size(auto_cast w)); + return int(io.size(auto_cast w) - size0); } wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { @@ -279,6 +255,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { end := len(fmt); was_prev_index := false; + size0 := io.size(auto_cast w); + loop: for i := 0; i < end; /**/ { fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}; @@ -544,12 +522,20 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { io.write_string(fi.writer, ")"); } - io.flush(auto_cast fi.writer); - return int(io.size(auto_cast fi.writer)); + io.flush(auto_cast w); + return int(io.size(auto_cast w) - size0); } - - +wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> int { + n := reflect.write_type(w, info); + io.flush(auto_cast w); + return n; +} +wprint_typeid :: proc(w: io.Writer, id: typeid) -> int { + n := reflect.write_type(w, type_info_of(id)); + io.flush(auto_cast w); + return n; +} diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 274625193..f84f350d6 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -378,67 +378,70 @@ write_type :: proc{ write_type_writer, }; -write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) { - write_type_writer(strings.to_writer(buf), ti); +write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) -> int { + return write_type_writer(strings.to_writer(buf), ti); } -write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) { +write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) { using strings; if ti == nil { - write_string(w, "nil"); - return; + return write_string(w, "nil"); } + _n1 :: proc(err: io.Error) -> int { return 1 if err == nil else 0; }; + _n2 :: proc(n: int, _: io.Error) -> int { return n; }; + _n :: proc{_n1, _n2}; + switch info in ti.variant { case Type_Info_Named: - write_string(w, info.name); + return write_string(w, info.name); case Type_Info_Integer: switch ti.id { - case int: write_string(w, "int"); - case uint: write_string(w, "uint"); - case uintptr: write_string(w, "uintptr"); + case int: return write_string(w, "int"); + case uint: return write_string(w, "uint"); + case uintptr: return write_string(w, "uintptr"); case: - io.write_byte(w, 'i' if info.signed else 'u'); - io.write_i64(w, i64(8*ti.size), 10); + n += _n(io.write_byte(w, 'i' if info.signed else 'u')); + n += _n(io.write_i64(w, i64(8*ti.size), 10)); switch info.endianness { case .Platform: // Okay - case .Little: write_string(w, "le"); - case .Big: write_string(w, "be"); + case .Little: n += write_string(w, "le"); + case .Big: n += write_string(w, "be"); } } case Type_Info_Rune: - io.write_string(w, "rune"); + n += _n(io.write_string(w, "rune")); case Type_Info_Float: - io.write_byte(w, 'f'); - io.write_i64(w, i64(8*ti.size), 10); + n += _n(io.write_byte(w, 'f')); + n += _n(io.write_i64(w, i64(8*ti.size), 10)); switch info.endianness { case .Platform: // Okay - case .Little: write_string(w, "le"); - case .Big: write_string(w, "be"); + case .Little: n += write_string(w, "le"); + case .Big: n += write_string(w, "be"); } case Type_Info_Complex: - io.write_string(w, "complex"); - io.write_i64(w, i64(8*ti.size), 10); + n += _n(io.write_string(w, "complex")); + n += _n(io.write_i64(w, i64(8*ti.size), 10)); case Type_Info_Quaternion: - io.write_string(w, "quaternion"); - io.write_i64(w, i64(8*ti.size), 10); + n += _n(io.write_string(w, "quaternion")); + n += _n(io.write_i64(w, i64(8*ti.size), 10)); case Type_Info_String: if info.is_cstring { - write_string(w, "cstring"); + n += write_string(w, "cstring"); } else { - write_string(w, "string"); + n += write_string(w, "string"); } case Type_Info_Boolean: switch ti.id { - case bool: write_string(w, "bool"); + case bool: n += write_string(w, "bool"); case: - io.write_byte(w, 'b'); - io.write_i64(w, i64(8*ti.size), 10); + n += _n(io.write_byte(w, 'b')); + n += _n(io.write_i64(w, i64(8*ti.size), 10)); } case Type_Info_Any: - write_string(w, "any"); + n += write_string(w, "any"); case Type_Info_Type_Id: - write_string(w, "typeid"); + n += write_string(w, "typeid"); case Type_Info_Pointer: if info.elem == nil { @@ -448,186 +451,188 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) { write_type(w, info.elem); } case Type_Info_Procedure: - write_string(w, "proc"); + n += write_string(w, "proc"); if info.params == nil { - write_string(w, "()"); + n += write_string(w, "()"); } else { t := info.params.variant.(Type_Info_Tuple); - write_string(w, "("); + n += write_string(w, "("); for t, i in t.types { if i > 0 { - write_string(w, ", "); + n += write_string(w, ", "); } - write_type(w, t); + n += write_type(w, t); } - write_string(w, ")"); + n += write_string(w, ")"); } if info.results != nil { - write_string(w, " -> "); - write_type(w, info.results); + n += write_string(w, " -> "); + n += write_type(w, info.results); } case Type_Info_Tuple: count := len(info.names); - if count != 1 { write_string(w, "("); } + if count != 1 { n += write_string(w, "("); } for name, i in info.names { - if i > 0 { write_string(w, ", "); } + if i > 0 { n += write_string(w, ", "); } t := info.types[i]; if len(name) > 0 { - write_string(w, name); - write_string(w, ": "); + n += write_string(w, name); + n += write_string(w, ": "); } - write_type(w, t); + n += write_type(w, t); } - if count != 1 { write_string(w, ")"); } + if count != 1 { n += write_string(w, ")"); } case Type_Info_Array: - io.write_string(w, "["); - io.write_i64(w, i64(info.count), 10); - io.write_string(w, "]"); - write_type(w, info.elem); + n += _n(io.write_string(w, "[")); + n += _n(io.write_i64(w, i64(info.count), 10)); + n += _n(io.write_string(w, "]")); + n += write_type(w, info.elem); case Type_Info_Enumerated_Array: - write_string(w, "["); - write_type(w, info.index); - write_string(w, "]"); - write_type(w, info.elem); + n += write_string(w, "["); + n += write_type(w, info.index); + n += write_string(w, "]"); + n += write_type(w, info.elem); case Type_Info_Dynamic_Array: - io.write_string(w, "[dynamic]"); - write_type(w, info.elem); + n += _n(io.write_string(w, "[dynamic]")); + n += write_type(w, info.elem); case Type_Info_Slice: - io.write_string(w, "[]"); - write_type(w, info.elem); + n += _n(io.write_string(w, "[]")); + n += write_type(w, info.elem); case Type_Info_Map: - io.write_string(w, "map["); - write_type(w, info.key); - io.write_byte(w, ']'); - write_type(w, info.value); + n += _n(io.write_string(w, "map[")); + n += write_type(w, info.key); + n += _n(io.write_byte(w, ']')); + n += write_type(w, info.value); case Type_Info_Struct: switch info.soa_kind { case .None: // Ignore case .Fixed: - io.write_string(w, "#soa["); - io.write_i64(w, i64(info.soa_len)); - io.write_byte(w, ']'); - write_type(w, info.soa_base_type); + n += _n(io.write_string(w, "#soa[")); + n += _n(io.write_i64(w, i64(info.soa_len))); + n += _n(io.write_byte(w, ']')); + n += write_type(w, info.soa_base_type); return; case .Slice: - io.write_string(w, "#soa[]"); - write_type(w, info.soa_base_type); + n += _n(io.write_string(w, "#soa[]")); + n += write_type(w, info.soa_base_type); return; case .Dynamic: - io.write_string(w, "#soa[dynamic]"); - write_type(w, info.soa_base_type); + n += _n(io.write_string(w, "#soa[dynamic]")); + n += write_type(w, info.soa_base_type); return; } - write_string(w, "struct "); - if info.is_packed { write_string(w, "#packed "); } - if info.is_raw_union { write_string(w, "#raw_union "); } + n += write_string(w, "struct "); + if info.is_packed { n += write_string(w, "#packed "); } + if info.is_raw_union { n += write_string(w, "#raw_union "); } if info.custom_align { - io.write_string(w, "#align "); - io.write_i64(w, i64(ti.align), 10); - io.write_byte(w, ' '); + n += _n(io.write_string(w, "#align ")); + n += _n(io.write_i64(w, i64(ti.align), 10)); + n += _n(io.write_byte(w, ' ')); } - io.write_byte(w, '{'); + n += _n(io.write_byte(w, '{')); for name, i in info.names { - if i > 0 { write_string(w, ", "); } - io.write_string(w, name); - io.write_string(w, ": "); - write_type(w, info.types[i]); + if i > 0 { n += write_string(w, ", "); } + n += _n(io.write_string(w, name)); + n += _n(io.write_string(w, ": ")); + n += write_type(w, info.types[i]); } - io.write_byte(w, '}'); + n += _n(io.write_byte(w, '}')); case Type_Info_Union: - write_string(w, "union "); + n += write_string(w, "union "); if info.custom_align { - write_string(w, "#align "); - io.write_i64(w, i64(ti.align), 10); - io.write_byte(w, ' '); + n += write_string(w, "#align "); + n += _n(io.write_i64(w, i64(ti.align), 10)); + n += _n(io.write_byte(w, ' ')); } - io.write_byte(w, '{'); + n += _n(io.write_byte(w, '{')); for variant, i in info.variants { - if i > 0 { write_string(w, ", "); } - write_type(w, variant); + if i > 0 { n += write_string(w, ", "); } + n += write_type(w, variant); } - io.write_byte(w, '}'); + n += _n(io.write_byte(w, '}')); case Type_Info_Enum: - write_string(w, "enum "); - write_type(w, info.base); - write_string(w, " {"); + n += write_string(w, "enum "); + n += write_type(w, info.base); + n += write_string(w, " {"); for name, i in info.names { - if i > 0 { write_string(w, ", "); } - write_string(w, name); + if i > 0 { n += write_string(w, ", "); } + n += write_string(w, name); } - io.write_byte(w, '}'); + n += _n(io.write_byte(w, '}')); case Type_Info_Bit_Field: - write_string(w, "bit_field "); + n += write_string(w, "bit_field "); if ti.align != 1 { - write_string(w, "#align "); - io.write_i64(w, i64(ti.align), 10); - io.write_byte(w, ' '); + n += write_string(w, "#align "); + n += _n(io.write_i64(w, i64(ti.align), 10)); + n += _n(io.write_byte(w, ' ')); } - write_string(w, " {"); + n += write_string(w, " {"); for name, i in info.names { - if i > 0 { write_string(w, ", "); } - write_string(w, name); - write_string(w, ": "); - io.write_i64(w, i64(info.bits[i]), 10); + if i > 0 { n += write_string(w, ", "); } + n += write_string(w, name); + n += write_string(w, ": "); + n += _n(io.write_i64(w, i64(info.bits[i]), 10)); } - io.write_byte(w, '}'); + n += _n(io.write_byte(w, '}')); case Type_Info_Bit_Set: - write_string(w, "bit_set["); + n += write_string(w, "bit_set["); switch { case is_enum(info.elem): - write_type(w, info.elem); + n += write_type(w, info.elem); case is_rune(info.elem): - write_encoded_rune(w, rune(info.lower)); - write_string(w, ".."); - write_encoded_rune(w, rune(info.upper)); + n += write_encoded_rune(w, rune(info.lower)); + n += write_string(w, ".."); + n += write_encoded_rune(w, rune(info.upper)); case: - io.write_i64(w, info.lower, 10); - write_string(w, ".."); - io.write_i64(w, info.upper, 10); + n += _n(io.write_i64(w, info.lower, 10)); + n += write_string(w, ".."); + n += _n(io.write_i64(w, info.upper, 10)); } if info.underlying != nil { - write_string(w, "; "); - write_type(w, info.underlying); + n += write_string(w, "; "); + n += write_type(w, info.underlying); } - io.write_byte(w, ']'); + n += _n(io.write_byte(w, ']')); case Type_Info_Opaque: - write_string(w, "opaque "); - write_type(w, info.elem); + n += write_string(w, "opaque "); + n += write_type(w, info.elem); case Type_Info_Simd_Vector: if info.is_x86_mmx { - write_string(w, "intrinsics.x86_mmx"); + n += write_string(w, "intrinsics.x86_mmx"); } else { - write_string(w, "#simd["); - io.write_i64(w, i64(info.count)); - io.write_byte(w, ']'); - write_type(w, info.elem); + n += write_string(w, "#simd["); + n += _n(io.write_i64(w, i64(info.count))); + n += _n(io.write_byte(w, ']')); + n += write_type(w, info.elem); } case Type_Info_Relative_Pointer: - write_string(w, "#relative("); - write_type(w, info.base_integer); - write_string(w, ") "); - write_type(w, info.pointer); + n += write_string(w, "#relative("); + n += write_type(w, info.base_integer); + n += write_string(w, ") "); + n += write_type(w, info.pointer); case Type_Info_Relative_Slice: - write_string(w, "#relative("); - write_type(w, info.base_integer); - write_string(w, ") "); - write_type(w, info.slice); + n += write_string(w, "#relative("); + n += write_type(w, info.base_integer); + n += write_string(w, ") "); + n += write_type(w, info.slice); } + + return; } diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 6ca5be27d..83e07b772 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -9,10 +9,6 @@ Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool); Builder :: struct { buf: [dynamic]byte, - - // The custom flush procedure allows for the ability to flush the buffer, i.e. write to file - flush_proc: Builder_Flush_Proc, - flush_data: rawptr, } make_builder_none :: proc(allocator := context.allocator) -> Builder { @@ -53,11 +49,6 @@ init_builder :: proc{ @(private) _builder_stream_vtable := &io.Stream_VTable{ - impl_flush = proc(s: io.Stream) -> io.Error { - b := (^Builder)(s.stream_data); - flush_builder(b); - return nil; - }, impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { b := (^Builder)(s.stream_data); n = write_bytes(b, p); @@ -80,7 +71,6 @@ _builder_stream_vtable := &io.Stream_VTable{ }, impl_destroy = proc(s: io.Stream) -> io.Error { b := (^Builder)(s.stream_data); - flush_builder(b); delete(b.buf); return .None; }, @@ -110,24 +100,6 @@ reset_builder :: proc(b: ^Builder) { clear(&b.buf); } -flush_builder :: proc(b: ^Builder) -> (was_reset: bool) { - if b.flush_proc != nil { - was_reset = b.flush_proc(b); - if was_reset { - reset_builder(b); - - } - } - return; -} - -flush_builder_check_space :: proc(b: ^Builder, required: int) -> (was_reset: bool) { - if n := max(cap(b.buf) - len(b.buf), 0); n < required { - was_reset = flush_builder(b); - } - return; -} - builder_from_slice :: proc(backing: []byte) -> Builder { s := transmute(mem.Raw_Slice)backing; @@ -156,7 +128,6 @@ builder_space :: proc(b: Builder) -> int { } write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { - flush_builder_check_space(b, 1); if builder_space(b^) > 0 { append(&b.buf, x); n += 1; @@ -167,7 +138,6 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { x := x; for len(x) != 0 { - flush_builder_check_space(b, len(x)); space := builder_space(b^); if space == 0 { break; // No need to append From b6bbe29c8f8f9223272f69346e729df4910734f4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 16:04:58 +0000 Subject: [PATCH 147/170] Remove `const` as a (reserved) keyword --- src/parser.cpp | 5 ++--- src/tokenizer.cpp | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index ec38dca9b..a70627ed9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3259,11 +3259,10 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) { return FieldPrefix_no_alias; } else if (f->curr_token.string == "c_vararg") { return FieldPrefix_c_var_arg; + } else if (f->curr_token.string == "const") { + return FieldPrefix_const; } break; - - case Token_const: - return FieldPrefix_const; } return FieldPrefix_Unknown; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index d89ec43b5..afa6e793a 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -119,7 +119,6 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_context, "context"), \ TOKEN_KIND(Token_asm, "asm"), \ TOKEN_KIND(Token_macro, "macro"), \ - TOKEN_KIND(Token_const, "const"), \ TOKEN_KIND(Token__KeywordEnd, ""), \ TOKEN_KIND(Token_Count, "") From c4cb7170ee14c6ebcf0d63efe73510aecb4c69d9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 16:13:05 +0000 Subject: [PATCH 148/170] Deprecate keyword `opaque` in favour of `#opaque` --- src/parser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/parser.cpp b/src/parser.cpp index a70627ed9..8a7819abd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1833,12 +1833,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_opaque: { Token token = expect_token(f, Token_opaque); + warning(token, "opaque is deprecated, please use #opaque"); Ast *type = parse_type(f); return ast_opaque_type(f, token, type); } case Token_Hash: { Token token = expect_token(f, Token_Hash); + if (allow_token(f, Token_opaque)) { + Ast *type = parse_type(f); + return ast_opaque_type(f, token, type); + } + Token name = expect_token(f, Token_Ident); if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); From 2a232f239786a01b613117396e025a050d498cac Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 16:14:11 +0000 Subject: [PATCH 149/170] Remove the (reserved) keyword `macro` --- src/tokenizer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index afa6e793a..99e0fbc81 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -118,7 +118,6 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_no_inline, "no_inline"), \ TOKEN_KIND(Token_context, "context"), \ TOKEN_KIND(Token_asm, "asm"), \ - TOKEN_KIND(Token_macro, "macro"), \ TOKEN_KIND(Token__KeywordEnd, ""), \ TOKEN_KIND(Token_Count, "") From fd453be83134182eaa05ae1bdff54460fbc65b9d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 18:49:39 +0000 Subject: [PATCH 150/170] Deprecate `opaque` in favour of `#opaque` in the core library --- core/dynlib/lib.odin | 2 +- core/reflect/types.odin | 2 +- core/runtime/print.odin | 2 +- core/sys/unix/pthread_darwin.odin | 18 +++++++++--------- core/sys/unix/pthread_freebsd.odin | 18 +++++++++--------- core/sys/unix/pthread_linux.odin | 18 +++++++++--------- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin index 66742b835..141154f02 100644 --- a/core/dynlib/lib.odin +++ b/core/dynlib/lib.odin @@ -1,3 +1,3 @@ package dynlib -Library :: opaque rawptr; +Library :: #opaque rawptr; diff --git a/core/reflect/types.odin b/core/reflect/types.odin index f84f350d6..a775669aa 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -607,7 +607,7 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) { n += _n(io.write_byte(w, ']')); case Type_Info_Opaque: - n += write_string(w, "opaque "); + n += write_string(w, "#opaque "); n += write_type(w, info.elem); case Type_Info_Simd_Vector: diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 49b0404a0..88e8c9d9e 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -350,7 +350,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) { print_byte(']'); case Type_Info_Opaque: - print_string("opaque "); + print_string("#opaque "); print_type(info.elem); case Type_Info_Simd_Vector: diff --git a/core/sys/unix/pthread_darwin.odin b/core/sys/unix/pthread_darwin.odin index 7f7f59189..8460c4852 100644 --- a/core/sys/unix/pthread_darwin.odin +++ b/core/sys/unix/pthread_darwin.odin @@ -14,44 +14,44 @@ PTHREAD_ONCE_SIZE :: 8; PTHREAD_RWLOCK_SIZE :: 192; PTHREAD_RWLOCKATTR_SIZE :: 16; -pthread_t :: opaque u64; +pthread_t :: #opaque u64; -pthread_attr_t :: opaque struct #align 16 { +pthread_attr_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_ATTR_SIZE] c.char, }; -pthread_cond_t :: opaque struct #align 16 { +pthread_cond_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_COND_SIZE] c.char, }; -pthread_condattr_t :: opaque struct #align 16 { +pthread_condattr_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_CONDATTR_SIZE] c.char, }; -pthread_mutex_t :: opaque struct #align 16 { +pthread_mutex_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_MUTEX_SIZE] c.char, }; -pthread_mutexattr_t :: opaque struct #align 16 { +pthread_mutexattr_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_MUTEXATTR_SIZE] c.char, }; -pthread_once_t :: opaque struct #align 16 { +pthread_once_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_ONCE_SIZE] c.char, }; -pthread_rwlock_t :: opaque struct #align 16 { +pthread_rwlock_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_RWLOCK_SIZE] c.char, }; -pthread_rwlockattr_t :: opaque struct #align 16 { +pthread_rwlockattr_t :: #opaque struct #align 16 { sig: c.long, _: [PTHREAD_RWLOCKATTR_SIZE] c.char, }; diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin index 77b922686..cbd7e99f1 100644 --- a/core/sys/unix/pthread_freebsd.odin +++ b/core/sys/unix/pthread_freebsd.odin @@ -26,32 +26,32 @@ when size_of(int) == 8 { PTHREAD_BARRIER_T_SIZE :: 20; } -pthread_cond_t :: opaque struct #align 16 { +pthread_cond_t :: #opaque struct #align 16 { _: [PTHREAD_COND_T_SIZE] c.char, }; -pthread_mutex_t :: opaque struct #align 16 { +pthread_mutex_t :: #opaque struct #align 16 { _: [PTHREAD_MUTEX_T_SIZE] c.char, }; -pthread_rwlock_t :: opaque struct #align 16 { +pthread_rwlock_t :: #opaque struct #align 16 { _: [PTHREAD_RWLOCK_T_SIZE] c.char, }; -pthread_barrier_t :: opaque struct #align 16 { +pthread_barrier_t :: #opaque struct #align 16 { _: [PTHREAD_BARRIER_T_SIZE] c.char, }; -pthread_attr_t :: opaque struct #align 16 { +pthread_attr_t :: #opaque struct #align 16 { _: [PTHREAD_ATTR_T_SIZE] c.char, }; -pthread_condattr_t :: opaque struct #align 16 { +pthread_condattr_t :: #opaque struct #align 16 { _: [PTHREAD_CONDATTR_T_SIZE] c.char, }; -pthread_mutexattr_t :: opaque struct #align 16 { +pthread_mutexattr_t :: #opaque struct #align 16 { _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, }; -pthread_rwlockattr_t :: opaque struct #align 16 { +pthread_rwlockattr_t :: #opaque struct #align 16 { _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, }; -pthread_barrierattr_t :: opaque struct #align 16 { +pthread_barrierattr_t :: #opaque struct #align 16 { _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, }; diff --git a/core/sys/unix/pthread_linux.odin b/core/sys/unix/pthread_linux.odin index 5edd1ef5a..76a0719fb 100644 --- a/core/sys/unix/pthread_linux.odin +++ b/core/sys/unix/pthread_linux.odin @@ -33,32 +33,32 @@ when size_of(int) == 8 { PTHREAD_BARRIER_T_SIZE :: 20; } -pthread_cond_t :: opaque struct #align 16 { +pthread_cond_t :: #opaque struct #align 16 { _: [PTHREAD_COND_T_SIZE] c.char, }; -pthread_mutex_t :: opaque struct #align 16 { +pthread_mutex_t :: #opaque struct #align 16 { _: [PTHREAD_MUTEX_T_SIZE] c.char, }; -pthread_rwlock_t :: opaque struct #align 16 { +pthread_rwlock_t :: #opaque struct #align 16 { _: [PTHREAD_RWLOCK_T_SIZE] c.char, }; -pthread_barrier_t :: opaque struct #align 16 { +pthread_barrier_t :: #opaque struct #align 16 { _: [PTHREAD_BARRIER_T_SIZE] c.char, }; -pthread_attr_t :: opaque struct #align 16 { +pthread_attr_t :: #opaque struct #align 16 { _: [PTHREAD_ATTR_T_SIZE] c.char, }; -pthread_condattr_t :: opaque struct #align 16 { +pthread_condattr_t :: #opaque struct #align 16 { _: [PTHREAD_CONDATTR_T_SIZE] c.char, }; -pthread_mutexattr_t :: opaque struct #align 16 { +pthread_mutexattr_t :: #opaque struct #align 16 { _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, }; -pthread_rwlockattr_t :: opaque struct #align 16 { +pthread_rwlockattr_t :: #opaque struct #align 16 { _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, }; -pthread_barrierattr_t :: opaque struct #align 16 { +pthread_barrierattr_t :: #opaque struct #align 16 { _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, }; From 7268c80d64b1359fd4d5d12eb0d53e8d6e898bd0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 18:50:05 +0000 Subject: [PATCH 151/170] Add `strings.fields` and `strings.fields_proc` --- core/strings/strings.odin | 99 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 4b0230f59..07907bb45 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -467,10 +467,12 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> return; } +@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1}; + + is_ascii_space :: proc(r: rune) -> bool { - switch r { - case '\t', '\n', '\v', '\f', '\r', ' ': - return true; + if r < utf8.RUNE_SELF { + return _ascii_space[u8(r)] != 0; } return false; } @@ -949,3 +951,94 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { p = p[width:]; } } + + +// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space +// returning a slice of substrings of s or an empty slice if s only contains white space +fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check { + n := 0; + was_space := 1; + set_bits := u8(0); + + // check to see + for i in 0..= utf8.RUNE_SELF { + return fields_proc(s, unicode.is_space, allocator); + } + + if n == 0 { + return nil; + } + + a := make([]string, n, allocator); + na := 0; + field_start := 0; + i := 0; + for i < len(s) && _ascii_space[s[i]] != 0 { + i += 1; + } + field_start = i; + for i < len(s) { + if _ascii_space[s[i]] == 0 { + i += 1; + continue; + } + a[na] = s[field_start : i]; + na += 1; + i += 1; + for i < len(s) && _ascii_space[s[i]] != 0 { + i += 1; + } + field_start = i; + } + if field_start < len(s) { + a[na] = s[field_start:]; + } + return a; +} + + +// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch) +// returns a slice of substrings of s +// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned +// +// fields_proc makes no guarantee about the order in which it calls f(ch) +// it assumes that `f` always returns the same value for a given ch +fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { + Span :: struct { + start: int, + end: int, + }; + + spans := make([dynamic]string, 0, 32, allocator); + + start := -1; + + for end, r in s { + if f(r) { + if start >= 0 { + append(&spans, s[start : end]); + // -1 could be used, but just speed it up through bitwise not + // gotta love 2's complement + start = ~start; + } + } else { + if start < 0 { + start = end; + } + } + } + + if start >= 0 { + append(&spans, s[start : end]); + } + + return spans[:]; +} From ecf324e2138c7e950f283ed18b11954858c8c0a9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 18:50:30 +0000 Subject: [PATCH 152/170] Remove buffered type --- core/strings/strings.odin | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 07907bb45..7bb6c33fc 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1012,19 +1012,14 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds // fields_proc makes no guarantee about the order in which it calls f(ch) // it assumes that `f` always returns the same value for a given ch fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { - Span :: struct { - start: int, - end: int, - }; - - spans := make([dynamic]string, 0, 32, allocator); + substrings := make([dynamic]string, 0, 32, allocator); start := -1; for end, r in s { if f(r) { if start >= 0 { - append(&spans, s[start : end]); + append(&substrings, s[start : end]); // -1 could be used, but just speed it up through bitwise not // gotta love 2's complement start = ~start; @@ -1037,8 +1032,8 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc } if start >= 0 { - append(&spans, s[start : end]); + append(&substrings, s[start : end]); } - return spans[:]; + return substrings[:]; } From edbb3e3b32c2d42f04655f2ef9d03b365c8a0cfe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 18:53:17 +0000 Subject: [PATCH 153/170] Fix typos --- core/strings/strings.odin | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 7bb6c33fc..09c39050f 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -2,6 +2,7 @@ package strings import "core:io" import "core:mem" +import "core:unicode" import "core:unicode/utf8" clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string { @@ -1014,9 +1015,9 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { substrings := make([dynamic]string, 0, 32, allocator); - start := -1; - - for end, r in s { + start, end := -1, -1; + for r, offset in s { + end = offset; if f(r) { if start >= 0 { append(&substrings, s[start : end]); From 63f248095195fb455250f6d06ae5c03688eb040b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 19:16:40 +0000 Subject: [PATCH 154/170] Begin work on `package bytes` (`[]byte` equivalent of `package strings`) --- core/bytes/reader.odin | 143 ++++++ core/bytes/strings.odin | 1032 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1175 insertions(+) create mode 100644 core/bytes/reader.odin create mode 100644 core/bytes/strings.odin diff --git a/core/bytes/reader.odin b/core/bytes/reader.odin new file mode 100644 index 000000000..0fca1162a --- /dev/null +++ b/core/bytes/reader.odin @@ -0,0 +1,143 @@ +package bytes + +import "core:io" +import "core:unicode/utf8" + +Reader :: struct { + using stream: io.Stream, + s: []byte, + i: i64, // current reading index + prev_rune: int, // previous reading index of rune or < 0 +} + +reader_reset :: proc(r: ^Reader, s: []byte) { + r.stream_data = r; + r.stream_vtable = _reader_vtable; + r.s = s; + r.i = 0; + r.prev_rune = -1; +} + +new_reader :: proc(s: []byte, allocator := context.allocator) -> ^Reader { + r := new(Reader, allocator); + reader_reset(r, s); + return r; +} + +@(private) +_reader_vtable := &io.Stream_VTable{ + impl_size = proc(s: io.Stream) -> i64 { + r := (^Reader)(s.stream_data); + return i64(len(r.s)); + }, + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + r := (^Reader)(s.stream_data); + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + r.prev_rune = -1; + n = copy(p, r.s[r.i:]); + r.i += i64(n); + return; + }, + impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) { + r := (^Reader)(s.stream_data); + if off < 0 { + return 0, .Invalid_Offset; + } + if off >= i64(len(r.s)) { + return 0, .EOF; + } + n = copy(p, r.s[off:]); + if n < len(p) { + err = .EOF; + } + return; + }, + impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { + r := (^Reader)(s.stream_data); + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + b := r.s[r.i]; + r.i += 1; + return b, nil; + }, + impl_unread_byte = proc(s: io.Stream) -> io.Error { + r := (^Reader)(s.stream_data); + if r.i <= 0 { + return .Invalid_Unread; + } + r.prev_rune = -1; + r.i -= 1; + return nil; + }, + impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) { + r := (^Reader)(s.stream_data); + if r.i >= i64(len(r.s)) { + r.prev_rune = -1; + return 0, 0, .EOF; + } + r.prev_rune = int(r.i); + if c := r.s[r.i]; c < utf8.RUNE_SELF { + r.i += 1; + return rune(c), 1, nil; + } + ch, size = utf8.decode_rune(r.s[r.i:]); + r.i += i64(size); + return; + }, + impl_unread_rune = proc(s: io.Stream) -> io.Error { + r := (^Reader)(s.stream_data); + if r.i <= 0 { + return .Invalid_Unread; + } + if r.prev_rune < 0 { + return .Invalid_Unread; + } + r.i = i64(r.prev_rune); + r.prev_rune = -1; + return nil; + }, + impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { + r := (^Reader)(s.stream_data); + r.prev_rune = -1; + abs: i64; + switch whence { + case .Start: + abs = offset; + case .Current: + abs = r.i + offset; + case .End: + abs = i64(len(r.s)) + offset; + case: + return 0, .Invalid_Whence; + } + + if abs < 0 { + return 0, .Invalid_Offset; + } + r.i = abs; + return abs, nil; + }, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + r := (^Reader)(s.stream_data); + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, nil; + } + s := r.s[r.i:]; + m: int; + m, err = io.write(w, s); + if m > len(s) { + panic("bytes.Reader.write_to: invalid io.write_string count"); + } + r.i += i64(m); + n = i64(m); + if m != len(s) && err == nil { + err = .Short_Write; + } + return; + }, +}; diff --git a/core/bytes/strings.odin b/core/bytes/strings.odin new file mode 100644 index 000000000..61ae4631a --- /dev/null +++ b/core/bytes/strings.odin @@ -0,0 +1,1032 @@ +package bytes + +import "core:mem" +import "core:unicode" +import "core:unicode/utf8" + +clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte { + c := make([]byte, len(s)+1, allocator, loc); + copy(c, s); + c[len(s)] = 0; + return c[:len(s)]; +} + +ptr_from_slice :: proc(str: []byte) -> ^byte { + d := transmute(mem.Raw_String)str; + return d.data; +} + +// Compares two strings, returning a value representing which one comes first lexiographically. +// -1 for `a`; 1 for `b`, or 0 if they are equal. +compare :: proc(lhs, rhs: []byte) -> int { + return mem.compare(lhs, rhs); +} + +contains_rune :: proc(s: []byte, r: rune) -> int { + for c, offset in string(s) { + if c == r { + return offset; + } + } + return -1; +} + +contains :: proc(s, substr: []byte) -> bool { + return index(s, substr) >= 0; +} + +contains_any :: proc(s, chars: []byte) -> bool { + return index_any(s, chars) >= 0; +} + + +rune_count :: proc(s: []byte) -> int { + return utf8.rune_count(s); +} + + +equal :: proc(a, b: []byte) -> bool { + return string(a) == string(b); +} + +equal_fold :: proc(u, v: []byte) -> bool { + s, t := string(u), string(v); + loop: for s != "" && t != "" { + sr, tr: rune; + if s[0] < utf8.RUNE_SELF { + sr, s = rune(s[0]), s[1:]; + } else { + r, size := utf8.decode_rune_in_string(s); + sr, s = r, s[size:]; + } + if t[0] < utf8.RUNE_SELF { + tr, t = rune(t[0]), t[1:]; + } else { + r, size := utf8.decode_rune_in_string(t); + tr, t = r, t[size:]; + } + + if tr == sr { // easy case + continue loop; + } + + if tr < sr { + tr, sr = sr, tr; + } + + if tr < utf8.RUNE_SELF { + switch sr { + case 'A'..'Z': + if tr == (sr+'a')-'A' { + continue loop; + } + } + return false; + } + + // TODO(bill): Unicode folding + + return false; + } + + return s == t; +} + +has_prefix :: proc(s, prefix: []byte) -> bool { + return len(s) >= len(prefix) && string(s[0:len(prefix)]) == string(prefix); +} + +has_suffix :: proc(s, suffix: []byte) -> bool { + return len(s) >= len(suffix) && string(s[len(s)-len(suffix):]) == string(suffix); +} + + +join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte { + if len(a) == 0 { + return nil; + } + + n := len(sep) * (len(a) - 1); + for s in a { + n += len(s); + } + + b := make([]byte, n, allocator); + i := copy(b, a[0]); + for s in a[1:] { + i += copy(b[i:], sep); + i += copy(b[i:], s); + } + return b; +} + +concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte { + if len(a) == 0 { + return nil; + } + + n := 0; + for s in a { + n += len(s); + } + b := make([]byte, n, allocator); + i := 0; + for s in a { + i += copy(b[i:], s); + } + return b; +} + +@private +_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte { + s, n := s, n; + + if n == 0 { + return nil; + } + + if sep == nil { + l := utf8.rune_count(s); + if n < 0 || n > l { + n = l; + } + + res := make([dynamic][]byte, n, allocator); + for i := 0; i < n-1; i += 1 { + _, w := utf8.decode_rune(s); + res[i] = s[:w]; + s = s[w:]; + } + if n > 0 { + res[n-1] = s; + } + return res[:]; + } + + if n < 0 { + n = count(s, sep) + 1; + } + + res := make([dynamic][]byte, n, allocator); + + n -= 1; + + i := 0; + for ; i < n; i += 1 { + m := index(s, sep); + if m < 0 { + break; + } + res[i] = s[:m+sep_save]; + s = s[m+len(sep):]; + } + res[i] = s; + + return res[:i+1]; +} + +split :: inline proc(s, sep: []byte, allocator := context.allocator) -> [][]byte { + return _split(s, sep, 0, -1, allocator); +} + +split_n :: inline proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte { + return _split(s, sep, 0, n, allocator); +} + +split_after :: inline proc(s, sep: []byte, allocator := context.allocator) -> [][]byte { + return _split(s, sep, len(sep), -1, allocator); +} + +split_after_n :: inline proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte { + return _split(s, sep, len(sep), n, allocator); +} + + + + +index_byte :: proc(s: []byte, c: byte) -> int { + for i := 0; i < len(s); i += 1 { + if s[i] == c { + return i; + } + } + return -1; +} + +// Returns -1 if c is not present +last_index_byte :: proc(s: []byte, c: byte) -> int { + for i := len(s)-1; i >= 0; i -= 1 { + if s[i] == c { + return i; + } + } + return -1; +} + + + +@private PRIME_RABIN_KARP :: 16777619; + +index :: proc(s, substr: []byte) -> int { + hash_str_rabin_karp :: proc(s: []byte) -> (hash: u32 = 0, pow: u32 = 1) { + for i := 0; i < len(s); i += 1 { + hash = hash*PRIME_RABIN_KARP + u32(s[i]); + } + sq := u32(PRIME_RABIN_KARP); + for i := len(s); i > 0; i >>= 1 { + if (i & 1) != 0 { + pow *= sq; + } + sq *= sq; + } + return; + } + + n := len(substr); + switch { + case n == 0: + return 0; + case n == 1: + return index_byte(s, substr[0]); + case n == len(s): + if string(s) == string(substr) { + return 0; + } + return -1; + case n > len(s): + return -1; + } + + hash, pow := hash_str_rabin_karp(substr); + h: u32; + for i := 0; i < n; i += 1 { + h = h*PRIME_RABIN_KARP + u32(s[i]); + } + if h == hash && string(s[:n]) == string(substr) { + return 0; + } + for i := n; i < len(s); /**/ { + h *= PRIME_RABIN_KARP; + h += u32(s[i]); + h -= pow * u32(s[i-n]); + i += 1; + if h == hash && string(s[i-n:i]) == string(substr) { + return i - n; + } + } + return -1; +} + +last_index :: proc(s, substr: []byte) -> int { + hash_str_rabin_karp_reverse :: proc(s: []byte) -> (hash: u32 = 0, pow: u32 = 1) { + for i := len(s) - 1; i >= 0; i -= 1 { + hash = hash*PRIME_RABIN_KARP + u32(s[i]); + } + sq := u32(PRIME_RABIN_KARP); + for i := len(s); i > 0; i >>= 1 { + if (i & 1) != 0 { + pow *= sq; + } + sq *= sq; + } + return; + } + + n := len(substr); + switch { + case n == 0: + return len(s); + case n == 1: + return last_index_byte(s, substr[0]); + case n == len(s): + return 0 if string(substr) == string(s) else -1; + case n > len(s): + return -1; + } + + hash, pow := hash_str_rabin_karp_reverse(substr); + last := len(s) - n; + h: u32; + for i := len(s)-1; i >= last; i -= 1 { + h = h*PRIME_RABIN_KARP + u32(s[i]); + } + if h == hash && string(s[last:]) == string(substr) { + return last; + } + + for i := last-1; i >= 0; i -= 1 { + h *= PRIME_RABIN_KARP; + h += u32(s[i]); + h -= pow * u32(s[i+n]); + if h == hash && string(s[i:i+n]) == string(substr) { + return i; + } + } + return -1; +} + +index_any :: proc(s, chars: []byte) -> int { + if chars == nil { + return -1; + } + + // TODO(bill): Optimize + for r, i in s { + for c in chars { + if r == c { + return i; + } + } + } + return -1; +} + +last_index_any :: proc(s, chars: []byte) -> int { + if chars == nil { + return -1; + } + + for i := len(s); i > 0; { + r, w := utf8.decode_last_rune(s[:i]); + i -= w; + for c in string(chars) { + if r == c { + return i; + } + } + } + return -1; +} + +count :: proc(s, substr: []byte) -> int { + if len(substr) == 0 { // special case + return rune_count(s) + 1; + } + if len(substr) == 1 { + c := substr[0]; + switch len(s) { + case 0: + return 0; + case 1: + return int(s[0] == c); + } + n := 0; + for i := 0; i < len(s); i += 1 { + if s[i] == c { + n += 1; + } + } + return n; + } + + // TODO(bill): Use a non-brute for approach + n := 0; + str := s; + for { + i := index(str, substr); + if i == -1 { + return n; + } + n += 1; + str = str[i+len(substr):]; + } + return n; +} + + +repeat :: proc(s: []byte, count: int, allocator := context.allocator) -> []byte { + if count < 0 { + panic("bytes: negative repeat count"); + } else if count > 0 && (len(s)*count)/count != len(s) { + panic("bytes: repeat count will cause an overflow"); + } + + b := make([]byte, len(s)*count, allocator); + i := copy(b, s); + for i < len(b) { // 2^N trick to reduce the need to copy + copy(b[i:], b[:i]); + i *= 2; + } + return b; +} + +replace_all :: proc(s, old, new: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) { + return replace(s, old, new, -1, allocator); +} + +// if n < 0, no limit on the number of replacements +replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) { + if string(old) == string(new) || n == 0 { + was_allocation = false; + output = s; + return; + } + byte_count := n; + if m := count(s, old); m == 0 { + was_allocation = false; + output = s; + return; + } else if n < 0 || m < n { + byte_count = m; + } + + + t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator); + was_allocation = true; + + w := 0; + start := 0; + for i := 0; i < byte_count; i += 1 { + j := start; + if len(old) == 0 { + if i > 0 { + _, width := utf8.decode_rune(s[start:]); + j += width; + } + } else { + j += index(s[start:], old); + } + w += copy(t[w:], s[start:j]); + w += copy(t[w:], new); + start = j + len(old); + } + w += copy(t[w:], s[start:]); + output = t[0:w]; + return; +} + +@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1}; + + +is_ascii_space :: proc(r: rune) -> bool { + if r < utf8.RUNE_SELF { + return _ascii_space[u8(r)] != 0; + } + return false; +} + +is_space :: proc(r: rune) -> bool { + if r < 0x2000 { + switch r { + case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xa0, 0x1680: + return true; + } + } else { + if r <= 0x200a { + return true; + } + switch r { + case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000: + return true; + } + } + return false; +} + +is_null :: proc(r: rune) -> bool { + return r == 0x0000; +} + +index_proc :: proc(s: []byte, p: proc(rune) -> bool, truth := true) -> int { + for r, i in string(s) { + if p(r) == truth { + return i; + } + } + return -1; +} + +index_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { + for r, i in string(s) { + if p(state, r) == truth { + return i; + } + } + return -1; +} + +last_index_proc :: proc(s: []byte, p: proc(rune) -> bool, truth := true) -> int { + // TODO(bill): Probably use Rabin-Karp Search + for i := len(s); i > 0; { + r, size := utf8.decode_last_rune(s[:i]); + i -= size; + if p(r) == truth { + return i; + } + } + return -1; +} + +last_index_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { + // TODO(bill): Probably use Rabin-Karp Search + for i := len(s); i > 0; { + r, size := utf8.decode_last_rune(s[:i]); + i -= size; + if p(state, r) == truth { + return i; + } + } + return -1; +} + +trim_left_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte { + i := index_proc(s, p, false); + if i == -1 { + return nil; + } + return s[i:]; +} + + +index_rune :: proc(s: []byte, r: rune) -> int { + switch { + case 0 <= r && r < utf8.RUNE_SELF: + return index_byte(s, byte(r)); + + case r == utf8.RUNE_ERROR: + for c, i in string(s) { + if c == utf8.RUNE_ERROR { + return i; + } + } + return -1; + + case !utf8.valid_rune(r): + return -1; + } + + b, w := utf8.encode_rune(r); + return index(s, b[:w]); +} + + +trim_left_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr) -> []byte { + i := index_proc_with_state(s, p, state, false); + if i == -1 { + return nil; + } + return s[i:]; +} + +trim_right_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte { + i := last_index_proc(s, p, false); + if i >= 0 && s[i] >= utf8.RUNE_SELF { + _, w := utf8.decode_rune(s[i:]); + i += w; + } else { + i += 1; + } + return s[0:i]; +} + +trim_right_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr) -> []byte { + i := last_index_proc_with_state(s, p, state, false); + if i >= 0 && s[i] >= utf8.RUNE_SELF { + _, w := utf8.decode_rune(s[i:]); + i += w; + } else { + i += 1; + } + return s[0:i]; +} + + +is_in_cutset :: proc(state: rawptr, r: rune) -> bool { + if state == nil { + return false; + } + cutset := (^string)(state)^; + for c in cutset { + if r == c { + return true; + } + } + return false; +} + + +trim_left :: proc(s: []byte, cutset: []byte) -> []byte { + if s == nil || cutset == nil { + return s; + } + state := cutset; + return trim_left_proc_with_state(s, is_in_cutset, &state); +} + +trim_right :: proc(s: []byte, cutset: []byte) -> []byte { + if s == nil || cutset == nil { + return s; + } + state := cutset; + return trim_right_proc_with_state(s, is_in_cutset, &state); +} + +trim :: proc(s: []byte, cutset: []byte) -> []byte { + return trim_right(trim_left(s, cutset), cutset); +} + +trim_left_space :: proc(s: []byte) -> []byte { + return trim_left_proc(s, is_space); +} + +trim_right_space :: proc(s: []byte) -> []byte { + return trim_right_proc(s, is_space); +} + +trim_space :: proc(s: []byte) -> []byte { + return trim_right_space(trim_left_space(s)); +} + + +trim_left_null :: proc(s: []byte) -> []byte { + return trim_left_proc(s, is_null); +} + +trim_right_null :: proc(s: []byte) -> []byte { + return trim_right_proc(s, is_null); +} + +trim_null :: proc(s: []byte) -> []byte { + return trim_right_null(trim_left_null(s)); +} + +trim_prefix :: proc(s, prefix: []byte) -> []byte { + if has_prefix(s, prefix) { + return s[len(prefix):]; + } + return s; +} + +trim_suffix :: proc(s, suffix: []byte) -> []byte { + if has_suffix(s, suffix) { + return s[:len(s)-len(suffix)]; + } + return s; +} + +split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator := context.allocator) -> [][]byte #no_bounds_check { + if s == nil || len(substrs) <= 0 { + return nil; + } + + sublen := len(substrs[0]); + + for substr in substrs[1:] { + sublen = min(sublen, len(substr)); + } + + shared := len(s) - sublen; + + if shared <= 0 { + return nil; + } + + // number, index, last + n, i, l := 0, 0, 0; + + // count results + first_pass: for i <= shared { + for substr in substrs { + if string(s[i:i+sublen]) == string(substr) { + if !skip_empty || i - l > 0 { + n += 1; + } + + i += sublen; + l = i; + + continue first_pass; + } + } + + _, skip := utf8.decode_rune(s[i:]); + i += skip; + } + + if !skip_empty || len(s) - l > 0 { + n += 1; + } + + if n < 1 { + // no results + return nil; + } + + buf := make([][]byte, n, allocator); + + n, i, l = 0, 0, 0; + + // slice results + second_pass: for i <= shared { + for substr in substrs { + if string(s[i:i+sublen]) == string(substr) { + if !skip_empty || i - l > 0 { + buf[n] = s[l:i]; + n += 1; + } + + i += sublen; + l = i; + + continue second_pass; + } + } + + _, skip := utf8.decode_rune(s[i:]); + i += skip; + } + + if !skip_empty || len(s) - l > 0 { + buf[n] = s[l:]; + } + + return buf; +} + +/* +// scrub scruvs invalid utf-8 characters and replaces them with the replacement string +// Adjacent invalid bytes are only replaced once +scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte { + str := s; + b: Builder; + init_builder(&b, 0, len(s), allocator); + + has_error := false; + cursor := 0; + origin := str; + + for len(str) > 0 { + r, w := utf8.decode_rune(str); + + if r == utf8.RUNE_ERROR { + if !has_error { + has_error = true; + write(&b, origin[:cursor]); + } + } else if has_error { + has_error = false; + write(&b, replacement); + + origin = origin[cursor:]; + cursor = 0; + } + + cursor += w; + str = str[w:]; + } + + return to_string(b); +} +*/ + + +reverse :: proc(s: []byte, allocator := context.allocator) -> []byte { + str := s; + n := len(str); + buf := make([]byte, n); + i := n; + + for len(str) > 0 { + _, w := utf8.decode_rune(str); + i -= w; + copy(buf[i:], str[:w]); + str = str[w:]; + } + return buf; +} + +/* +expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { + if tab_size <= 0 { + panic("tab size must be positive"); + } + + + if s == nil { + return nil; + } + + b: Builder; + init_builder(&b, allocator); + writer := to_writer(&b); + str := s; + column: int; + + for len(str) > 0 { + r, w := utf8.decode_rune_in_string(str); + + if r == '\t' { + expand := tab_size - column%tab_size; + + for i := 0; i < expand; i += 1 { + io.write_byte(writer, ' '); + } + + column += expand; + } else { + if r == '\n' { + column = 0; + } else { + column += w; + } + + io.write_rune(writer, r); + } + + str = str[w:]; + } + + return to_string(b); +} +*/ + +partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) { + i := index(str, sep); + if i == -1 { + head = str; + return; + } + + head = str[:i]; + match = str[i:i+len(sep)]; + tail = str[i+len(sep):]; + return; +} + +/* +center_justify :: centre_justify; // NOTE(bill): Because Americans exist + +// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length +centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { + n := rune_count(str); + if n >= length || pad == nil { + return clone(str, allocator); + } + + remains := length-1; + pad_len := rune_count(pad); + + b: Builder; + init_builder(&b, allocator); + grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + + w := to_writer(&b); + + write_pad_string(w, pad, pad_len, remains/2); + io.write_string(w, str); + write_pad_string(w, pad, pad_len, (remains+1)/2); + + return to_string(b); +} + +// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length +left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { + n := rune_count(str); + if n >= length || pad == nil { + return clone(str, allocator); + } + + remains := length-1; + pad_len := rune_count(pad); + + b: Builder; + init_builder(&b, allocator); + grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + + w := to_writer(&b); + + io.write_string(w, str); + write_pad_string(w, pad, pad_len, remains); + + return to_string(b); +} + +// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length +right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { + n := rune_count(str); + if n >= length || pad == nil { + return clone(str, allocator); + } + + remains := length-1; + pad_len := rune_count(pad); + + b: Builder; + init_builder(&b, allocator); + grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + + w := to_writer(&b); + + write_pad_string(w, pad, pad_len, remains); + io.write_string(w, str); + + return to_string(b); +} + + + + +@private +write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { + repeats := remains / pad_len; + + for i := 0; i < repeats; i += 1 { + io.write_string(w, pad); + } + + n := remains % pad_len; + p := pad; + + for i := 0; i < n; i += 1 { + r, width := utf8.decode_rune_in_string(p); + io.write_rune(w, r); + p = p[width:]; + } +} +*/ + + +// fields splits the byte slice s around each instance of one or more consecutive white space character, defined by unicode.is_space +// returning a slice of subslices of s or an empty slice if s only contains white space +fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds_check { + n := 0; + was_space := 1; + set_bits := u8(0); + + // check to see + for i in 0..= utf8.RUNE_SELF { + return fields_proc(s, unicode.is_space, allocator); + } + + if n == 0 { + return nil; + } + + a := make([][]byte, n, allocator); + na := 0; + field_start := 0; + i := 0; + for i < len(s) && _ascii_space[s[i]] != 0 { + i += 1; + } + field_start = i; + for i < len(s) { + if _ascii_space[s[i]] == 0 { + i += 1; + continue; + } + a[na] = s[field_start : i]; + na += 1; + i += 1; + for i < len(s) && _ascii_space[s[i]] != 0 { + i += 1; + } + field_start = i; + } + if field_start < len(s) { + a[na] = s[field_start:]; + } + return a; +} + + +// fields_proc splits the byte slice s at each run of unicode code points `ch` satisfying f(ch) +// returns a slice of subslices of s +// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned +// +// fields_proc makes no guarantee about the order in which it calls f(ch) +// it assumes that `f` always returns the same value for a given ch +fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.allocator) -> [][]byte #no_bounds_check { + subslices := make([dynamic][]byte, 0, 32, allocator); + + start, end := -1, -1; + for r, offset in string(s) { + end = offset; + if f(r) { + if start >= 0 { + append(&subslices, s[start : end]); + // -1 could be used, but just speed it up through bitwise not + // gotta love 2's complement + start = ~start; + } + } else { + if start < 0 { + start = end; + } + } + } + + if start >= 0 { + append(&subslices, s[start : end]); + } + + return subslices[:]; +} From 9b22583397037de55cae23d93a12025eddcb4f4a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 20:18:41 +0000 Subject: [PATCH 155/170] Add `bytes.Buffer` --- core/bytes/buffer.odin | 331 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 core/bytes/buffer.odin diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin new file mode 100644 index 000000000..427fc7253 --- /dev/null +++ b/core/bytes/buffer.odin @@ -0,0 +1,331 @@ +package bytes + +import "core:io" +import "core:unicode/utf8" + +MIN_READ :: 512; + +@(private) +SMALL_BUFFER_SIZE :: 64; + +Buffer :: struct { + buf: [dynamic]byte, + off: int, + last_read: Read_Op, +} + +@(private) +Read_Op :: enum i8 { + Read = -1, + Invalid = 0, + Read_Rune1 = 1, + Read_Rune2 = 2, + Read_Rune3 = 3, + Read_Rune4 = 4, +} + + +buffer_init :: proc(b: ^Buffer, buf: []byte) { + resize(&b.buf, len(buf)); + copy(b.buf[:], buf); +} + +buffer_init_string :: proc(b: ^Buffer, s: string) { + resize(&b.buf, len(s)); + copy(b.buf[:], s); +} + + +buffer_destroy :: proc(b: ^Buffer) { + delete(b.buf); + buffer_reset(b); +} + +buffer_to_bytes :: proc(b: ^Buffer) -> []byte { + return b.buf[b.off:]; +} + +buffer_to_string :: proc(b: ^Buffer) -> string { + if b == nil { + return ""; + } + return string(b.buf[b.off:]); +} + +buffer_is_empty :: proc(b: ^Buffer) -> bool { + return len(b.buf) <= b.off; +} + +buffer_length :: proc(b: ^Buffer) -> int { + return len(b.buf) - b.off; +} + +buffer_capacity :: proc(b: ^Buffer) -> int { + return cap(b.buf); +} + +buffer_reset :: proc(b: ^Buffer) { + clear(&b.buf); + b.off = 0; + b.last_read = .Invalid; +} + + +buffer_truncate :: proc(b: ^Buffer, n: int) { + if n == 0 { + buffer_reset(b); + return; + } + b.last_read = .Invalid; + if n < 0 || n > buffer_length(b) { + panic("bytes.truncate: truncation out of range"); + } + resize(&b.buf, b.off+n); +} + +@(private) +_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) { + if l := len(b.buf); n <= cap(b.buf)-l { + resize(&b.buf, l+n); + return l, true; + } + return 0, false; +} + +@(private) +_buffer_grow :: proc(b: ^Buffer, n: int) -> int { + m := buffer_length(b); + if m == 0 && b.off != 0 { + buffer_reset(b); + } + if i, ok := _buffer_try_grow(b, n); ok { + return i; + } + if b.buf == nil && n <= SMALL_BUFFER_SIZE { + b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE); + return 0; + } + + c := cap(b.buf); + if n <= c/2 - m { + copy(b.buf[:], b.buf[b.off:]); + } else if c > max(int) - c - n { + panic("bytes.Buffer: too large"); + } else { + resize(&b.buf, 2*c + n); + copy(b.buf[:], b.buf[b.off:]); + } + b.off = 0; + resize(&b.buf, m+n); + return m; +} + +buffer_grow :: proc(b: ^Buffer, n: int) { + if n < 0 { + panic("bytes.buffer_grow: negative count"); + } + m := _buffer_grow(b, n); + resize(&b.buf, m); +} + + +buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) { + b.last_read = .Invalid; + m, ok := _buffer_try_grow(b, len(p)); + if !ok { + m = _buffer_grow(b, len(p)); + } + return copy(b.buf[m:], p), nil; +} + +buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) { + b.last_read = .Invalid; + m, ok := _buffer_try_grow(b, len(s)); + if !ok { + m = _buffer_grow(b, len(s)); + } + return copy(b.buf[m:], s), nil; +} + +buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error { + b.last_read = .Invalid; + m, ok := _buffer_try_grow(b, 1); + if !ok { + m = _buffer_grow(b, 1); + } + b.buf[m] = c; + return nil; +} + +buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) { + if r < utf8.RUNE_SELF { + buffer_write_byte(b, byte(r)); + return 1, nil; + } + b.last_read = .Invalid; + m, ok := _buffer_try_grow(b, utf8.UTF_MAX); + if !ok { + m = _buffer_grow(b, utf8.UTF_MAX); + } + res: [4]byte; + res, n = utf8.encode_rune(r); + copy(b.buf[m:][:utf8.UTF_MAX], res[:n]); + resize(&b.buf, m+n); + return; +} + +buffer_next :: proc(b: ^Buffer, n: int) -> []byte { + n := n; + b.last_read = .Invalid; + m := buffer_length(b); + if n > m { + n = m; + } + data := b.buf[b.off : b.off + n]; + b.off += n; + if n > 0 { + b.last_read = .Read; + } + return data; +} + +buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) { + b.last_read = .Invalid; + if buffer_is_empty(b) { + buffer_reset(b); + if len(p) == 0 { + return 0, nil; + } + return 0, .EOF; + } + n = copy(p, b.buf[b.off:]); + b.off += n; + if n > 0 { + b.last_read = .Read; + } + return; +} + +buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) { + if buffer_is_empty(b) { + buffer_reset(b); + return 0, .EOF; + } + c := b.buf[b.off]; + b.off += 1; + b.last_read = .Read; + return c, nil; +} + +buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) { + if buffer_is_empty(b) { + buffer_reset(b); + return 0, 0, .EOF; + } + c := b.buf[b.off]; + if c < utf8.RUNE_SELF { + b.off += 1; + b.last_read = .Read_Rune1; + return rune(c), 1, nil; + } + r, size = utf8.decode_rune(b.buf[b.off:]); + b.off += size; + b.last_read = Read_Op(i8(size)); + return; +} + +buffer_unread_byte :: proc(b: ^Buffer) -> io.Error { + if b.last_read == .Invalid { + return .Invalid_Unread; + } + b.last_read = .Invalid; + if b.off > 0 { + b.off -= 1; + } + return nil; +} + +buffer_unread_rune :: proc(b: ^Buffer) -> io.Error { + if b.last_read <= .Invalid { + return .Invalid_Unread; + } + if b.off >= int(b.last_read) { + b.off -= int(i8(b.last_read)); + } + b.last_read = .Invalid; + return nil; +} + + +buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) { + i := index_byte(b.buf[b.off:], delim); + end := b.off + i + 1; + if i < 0 { + end = len(b.buf); + err = .EOF; + } + line = b.buf[b.off:end]; + b.off = end; + b.last_read = .Read; + return; +} + +buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) { + slice: []byte; + slice, err = buffer_read_bytes(b, delim); + return string(slice), err; +} + + + +buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) { + s.stream_data = b; + s.stream_vtable = _buffer_vtable; + return; +} + +@(private) +_buffer_vtable := &io.Stream_VTable{ + impl_size = proc(s: io.Stream) -> i64 { + b := (^Buffer)(s.stream_data); + return i64(buffer_capacity(b)); + }, + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_read(b, p); + }, + impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_read_byte(b); + }, + impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_read_rune(b); + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_write(b, p); + }, + impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { + b := (^Buffer)(s.stream_data); + return buffer_write_byte(b, c); + }, + impl_unread_byte = proc(s: io.Stream) -> io.Error { + b := (^Buffer)(s.stream_data); + return buffer_unread_byte(b); + }, + impl_unread_rune = proc(s: io.Stream) -> io.Error { + b := (^Buffer)(s.stream_data); + return buffer_unread_rune(b); + }, + impl_destroy = proc(s: io.Stream) -> io.Error { + b := (^Buffer)(s.stream_data); + buffer_destroy(b); + return nil; + }, + + // TODO(bill): write_to and read_from + // impl_write_to = nil, + // impl_read_from = nil, +}; + From dd63665b58e3d1e747f09d4a9da5ddc0f750f2b3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 20:33:14 +0000 Subject: [PATCH 156/170] Update bytes.Reader --- core/bytes/reader.odin | 224 ++++++++++++++++++++++++----------------- 1 file changed, 129 insertions(+), 95 deletions(-) diff --git a/core/bytes/reader.odin b/core/bytes/reader.odin index 0fca1162a..deaaf4ac6 100644 --- a/core/bytes/reader.odin +++ b/core/bytes/reader.odin @@ -4,140 +4,174 @@ import "core:io" import "core:unicode/utf8" Reader :: struct { - using stream: io.Stream, s: []byte, i: i64, // current reading index prev_rune: int, // previous reading index of rune or < 0 } -reader_reset :: proc(r: ^Reader, s: []byte) { - r.stream_data = r; - r.stream_vtable = _reader_vtable; +reader_init :: proc(r: ^Reader, s: []byte) { r.s = s; r.i = 0; r.prev_rune = -1; } -new_reader :: proc(s: []byte, allocator := context.allocator) -> ^Reader { - r := new(Reader, allocator); - reader_reset(r, s); - return r; +reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { + s.stream_data = r; + s.stream_vtable = _reader_vtable; + return; } +reader_length :: proc(r: ^Reader) -> int { + if r.i >= i64(len(r.s)) { + return 0; + } + return int(i64(len(r.s)) - r.i); +} + +reader_size :: proc(r: ^Reader) -> i64 { + return i64(len(r.s)); +} + +reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + r.prev_rune = -1; + n = copy(p, r.s[r.i:]); + r.i += i64(n); + return; +} +reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) { + if off < 0 { + return 0, .Invalid_Offset; + } + if off >= i64(len(r.s)) { + return 0, .EOF; + } + n = copy(p, r.s[off:]); + if n < len(p) { + err = .EOF; + } + return; +} +reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + b := r.s[r.i]; + r.i += 1; + return b, nil; +} +reader_unread_byte :: proc(r: ^Reader) -> io.Error { + if r.i <= 0 { + return .Invalid_Unread; + } + r.prev_rune = -1; + r.i -= 1; + return nil; +} +reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { + if r.i >= i64(len(r.s)) { + r.prev_rune = -1; + return 0, 0, .EOF; + } + r.prev_rune = int(r.i); + if c := r.s[r.i]; c < utf8.RUNE_SELF { + r.i += 1; + return rune(c), 1, nil; + } + ch, size = utf8.decode_rune(r.s[r.i:]); + r.i += i64(size); + return; +} +reader_unread_rune :: proc(r: ^Reader) -> io.Error { + if r.i <= 0 { + return .Invalid_Unread; + } + if r.prev_rune < 0 { + return .Invalid_Unread; + } + r.i = i64(r.prev_rune); + r.prev_rune = -1; + return nil; +} +reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { + r.prev_rune = -1; + abs: i64; + switch whence { + case .Start: + abs = offset; + case .Current: + abs = r.i + offset; + case .End: + abs = i64(len(r.s)) + offset; + case: + return 0, .Invalid_Whence; + } + + if abs < 0 { + return 0, .Invalid_Offset; + } + r.i = abs; + return abs, nil; +} +reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, nil; + } + s := r.s[r.i:]; + m: int; + m, err = io.write(w, s); + if m > len(s) { + panic("bytes.Reader.write_to: invalid io.write_string count"); + } + r.i += i64(m); + n = i64(m); + if m != len(s) && err == nil { + err = .Short_Write; + } + return; +} + + @(private) _reader_vtable := &io.Stream_VTable{ impl_size = proc(s: io.Stream) -> i64 { r := (^Reader)(s.stream_data); - return i64(len(r.s)); + return reader_size(r); }, impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { r := (^Reader)(s.stream_data); - if r.i >= i64(len(r.s)) { - return 0, .EOF; - } - r.prev_rune = -1; - n = copy(p, r.s[r.i:]); - r.i += i64(n); - return; + return reader_read(r, p); }, impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) { r := (^Reader)(s.stream_data); - if off < 0 { - return 0, .Invalid_Offset; - } - if off >= i64(len(r.s)) { - return 0, .EOF; - } - n = copy(p, r.s[off:]); - if n < len(p) { - err = .EOF; - } - return; + return reader_read_at(r, p, off); }, impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { r := (^Reader)(s.stream_data); - r.prev_rune = -1; - if r.i >= i64(len(r.s)) { - return 0, .EOF; - } - b := r.s[r.i]; - r.i += 1; - return b, nil; + return reader_read_byte(r); }, impl_unread_byte = proc(s: io.Stream) -> io.Error { r := (^Reader)(s.stream_data); - if r.i <= 0 { - return .Invalid_Unread; - } - r.prev_rune = -1; - r.i -= 1; - return nil; + return reader_unread_byte(r); }, impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) { r := (^Reader)(s.stream_data); - if r.i >= i64(len(r.s)) { - r.prev_rune = -1; - return 0, 0, .EOF; - } - r.prev_rune = int(r.i); - if c := r.s[r.i]; c < utf8.RUNE_SELF { - r.i += 1; - return rune(c), 1, nil; - } - ch, size = utf8.decode_rune(r.s[r.i:]); - r.i += i64(size); - return; + return reader_read_rune(r); }, impl_unread_rune = proc(s: io.Stream) -> io.Error { r := (^Reader)(s.stream_data); - if r.i <= 0 { - return .Invalid_Unread; - } - if r.prev_rune < 0 { - return .Invalid_Unread; - } - r.i = i64(r.prev_rune); - r.prev_rune = -1; - return nil; + return reader_unread_rune(r); }, impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { r := (^Reader)(s.stream_data); - r.prev_rune = -1; - abs: i64; - switch whence { - case .Start: - abs = offset; - case .Current: - abs = r.i + offset; - case .End: - abs = i64(len(r.s)) + offset; - case: - return 0, .Invalid_Whence; - } - - if abs < 0 { - return 0, .Invalid_Offset; - } - r.i = abs; - return abs, nil; + return reader_seek(r, offset, whence); }, impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { r := (^Reader)(s.stream_data); - r.prev_rune = -1; - if r.i >= i64(len(r.s)) { - return 0, nil; - } - s := r.s[r.i:]; - m: int; - m, err = io.write(w, s); - if m > len(s) { - panic("bytes.Reader.write_to: invalid io.write_string count"); - } - r.i += i64(m); - n = i64(m); - if m != len(s) && err == nil { - err = .Short_Write; - } - return; + return reader_write_to(r, w); }, }; From 0ed1300bd6a436b3cd49ed1109987a467dce9685 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Dec 2020 21:25:13 +0000 Subject: [PATCH 157/170] Make bytes.Reader and strings.Reader have the same interface --- core/bytes/reader.odin | 6 +- core/strings/reader.odin | 230 ++++++++++++++++++++++----------------- 2 files changed, 135 insertions(+), 101 deletions(-) diff --git a/core/bytes/reader.odin b/core/bytes/reader.odin index deaaf4ac6..2032616d0 100644 --- a/core/bytes/reader.odin +++ b/core/bytes/reader.odin @@ -4,9 +4,9 @@ import "core:io" import "core:unicode/utf8" Reader :: struct { - s: []byte, - i: i64, // current reading index - prev_rune: int, // previous reading index of rune or < 0 + s: []byte, // read-only buffer + i: i64, // current reading index + prev_rune: int, // previous reading index of rune or < 0 } reader_init :: proc(r: ^Reader, s: []byte) { diff --git a/core/strings/reader.odin b/core/strings/reader.odin index 46256641f..468c2f836 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -4,140 +4,174 @@ import "core:io" import "core:unicode/utf8" Reader :: struct { - using stream: io.Stream, - s: string, - i: i64, // current reading index - prev_rune: int, // previous reading index of rune or < 0 + s: string, // read-only buffer + i: i64, // current reading index + prev_rune: int, // previous reading index of rune or < 0 } -reader_reset :: proc(r: ^Reader, s: string) { - r.stream_data = r; - r.stream_vtable = _reader_vtable; +reader_init :: proc(r: ^Reader, s: string) { r.s = s; r.i = 0; r.prev_rune = -1; } -new_reader :: proc(s: string, allocator := context.allocator) -> ^Reader { - r := new(Reader, allocator); - reader_reset(r, s); - return r; +reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { + s.stream_data = r; + s.stream_vtable = _reader_vtable; + return; } +reader_length :: proc(r: ^Reader) -> int { + if r.i >= i64(len(r.s)) { + return 0; + } + return int(i64(len(r.s)) - r.i); +} + +reader_size :: proc(r: ^Reader) -> i64 { + return i64(len(r.s)); +} + +reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + r.prev_rune = -1; + n = copy(p, r.s[r.i:]); + r.i += i64(n); + return; +} +reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) { + if off < 0 { + return 0, .Invalid_Offset; + } + if off >= i64(len(r.s)) { + return 0, .EOF; + } + n = copy(p, r.s[off:]); + if n < len(p) { + err = .EOF; + } + return; +} +reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, .EOF; + } + b := r.s[r.i]; + r.i += 1; + return b, nil; +} +reader_unread_byte :: proc(r: ^Reader) -> io.Error { + if r.i <= 0 { + return .Invalid_Unread; + } + r.prev_rune = -1; + r.i -= 1; + return nil; +} +reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { + if r.i >= i64(len(r.s)) { + r.prev_rune = -1; + return 0, 0, .EOF; + } + r.prev_rune = int(r.i); + if c := r.s[r.i]; c < utf8.RUNE_SELF { + r.i += 1; + return rune(c), 1, nil; + } + ch, size = utf8.decode_rune_in_string(r.s[r.i:]); + r.i += i64(size); + return; +} +reader_unread_rune :: proc(r: ^Reader) -> io.Error { + if r.i <= 0 { + return .Invalid_Unread; + } + if r.prev_rune < 0 { + return .Invalid_Unread; + } + r.i = i64(r.prev_rune); + r.prev_rune = -1; + return nil; +} +reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { + r.prev_rune = -1; + abs: i64; + switch whence { + case .Start: + abs = offset; + case .Current: + abs = r.i + offset; + case .End: + abs = i64(len(r.s)) + offset; + case: + return 0, .Invalid_Whence; + } + + if abs < 0 { + return 0, .Invalid_Offset; + } + r.i = abs; + return abs, nil; +} +reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { + r.prev_rune = -1; + if r.i >= i64(len(r.s)) { + return 0, nil; + } + s := r.s[r.i:]; + m: int; + m, err = io.write_string(w, s); + if m > len(s) { + panic("bytes.Reader.write_to: invalid io.write_string count"); + } + r.i += i64(m); + n = i64(m); + if m != len(s) && err == nil { + err = .Short_Write; + } + return; +} + + @(private) _reader_vtable := &io.Stream_VTable{ impl_size = proc(s: io.Stream) -> i64 { r := (^Reader)(s.stream_data); - return i64(len(r.s)); + return reader_size(r); }, impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { r := (^Reader)(s.stream_data); - if r.i >= i64(len(r.s)) { - return 0, .EOF; - } - r.prev_rune = -1; - n = copy(p, r.s[r.i:]); - r.i += i64(n); - return; + return reader_read(r, p); }, impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) { r := (^Reader)(s.stream_data); - if off < 0 { - return 0, .Invalid_Offset; - } - if off >= i64(len(r.s)) { - return 0, .EOF; - } - n = copy(p, r.s[off:]); - if n < len(p) { - err = .EOF; - } - return; + return reader_read_at(r, p, off); }, impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { r := (^Reader)(s.stream_data); - r.prev_rune = -1; - if r.i >= i64(len(r.s)) { - return 0, .EOF; - } - b := r.s[r.i]; - r.i += 1; - return b, nil; + return reader_read_byte(r); }, impl_unread_byte = proc(s: io.Stream) -> io.Error { r := (^Reader)(s.stream_data); - if r.i <= 0 { - return .Invalid_Unread; - } - r.prev_rune = -1; - r.i -= 1; - return nil; + return reader_unread_byte(r); }, impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) { r := (^Reader)(s.stream_data); - if r.i >= i64(len(r.s)) { - r.prev_rune = -1; - return 0, 0, .EOF; - } - r.prev_rune = int(r.i); - if c := r.s[r.i]; c < utf8.RUNE_SELF { - r.i += 1; - return rune(c), 1, nil; - } - ch, size = utf8.decode_rune_in_string(r.s[r.i:]); - r.i += i64(size); - return; + return reader_read_rune(r); }, impl_unread_rune = proc(s: io.Stream) -> io.Error { r := (^Reader)(s.stream_data); - if r.i <= 0 { - return .Invalid_Unread; - } - if r.prev_rune < 0 { - return .Invalid_Unread; - } - r.i = i64(r.prev_rune); - r.prev_rune = -1; - return nil; + return reader_unread_rune(r); }, impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { r := (^Reader)(s.stream_data); - r.prev_rune = -1; - abs: i64; - switch whence { - case .Start: - abs = offset; - case .Current: - abs = r.i + offset; - case .End: - abs = i64(len(r.s)) + offset; - case: - return 0, .Invalid_Whence; - } - - if abs < 0 { - return 0, .Invalid_Offset; - } - r.i = abs; - return abs, nil; + return reader_seek(r, offset, whence); }, impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { r := (^Reader)(s.stream_data); - r.prev_rune = -1; - if r.i >= i64(len(r.s)) { - return 0, nil; - } - s := r.s[r.i:]; - m: int; - m, err = io.write_string(w, s); - if m > len(s) { - panic("strings.Reader.write_to: invalid io.write_string count"); - } - r.i += i64(m); - n = i64(m); - if m != len(s) && err == nil { - err = .Short_Write; - } - return; + return reader_write_to(r, w); }, }; From 4c41045133610ef6425e79239f8522c91e351e39 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 12:03:07 +0000 Subject: [PATCH 158/170] Fix typo in package io --- core/io/io.odin | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/io/io.odin b/core/io/io.odin index bfa81d417..b4d2c16ec 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -37,6 +37,8 @@ Error :: enum i32 { Negative_Read, Negative_Write, + Negative_Count, + Buffer_Full, // Empty is returned when a procedure has not been implemented for an io.Stream Empty = -1, @@ -244,7 +246,7 @@ write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) { return w->impl_write(p); } -write_to :: proc(r: Reader, w: Writer) -> (n: i64, err: Error) { +write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) { if r.stream_vtable == nil || w.stream_vtable == nil { return 0, .Empty; } @@ -253,7 +255,7 @@ write_to :: proc(r: Reader, w: Writer) -> (n: i64, err: Error) { } return 0, .Empty; } -read_from :: proc(w: Writer, r: Reader) -> (n: i64, err: Error) { +read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) { if r.stream_vtable == nil || w.stream_vtable == nil { return 0, .Empty; } From 21c1abe15aa72a1619edaf8f8f390444e220b09f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 12:03:38 +0000 Subject: [PATCH 159/170] Add fmt formatting for runtime.Source_Code_Location --- core/fmt/fmt.odin | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 3241548a3..84cf4f160 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1339,6 +1339,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Named: // Built-in Custom Formatters for core library types switch a in v { + case runtime.Source_Code_Location: + io.write_string(fi.writer, a.file_path); + io.write_byte(fi.writer, '('); + io.write_int(fi.writer, a.line); + io.write_byte(fi.writer, ':'); + io.write_int(fi.writer, a.column); + io.write_byte(fi.writer, ')'); + return; + case time.Duration: ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) { v := v; From 2ab6cdb98e0f08bcbc7fccd58b5d9192fda9328c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 12:03:57 +0000 Subject: [PATCH 160/170] Add `package bufio`; add `bufio.Reader` --- core/bufio/reader.odin | 474 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 core/bufio/reader.odin diff --git a/core/bufio/reader.odin b/core/bufio/reader.odin new file mode 100644 index 000000000..f35706f9b --- /dev/null +++ b/core/bufio/reader.odin @@ -0,0 +1,474 @@ +package bufio + +import "core:io" +import "core:mem" +import "core:unicode/utf8" +import "core:bytes" + +// Reader is a buffered wrapper for an io.Reader +Reader :: struct { + buf: []byte, + buf_allocator: mem.Allocator, + + rd: io.Reader, // reader + r, w: int, // read and write positions for buf + + err: io.Error, + + last_byte: int, // last byte read, invalid is -1 + last_rune_size: int, // size of last rune read, invalid is -1 +} + + +DEFAULT_BUF_SIZE :: 4096; + +@(private) +MIN_READ_BUFFER_SIZE :: 16; +@(private) +MAX_CONSECUTIVE_EMPTY_READS :: 128; + +reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) { + size := size; + size = max(size, MIN_READ_BUFFER_SIZE); + reader_reset(b, rd); + b.buf_allocator = allocator; + b.buf = make([]byte, size, allocator); +} + +reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) { + reader_reset(b, rd); + b.buf_allocator = {}; + b.buf = buf; +} + +// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set +reader_destroy :: proc(b: ^Reader) { + delete(b.buf, b.buf_allocator); + b^ = {}; +} + +reader_size :: proc(b: ^Reader) -> int { + return len(b.buf); +} + +reader_reset :: proc(b: ^Reader, r: io.Reader) { + b.rd = r; + b.r, b.w = 0, 0; + b.err = nil; + b.last_byte = -1; + b.last_rune_size = -1; +} + +@(private) +_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error { + if b.r > 0 { + copy(b.buf, b.buf[b.r:b.w]); + b.w -= b.r; + b.r = 0; + } + + if b.w >= len(b.buf) { + return .Buffer_Full; + } + + // read new data, and try a limited number of times + for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 { + n, err := io.read(b.rd, b.buf[b.w:]); + if n < 0 { + return .Negative_Read; + } + b.w += n; + if err != nil { + b.err = err; + return nil; + } + if n > 0 { + return nil; + } + } + b.err = .No_Progress; + return nil; +} + +@(private) +_reader_consume_err :: proc(b: ^Reader) -> io.Error { + err := b.err; + b.err = nil; + return err; +} + +// reader_peek returns the next n bytes without advancing the reader +// The bytes stop being valid on the next read call +// If reader_peek returns fewer than n bytes, it also return an error +// explaining why the read is short +// The error will be .Buffer_Full if n is larger than the internal buffer size +reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) { + n := n; + + if n < 0 { + return nil, .Negative_Count; + } + b.last_byte = -1; + b.last_rune_size = -1; + + for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil { + if fill_err := _reader_read_new_chunk(b); fill_err != nil { + return nil, fill_err; + } + } + + if n > len(b.buf) { + return b.buf[b.r : b.w], .Buffer_Full; + } + + if available := b.w - b.r; available < n { + n = available; + err = _reader_consume_err(b); + if err == nil { + err = .Buffer_Full; + } + } + + return b.buf[b.r : b.r+n], err; +} + +// reader_buffered returns the number of bytes that can be read from the current buffer +reader_buffered :: proc(b: ^Reader) -> int { + return b.w - b.r; +} + +// reader_discard skips the next n bytes, and returns the number of bytes that were discarded +reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) { + if n < 0 { + return 0, .Negative_Count; + } + if n == 0 { + return; + } + + remaining := n; + for { + skip := reader_buffered(b); + if skip == 0 { + if fill_err := _reader_read_new_chunk(b); fill_err != nil { + return 0, fill_err; + } + skip = reader_buffered(b); + } + skip = min(skip, remaining); + b.r += skip; + remaining -= skip; + if remaining == 0 { + return n, nil; + } + if b.err != nil { + return n - remaining, _reader_consume_err(b); + } + } + + return; +} + +// reader_read reads data into p +// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p) +reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) { + n = len(p); + if n == 0 { + if reader_buffered(b) > 0 { + return 0, nil; + } + return 0, _reader_consume_err(b); + } + if b.r == b.w { + if b.err != nil { + return 0, _reader_consume_err(b); + } + + if len(p) >= len(b.buf) { + n, b.err = io.read(b.rd, p); + if n < 0 { + return 0, .Negative_Read; + } + + if n > 0 { + b.last_byte = int(p[n-1]); + b.last_rune_size = -1; + } + return n, _reader_consume_err(b); + } + + b.r, b.w = 0, 0; + n, b.err = io.read(b.rd, b.buf); + if n < 0 { + return 0, .Negative_Read; + } + if n == 0 { + return 0, _reader_consume_err(b); + } + b.w += n; + } + + n = copy(p, b.buf[b.r:b.w]); + b.r += n; + b.last_byte = int(b.buf[b.r-1]); + b.last_rune_size = -1; + return n, nil; +} + +// reader_read_byte reads and returns a single byte +// If no byte is available, it return an error +reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) { + b.last_rune_size = -1; + for b.r == b.w { + if b.err != nil { + return 0, _reader_consume_err(b); + } + if err := _reader_read_new_chunk(b); err != nil { + return 0, err; + } + } + c := b.buf[b.r]; + b.r += 1; + b.last_byte = int(c); + return c, nil; +} + +// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread +reader_unread_byte :: proc(b: ^Reader) -> io.Error { + if b.last_byte < 0 || b.r == 0 && b.w > 0 { + return .Invalid_Unread; + } + if b.r > 0 { + b.r -= 1; + } else { + // b.r == 0 && b.w == 0 + b.w = 1; + } + b.buf[b.r] = byte(b.last_byte); + b.last_byte = -1; + b.last_rune_size = -1; + return nil; +} + +// reader_read_rune reads a single UTF-8 encoded unicode character +// and returns the rune and its size in bytes +// If the encoded rune is invalid, it consumes one byte and returns utf8.RUNE_ERROR (U+FFFD) with a size of 1 +reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) { + for b.r+utf8.UTF_MAX > b.w && + !utf8.full_rune(b.buf[b.r:b.w]) && + b.err == nil && + b.w-b.w < len(b.buf) { + if err = _reader_read_new_chunk(b); err != nil { + return; + } + } + + b.last_rune_size = -1; + if b.r == b.w { + err = _reader_consume_err(b); + return; + } + r, size = rune(b.buf[b.r]), 1; + if r >= utf8.RUNE_SELF { + r, size = utf8.decode_rune(b.buf[b.r : b.w]); + } + b.r += size; + b.last_byte = int(b.buf[b.r-1]); + b.last_rune_size = size; + return; +} + +// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread +reader_unread_rune :: proc(b: ^Reader) -> io.Error { + if b.last_rune_size < 0 || b.r < b.last_rune_size { + return .Invalid_Unread; + } + b.r -= b.last_rune_size; + b.last_byte = -1; + b.last_rune_size = -1; + return nil; +} + +reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { + write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) { + n, err := io.write(w, b.buf[b.r:b.w]); + if n < 0 { + return 0, .Negative_Write; + } + b.r += n; + return i64(n), err; + } + + n, err = write_buf(b, w); + if err != nil { + return; + } + + m: i64; + if nr, cerr := io.to_writer_to(b.rd); cerr == nil { + m, err = io.write_to(nr, w); + n += m; + return n, err; + } + + if nw, cerr := io.to_reader_from(w); cerr == nil { + m, err = io.read_from(nw, b.rd); + n += m; + return n, err; + } + + if b.w-b.r < len(b.buf) { + if err = _reader_read_new_chunk(b); err != nil { + return; + } + } + + for b.r < b.w { + m, err = write_buf(b, w); + n += m; + if err != nil { + return; + } + if err = _reader_read_new_chunk(b); err != nil { + return; + } + } + + if b.err == .EOF { + b.err = nil; + } + + err = _reader_consume_err(b); + return; +} + + + +// reader_to_stream converts a Reader into an io.Stream +reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) { + s.stream_data = b; + s.stream_vtable = _reader_vtable; + return; +} + + + +@(private) +_reader_vtable := &io.Stream_VTable{ + impl_destroy = proc(s: io.Stream) -> io.Error { + b := (^Reader)(s.stream_data); + reader_destroy(b); + return nil; + }, + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Reader)(s.stream_data); + return reader_read(b, p); + }, + impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) { + b := (^Reader)(s.stream_data); + return reader_read_byte(b); + }, + impl_unread_byte = proc(s: io.Stream) -> io.Error { + b := (^Reader)(s.stream_data); + return reader_unread_byte(b); + }, + impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) { + b := (^Reader)(s.stream_data); + return reader_read_rune(b); + }, + impl_unread_rune = proc(s: io.Stream) -> io.Error { + b := (^Reader)(s.stream_data); + return reader_unread_rune(b); + }, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + b := (^Reader)(s.stream_data); + return reader_write_to(b, w); + }, +}; + + + +// +// Utility procedures +// + + +// reader_read_slice reads until the first occurrence of delim from the reader +// It returns a slice pointing at the bytes in the buffer +// The bytes stop being valid at the next read +// If reader_read_slice encounters an error before finding a delimiter +// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim +// Because the data returned from reader_read_slice will be overwritten on the +// next IO operation, reader_read_bytes or reader_read_string is usually preferred +// +// reader_read_slice returns err != nil if and only if line does not end in delim +// +reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) { + s := 0; + for { + if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 { + i += s; + line = b.buf[b.r:][:i+1]; + b.r += i + 1; + break; + } + + if b.err != nil { + line = b.buf[b.r : b.w]; + b.r = b.w; + err = _reader_consume_err(b); + break; + } + + if reader_buffered(b) >= len(b.buf) { + b.r = b.w; + line = b.buf; + err = .Buffer_Full; + break; + } + + s = b.w - b.r; + + if err = _reader_read_new_chunk(b); err != nil { + break; + } + } + + if i := len(line)-1; i >= 0 { + b.last_byte = int(line[i]); + b.last_rune_size = -1; + } + + return; +} + +// reader_read_bytes reads until the first occurrence of delim from the Reader +// It returns an allocated slice containing the data up to and including the delimiter +reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) { + full: [dynamic]byte; + full.allocator = allocator; + + frag: []byte; + for { + e: io.Error; + frag, e = reader_read_slice(b, delim); + if e == nil { + break; + } + if e != .Buffer_Full { + err = e; + break; + } + + append(&full, ..frag); + } + append(&full, ..frag); + return full[:], err; +} + +// reader_read_string reads until the first occurrence of delim from the Reader +// It returns an allocated string containing the data up to and including the delimiter +reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) { + buf, err := reader_read_bytes(b, delim, allocator); + return string(buf), err; +} From 14ae2e0a8d36010d41c52e6aee4d8c60607ce1bc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 15:14:21 +0000 Subject: [PATCH 161/170] Add bufio.Writer and bufio.Read_Writer --- core/bufio/read_writer.odin | 68 ++++++++++ core/bufio/writer.odin | 255 ++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 core/bufio/read_writer.odin create mode 100644 core/bufio/writer.odin diff --git a/core/bufio/read_writer.odin b/core/bufio/read_writer.odin new file mode 100644 index 000000000..f60e17d2d --- /dev/null +++ b/core/bufio/read_writer.odin @@ -0,0 +1,68 @@ +package bufio + +import "core:io" + +// Read_Writer stores pointers to a Reader and a Writer +Read_Writer :: struct { + r: ^Reader, + w: ^Writer, +} + + +read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) { + rw.r, rw.w = r, w; +} + +read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) { + s.stream_data = rw; + s.stream_vtable = _read_writer_vtable; + return; +} + +@(private) +_read_writer_vtable := &io.Stream_VTable{ + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_read(b, p); + }, + impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_read_byte(b); + }, + impl_unread_byte = proc(s: io.Stream) -> io.Error { + b := (^Read_Writer)(s.stream_data).r; + return reader_unread_byte(b); + }, + impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_read_rune(b); + }, + impl_unread_rune = proc(s: io.Stream) -> io.Error { + b := (^Read_Writer)(s.stream_data).r; + return reader_unread_rune(b); + }, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_write_to(b, w); + }, + impl_flush = proc(s: io.Stream) -> io.Error { + b := (^Read_Writer)(s.stream_data).w; + return writer_flush(b); + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Read_Writer)(s.stream_data).w; + return writer_write(b, p); + }, + impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { + b := (^Read_Writer)(s.stream_data).w; + return writer_write_byte(b, c); + }, + impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { + b := (^Read_Writer)(s.stream_data).w; + return writer_write_rune(b, r); + }, + impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { + b := (^Read_Writer)(s.stream_data).w; + return writer_read_from(b, r); + }, +}; diff --git a/core/bufio/writer.odin b/core/bufio/writer.odin new file mode 100644 index 000000000..97f24a224 --- /dev/null +++ b/core/bufio/writer.odin @@ -0,0 +1,255 @@ +package bufio + +import "core:io" +import "core:mem" +import "core:unicode/utf8" +// import "core:bytes" + +// Writer is a buffered wrapper for an io.Writer +Writer :: struct { + buf: []byte, + buf_allocator: mem.Allocator, + + wr: io.Writer, + n: int, + + err: io.Error, + +} + +writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) { + size := size; + size = max(size, MIN_READ_BUFFER_SIZE); + writer_reset(b, wr); + b.buf_allocator = allocator; + b.buf = make([]byte, size, allocator); +} + +writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) { + writer_reset(b, wr); + b.buf_allocator = {}; + b.buf = buf; +} + +// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set +writer_destroy :: proc(b: ^Writer) { + delete(b.buf, b.buf_allocator); + b^ = {}; +} + +// writer_size returns the size of underlying buffer in bytes +writer_size :: proc(b: ^Writer) -> int { + return len(b.buf); +} + +writer_reset :: proc(b: ^Writer, w: io.Writer) { + b.wr = w; + b.n = 0; + b.err = nil; +} + + +// writer_flush writes any buffered data into the underlying io.Writer +writer_flush :: proc(b: ^Writer) -> io.Error { + if b.err != nil { + return b.err; + } + if b.n == 0 { + return nil; + } + + n, err := io.write(b.wr, b.buf[0:b.n]); + if n < b.n && err == nil { + err = .Short_Write; + } + if err != nil { + if n > 0 && n < b.n { + copy(b.buf[:b.n-n], b.buf[n : b.n]); + } + b.n -= n; + b.err = err; + return err; + } + b.n = 0; + return nil; +} + +// writer_available returns how many bytes are unused in the buffer +writer_available :: proc(b: ^Writer) -> int { + return len(b.buf) - b.n; +} + +// writer_buffered returns the number of bytes that have been writted into the current buffer +writer_buffered :: proc(b: ^Writer) -> int { + return b.n; +} + +// writer_write writes the contents of p into the buffer +// It returns the number of bytes written +// If n < len(p), it will return an error explaining why the write is short +writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) { + p := p; + for len(p) > writer_available(b) && b.err == nil { + m: int; + if writer_buffered(b) == 0 { + m, b.err = io.write(b.wr, p); + } else { + m = copy(b.buf[b.n:], p); + b.n += m; + writer_flush(b); + } + n += m; + p = p[m:]; + } + if b.err != nil { + return n, b.err; + } + m := copy(b.buf[b.n:], p); + b.n += m; + m += n; + return m, nil; +} + +// writer_write_byte writes a single byte +writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error { + if b.err != nil { + return b.err; + } + if writer_available(b) <= 0 && writer_flush(b) != nil { + return b.err; + } + b.buf[b.n] = c; + b.n += 1; + return nil; +} + +// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error +writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) { + if r < utf8.RUNE_SELF { + err = writer_write_byte(b, byte(r)); + size = 0 if err != nil else 1; + return; + } + if b.err != nil { + return 0, b.err; + } + + buf: [4]u8; + + n := writer_available(b); + if n < utf8.UTF_MAX { + writer_flush(b); + if b.err != nil { + return 0, b.err; + } + n = writer_available(b); + if n < utf8.UTF_MAX { + // this only happens if the buffer is very small + w: int; + buf, w = utf8.encode_rune(r); + return writer_write(b, buf[:w]); + } + } + + buf, size = utf8.encode_rune(r); + copy(b.buf[b.n:], buf[:size]); + b.n += size; + return; +} + +// writer_write writes a string into the buffer +// It returns the number of bytes written +// If n < len(p), it will return an error explaining why the write is short +writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) { + return writer_write(b, transmute([]byte)s); +} + +// writer_read_from is to support io.Reader_From types +// If the underlying writer supports the io,read_from, and b has no buffered data yet, +// this procedure calls the underlying read_from implementation without buffering +writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) { + if b.err != nil { + return 0, b.err; + } + if writer_buffered(b) == 0 { + if w, cerr := io.to_reader_from(b.wr); cerr != nil { + n, err = io.read_from(w, r); + b.err = err; + return; + } + } + + for { + if writer_available(b) == 0 { + if ferr := writer_flush(b); ferr != nil { + return n, ferr; + } + } + m: int; + nr := 0; + for nr < MAX_CONSECUTIVE_EMPTY_READS { + m, err = io.read(r, b.buf[b.n:]); + if m != 0 || err != nil { + break; + } + nr += 1; + } + if nr == MAX_CONSECUTIVE_EMPTY_READS { + return n, .No_Progress; + } + b.n += m; + n += i64(m); + if err != nil { + break; + } + } + + if err == .EOF { + if writer_available(b) == 0 { + err = writer_flush(b); + } else { + err = nil; + } + } + return; +} + + + +// writer_to_stream converts a Writer into an io.Stream +writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) { + s.stream_data = b; + s.stream_vtable = _writer_vtable; + return; +} + + + +@(private) +_writer_vtable := &io.Stream_VTable{ + impl_destroy = proc(s: io.Stream) -> io.Error { + b := (^Writer)(s.stream_data); + writer_destroy(b); + return nil; + }, + impl_flush = proc(s: io.Stream) -> io.Error { + b := (^Writer)(s.stream_data); + return writer_flush(b); + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Writer)(s.stream_data); + return writer_write(b, p); + }, + impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { + b := (^Writer)(s.stream_data); + return writer_write_byte(b, c); + }, + impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { + b := (^Writer)(s.stream_data); + return writer_write_rune(b, r); + }, + impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { + b := (^Writer)(s.stream_data); + return writer_read_from(b, r); + }, +}; From a82c902f991b11b0f846e4379dfbf19ae962afde Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 15:36:02 +0000 Subject: [PATCH 162/170] Minor correction to bytes.Buffer's vtable --- core/bytes/buffer.odin | 4 ++++ core/io/io.odin | 44 +++++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index 427fc7253..61fde9605 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -310,6 +310,10 @@ _buffer_vtable := &io.Stream_VTable{ b := (^Buffer)(s.stream_data); return buffer_write_byte(b, c); }, + impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_write_rune(b, r); + }, impl_unread_byte = proc(s: io.Stream) -> io.Error { b := (^Buffer)(s.stream_data); return buffer_unread_byte(b); diff --git a/core/io/io.odin b/core/io/io.odin index b4d2c16ec..e33b33064 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -44,22 +44,23 @@ Error :: enum i32 { Empty = -1, } -Close_Proc :: distinct proc(using s: Stream) -> Error; -Flush_Proc :: distinct proc(using s: Stream) -> Error; -Seek_Proc :: distinct proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error); -Size_Proc :: distinct proc(using s: Stream) -> i64; -Read_Proc :: distinct proc(using s: Stream, p: []byte) -> (n: int, err: Error); -Read_At_Proc :: distinct proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error); -Read_From_Proc :: distinct proc(using s: Stream, r: Reader) -> (n: i64, err: Error); -Read_Byte_Proc :: distinct proc(using s: Stream) -> (byte, Error); -Read_Rune_Proc :: distinct proc(using s: Stream) -> (ch: rune, size: int, err: Error); -Unread_Byte_Proc :: distinct proc(using s: Stream) -> Error; -Unread_Rune_Proc :: distinct proc(using s: Stream) -> Error; -Write_Proc :: distinct proc(using s: Stream, p: []byte) -> (n: int, err: Error); -Write_At_Proc :: distinct proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error); -Write_To_Proc :: distinct proc(using s: Stream, w: Writer) -> (n: i64, err: Error); -Write_Byte_Proc :: distinct proc(using s: Stream, c: byte) -> Error; -Destroy_Proc :: distinct proc(using s: Stream) -> Error; +Close_Proc :: proc(using s: Stream) -> Error; +Flush_Proc :: proc(using s: Stream) -> Error; +Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error); +Size_Proc :: proc(using s: Stream) -> i64; +Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error); +Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error); +Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error); +Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error); +Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error); +Unread_Byte_Proc :: proc(using s: Stream) -> Error; +Unread_Rune_Proc :: proc(using s: Stream) -> Error; +Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error); +Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error); +Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error); +Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error; +Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error); +Destroy_Proc :: proc(using s: Stream) -> Error; Stream :: struct { @@ -82,6 +83,7 @@ Stream_VTable :: struct { impl_write: Write_Proc, impl_write_at: Write_At_Proc, impl_write_byte: Write_Byte_Proc, + impl_write_rune: Write_Rune_Proc, impl_read_from: Read_From_Proc, impl_unread_byte: Unread_Byte_Proc, @@ -371,10 +373,16 @@ write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) { return write(s, transmute([]byte)str); } -write_rune :: proc(s: Writer, r: rune) -> (n: int, err: Error) { +write_rune :: proc(s: Writer, r: rune) -> (size: int, err: Error) { + if s.stream_vtable != nil && s.impl_write_rune != nil { + return s->impl_write_rune(r); + } + if r < utf8.RUNE_SELF { err = write_byte(s, byte(r)); - n = 1 if err == nil else 0; + if err == nil { + size = 1; + } return; } buf, w := utf8.encode_rune(r); From 7b36174c17b11674d6bd76c21b626c464a20cb05 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 16:38:26 +0000 Subject: [PATCH 163/170] Fix formatting --- core/io/conv.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/io/conv.odin b/core/io/conv.odin index e976bb7b7..f164c09e7 100644 --- a/core/io/conv.odin +++ b/core/io/conv.odin @@ -114,14 +114,14 @@ to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) { } return; } -to_Write_Closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) { +to_write_closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) { w.stream = s; if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil { err = .Missing_Procedure; } return; } -to_Write_Seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) { +to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) { w.stream = s; if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil { err = .Missing_Procedure; From 09a52b7ee662f364ecd0ee00d2379de612d86bc7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 16:56:09 +0000 Subject: [PATCH 164/170] Fix typos --- core/io/multi.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/io/multi.odin b/core/io/multi.odin index fa26b0052..990cda61d 100644 --- a/core/io/multi.odin +++ b/core/io/multi.odin @@ -36,7 +36,7 @@ _multi_reader_vtable := &Stream_VTable{ }, }; -mutlti_reader :: proc(readers: ..Reader, allocator := context.allocator) -> (r: Reader) { +multi_reader :: proc(readers: ..Reader, allocator := context.allocator) -> (r: Reader) { context.allocator = allocator; mr := new(Multi_Reader); all_readers := make([dynamic]Reader, 0, len(readers)); @@ -90,7 +90,7 @@ _multi_writer_vtable := &Stream_VTable{ }, }; -mutlti_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (out: Writer) { +multi_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (out: Writer) { context.allocator = allocator; mw := new(Multi_Writer); mw.allocator = allocator; From 76e6624dbb065c0bba8d5512613cd3083dd79fda Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 19:21:00 +0000 Subject: [PATCH 165/170] Remove type name generation for procedures in ir.cpp --- src/ir.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 0d9292a50..867557e21 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7064,6 +7064,9 @@ void ir_gen_global_type_name(irModule *m, Entity *e, String name) { if (!ir_min_dep_entity(m, e)) { return; } + if (is_type_proc(e->type)) { + return; + } irValue *t = ir_value_type_name(name, e->type); ir_module_add_value(m, e, t); string_map_set(&m->members, name, t); From 1a8ea6113a16e1a762106ba97442c1e991041841 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 19:52:08 +0000 Subject: [PATCH 166/170] Remove `hash` field in runtime.Source_Code_Location --- core/runtime/core.odin | 1 - core/runtime/error_checks.odin | 10 +++++----- src/ir.cpp | 13 ------------- src/ir_print.cpp | 3 --- src/llvm_backend.cpp | 15 ++------------- 5 files changed, 7 insertions(+), 35 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index b23358b5c..0dfef269c 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -257,7 +257,6 @@ Source_Code_Location :: struct { file_path: string, line, column: int, procedure: string, - hash: u64, } Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location); diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index 7c442510e..38d6641ab 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -23,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index } handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Index "); print_i64(i64(index)); print_string(" is out of bounds range 0:"); @@ -36,7 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Invalid slice indices: "); print_i64(i64(lo)); print_string(":"); @@ -67,7 +67,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int, } handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Invalid dynamic array values: "); print_i64(i64(low)); print_string(":"); @@ -87,7 +87,7 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column } handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Invalid type assertion from "); print_typeid(from); print_string(" to "); @@ -135,7 +135,7 @@ type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, colum actual := variant_type(from, from_data); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Invalid type assertion from "); print_typeid(from); print_string(" to "); diff --git a/src/ir.cpp b/src/ir.cpp index 867557e21..1e364c7ba 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -458,7 +458,6 @@ struct irValueSourceCodeLocation { irValue *line; irValue *column; irValue *procedure; - u64 hash; }; @@ -7176,17 +7175,6 @@ bool is_double_pointer(Type *t) { return is_type_pointer(td); } - -u64 ir_generate_source_code_location_hash(TokenPos pos) { - u64 h = 0xcbf29ce484222325; - for (isize i = 0; i < pos.file.len; i++) { - h = (h ^ u64(pos.file[i])) * 0x100000001b3; - } - h = h ^ (u64(pos.line) * 0x100000001b3); - h = h ^ (u64(pos.column) * 0x100000001b3); - return h; -} - irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, TokenPos pos) { gbAllocator a = ir_allocator(); irValue *v = ir_alloc_value(irValue_SourceCodeLocation); @@ -7194,7 +7182,6 @@ irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, Token v->SourceCodeLocation.line = ir_const_int(pos.line); v->SourceCodeLocation.column = ir_const_int(pos.column); v->SourceCodeLocation.procedure = ir_find_or_add_entity_string(proc->module, procedure); - v->SourceCodeLocation.hash = ir_generate_source_code_location_hash(pos); return v; } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a04f7b657..a58ddbe0f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1430,7 +1430,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin irValue *line = value->SourceCodeLocation.line; irValue *column = value->SourceCodeLocation.column; irValue *procedure = value->SourceCodeLocation.procedure; - u64 hash = value->SourceCodeLocation.hash; ir_write_byte(f, '{'); ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, file, t_string); @@ -1440,8 +1439,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin ir_print_type(f, m, t_int); ir_write_byte(f, ' '); ir_print_value(f, m, column, t_int); ir_write_string(f, str_lit(", ")); ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, procedure, t_string); - ir_write_string(f, str_lit(", ")); - ir_print_type(f, m, t_u64); ir_write_byte(f, ' '); ir_write_u64(f, hash); ir_write_byte(f, '}'); break; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8063bdc8b..213c05a52 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5667,28 +5667,17 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } -u64 lb_generate_source_code_location_hash(TokenPos const &pos) { - u64 h = 0xcbf29ce484222325; - for (isize i = 0; i < pos.file.len; i++) { - h = (h ^ u64(pos.file[i])) * 0x100000001b3; - } - h = h ^ (u64(pos.line) * 0x100000001b3); - h = h ^ (u64(pos.column) * 0x100000001b3); - return h; -} - lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos) { lbModule *m = p->module; - LLVMValueRef fields[5] = {}; + LLVMValueRef fields[4] = {}; fields[0]/*file*/ = lb_find_or_add_entity_string(p->module, pos.file).value; fields[1]/*line*/ = lb_const_int(m, t_int, pos.line).value; fields[2]/*column*/ = lb_const_int(m, t_int, pos.column).value; fields[3]/*procedure*/ = lb_find_or_add_entity_string(p->module, procedure).value; - fields[4]/*hash*/ = lb_const_int(m, t_u64, lb_generate_source_code_location_hash(pos)).value; lbValue res = {}; - res.value = LLVMConstNamedStruct(lb_type(m, t_source_code_location), fields, 5); + res.value = LLVMConstNamedStruct(lb_type(m, t_source_code_location), fields, gb_count_of(fields)); res.type = t_source_code_location; return res; } From 069c6cac3f42ec7a2428062b0e8c481b755534ad Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Dec 2020 20:47:50 +0000 Subject: [PATCH 167/170] Add package name and source code location to `Type_Info_Named` --- core/runtime/core.odin | 7 ++++++- src/ir.cpp | 15 +++++++++++++++ src/llvm_backend.cpp | 23 ++++++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 0dfef269c..2a744a241 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -55,7 +55,12 @@ Type_Info_Struct_Soa_Kind :: enum u8 { } // Variant Types -Type_Info_Named :: struct {name: string, base: ^Type_Info}; +Type_Info_Named :: struct { + name: string, + base: ^Type_Info, + pkg: string, + loc: Source_Code_Location, +}; Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}; Type_Info_Rune :: struct {}; Type_Info_Float :: struct {endianness: Platform_Endianness}; diff --git a/src/ir.cpp b/src/ir.cpp index 1e364c7ba..e46eb27fb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12172,6 +12172,21 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip); + + if (t->Named.type_name->pkg) { + irValue *name = ir_const_string(proc->module, t->Named.type_name->pkg->name); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), name); + } + + String proc_name = {}; + if (t->Named.type_name->parent_proc_decl) { + DeclInfo *decl = t->Named.type_name->parent_proc_decl; + if (decl->entity && decl->entity->kind == Entity_Procedure) { + proc_name = decl->entity->token.string; + } + } + irValue *loc = ir_emit_source_code_location(proc, proc_name, t->Named.type_name->token.pos); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), loc); break; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 213c05a52..d92108044 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11973,9 +11973,30 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da switch (t->kind) { case Type_Named: { tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr); - LLVMValueRef vals[2] = { + + LLVMValueRef pkg_name = nullptr; + if (t->Named.type_name->pkg) { + pkg_name = lb_const_string(m, t->Named.type_name->pkg->name).value; + } else { + pkg_name = LLVMConstNull(lb_type(m, t_string)); + } + + String proc_name = {}; + if (t->Named.type_name->parent_proc_decl) { + DeclInfo *decl = t->Named.type_name->parent_proc_decl; + if (decl->entity && decl->entity->kind == Entity_Procedure) { + proc_name = decl->entity->token.string; + } + } + TokenPos pos = t->Named.type_name->token.pos; + + lbValue loc = lb_emit_source_code_location(p, proc_name, pos); + + LLVMValueRef vals[4] = { lb_const_string(p->module, t->Named.type_name->token.string).value, lb_get_type_info_ptr(m, t->Named.base).value, + pkg_name, + loc.value }; lbValue res = {}; From 80fead1de56c119eacb586157a66c9ec5db0a33f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Dec 2020 00:03:45 +0000 Subject: [PATCH 168/170] Add `ast.Selector_Expr_Call` --- core/odin/ast/ast.odin | 8 ++++++++ core/odin/parser/parser.odin | 37 +++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 3b0df5fda..b7ece0ef1 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -146,6 +146,7 @@ Paren_Expr :: struct { Selector_Expr :: struct { using node: Expr, expr: ^Expr, + op: tokenizer.Token, field: ^Ident, } @@ -154,6 +155,13 @@ Implicit_Selector_Expr :: struct { field: ^Ident, } +Selector_Call_Expr :: struct { + using node: Expr, + expr: ^Expr, + call: ^Call_Expr, + modified_call: bool, +} + Index_Expr :: struct { using node: Expr, expr: ^Expr, diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 70bdc9e26..259d8f482 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2353,10 +2353,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { where_clauses: []^ast.Expr; if (p.curr_tok.kind == .Where) { where_token = expect_token(p, .Where); - prev_level := p.expr_level; + where_prev_level := p.expr_level; p.expr_level = -1; where_clauses = parse_rhs_expr_list(p); - p.expr_level = prev_level; + p.expr_level = where_prev_level; } expect_token(p, .Open_Brace); @@ -2416,10 +2416,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { where_clauses: []^ast.Expr; if (p.curr_tok.kind == .Where) { where_token = expect_token(p, .Where); - prev_level := p.expr_level; + where_prev_level := p.expr_level; p.expr_level = -1; where_clauses = parse_rhs_expr_list(p); - p.expr_level = prev_level; + p.expr_level = where_prev_level; } variants: [dynamic]^ast.Expr; @@ -2628,7 +2628,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit { return lit; } -parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr { +parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr { args: [dynamic]^ast.Expr; ellipsis: tokenizer.Token; @@ -2686,6 +2686,14 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr { ce.ellipsis = ellipsis; ce.close = close.pos; + o := ast.unparen_expr(operand); + if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right { + sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end); + sce.expr = o; + sce.call = ce; + return sce; + } + return ce; } @@ -2776,6 +2784,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a sel := ast.new(ast.Selector_Expr, operand.pos, field.end); sel.expr = operand; + sel.op = tok; sel.field = field; operand = sel; @@ -2811,6 +2820,24 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)); } + case .Arrow_Right: + tok := expect_token(p, .Arrow_Right); + #partial switch p.curr_tok.kind { + case .Ident: + field := parse_ident(p); + + sel := ast.new(ast.Selector_Expr, operand.pos, field.end); + sel.expr = operand; + sel.op = tok; + sel.field = field; + + operand = sel; + case: + error(p, p.curr_tok.pos, "expected a selector"); + advance_token(p); + operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)); + } + case .Pointer: op := expect_token(p, .Pointer); deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op)); From d94414b0f4eba7d6e42da1912a43731fcbbd94c1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Dec 2020 00:15:35 +0000 Subject: [PATCH 169/170] Correct ternary if/when parsing --- core/odin/ast/ast.odin | 7 +++--- core/odin/parser/parser.odin | 39 ++++++++++------------------------ core/odin/tokenizer/token.odin | 12 ----------- 3 files changed, 14 insertions(+), 44 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index b7ece0ef1..990e7b47e 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -214,9 +214,9 @@ Ternary_Expr :: struct { Ternary_If_Expr :: struct { using node: Expr, - x: ^Expr, + x: ^Expr, op1: tokenizer.Token, - cond: ^Expr, + cond: ^Expr, op2: tokenizer.Token, y: ^Expr, } @@ -225,7 +225,7 @@ Ternary_When_Expr :: struct { using node: Expr, x: ^Expr, op1: tokenizer.Token, - cond: ^Expr, + cond: ^Expr, op2: tokenizer.Token, y: ^Expr, } @@ -569,7 +569,6 @@ Distinct_Type :: struct { Opaque_Type :: struct { using node: Expr, - tok: tokenizer.Token_Kind, type: ^Expr, } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 259d8f482..4d94ed479 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -300,7 +300,9 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string) expect_operator :: proc(p: ^Parser) -> tokenizer.Token { prev := p.curr_tok; - if !tokenizer.is_operator(prev.kind) { + if prev.kind == .If || prev.kind == .When { + // okay + } else if !tokenizer.is_operator(prev.kind) { g := tokenizer.to_string(prev.kind); error(p, prev.pos, "expected an operator, got '%s'", g); } @@ -1957,13 +1959,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { bl.tok = tok; return bl; - - case .Size_Of, .Align_Of, .Offset_Of: - tok := advance_token(p); - expr := ast.new(ast.Implicit, tok.pos, end_pos(tok)); - expr.tok = tok; - return parse_call_expr(p, expr); - case .Open_Brace: if !lhs { return parse_literal_value(p, nil); @@ -1992,15 +1987,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { case .Opaque: tok := advance_token(p); + warn(p, tok.pos, "opaque is deprecated in favour of #opaque"); type := parse_type(p); ot := ast.new(ast.Opaque_Type, tok.pos, type.end); - ot.tok = tok.kind; ot.type = type; return ot; + case .Hash: tok := expect_token(p, .Hash); name := expect_token(p, .Ident); switch name.text { + case "opaque": + type := parse_type(p); + ot := ast.new(ast.Opaque_Type, tok.pos, type.end); + ot.type = type; + return ot; + case "type": type := parse_type(p); hp := ast.new(ast.Helper_Type, tok.pos, type.end); @@ -2225,25 +2227,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { ti.specialization = nil; return ti; - case .Type_Of: - tok := advance_token(p); - i := ast.new(ast.Implicit, tok.pos, end_pos(tok)); - i.tok = tok; - type: ^ast.Expr = parse_call_expr(p, i); - for p.curr_tok.kind == .Period { - period := advance_token(p); - - field := parse_ident(p); - sel := ast.new(ast.Selector_Expr, period.pos, field.end); - sel.expr = type; - sel.field = field; - - type = sel; - } - - return type; - - case .Pointer: tok := expect_token(p, .Pointer); elem := parse_type(p); diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 997ca7ac1..54110cf02 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -133,7 +133,6 @@ Token_Kind :: enum u32 { Defer, Return, Proc, - Macro, Struct, Union, Enum, @@ -150,11 +149,6 @@ Token_Kind :: enum u32 { Inline, No_Inline, Context, - Size_Of, - Align_Of, - Offset_Of, - Type_Of, - Const, B_Keyword_End, COUNT, @@ -268,7 +262,6 @@ tokens := [Token_Kind.COUNT]string { "defer", "return", "proc", - "macro", "struct", "union", "enum", @@ -285,11 +278,6 @@ tokens := [Token_Kind.COUNT]string { "inline", "no_inline", "context", - "size_of", - "align_of", - "offset_of", - "type_of", - "const", "", }; From ca4657fd31b9efc7ab52f7e1b6f4145d5ed28fb7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Dec 2020 00:47:58 +0000 Subject: [PATCH 170/170] Add custom semicolon insertion to odin/tokenizer and odin/parser --- core/odin/parser/parser.odin | 91 ++++++++++++++++++++++++++---- core/odin/tokenizer/token.odin | 13 +++++ core/odin/tokenizer/tokenizer.odin | 79 +++++++++++++++++++++----- 3 files changed, 157 insertions(+), 26 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 4d94ed479..583a4cc03 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -190,6 +190,50 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0) return; } +peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) { + prev_parser := p^; + defer p^ = prev_parser; + + p.tok.err = nil; + for i := 0; i <= lookahead; i += 1 { + advance_token(p); + } + tok = p.curr_tok; + return; +} +skip_possible_newline :: proc(p: ^Parser) -> bool { + if .Insert_Semicolon not_in p.tok.flags { + return false; + } + + prev := p.curr_tok; + if tokenizer.is_newline(prev) { + advance_token(p); + return true; + } + return false; +} + +skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool { + if .Insert_Semicolon not_in p.tok.flags { + return false; + } + + curr_pos := p.curr_tok.pos; + if tokenizer.is_newline(p.curr_tok) { + next := peek_token(p); + if curr_pos.line+1 >= next.pos.line { + #partial switch next.kind { + case .Open_Brace, .Else, .Where: + advance_token(p); + return true; + } + } + } + + return false; +} + next_token0 :: proc(p: ^Parser) -> bool { p.curr_tok = tokenizer.scan(&p.tok); @@ -280,7 +324,7 @@ expect_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> tokenizer.Token prev := p.curr_tok; if prev.kind != kind { e := tokenizer.to_string(kind); - g := tokenizer.to_string(prev.kind); + g := tokenizer.token_to_string(prev); error(p, prev.pos, "expected '%s', got '%s'", e, g); } advance_token(p); @@ -291,7 +335,7 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string) prev := p.curr_tok; if prev.kind != kind { e := tokenizer.to_string(kind); - g := tokenizer.to_string(prev.kind); + g := tokenizer.token_to_string(prev); error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g); } advance_token(p); @@ -303,7 +347,7 @@ expect_operator :: proc(p: ^Parser) -> tokenizer.Token { if prev.kind == .If || prev.kind == .When { // okay } else if !tokenizer.is_operator(prev.kind) { - g := tokenizer.to_string(prev.kind); + g := tokenizer.token_to_string(prev); error(p, prev.pos, "expected an operator, got '%s'", g); } advance_token(p); @@ -400,7 +444,16 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { } if node != nil { - if prev.pos.line != p.curr_tok.pos.line { + if .Insert_Semicolon in p.tok.flags { + #partial switch p.curr_tok.kind { + case .Close_Brace, .Close_Paren, .Else, .EOF: + return true; + } + + if is_semicolon_optional_for_node(p, node) { + return true; + } + } else if prev.pos.line != p.curr_tok.pos.line { if is_semicolon_optional_for_node(p, node) { return true; } @@ -420,7 +473,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { } } - error(p, prev.pos, "expected ';', got %s", tokenizer.to_string(p.curr_tok.kind)); + error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok)); return false; } @@ -493,6 +546,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, true); + skip_possible_newline_for_literal(p); } if allow_token(p, .Else) { @@ -568,6 +622,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, false); + skip_possible_newline_for_literal(p); } if allow_token(p, .Else) { @@ -629,6 +684,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_body(p); + skip_possible_newline_for_literal(p); } range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end); @@ -663,6 +719,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_body(p); + skip_possible_newline_for_literal(p); } @@ -840,6 +897,8 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind: attribute.elems = elems[:]; attribute.close = close.pos; + skip_possible_newline(p); + decl := parse_stmt(p); switch d in &decl.derived { case ast.Value_Decl: @@ -1028,10 +1087,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, false); + skip_possible_newline_for_literal(p); } if bad_stmt { - return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok)); + return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok)); } range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end); @@ -1206,7 +1266,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } tok := advance_token(p); - error(p, tok.pos, "expected a statement, got %s", tokenizer.to_string(tok.kind)); + error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok)); s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok)); return s; } @@ -2158,7 +2218,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { where_token: tokenizer.Token; where_clauses: []^ast.Expr; - if (p.curr_tok.kind == .Where) { + + skip_possible_newline_for_literal(p); + + if p.curr_tok.kind == .Where { where_token = expect_token(p, .Where); prev_level := p.expr_level; p.expr_level = -1; @@ -2334,7 +2397,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { where_token: tokenizer.Token; where_clauses: []^ast.Expr; - if (p.curr_tok.kind == .Where) { + + skip_possible_newline_for_literal(p); + + if p.curr_tok.kind == .Where { where_token = expect_token(p, .Where); where_prev_level := p.expr_level; p.expr_level = -1; @@ -2397,7 +2463,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { where_token: tokenizer.Token; where_clauses: []^ast.Expr; - if (p.curr_tok.kind == .Where) { + + skip_possible_newline_for_literal(p); + + if p.curr_tok.kind == .Where { where_token = expect_token(p, .Where); where_prev_level := p.expr_level; p.expr_level = -1; @@ -2730,7 +2799,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a case .Colon: interval = advance_token(p); is_slice_op = true; - if (p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF) { + if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF { indicies[1] = parse_expr(p, false); } } diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 54110cf02..997b4967d 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -283,6 +283,19 @@ tokens := [Token_Kind.COUNT]string { custom_keyword_tokens: []string; + +is_newline :: proc(tok: Token) -> bool { + return tok.kind == .Semicolon && tok.text == "\n"; +} + + +token_to_string :: proc(tok: Token) -> string { + if is_newline(tok) { + return "newline"; + } + return to_string(tok.kind); +} + to_string :: proc(kind: Token_Kind) -> string { if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT { return tokens[kind]; diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 132b63572..3df65e49b 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -1,22 +1,31 @@ package odin_tokenizer import "core:fmt" +import "core:unicode" import "core:unicode/utf8" Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any); +Flag :: enum { + Insert_Semicolon, +} +Flags :: distinct bit_set[Flag; u32]; + Tokenizer :: struct { // Immutable data path: string, src: []byte, err: Error_Handler, + flags: Flags, + // Tokenizing state ch: rune, offset: int, read_offset: int, line_offset: int, line_count: int, + insert_semicolon: bool, // Mutable data error_count: int, @@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte { } skip_whitespace :: proc(t: ^Tokenizer) { - for t.ch == ' ' || - t.ch == '\t' || - t.ch == '\n' || - t.ch == '\r' { - advance_rune(t); + for { + switch t.ch { + case ' ', '\t', '\r': + advance_rune(t); + case '\n': + if t.insert_semicolon { + return; + } + advance_rune(t); + case: + return; + } } } @@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool { return true; } } - // TODO(bill): Add unicode lookup tables - return false; + return unicode.is_letter(r); } is_digit :: proc(r: rune) -> bool { - // TODO(bill): Add unicode lookup tables - return '0' <= r && r <= '9'; + if '0' <= r && r <= '9' { + return true; + } + return unicode.is_digit(r); } @@ -491,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token { lit: string; pos := offset_to_pos(t, offset); + insert_semicolon := false; + switch ch := t.ch; true { case is_letter(ch): lit = scan_identifier(t); @@ -509,24 +528,39 @@ scan :: proc(t: ^Tokenizer) -> Token { break check_keyword; } } - if kind == .Ident && lit == "notin" { - kind = .Not_In; + + #partial switch kind { + case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return: + insert_semicolon = true; } } case '0' <= ch && ch <= '9': + insert_semicolon = true; kind, lit = scan_number(t, false); case: advance_rune(t); switch ch { case -1: kind = .EOF; + if t.insert_semicolon { + t.insert_semicolon = false; + kind = .Semicolon; + lit = "\n"; + } + case '\n': + t.insert_semicolon = false; + kind = .Semicolon; + lit = "\n"; case '"': + insert_semicolon = true; kind = .String; lit = scan_string(t); case '\'': + insert_semicolon = true; kind = .Rune; lit = scan_rune(t); case '`': + insert_semicolon = true; kind = .String; lit = scan_raw_string(t); case '=': @@ -540,10 +574,13 @@ scan :: proc(t: ^Tokenizer) -> Token { case '#': kind = .Hash; if t.ch == '!' { + insert_semicolon = t.insert_semicolon; kind = .Comment; lit = scan_comment(t); } - case '?': kind = .Question; + case '?': + insert_semicolon = true; + kind = .Question; case '@': kind = .At; case '$': kind = .Dollar; case '^': kind = .Pointer; @@ -562,6 +599,7 @@ scan :: proc(t: ^Tokenizer) -> Token { case '*': kind = switch2(t, .Mul, .Mul_Eq); case '/': if t.ch == '/' || t.ch == '*' { + insert_semicolon = t.insert_semicolon; kind = .Comment; lit = scan_comment(t); } else { @@ -604,11 +642,17 @@ scan :: proc(t: ^Tokenizer) -> Token { case ',': kind = .Comma; case ';': kind = .Semicolon; case '(': kind = .Open_Paren; - case ')': kind = .Close_Paren; + case ')': + insert_semicolon = true; + kind = .Close_Paren; case '[': kind = .Open_Bracket; - case ']': kind = .Close_Bracket; + case ']': + insert_semicolon = true; + kind = .Close_Bracket; case '{': kind = .Open_Brace; - case '}': kind = .Close_Brace; + case '}': + insert_semicolon = true; + kind = .Close_Brace; case '\\': kind = .Back_Slash; @@ -616,10 +660,15 @@ scan :: proc(t: ^Tokenizer) -> Token { if ch != utf8.RUNE_BOM { error(t, t.offset, "illegal character '%r': %d", ch, ch); } + insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info kind = .Invalid; } } + if .Insert_Semicolon in t.flags { + t.insert_semicolon = insert_semicolon; + } + if lit == "" { lit = string(t.src[offset : t.offset]); }