aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Jacquier <benoit.jacquier@gmail.com>2022-08-27 16:22:37 +0200
committerBenoit Jacquier <benoit.jacquier@gmail.com>2022-08-27 16:22:37 +0200
commit4e5337412a4e46fb26250f8adf1d019ddd8366c7 (patch)
treea49c22bd4c894a26ddf8da92c10894fb8e03383f
parent00f2e911a73e99b1283306272ff433984d90486c (diff)
parentc82d7d3d87c2dc77ce942b1cc450734baca3da14 (diff)
Merge remote-tracking branch 'origin/master'
-rwxr-xr-xbuild_odin.sh56
-rw-r--r--core/bufio/scanner.odin2
-rw-r--r--core/c/libc/math.odin2
-rw-r--r--core/container/priority_queue/priority_queue.odin1
-rw-r--r--core/crypto/README.md2
-rw-r--r--core/crypto/_fiat/README.md2
-rw-r--r--core/crypto/siphash/siphash.odin2
-rw-r--r--core/encoding/hxa/hxa.odin2
-rw-r--r--core/encoding/json/marshal.odin255
-rw-r--r--core/encoding/json/unmarshal.odin4
-rw-r--r--core/fmt/fmt.odin34
-rw-r--r--core/image/common.odin10
-rw-r--r--core/image/png/example.odin2
-rw-r--r--core/image/png/png.odin6
-rw-r--r--core/intrinsics/intrinsics.odin3
-rw-r--r--core/log/file_console_logger.odin4
-rw-r--r--core/log/log.odin11
-rw-r--r--core/log/log_allocator.odin107
-rw-r--r--core/math/big/prime.odin2
-rw-r--r--core/math/linalg/general.odin6
-rw-r--r--core/math/math.odin46
-rw-r--r--core/mem/alloc.odin94
-rw-r--r--core/mem/allocators.odin35
-rw-r--r--core/mem/mem.odin43
-rw-r--r--core/mem/raw.odin1
-rw-r--r--core/mem/virtual/arena_util.odin5
-rw-r--r--core/mem/virtual/growing_arena.odin15
-rw-r--r--core/mem/virtual/virtual_darwin.odin148
-rw-r--r--core/mem/virtual/virtual_linux.odin2
-rw-r--r--core/odin/ast/ast.odin38
-rw-r--r--core/odin/ast/clone.odin1
-rw-r--r--core/odin/doc-format/doc_format.odin2
-rw-r--r--core/odin/parser/parser.odin118
-rw-r--r--core/os/os2/user.odin20
-rw-r--r--core/os/os_linux.odin2
-rw-r--r--core/path/filepath/match.odin2
-rw-r--r--core/reflect/reflect.odin63
-rw-r--r--core/reflect/types.odin18
-rw-r--r--core/runtime/core.odin10
-rw-r--r--core/runtime/core_builtin.odin115
-rw-r--r--core/runtime/dynamic_array_internal.odin10
-rw-r--r--core/runtime/dynamic_map_internal.odin13
-rw-r--r--core/runtime/internal.odin100
-rw-r--r--core/runtime/print.odin3
-rw-r--r--core/runtime/procs.odin28
-rw-r--r--core/slice/slice.odin4
-rw-r--r--core/sort/sort.odin7
-rw-r--r--core/strconv/strconv.odin49
-rw-r--r--core/strings/intern.odin22
-rw-r--r--core/sync/futex_windows.odin26
-rw-r--r--core/sys/unix/syscalls_linux.odin6
-rw-r--r--core/sys/valgrind/callgrind.odin63
-rw-r--r--core/sys/valgrind/memcheck.odin169
-rw-r--r--core/sys/valgrind/valgrind.odin182
-rw-r--r--core/sys/wasm/wasi/wasi_api.odin430
-rw-r--r--core/sys/windows/kernel32.odin4
-rw-r--r--core/sys/windows/user32.odin35
-rw-r--r--core/sys/windows/wgl.odin2
-rw-r--r--core/sys/windows/wglext.odin147
-rw-r--r--core/sys/windows/window_messages.odin39
-rw-r--r--core/text/i18n/gettext.odin4
-rw-r--r--core/text/i18n/qt_linguist.odin10
-rw-r--r--src/build_settings.cpp110
-rw-r--r--src/check_builtin.cpp992
-rw-r--r--src/check_decl.cpp2
-rw-r--r--src/check_expr.cpp140
-rw-r--r--src/check_stmt.cpp59
-rw-r--r--src/check_type.cpp33
-rw-r--r--src/checker.cpp18
-rw-r--r--src/checker.hpp9
-rw-r--r--src/checker_builtin_procs.hpp12
-rw-r--r--src/docs_format.cpp2
-rw-r--r--src/docs_writer.cpp4
-rw-r--r--src/gb/gb.h2
-rw-r--r--src/llvm_abi.cpp28
-rw-r--r--src/llvm_backend.cpp11
-rw-r--r--src/llvm_backend.hpp33
-rw-r--r--src/llvm_backend_const.cpp15
-rw-r--r--src/llvm_backend_expr.cpp2317
-rw-r--r--src/llvm_backend_general.cpp422
-rw-r--r--src/llvm_backend_opt.cpp4
-rw-r--r--src/llvm_backend_proc.cpp317
-rw-r--r--src/llvm_backend_stmt.cpp27
-rw-r--r--src/llvm_backend_type.cpp95
-rw-r--r--src/llvm_backend_utility.cpp326
-rw-r--r--src/main.cpp8
-rw-r--r--src/microsoft_craziness.h355
-rw-r--r--src/parser.cpp76
-rw-r--r--src/parser.hpp11
-rw-r--r--src/string.cpp10
-rw-r--r--src/types.cpp121
-rw-r--r--vendor/directx/d3d11/d3d11.odin33
-rw-r--r--vendor/directx/dxgi/dxgi.odin8
-rw-r--r--vendor/glfw/bindings/bindings.odin7
-rw-r--r--vendor/glfw/constants.odin2
-rw-r--r--vendor/glfw/lib/darwin/libglfw3.abin0 -> 595160 bytes
-rw-r--r--vendor/glfw/lib/glfw3.dllbin226304 -> 216576 bytes
-rw-r--r--vendor/glfw/lib/glfw3.libbin650328 -> 634898 bytes
-rw-r--r--vendor/glfw/lib/glfw3_mt.libbin649750 -> 634316 bytes
-rw-r--r--vendor/glfw/lib/glfw3dll.libbin30306 -> 30306 bytes
-rw-r--r--vendor/glfw/native.odin2
-rw-r--r--vendor/raylib/raylib.odin10
-rw-r--r--vendor/stb/image/stb_image_resize.odin2
-rw-r--r--vendor/stb/lib/darwin/libstb_image.abin0 -> 55744 bytes
-rw-r--r--vendor/stb/lib/darwin/stb_image.abin0 -> 97544 bytes
-rw-r--r--vendor/stb/lib/darwin/stb_image_resize.abin0 -> 36824 bytes
-rw-r--r--vendor/stb/lib/darwin/stb_image_write.abin0 -> 32896 bytes
-rw-r--r--vendor/stb/lib/darwin/stb_rect_pack.abin0 -> 5064 bytes
-rw-r--r--vendor/stb/lib/darwin/stb_truetype.abin0 -> 67008 bytes
109 files changed, 4865 insertions, 3402 deletions
diff --git a/build_odin.sh b/build_odin.sh
index b00d33323..b8cd09c54 100755
--- a/build_odin.sh
+++ b/build_odin.sh
@@ -1,12 +1,20 @@
#!/usr/bin/env bash
set -eu
-GIT_SHA=$(git rev-parse --short HEAD)
+: ${CXX=clang++}
+: ${CPPFLAGS=}
+: ${CXXFLAGS=}
+: ${LDFLAGS=}
+: ${ODIN_VERSION=dev-$(date +"%Y-%m")}
+
+CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
+CXXFLAGS="$CXXFLAGS -std=c++14"
+LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
+
+GIT_SHA=$(git rev-parse --short HEAD || :)
+if [ "$GIT_SHA" ]; then CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""; fi
+
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
-LDFLAGS="-pthread -lm -lstdc++"
-CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\""
-CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
-CC=clang
OS=$(uname)
panic() {
@@ -18,13 +26,13 @@ version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
config_darwin() {
ARCH=$(uname -m)
- LLVM_CONFIG=llvm-config
+ : ${LLVM_CONFIG=llvm-config}
# allow for arm only llvm's with version 13
if [ ARCH == arm64 ]; then
MIN_LLVM_VERSION=("13.0.0")
else
- # allow for x86 / amd64 all llvm versions begining from 11
+ # allow for x86 / amd64 all llvm versions beginning from 11
MIN_LLVM_VERSION=("11.1.0")
fi
@@ -37,34 +45,38 @@ config_darwin() {
fi
LDFLAGS="$LDFLAGS -liconv -ldl"
- CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -lLLVM-C"
}
config_freebsd() {
- LLVM_CONFIG=/usr/local/bin/llvm-config11
+ : ${LLVM_CONFIG=/usr/local/bin/llvm-config11}
- CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
}
config_openbsd() {
- LLVM_CONFIG=/usr/local/bin/llvm-config
+ : ${LLVM_CONFIG=/usr/local/bin/llvm-config}
LDFLAGS="$LDFLAGS -liconv"
- CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
}
config_linux() {
- if which llvm-config > /dev/null 2>&1; then
- LLVM_CONFIG=llvm-config
- elif which llvm-config-11 > /dev/null 2>&1; then
- LLVM_CONFIG=llvm-config-11
- elif which llvm-config-11-64 > /dev/null 2>&1; then
- LLVM_CONFIG=llvm-config-11-64
- else
- panic "Unable to find LLVM-config"
+ : ${LLVM_CONFIG=}
+
+ if [ ! "$LLVM_CONFIG" ]; then
+ if which llvm-config > /dev/null 2>&1; then
+ LLVM_CONFIG=llvm-config
+ elif which llvm-config-11 > /dev/null 2>&1; then
+ LLVM_CONFIG=llvm-config-11
+ elif which llvm-config-11-64 > /dev/null 2>&1; then
+ LLVM_CONFIG=llvm-config-11-64
+ else
+ panic "Unable to find LLVM-config"
+ fi
fi
MIN_LLVM_VERSION=("11.0.0")
@@ -74,7 +86,7 @@ config_linux() {
fi
LDFLAGS="$LDFLAGS -ldl"
- CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
}
@@ -97,7 +109,7 @@ build_odin() {
esac
set -x
- $CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin
+ $CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
set +x
}
diff --git a/core/bufio/scanner.odin b/core/bufio/scanner.odin
index 86e6d8eb0..b9e620250 100644
--- a/core/bufio/scanner.odin
+++ b/core/bufio/scanner.odin
@@ -66,7 +66,7 @@ scanner_destroy :: proc(s: ^Scanner) {
}
-// Returns the first non-EOF error that was encounted by the scanner
+// Returns the first non-EOF error that was encountered by the scanner
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
switch s._err {
case .EOF, nil:
diff --git a/core/c/libc/math.odin b/core/c/libc/math.odin
index 6a7b81850..0a6ecc0c3 100644
--- a/core/c/libc/math.odin
+++ b/core/c/libc/math.odin
@@ -331,7 +331,7 @@ fmin :: proc{libc_fmin, libc_fminf}
fma :: proc{libc_fma, libc_fmaf}
// But retain the 'f' suffix-variant functions as well so they can be used,
-// a trick is used here where we use explicit procedrual overloading of one
+// a trick is used here where we use explicit procedural overloading of one
// procedure. This is done because the foreign block is marked @(private) and
// aliasing functions does not remove privateness from the entity.
acosf :: proc{libc_acosf}
diff --git a/core/container/priority_queue/priority_queue.odin b/core/container/priority_queue/priority_queue.odin
index e324287f3..0c5c4931d 100644
--- a/core/container/priority_queue/priority_queue.odin
+++ b/core/container/priority_queue/priority_queue.odin
@@ -85,7 +85,6 @@ _shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
j := j
queue := pq.queue[:]
- n := builtin.len(queue)
for 0 <= j {
i := (j-1)/2
if i == j || !pq.less(queue[j], queue[i]) {
diff --git a/core/crypto/README.md b/core/crypto/README.md
index ddcb12d81..dd594d5c2 100644
--- a/core/crypto/README.md
+++ b/core/crypto/README.md
@@ -81,7 +81,7 @@ The crypto package is not thread-safe at the moment. This may change in the futu
### Disclaimer
The algorithms were ported out of curiosity and due to interest in the field.
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
-Whereever we were able to find official test vectors, those were used to verify the implementation.
+Wherever we were able to find official test vectors, those were used to verify the implementation.
We do not recommend using them in a production environment, without any additional testing and/or verification.
### ToDo
diff --git a/core/crypto/_fiat/README.md b/core/crypto/_fiat/README.md
index cd510d442..4011a06c0 100644
--- a/core/crypto/_fiat/README.md
+++ b/core/crypto/_fiat/README.md
@@ -30,6 +30,6 @@ equivalence.
For the most part, alterations to the base fiat-crypto generated code was
kept to a minimum, to aid auditability. This results in a somewhat
-ideosyncratic style, and in some cases minor performance penalties.
+idiosyncratic style, and in some cases minor performance penalties.
[1]: https://github.com/mit-plv/fiat-crypto
diff --git a/core/crypto/siphash/siphash.odin b/core/crypto/siphash/siphash.odin
index a6c75f315..2d03829c2 100644
--- a/core/crypto/siphash/siphash.odin
+++ b/core/crypto/siphash/siphash.odin
@@ -233,7 +233,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
}
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
+ assert(ctx.is_initialized, "crypto/siphash: Context is not initialized")
ctx.last_block = len(data) / 8 * 8
ctx.buf = data
i := 0
diff --git a/core/encoding/hxa/hxa.odin b/core/encoding/hxa/hxa.odin
index f47661bad..9b24ede9c 100644
--- a/core/encoding/hxa/hxa.odin
+++ b/core/encoding/hxa/hxa.odin
@@ -107,7 +107,7 @@ Node :: struct {
/* Conventions */
/* ------------
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
-A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data.
+A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basically recomendations of how to store common data.
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
*/
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index 54fab44c6..8f7749aba 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -17,25 +17,54 @@ Marshal_Error :: union #shared_nil {
io.Error,
}
-marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
+// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
+Marshal_Options :: struct {
+ // output based on spec
+ spec: Specification,
+
+ // use line breaks & tab|spaces
+ pretty: bool,
+
+ // spacing
+ use_spaces: bool,
+ spaces: int,
+
+ // state
+ indentation: int,
+
+ // option to output uint in JSON5 & MJSON
+ write_uint_as_hex: bool,
+
+ // mjson output options
+ mjson_keys_use_quotes: bool,
+ mjson_keys_use_equal_sign: bool,
+
+ // mjson state
+ mjson_skipped_first_braces_start: bool,
+ mjson_skipped_first_braces_end: bool,
+}
+
+marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.builder_make(allocator)
defer if err != nil {
strings.builder_destroy(&b)
}
- marshal_to_builder(&b, v) or_return
+ opt := opt
+ marshal_to_builder(&b, v, &opt) or_return
if len(b.buf) != 0 {
data = b.buf[:]
}
+
return data, nil
}
-marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
- return marshal_to_writer(strings.to_writer(b), v)
+marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
+ return marshal_to_writer(strings.to_writer(b), v, opt)
}
-marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
+marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
if v == nil {
io.write_string(w, "null") or_return
return
@@ -56,6 +85,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case i16: u = u128(i)
case i32: u = u128(i)
case i64: u = u128(i)
+ case i128: u = u128(i)
case int: u = u128(i)
case u8: u = u128(i)
case u16: u = u128(i)
@@ -82,7 +112,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case u128be: u = u128(i)
}
- s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
+ s: string
+
+ // allow uints to be printed as hex
+ if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
+ switch i in a {
+ case u8, u16, u32, u64, u128:
+ s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
+
+ case:
+ s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
+ }
+ } else {
+ s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
+ }
+
io.write_string(w, s) or_return
@@ -147,6 +191,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case runtime.Type_Info_Multi_Pointer:
return .Unsupported_Type
+ case runtime.Type_Info_Soa_Pointer:
+ return .Unsupported_Type
+
case runtime.Type_Info_Procedure:
return .Unsupported_Type
@@ -166,52 +213,48 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
return .Unsupported_Type
case runtime.Type_Info_Array:
- io.write_byte(w, '[') or_return
+ opt_write_start(w, opt, '[') or_return
for i in 0..<info.count {
- if i > 0 { io.write_string(w, ", ") or_return }
-
+ opt_write_iteration(w, opt, i) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size)
- marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
+ marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
- io.write_byte(w, ']') or_return
+ opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Enumerated_Array:
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
- io.write_byte(w, '[') or_return
+ opt_write_start(w, opt, '[') or_return
for i in 0..<info.count {
- if i > 0 { io.write_string(w, ", ") or_return }
-
+ opt_write_iteration(w, opt, i) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size)
- marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
+ marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
- io.write_byte(w, ']') or_return
+ opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Dynamic_Array:
- io.write_byte(w, '[') or_return
+ opt_write_start(w, opt, '[') or_return
array := cast(^mem.Raw_Dynamic_Array)v.data
for i in 0..<array.len {
- if i > 0 { io.write_string(w, ", ") or_return }
-
+ opt_write_iteration(w, opt, i) or_return
data := uintptr(array.data) + uintptr(i*info.elem_size)
- marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
+ marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
- io.write_byte(w, ']') or_return
+ opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Slice:
- io.write_byte(w, '[') or_return
+ opt_write_start(w, opt, '[') or_return
slice := cast(^mem.Raw_Slice)v.data
for i in 0..<slice.len {
- if i > 0 { io.write_string(w, ", ") or_return }
-
+ opt_write_iteration(w, opt, i) or_return
data := uintptr(slice.data) + uintptr(i*info.elem_size)
- marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
+ marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
- io.write_byte(w, ']') or_return
+ opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Map:
m := (^mem.Raw_Map)(v.data)
+ opt_write_start(w, opt, '{') or_return
- io.write_byte(w, '{') or_return
if m != nil {
if info.generated_struct == nil {
return .Unsupported_Type
@@ -223,31 +266,50 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
entry_size := ed.elem_size
for i in 0..<entries.len {
- if i > 0 { io.write_string(w, ", ") or_return }
+ opt_write_iteration(w, opt, i) or_return
data := uintptr(entries.data) + uintptr(i*entry_size)
key := rawptr(data + entry_type.offsets[2])
value := rawptr(data + entry_type.offsets[3])
- marshal_to_writer(w, any{key, info.key.id}) or_return
- io.write_string(w, ": ") or_return
- marshal_to_writer(w, any{value, info.value.id}) or_return
+ // check for string type
+ {
+ v := any{key, info.key.id}
+ ti := runtime.type_info_base(type_info_of(v.id))
+ a := any{v.data, ti.id}
+ name: string
+
+ #partial switch info in ti.variant {
+ case runtime.Type_Info_String:
+ switch s in a {
+ case string: name = s
+ case cstring: name = string(s)
+ }
+ opt_write_key(w, opt, name) or_return
+
+ case: return .Unsupported_Type
+ }
+ }
+
+ marshal_to_writer(w, any{value, info.value.id}, opt) or_return
}
}
- io.write_byte(w, '}') or_return
+
+ opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Struct:
- io.write_byte(w, '{') or_return
+ opt_write_start(w, opt, '{') or_return
+
for name, i in info.names {
- if i > 0 { io.write_string(w, ", ") or_return }
- io.write_quoted_string(w, name) or_return
- io.write_string(w, ": ") or_return
+ opt_write_iteration(w, opt, i) or_return
+ opt_write_key(w, opt, name) or_return
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i])
- marshal_to_writer(w, any{data, id}) or_return
+ marshal_to_writer(w, any{data, id}, opt) or_return
}
- io.write_byte(w, '}') or_return
+
+ opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Union:
tag_ptr := uintptr(v.data) + info.tag_offset
@@ -270,11 +332,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
io.write_string(w, "null") or_return
} else {
id := info.variants[tag-1].id
- return marshal_to_writer(w, any{v.data, id})
+ return marshal_to_writer(w, any{v.data, id}, opt)
}
case runtime.Type_Info_Enum:
- return marshal_to_writer(w, any{v.data, info.base.id})
+ return marshal_to_writer(w, any{v.data, info.base.id}, opt)
case runtime.Type_Info_Bit_Set:
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
@@ -330,3 +392,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
return
}
+
+// write key as quoted string or with optional quotes in mjson
+opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
+ switch opt.spec {
+ case .JSON, .JSON5:
+ io.write_quoted_string(w, name) or_return
+ io.write_string(w, ": ") or_return
+
+ case .MJSON:
+ if opt.mjson_keys_use_quotes {
+ io.write_quoted_string(w, name) or_return
+ } else {
+ io.write_string(w, name) or_return
+ }
+
+ if opt.mjson_keys_use_equal_sign {
+ io.write_string(w, " = ") or_return
+ } else {
+ io.write_string(w, ": ") or_return
+ }
+ }
+
+ return
+}
+
+// insert start byte and increase indentation on pretty
+opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
+ // skip mjson starting braces
+ if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
+ opt.mjson_skipped_first_braces_start = true
+ return
+ }
+
+ io.write_byte(w, c) or_return
+ opt.indentation += 1
+
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ }
+
+ return
+}
+
+// insert comma seperation and write indentations
+opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
+ switch opt.spec {
+ case .JSON, .JSON5:
+ if iteration > 0 {
+ io.write_string(w, ", ") or_return
+
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ }
+ }
+
+ opt_write_indentation(w, opt) or_return
+
+ case .MJSON:
+ if iteration > 0 {
+ // on pretty no commas necessary
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ } else {
+ // comma seperation necessary for non pretty output!
+ io.write_string(w, ", ") or_return
+ }
+ }
+
+ opt_write_indentation(w, opt) or_return
+ }
+
+ return
+}
+
+// decrease indent, write spacing and insert end byte
+opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
+ if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
+ if opt.indentation == 0 {
+ opt.mjson_skipped_first_braces_end = true
+ return
+ }
+ }
+
+ opt.indentation -= 1
+
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ opt_write_indentation(w, opt) or_return
+ }
+
+ io.write_byte(w, c) or_return
+ return
+}
+
+// writes current indentation level based on options
+opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) {
+ if !opt.pretty {
+ return
+ }
+
+ if opt.use_spaces {
+ spaces := opt.spaces == 0 ? 4 : opt.spaces
+ for _ in 0..<opt.indentation * spaces {
+ io.write_byte(w, ' ') or_return
+ }
+ } else {
+ for _ in 0..<opt.indentation {
+ io.write_byte(w, '\t') or_return
+ }
+ }
+
+ return
+}
diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin
index 2ff268a21..97d2421d4 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
if end_token == .Close_Brace {
- assert(expect_token(p, .Open_Brace) == nil)
+ unmarshal_expect_token(p, .Open_Brace)
}
v := v
@@ -473,7 +473,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
if end_token == .Close_Brace {
- assert(expect_token(p, .Close_Brace) == nil)
+ unmarshal_expect_token(p, .Close_Brace)
}
return
}
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 4db140afa..7429a093d 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -1031,6 +1031,15 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
}
}
+fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
+ io.write_string(fi.writer, "#soa{data=0x", &fi.n)
+ _fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
+ io.write_string(fi.writer, ", index=", &fi.n)
+ _fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
+ io.write_string(fi.writer, "}", &fi.n)
+}
+
+
enum_value_to_string :: proc(val: any) -> (string, bool) {
v := val
v.id = runtime.typeid_base(v.id)
@@ -1867,6 +1876,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_pointer(fi, ptr, verb)
}
+ case runtime.Type_Info_Soa_Pointer:
+ ptr := (^runtime.Raw_Soa_Pointer)(v.data)^
+ fmt_soa_pointer(fi, ptr, verb)
+
case runtime.Type_Info_Multi_Pointer:
ptr := (^rawptr)(v.data)^
if ptr == nil {
@@ -2046,18 +2059,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
entry_size := ed.elem_size
-
+ /*
+ NOTE: The layout of a `map` is as follows:
+
+ map[Key]Value
+
+ ## Internal Layout
+ struct {
+ hashes: []int,
+ entries: [dynamic]struct{
+ hash: uintptr,
+ next: int,
+ key: Key,
+ value: Value,
+ },
+ }
+ */
for i in 0..<entries.len {
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(entries.data) + uintptr(i*entry_size)
- key := data + entry_type.offsets[2]
+ key := data + entry_type.offsets[2] // key: Key
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
io.write_string(fi.writer, "=", &fi.n)
- value := data + entry_type.offsets[3]
+ value := data + entry_type.offsets[3] // value: Value
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
}
}
diff --git a/core/image/common.odin b/core/image/common.odin
index beb3f93ee..a627fa68b 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -475,7 +475,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
}
// Does the image have 1 or 2 channels, a valid bit depth (8 or 16),
-// Is the pointer valid, are the dimenions valid?
+// Is the pointer valid, are the dimensions valid?
is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
// Were we actually given a valid image?
if img == nil {
@@ -495,7 +495,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
// This returns 0 if any of the inputs is zero.
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
- // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
+ // If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
return false
}
@@ -504,7 +504,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
}
// Does the image have 3 or 4 channels, a valid bit depth (8 or 16),
-// Is the pointer valid, are the dimenions valid?
+// Is the pointer valid, are the dimensions valid?
is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
// Were we actually given a valid image?
if img == nil {
@@ -524,7 +524,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
// This returns 0 if any of the inputs is zero.
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
- // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
+ // If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
return false
}
@@ -533,7 +533,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
}
// Does the image have 1..4 channels, a valid bit depth (8 or 16),
-// Is the pointer valid, are the dimenions valid?
+// Is the pointer valid, are the dimensions valid?
is_valid_image :: proc(img: ^Image) -> (ok: bool) {
// Were we actually given a valid image?
if img == nil {
diff --git a/core/image/png/example.odin b/core/image/png/example.odin
index 17436c260..c1e2f0c73 100644
--- a/core/image/png/example.odin
+++ b/core/image/png/example.odin
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
defer close(fd)
write_string(fd,
- fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
+ fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
)
if channels == 3 {
diff --git a/core/image/png/png.odin b/core/image/png/png.odin
index 35fdb58d8..3faa39c83 100644
--- a/core/image/png/png.odin
+++ b/core/image/png/png.odin
@@ -1002,7 +1002,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
o16 = o16[out_image_channels:]
}
case:
- unreachable("We should never seen # channels other than 1-4 inclusive.")
+ panic("We should never seen # channels other than 1-4 inclusive.")
}
img.pixels = t
@@ -1195,7 +1195,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
o = o[out_image_channels:]
}
case:
- unreachable("We should never seen # channels other than 1-4 inclusive.")
+ panic("We should never seen # channels other than 1-4 inclusive.")
}
img.pixels = t
@@ -1206,7 +1206,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw
returns will likely bypass this processing pipeline.
*/
- unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here.")
+ panic("We should never see bit depths other than 8, 16 and 'Paletted' here.")
}
return img, nil
diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin
index 22b5d953d..0b682fbc3 100644
--- a/core/intrinsics/intrinsics.odin
+++ b/core/intrinsics/intrinsics.odin
@@ -295,6 +295,9 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
objc_find_class :: proc($name: string) -> objc_Class ---
objc_register_class :: proc($name: string) -> objc_Class ---
+
+valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
+
// Internal compiler use only
__entry_point :: proc() --- \ No newline at end of file
diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin
index 7f0d3b07a..bf537a161 100644
--- a/core/log/file_console_logger.odin
+++ b/core/log/file_console_logger.odin
@@ -56,7 +56,7 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg
return Logger{file_console_logger_proc, data, lowest, opt}
}
-destroy_console_logger :: proc(log: ^Logger) {
+destroy_console_logger :: proc(log: Logger) {
free(log.data)
}
@@ -95,7 +95,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
fmt.sbprintf(&buf, "[%s] ", data.ident)
}
//TODO(Hoej): When we have better atomics and such, make this thread-safe
- fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text)
+ fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
}
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
diff --git a/core/log/log.odin b/core/log/log.odin
index d34a135cc..a699247b8 100644
--- a/core/log/log.odin
+++ b/core/log/log.odin
@@ -6,7 +6,6 @@ import "core:fmt"
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
-Level :: runtime.Logger_Level
/*
Logger_Level :: enum {
Debug = 0,
@@ -16,8 +15,8 @@ Logger_Level :: enum {
Fatal = 40,
}
*/
+Level :: runtime.Logger_Level
-Option :: runtime.Logger_Option
/*
Option :: enum {
Level,
@@ -30,11 +29,12 @@ Option :: enum {
Terminal_Color
}
*/
+Option :: runtime.Logger_Option
-Options :: runtime.Logger_Options
/*
Options :: bit_set[Option];
*/
+Options :: runtime.Logger_Options
Full_Timestamp_Opts :: Options{
.Date,
@@ -52,12 +52,11 @@ Location_File_Opts :: Options{
}
-Logger_Proc :: runtime.Logger_Proc
/*
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
*/
+Logger_Proc :: runtime.Logger_Proc
-Logger :: runtime.Logger
/*
Logger :: struct {
procedure: Logger_Proc,
@@ -66,6 +65,7 @@ Logger :: struct {
options: Logger_Options,
}
*/
+Logger :: runtime.Logger
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
// Do nothing
@@ -75,7 +75,6 @@ nil_logger :: proc() -> Logger {
return Logger{nil_logger_proc, nil, Level.Debug, nil}
}
-// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location)
}
diff --git a/core/log/log_allocator.odin b/core/log/log_allocator.odin
new file mode 100644
index 000000000..997bd1a68
--- /dev/null
+++ b/core/log/log_allocator.odin
@@ -0,0 +1,107 @@
+package log
+
+import "core:runtime"
+
+Log_Allocator :: struct {
+ allocator: runtime.Allocator,
+ level: Level,
+ prefix: string,
+ locked: bool,
+}
+
+log_allocator_init :: proc(la: ^Log_Allocator, level: Level, allocator := context.allocator, prefix := "") {
+ la.allocator = allocator
+ la.level = level
+ la.prefix = prefix
+ la.locked = false
+}
+
+
+log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator {
+ return runtime.Allocator{
+ procedure = log_allocator_proc,
+ data = la,
+ }
+}
+
+log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
+ size, alignment: int,
+ old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
+ la := (^Log_Allocator)(allocator_data)
+
+ padding := " " if la.prefix != "" else ""
+
+ if !la.locked {
+ la.locked = true
+ defer la.locked = false
+
+ switch mode {
+ case .Alloc:
+ logf(
+ level=la.level,
+ fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
+ args = {la.prefix, padding, size, alignment},
+ location = location,
+ )
+ case .Free:
+ if old_size != 0 {
+ logf(
+ level=la.level,
+ fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
+ args = {la.prefix, padding, old_memory, old_size},
+ location = location,
+ )
+ } else {
+ logf(
+ level=la.level,
+ fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
+ args = {la.prefix, padding, old_memory},
+ location = location,
+ )
+ }
+ case .Free_All:
+ logf(
+ level=la.level,
+ fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)",
+ args = {la.prefix, padding},
+ location = location,
+ )
+ case .Resize:
+ logf(
+ level=la.level,
+ fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
+ args = {la.prefix, padding, old_memory, old_size, size, alignment},
+ location = location,
+ )
+ case .Query_Features:
+ logf(
+ level=la.level,
+ fmt_str = "%s%ALLOCATOR(mode=.Query_Features)",
+ args = {la.prefix, padding},
+ location = location,
+ )
+ case .Query_Info:
+ logf(
+ level=la.level,
+ fmt_str = "%s%ALLOCATOR(mode=.Query_Info)",
+ args = {la.prefix, padding},
+ location = location,
+ )
+ }
+ }
+
+ data, err := la.allocator.procedure(la.allocator.data, mode, size, alignment, old_memory, old_size, location)
+ if !la.locked {
+ la.locked = true
+ defer la.locked = false
+ if err != nil {
+ logf(
+ level=la.level,
+ fmt_str = "%s%ALLOCATOR ERROR=%v",
+ args = {la.prefix, padding, error},
+ location = location,
+ )
+ }
+ }
+ return data, err
+} \ No newline at end of file
diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin
index 3cce69675..6f972937a 100644
--- a/core/math/big/prime.odin
+++ b/core/math/big/prime.odin
@@ -449,7 +449,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
in the loop is non-zero, although very low.
-- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas.
- If the BPSW test and/or the addtional Frobenious test have been
+ If the BPSW test and/or the additional Frobenious test have been
performed instead of just the Miller-Rabin test with the bases 2 and 3,
a single extra test should suffice, so such a very unlikely event will not do much harm.
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index 9f22fa45e..c11151b25 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -94,7 +94,7 @@ quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3}
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
-vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
+vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
return v / length(v)
}
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
@@ -102,7 +102,7 @@ quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
}
normalize :: proc{vector_normalize, quaternion_normalize}
-vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
+vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
m := length(v)
return 0 if m == 0 else v/m
}
@@ -113,7 +113,7 @@ quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
normalize0 :: proc{vector_normalize0, quaternion_normalize0}
-vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
+vector_length :: proc(v: $T/[$N]$E) -> E where IS_FLOAT(E) {
return math.sqrt(dot(v, v))
}
diff --git a/core/math/math.odin b/core/math/math.odin
index 88cba965a..5f9c737ab 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -185,16 +185,23 @@ log :: proc{
log_f64, log_f64le, log_f64be,
}
-log2_f16 :: logb_f16
-log2_f16le :: logb_f16le
-log2_f16be :: logb_f16be
-log2_f32 :: logb_f32
-log2_f32le :: logb_f32le
-log2_f32be :: logb_f32be
-log2_f64 :: logb_f64
-log2_f64le :: logb_f64le
-log2_f64be :: logb_f64be
-log2 :: logb
+log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
+log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
+log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
+
+log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
+log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
+log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
+
+log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
+log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) }
+log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) }
+
+log2 :: proc{
+ log2_f16, log2_f16le, log2_f16be,
+ log2_f32, log2_f32le, log2_f32be,
+ log2_f64, log2_f64le, log2_f64be,
+}
log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
@@ -607,6 +614,25 @@ floor_mod :: proc "contextless" (x, y: $T) -> T
return r
}
+divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
+ where intrinsics.type_is_integer(T) {
+ div = x / y
+ mod = x % y
+ return
+}
+
+floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
+ where intrinsics.type_is_integer(T) {
+ div = x / y
+ mod = x % y
+ if (div > 0 && y < 0) || (mod < 0 && y > 0) {
+ div -= 1
+ mod += y
+ }
+ return
+}
+
+
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
shift :: F16_SHIFT
mask :: F16_MASK
diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin
index 7416ebdb7..54004d333 100644
--- a/core/mem/alloc.odin
+++ b/core/mem/alloc.odin
@@ -61,114 +61,38 @@ DEFAULT_PAGE_SIZE ::
4 * 1024
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
- if size == 0 {
- return nil
- }
- if allocator.procedure == nil {
- return nil
- }
- data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
- _ = err
+ data, _ := runtime.mem_alloc(size, alignment, allocator, loc)
return raw_data(data)
}
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
- if size == 0 {
- return nil, nil
- }
- if allocator.procedure == nil {
- return nil, nil
- }
- return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
+ return runtime.mem_alloc(size, alignment, allocator, loc)
}
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
- if ptr == nil {
- return nil
- }
- if allocator.procedure == nil {
- return nil
- }
- _, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
- return err
+ return runtime.mem_free(ptr, allocator, loc)
}
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
- if bytes == nil {
- return nil
- }
- if allocator.procedure == nil {
- return nil
- }
- _, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc)
- return err
+ return runtime.mem_free_bytes(bytes, allocator, loc)
}
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
- if allocator.procedure != nil {
- _, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc)
- return err
- }
- return nil
+ return runtime.mem_free_all(allocator, loc)
}
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
- if allocator.procedure == nil {
- return nil
- }
- if new_size == 0 {
- if ptr != nil {
- allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
- }
- return nil
- } else if ptr == nil {
- _, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
- _ = err
- return nil
- }
- data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
- if err == .Mode_Not_Implemented {
- data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
- if err != nil {
- return nil
- }
- runtime.copy(data, byte_slice(ptr, old_size))
- _, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
- return raw_data(data)
- }
+ data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
return raw_data(data)
}
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
- if allocator.procedure == nil {
- return nil, nil
- }
- ptr := raw_data(old_data)
- old_size := len(old_data)
- if new_size == 0 {
- if ptr != nil {
- _, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
- return nil, err
- }
- return nil, nil
- } else if ptr == nil {
- return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
- }
- data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
- if err == .Mode_Not_Implemented {
- data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
- if err != nil {
- return data, err
- }
- runtime.copy(data, old_data)
- _, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
- }
- return data, err
+ return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc)
}
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
if allocator.procedure != nil {
- allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc)
+ allocator.procedure(allocator.data, .Query_Features, 0, 0, &set, 0, loc)
return set
}
return nil
@@ -177,7 +101,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
props.pointer = pointer
if allocator.procedure != nil {
- allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc)
+ allocator.procedure(allocator.data, .Query_Info, 0, 0, &props, 0, loc)
}
return
}
diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin
index e2a0cc0fc..0d52d2599 100644
--- a/core/mem/allocators.odin
+++ b/core/mem/allocators.odin
@@ -31,6 +31,14 @@ Arena_Temp_Memory :: struct {
}
+arena_init :: proc(a: ^Arena, data: []byte) {
+ a.data = data
+ a.offset = 0
+ a.peak_used = 0
+ a.temp_count = 0
+}
+
+@(deprecated="prefer 'mem.arena_init'")
init_arena :: proc(a: ^Arena, data: []byte) {
a.data = data
a.offset = 0
@@ -293,6 +301,14 @@ Stack :: struct {
peak_used: int,
}
+stack_init :: proc(s: ^Stack, data: []byte) {
+ s.data = data
+ s.prev_offset = 0
+ s.curr_offset = 0
+ s.peak_used = 0
+}
+
+@(deprecated="prefer 'mem.stack_init'")
init_stack :: proc(s: ^Stack, data: []byte) {
s.data = data
s.prev_offset = 0
@@ -445,27 +461,34 @@ Small_Stack_Allocation_Header :: struct {
// Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order
Small_Stack :: struct {
- data: []byte,
- offset: int,
+ data: []byte,
+ offset: int,
peak_used: int,
}
+small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
+ s.data = data
+ s.offset = 0
+ s.peak_used = 0
+}
+
+@(deprecated="prefer 'small_stack_init'")
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
- s.data = data
- s.offset = 0
+ s.data = data
+ s.offset = 0
s.peak_used = 0
}
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
return Allocator{
procedure = small_stack_allocator_proc,
- data = stack,
+ data = stack,
}
}
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
- old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
+ old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
s := cast(^Small_Stack)allocator_data
if s.data == nil {
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index 7295a2afa..f7be69adc 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -54,48 +54,7 @@ compare :: proc "contextless" (a, b: []byte) -> int {
}
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
- switch {
- case a == b:
- return 0
- case a == nil:
- return -1
- case b == nil:
- return -1
- case n == 0:
- return 0
- }
-
- x := slice_ptr(a, n)
- y := slice_ptr(b, n)
-
- SU :: size_of(uintptr)
- fast := n/SU + 1
- offset := (fast-1)*SU
- curr_block := 0
- if n < SU {
- fast = 0
- }
-
- la := slice_ptr((^uintptr)(a), fast)
- lb := slice_ptr((^uintptr)(b), fast)
-
- for /**/; curr_block < fast; curr_block += 1 {
- if la[curr_block] ~ lb[curr_block] != 0 {
- for pos := curr_block*SU; pos < n; pos += 1 {
- if x[pos] ~ y[pos] != 0 {
- return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1
- }
- }
- }
- }
-
- for /**/; offset < n; offset += 1 {
- if x[offset] ~ y[offset] != 0 {
- return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1
- }
- }
-
- return 0
+ return runtime.memory_compare(a, b, n)
}
check_zero :: proc(data: []byte) -> bool {
diff --git a/core/mem/raw.odin b/core/mem/raw.odin
index 2bce2d7aa..8322ace30 100644
--- a/core/mem/raw.odin
+++ b/core/mem/raw.odin
@@ -8,6 +8,7 @@ Raw_Cstring :: runtime.Raw_Cstring
Raw_Slice :: runtime.Raw_Slice
Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
Raw_Map :: runtime.Raw_Map
+Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer
Raw_Complex64 :: struct {real, imag: f32}
Raw_Complex128 :: struct {real, imag: f64}
diff --git a/core/mem/virtual/arena_util.odin b/core/mem/virtual/arena_util.odin
index 408566299..0e152db7a 100644
--- a/core/mem/virtual/arena_util.odin
+++ b/core/mem/virtual/arena_util.odin
@@ -1,5 +1,10 @@
package mem_virtual
+arena_init :: proc{
+ static_arena_init,
+ growing_arena_init,
+}
+
arena_temp_begin :: proc{
static_arena_temp_begin,
growing_arena_temp_begin,
diff --git a/core/mem/virtual/growing_arena.odin b/core/mem/virtual/growing_arena.odin
index 9a4272a1e..f1067071b 100644
--- a/core/mem/virtual/growing_arena.odin
+++ b/core/mem/virtual/growing_arena.odin
@@ -13,6 +13,13 @@ Growing_Arena :: struct {
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
+growing_arena_init :: proc(arena: ^Static_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
+ arena.block = memory_block_alloc(0, reserved, {}) or_return
+ arena.total_used = 0
+ arena.total_reserved = arena.block.reserved
+ return
+}
+
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
alignment_offset := uint(0)
@@ -37,7 +44,7 @@ growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int
block_size := max(size, arena.minimum_block_size)
- new_block := memory_block_alloc(block_size, block_size, {}) or_return
+ new_block := memory_block_alloc(size, block_size, {}) or_return
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_reserved += new_block.reserved
@@ -95,9 +102,9 @@ growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
}
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
- size, alignment: int,
- old_memory: rawptr, old_size: int,
- location := #caller_location) -> (data: []byte, err: Allocator_Error) {
+ size, alignment: int,
+ old_memory: rawptr, old_size: int,
+ location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Growing_Arena)(allocator_data)
switch mode {
diff --git a/core/mem/virtual/virtual_darwin.odin b/core/mem/virtual/virtual_darwin.odin
new file mode 100644
index 000000000..5505149b0
--- /dev/null
+++ b/core/mem/virtual/virtual_darwin.odin
@@ -0,0 +1,148 @@
+//+build darwin
+//+private
+package mem_virtual
+
+foreign import libc "System.framework"
+import "core:c"
+
+PROT_NONE :: 0x0 /* [MC2] no permissions */
+PROT_READ :: 0x1 /* [MC2] pages can be read */
+PROT_WRITE :: 0x2 /* [MC2] pages can be written */
+PROT_EXEC :: 0x4 /* [MC2] pages can be executed */
+
+// Sharing options
+MAP_SHARED :: 0x1 /* [MF|SHM] share changes */
+MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */
+
+// Other flags
+MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
+MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */
+MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */
+MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */
+MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */
+MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
+MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
+MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
+
+// Mapping type
+MAP_FILE :: 0x0000 /* map from file (default) */
+MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
+
+
+/*
+ * The MAP_RESILIENT_* flags can be used when the caller wants to map some
+ * possibly unreliable memory and be able to access it safely, possibly
+ * getting the wrong contents rather than raising any exception.
+ * For safety reasons, such mappings have to be read-only (PROT_READ access
+ * only).
+ *
+ * MAP_RESILIENT_CODESIGN:
+ * accessing this mapping will not generate code-signing violations,
+ * even if the contents are tainted.
+ * MAP_RESILIENT_MEDIA:
+ * accessing this mapping will not generate an exception if the contents
+ * are not available (unreachable removable or remote media, access beyond
+ * end-of-file, ...). Missing contents will be replaced with zeroes.
+ */
+MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */
+MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */
+
+MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
+
+// Flags used to support translated processes.
+MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */
+MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */
+
+// Process memory locking
+MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */
+MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */
+
+MADV_NORMAL :: 0 /* [MC1] no further special treatment */
+MADV_RANDOM :: 1 /* [MC1] expect random page refs */
+MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */
+MADV_WILLNEED :: 3 /* [MC1] will need these pages */
+MADV_DONTNEED :: 4 /* [MC1] dont need these pages */
+MADV_FREE :: 5 /* pages unneeded, discard contents */
+MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */
+MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */
+MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */
+MADV_CAN_REUSE :: 9
+MADV_PAGEOUT :: 10 /* page out now (internal only) */
+
+// msync() flags
+MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */
+MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */
+MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */
+MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */
+MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */
+
+// Return bits from mincore
+MINCORE_INCORE :: 0x1 /* Page is incore */
+MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */
+MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */
+MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */
+MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */
+MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */
+MINCORE_COPIED :: 0x40 /* Page has been copied */
+MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */
+
+// Allocation failure result
+MAP_FAILED : rawptr = rawptr(~uintptr(0))
+
+foreign libc {
+ @(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int ---
+ @(link_name="munlockall") _munlockall :: proc() -> c.int ---
+ @(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
+ @(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr ---
+ @(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int ---
+ @(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int ---
+ @(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
+ @(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int ---
+ @(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int ---
+ @(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int ---
+ @(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
+ @(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
+ @(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int ---
+ @(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int ---
+}
+
+
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
+ result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
+ if result == MAP_FAILED {
+ return nil, .Out_Of_Memory
+ }
+ return ([^]byte)(uintptr(result))[:size], nil
+}
+
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
+ result := _mprotect(data, size, PROT_READ|PROT_WRITE)
+ if result != 0 {
+ return .Out_Of_Memory
+ }
+ return nil
+}
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
+ _mprotect(data, size, PROT_NONE)
+ _madvise(data, size, MADV_FREE)
+}
+_release :: proc "contextless" (data: rawptr, size: uint) {
+ _munmap(data, size)
+}
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+ pflags: c.int
+ pflags = PROT_NONE
+ if .Read in flags { pflags |= PROT_READ }
+ if .Write in flags { pflags |= PROT_WRITE }
+ if .Execute in flags { pflags |= PROT_EXEC }
+ err := _mprotect(data, size, pflags)
+ return err != 0
+}
+
+
+_platform_memory_init :: proc() {
+ DEFAULT_PAGE_SIZE = 4096
+
+ // is power of two
+ assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
+}
diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin
index 2f6fbdd01..0f376312f 100644
--- a/core/mem/virtual/virtual_linux.odin
+++ b/core/mem/virtual/virtual_linux.odin
@@ -48,7 +48,7 @@ munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
}
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
- res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
+ res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))
return c.int(res)
}
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index f4aa67446..c8f73a31b 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -552,13 +552,20 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
return
}
+Field_Flags :: distinct bit_set[Field_Flag]
+
Field_Flag :: enum {
+ Invalid,
+ Unknown,
+
Ellipsis,
Using,
No_Alias,
C_Vararg,
Auto_Cast,
Any_Int,
+ Subtype,
+ By_Ptr,
Results,
Tags,
@@ -566,11 +573,38 @@ Field_Flag :: enum {
Typeid_Token,
}
-Field_Flags :: distinct bit_set[Field_Flag]
+field_flag_strings := [Field_Flag]string{
+ .Invalid = "",
+ .Unknown = "",
+
+ .Ellipsis = "..",
+ .Using = "using",
+ .No_Alias = "#no_alias",
+ .C_Vararg = "#c_vararg",
+ .Auto_Cast = "auto_cast",
+ .Any_Int = "#any_int",
+ .Subtype = "#subtype",
+ .By_Ptr = "#by_ptr",
+
+ .Results = "results",
+ .Tags = "field tag",
+ .Default_Parameters = "default parameters",
+ .Typeid_Token = "typeid",
+}
+
+field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
+ {"no_alias", .No_Alias},
+ {"c_vararg", .C_Vararg},
+ {"any_int", .Any_Int},
+ {"subtype", .Subtype},
+ {"by_ptr", .By_Ptr},
+}
+
Field_Flags_Struct :: Field_Flags{
.Using,
.Tags,
+ .Subtype,
}
Field_Flags_Record_Poly_Params :: Field_Flags{
.Typeid_Token,
@@ -583,6 +617,7 @@ Field_Flags_Signature :: Field_Flags{
.C_Vararg,
.Auto_Cast,
.Any_Int,
+ .By_Ptr,
.Default_Parameters,
}
@@ -665,6 +700,7 @@ Proc_Type :: struct {
Pointer_Type :: struct {
using node: Expr,
+ tag: ^Expr,
pointer: tokenizer.Pos,
elem: ^Expr,
}
diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin
index 400c064f5..5ec6bc335 100644
--- a/core/odin/ast/clone.odin
+++ b/core/odin/ast/clone.odin
@@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
r.results = auto_cast clone(r.results)
case ^Pointer_Type:
r.elem = clone(r.elem)
+ r.tag = clone(r.tag)
case ^Multi_Pointer_Type:
r.elem = clone(r.elem)
case ^Array_Type:
diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin
index 62682004d..895fcf70d 100644
--- a/core/odin/doc-format/doc_format.odin
+++ b/core/odin/doc-format/doc_format.odin
@@ -186,6 +186,7 @@ Type_Kind :: enum u32le {
Relative_Slice = 21,
Multi_Pointer = 22,
Matrix = 23,
+ Soa_Pointer = 24,
}
Type_Elems_Cap :: 4
@@ -245,6 +246,7 @@ Type :: struct {
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
// .Multi_Pointer - 1 type: 0=element
// .Matrix - 1 type: 0=element
+ // .Soa_Pointer - 1 type: 0=element
types: Array(Type_Index),
// Used by:
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 52ecb4781..bd1422da3 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -245,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
return
}
skip_possible_newline :: proc(p: ^Parser) -> bool {
- if .Optional_Semicolons not_in p.flags {
- return false
- }
-
- prev := p.curr_tok
- if tokenizer.is_newline(prev) {
+ if tokenizer.is_newline(p.curr_tok) {
advance_token(p)
return true
}
@@ -1611,20 +1606,6 @@ new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.E
return field
}
-
-Field_Prefix :: enum {
- Invalid,
- Unknown,
-
- Using,
- No_Alias,
- C_Vararg,
- Auto_Cast,
- Any_Int,
-}
-
-Field_Prefixes :: distinct bit_set[Field_Prefix]
-
Expr_And_Flags :: struct {
expr: ^ast.Expr,
flags: ast.Field_Flags,
@@ -1666,7 +1647,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
return idents[:]
}
-is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
+is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag {
#partial switch p.curr_tok.kind {
case .EOF:
return .Invalid
@@ -1677,17 +1658,15 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
advance_token(p)
return .Auto_Cast
case .Hash:
+ tok: tokenizer.Token
advance_token(p)
- defer advance_token(p)
- #partial switch p.curr_tok.kind {
- case .Ident:
- switch p.curr_tok.text {
- case "no_alias":
- return .No_Alias
- case "c_vararg":
- return .C_Vararg
- case "any_int":
- return .Any_Int
+ tok = p.curr_tok
+ advance_token(p)
+ if tok.kind == .Ident {
+ for kf in ast.field_hash_flag_strings {
+ if kf.key == tok.text {
+ return kf.flag
+ }
}
}
return .Unknown
@@ -1695,8 +1674,8 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
return .Invalid
}
-parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
- counts: [len(Field_Prefix)]int
+parse_field_prefixes :: proc(p: ^Parser) -> (flags: ast.Field_Flags) {
+ counts: [len(ast.Field_Flag)]int
for {
kind := is_token_field_prefix(p)
@@ -1712,31 +1691,17 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
counts[kind] += 1
}
- flags: ast.Field_Flags
-
- for kind in Field_Prefix {
+ for kind in ast.Field_Flag {
count := counts[kind]
- switch kind {
- case .Invalid, .Unknown: // Ignore
- case .Using:
- if count > 1 { error(p, p.curr_tok.pos, "multiple 'using' in this field list") }
- if count > 0 { flags += {.Using} }
- case .No_Alias:
- if count > 1 { error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list") }
- if count > 0 { flags += {.No_Alias} }
- case .C_Vararg:
- if count > 1 { error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list") }
- if count > 0 { flags += {.C_Vararg} }
- case .Auto_Cast:
- if count > 1 { error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list") }
- if count > 0 { flags += {.Auto_Cast} }
- case .Any_Int:
- if count > 1 { error(p, p.curr_tok.pos, "multiple '#any_int' in this field list") }
- if count > 0 { flags += {.Any_Int} }
+ if kind == .Invalid || kind == .Unknown {
+ // Ignore
+ } else {
+ if count > 1 { error(p, p.curr_tok.pos, "multiple '%s' in this field list", ast.field_flag_strings[kind]) }
+ if count > 0 { flags += {kind} }
}
}
- return flags
+ return
}
check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) {
@@ -1748,19 +1713,13 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
for flag in ast.Field_Flag {
if flag not_in allowed_flags && flag in flags {
- switch flag {
- case .Using:
- error(p, p.curr_tok.pos, "'using' is not allowed within this field list")
- case .No_Alias:
- error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list")
- case .C_Vararg:
- error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list")
- case .Auto_Cast:
- error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list")
- case .Any_Int:
- error(p, p.curr_tok.pos, "'#any_int' is not allowed within this field list")
+ #partial switch flag {
+ case .Unknown, .Invalid:
+ // ignore
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
panic("Impossible prefixes")
+ case:
+ error(p, p.curr_tok.pos, "'%s' is not allowed within this field list", ast.field_flag_strings[flag])
}
flags -= {flag}
}
@@ -2271,7 +2230,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
return parse_call_expr(p, bd)
- case "soa", "simd":
+ case "soa":
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
bd.tok = tok
bd.name = name.text
@@ -2280,6 +2239,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
#partial switch t in type.derived_expr {
case ^ast.Array_Type: t.tag = bd
case ^ast.Dynamic_Array_Type: t.tag = bd
+ case ^ast.Pointer_Type: t.tag = bd
+ case:
+ error(p, original_type.pos, "expected an array or pointer type after #%s", name.text)
+ }
+ return original_type
+
+ case "simd":
+ bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
+ bd.tok = tok
+ bd.name = name.text
+ original_type := parse_type(p)
+ type := ast.unparen_expr(original_type)
+ #partial switch t in type.derived_expr {
+ case ^ast.Array_Type: t.tag = bd
case:
error(p, original_type.pos, "expected an array type after #%s", name.text)
}
@@ -2631,7 +2604,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Union)
poly_params: ^ast.Field_List
align: ^ast.Expr
- is_maybe: bool
is_no_nil: bool
is_shared_nil: bool
@@ -2656,10 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
align = parse_expr(p, true)
case "maybe":
- if is_maybe {
- error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
- }
- is_maybe = true
+ error(p, tag.pos, "#%s functionality has now been merged with standard 'union' functionality", tag.text)
case "no_nil":
if is_no_nil {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
@@ -2676,19 +2645,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
p.expr_level = prev_level
- if is_no_nil && is_maybe {
- error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
- }
if is_no_nil && is_shared_nil {
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
}
- if is_shared_nil && is_maybe {
- error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
- }
union_kind := ast.Union_Type_Kind.Normal
switch {
- case is_maybe: union_kind = .maybe
case is_no_nil: union_kind = .no_nil
case is_shared_nil: union_kind = .shared_nil
}
diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin
index 1fb653b85..00cccd2a7 100644
--- a/core/os/os2/user.odin
+++ b/core/os/os2/user.odin
@@ -6,19 +6,19 @@ import "core:runtime"
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Windows:
- dir = get_env("LocalAppData")
+ dir = get_env("LocalAppData", allocator)
if dir != "" {
dir = strings.clone_safe(dir, allocator) or_return
}
case .Darwin:
- dir = get_env("HOME")
+ dir = get_env("HOME", allocator)
if dir != "" {
dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
}
case: // All other UNIX systems
- dir = get_env("XDG_CACHE_HOME")
+ dir = get_env("XDG_CACHE_HOME", allocator)
if dir == "" {
- dir = get_env("HOME")
+ dir = get_env("HOME", allocator)
if dir == "" {
return
}
@@ -34,19 +34,19 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Windows:
- dir = get_env("AppData")
+ dir = get_env("AppData", allocator)
if dir != "" {
dir = strings.clone_safe(dir, allocator) or_return
}
case .Darwin:
- dir = get_env("HOME")
+ dir = get_env("HOME", allocator)
if dir != "" {
dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
}
case: // All other UNIX systems
- dir = get_env("XDG_CACHE_HOME")
+ dir = get_env("XDG_CACHE_HOME", allocator)
if dir == "" {
- dir = get_env("HOME")
+ dir = get_env("HOME", allocator)
if dir == "" {
return
}
@@ -59,13 +59,13 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
return
}
-user_home_dir :: proc() -> (dir: string, err: Error) {
+user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
env := "HOME"
#partial switch ODIN_OS {
case .Windows:
env = "USERPROFILE"
}
- if v := get_env(env); v != "" {
+ if v := get_env(env, allocator); v != "" {
return v, nil
}
return "", .Invalid_Path
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index de3a22187..1064f4aa8 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -311,7 +311,7 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
low := uintptr(offset & 0xFFFFFFFF)
high := uintptr(offset >> 32)
result: i64
- res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
+ res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
return -1 if res < 0 else result
}
}
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index 00a9c9fb0..c932f202a 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -271,7 +271,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
}
- d, derr := os.open(dir)
+ d, derr := os.open(dir, os.O_RDONLY)
if derr != 0 {
return
}
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index 27a83e680..07ab94183 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -1,7 +1,6 @@
package reflect
import "core:runtime"
-import "core:mem"
import "core:intrinsics"
_ :: intrinsics
@@ -34,6 +33,7 @@ Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice
Type_Info_Matrix :: runtime.Type_Info_Matrix
+Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
@@ -68,6 +68,7 @@ Type_Kind :: enum {
Relative_Pointer,
Relative_Slice,
Matrix,
+ Soa_Pointer,
}
@@ -102,6 +103,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
case Type_Info_Relative_Pointer: return .Relative_Pointer
case Type_Info_Relative_Slice: return .Relative_Slice
case Type_Info_Matrix: return .Matrix
+ case Type_Info_Soa_Pointer: return .Soa_Pointer
}
}
@@ -194,6 +196,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
}
case Type_Info_Pointer: return v.elem.id
case Type_Info_Multi_Pointer: return v.elem.id
+ case Type_Info_Soa_Pointer: return v.elem.id
case Type_Info_Array: return v.elem.id
case Type_Info_Enumerated_Array: return v.elem.id
case Type_Info_Slice: return v.elem.id
@@ -220,7 +223,7 @@ align_of_typeid :: proc(T: typeid) -> int {
as_bytes :: proc(v: any) -> []byte {
if v != nil {
sz := size_of_typeid(v.id)
- return mem.slice_ptr((^byte)(v.data), sz)
+ return ([^]byte)(v.data)[:sz]
}
return nil
}
@@ -262,19 +265,19 @@ length :: proc(val: any) -> int {
return a.count
case Type_Info_Slice:
- return (^mem.Raw_Slice)(val.data).len
+ return (^runtime.Raw_Slice)(val.data).len
case Type_Info_Dynamic_Array:
- return (^mem.Raw_Dynamic_Array)(val.data).len
+ return (^runtime.Raw_Dynamic_Array)(val.data).len
case Type_Info_Map:
- return (^mem.Raw_Map)(val.data).entries.len
+ return (^runtime.Raw_Map)(val.data).entries.len
case Type_Info_String:
if a.is_cstring {
return len((^cstring)(val.data)^)
} else {
- return (^mem.Raw_String)(val.data).len
+ return (^runtime.Raw_String)(val.data).len
}
}
return 0
@@ -297,10 +300,10 @@ capacity :: proc(val: any) -> int {
return a.count
case Type_Info_Dynamic_Array:
- return (^mem.Raw_Dynamic_Array)(val.data).cap
+ return (^runtime.Raw_Dynamic_Array)(val.data).cap
case Type_Info_Map:
- return (^mem.Raw_Map)(val.data).entries.cap
+ return (^runtime.Raw_Map)(val.data).entries.cap
}
return 0
}
@@ -340,14 +343,14 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
return any{data, a.elem.id}
case Type_Info_Slice:
- raw := (^mem.Raw_Slice)(val.data)
+ raw := (^runtime.Raw_Slice)(val.data)
runtime.bounds_check_error_loc(loc, i, raw.len)
offset := uintptr(a.elem.size * i)
data := rawptr(uintptr(raw.data) + offset)
return any{data, a.elem.id}
case Type_Info_Dynamic_Array:
- raw := (^mem.Raw_Dynamic_Array)(val.data)
+ raw := (^runtime.Raw_Dynamic_Array)(val.data)
runtime.bounds_check_error_loc(loc, i, raw.len)
offset := uintptr(a.elem.size * i)
data := rawptr(uintptr(raw.data) + offset)
@@ -356,7 +359,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
case Type_Info_String:
if a.is_cstring { return nil }
- raw := (^mem.Raw_String)(val.data)
+ raw := (^runtime.Raw_String)(val.data)
runtime.bounds_check_error_loc(loc, i, raw.len)
offset := uintptr(size_of(u8) * i)
data := rawptr(uintptr(raw.data) + offset)
@@ -725,6 +728,17 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
panic("expected a union to reflect.get_union_variant_raw_tag")
}
+get_union_variant :: proc(a: any) -> any {
+ if a == nil {
+ return nil
+ }
+ id := union_variant_typeid(a)
+ if id == nil {
+ return nil
+ }
+ return any{a.data, id}
+}
+
set_union_variant_raw_tag :: proc(a: any, tag: i64) {
if a == nil { return }
@@ -822,17 +836,17 @@ set_union_value :: proc(dst: any, value: any) -> bool {
ti := runtime.type_info_base(type_info_of(dst.id))
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
if value.id == nil {
- mem.zero(dst.data, ti.size)
+ intrinsics.mem_zero(dst.data, ti.size)
return true
}
if ti.id == runtime.typeid_base(value.id) {
- mem.copy(dst.data, value.data, ti.size)
+ intrinsics.mem_copy(dst.data, value.data, ti.size)
return true
}
if type_info_union_is_pure_maybe(info) {
if variant := info.variants[0]; variant.id == value.id {
- mem.copy(dst.data, value.data, variant.size)
+ intrinsics.mem_copy(dst.data, value.data, variant.size)
return true
}
return false
@@ -844,7 +858,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
if !info.no_nil {
tag += 1
}
- mem.copy(dst.data, value.data, variant.size)
+ intrinsics.mem_copy(dst.data, value.data, variant.size)
set_union_variant_raw_tag(dst, tag)
return true
}
@@ -1337,11 +1351,11 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
case Type_Info_Slice:
valid = true
- value = (^mem.Raw_Slice)(a.data).data
+ value = (^runtime.Raw_Slice)(a.data).data
case Type_Info_Dynamic_Array:
valid = true
- value = (^mem.Raw_Dynamic_Array)(a.data).data
+ value = (^runtime.Raw_Dynamic_Array)(a.data).data
}
return
@@ -1383,7 +1397,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
}
if .Simple_Compare in t.flags {
- return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
+ return runtime.memory_compare(a.data, b.data, t.size) == 0
}
t = runtime.type_info_core(t)
@@ -1419,8 +1433,9 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
Type_Info_Enum,
Type_Info_Simd_Vector,
Type_Info_Relative_Pointer,
+ Type_Info_Soa_Pointer,
Type_Info_Matrix:
- return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
+ return runtime.memory_compare(a.data, b.data, t.size) == 0
case Type_Info_String:
if v.is_cstring {
@@ -1474,8 +1489,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
if !including_indirect_array_recursion {
return false
}
- array_a := (^mem.Raw_Slice)(a.data)
- array_b := (^mem.Raw_Slice)(b.data)
+ array_a := (^runtime.Raw_Slice)(a.data)
+ array_b := (^runtime.Raw_Slice)(b.data)
if array_a.len != array_b.len {
return false
}
@@ -1494,8 +1509,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
if !including_indirect_array_recursion {
return false
}
- array_a := (^mem.Raw_Dynamic_Array)(a.data)
- array_b := (^mem.Raw_Dynamic_Array)(b.data)
+ array_a := (^runtime.Raw_Dynamic_Array)(a.data)
+ array_b := (^runtime.Raw_Dynamic_Array)(b.data)
if array_a.len != array_b.len {
return false
}
@@ -1503,7 +1518,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
return true
}
if .Simple_Compare in v.elem.flags {
- return mem.compare_byte_ptrs((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
+ return runtime.memory_compare((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
}
for i in 0..<array_a.len {
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index 7be7ff812..f53b18e0d 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -68,6 +68,11 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
y := b.variant.(Type_Info_Multi_Pointer) or_return
return are_types_identical(x.elem, y.elem)
+ case Type_Info_Soa_Pointer:
+ y := b.variant.(Type_Info_Soa_Pointer) or_return
+ return are_types_identical(x.elem, y.elem)
+
+
case Type_Info_Procedure:
y := b.variant.(Type_Info_Procedure) or_return
switch {
@@ -256,6 +261,11 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
return ok
}
+is_soa_pointer :: proc(info: ^Type_Info) -> bool {
+ if info == nil { return false }
+ _, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
+ return ok
+}
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch v in info.variant {
@@ -292,6 +302,11 @@ is_dynamic_map :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Map)
return ok
}
+is_bit_set :: proc(info: ^Type_Info) -> bool {
+ if info == nil { return false }
+ _, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
+ return ok
+}
is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Slice)
@@ -437,6 +452,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
case Type_Info_Multi_Pointer:
io.write_string(w, "[^]", &n) or_return
write_type(w, info.elem, &n) or_return
+ case Type_Info_Soa_Pointer:
+ io.write_string(w, "#soa ^", &n) or_return
+ write_type(w, info.elem, &n) or_return
case Type_Info_Procedure:
io.write_string(w, "proc", &n) or_return
if info.params == nil {
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index 8fb3d7210..0310aff6d 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -176,6 +176,9 @@ Type_Info_Matrix :: struct {
column_count: int,
// Total element count = column_count * elem_stride
}
+Type_Info_Soa_Pointer :: struct {
+ elem: ^Type_Info,
+}
Type_Info_Flag :: enum u8 {
Comparable = 0,
@@ -217,6 +220,7 @@ Type_Info :: struct {
Type_Info_Relative_Pointer,
Type_Info_Relative_Slice,
Type_Info_Matrix,
+ Type_Info_Soa_Pointer,
},
}
@@ -403,6 +407,12 @@ Raw_Cstring :: struct {
data: [^]byte,
}
+Raw_Soa_Pointer :: struct {
+ data: rawptr,
+ index: int,
+}
+
+
/*
// Defined internally by the compiler
diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin
index 4f698a270..e3960088d 100644
--- a/core/runtime/core_builtin.odin
+++ b/core/runtime/core_builtin.odin
@@ -143,7 +143,7 @@ free_all :: proc{mem_free_all}
@builtin
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
- return mem_free(raw_data(str), allocator, loc)
+ return mem_free_with_size(raw_data(str), len(str), allocator, loc)
}
@builtin
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
@@ -151,17 +151,24 @@ delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #cal
}
@builtin
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
- return mem_free(raw_data(array), array.allocator, loc)
+ return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
}
@builtin
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
- return mem_free(raw_data(array), allocator, loc)
+ return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
}
@builtin
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
+ Entry :: struct {
+ hash: uintptr,
+ next: int,
+ key: K,
+ value: V,
+ }
+
raw := transmute(Raw_Map)m
err := delete_slice(raw.hashes, raw.entries.allocator, loc)
- err1 := mem_free(raw.entries.data, raw.entries.allocator, loc)
+ err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc)
if err == nil {
err = err1
}
@@ -335,68 +342,80 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
@builtin
-append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
+append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
if array == nil {
- return
- }
-
- if cap(array) < len(array)+1 {
- cap := 2 * cap(array) + max(8, 1)
- _ = reserve(array, cap, loc)
+ return 0
}
- if cap(array)-len(array) > 0 {
- a := (^Raw_Dynamic_Array)(array)
- when size_of(E) != 0 {
- data := ([^]E)(a.data)
- assert(condition=data != nil, loc=loc)
- data[a.len] = arg
+ when size_of(E) == 0 {
+ array.len += 1
+ return 1
+ } else {
+ if cap(array) < len(array)+1 {
+ cap := 2 * cap(array) + max(8, 1)
+ _ = reserve(array, cap, loc)
}
- a.len += 1
+ if cap(array)-len(array) > 0 {
+ a := (^Raw_Dynamic_Array)(array)
+ when size_of(E) != 0 {
+ data := ([^]E)(a.data)
+ assert(condition=data != nil, loc=loc)
+ data[a.len] = arg
+ }
+ a.len += 1
+ return 1
+ }
+ return 0
}
}
@builtin
-append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
+append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
if array == nil {
- return
+ return 0
}
arg_len := len(args)
if arg_len <= 0 {
- return
+ return 0
}
-
- 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)
- when size_of(E) != 0 {
- data := ([^]E)(a.data)
- assert(condition=data != nil, loc=loc)
- intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
+ when size_of(E) == 0 {
+ array.len += arg_len
+ return arg_len
+ } else {
+ if cap(array) < len(array)+arg_len {
+ cap := 2 * cap(array) + max(8, arg_len)
+ _ = reserve(array, cap, loc)
}
- a.len += arg_len
+ arg_len = min(cap(array)-len(array), arg_len)
+ if arg_len > 0 {
+ a := (^Raw_Dynamic_Array)(array)
+ when size_of(E) != 0 {
+ data := ([^]E)(a.data)
+ assert(condition=data != nil, loc=loc)
+ intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
+ }
+ a.len += arg_len
+ }
+ return 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) {
+append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int {
args := transmute([]E)arg
- append_elems(array=array, args=args, loc=loc)
+ return append_elems(array=array, args=args, loc=loc)
}
// 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) {
+append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) {
for arg in args {
- append(array = array, args = transmute([]E)(arg), loc = loc)
+ n += append(array = array, args = transmute([]E)(arg), loc = loc)
}
+ return
}
// The append built-in procedure appends elements to the end of a dynamic array
@@ -404,11 +423,13 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
@builtin
-append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
+append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int {
if array == nil {
- return
+ return 0
}
+ prev_len := len(array)
resize(array, len(array)+1)
+ return len(array)-prev_len
}
@@ -779,17 +800,3 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
}
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
index d39c2dd0b..b6a685fcf 100644
--- a/core/runtime/dynamic_array_internal.odin
+++ b/core/runtime/dynamic_array_internal.odin
@@ -29,11 +29,15 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
new_size := cap * elem_size
allocator := array.allocator
- new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc)
+ new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc)
if err != nil {
return false
}
- if new_data != nil || elem_size == 0 {
+ if elem_size == 0 {
+ array.data = raw_data(new_data)
+ array.cap = cap
+ return true
+ } else if new_data != nil {
array.data = raw_data(new_data)
array.cap = min(cap, len(new_data)/elem_size)
return true
@@ -59,7 +63,7 @@ __dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_c
new_size := new_cap * elem_size
allocator := array.allocator
- new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc)
+ new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc)
if err != nil {
return
}
diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin
index fee0f570f..35b42d488 100644
--- a/core/runtime/dynamic_map_internal.odin
+++ b/core/runtime/dynamic_map_internal.odin
@@ -194,12 +194,15 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
new_size := new_count*size_of(T)
new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc)
- if new_data == nil || err != nil {
+ if err != nil {
return false
}
- array.data = new_data
- array.len = new_count
- return true
+ if new_data != nil || size_of(E) == 0 {
+ array.data = raw_data(new_data)
+ array.len = new_count
+ return true
+ }
+ return false
}
__dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) {
@@ -207,7 +210,7 @@ __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_loc
m.hashes[i] = -1
}
- for i in 0 ..< m.entries.len {
+ for i in 0..<m.entries.len {
entry_header := __dynamic_map_get_entry(header, i)
entry_hash := __get_map_hash_from_entry(header, entry_header)
entry_header.next = -1
diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin
index 30798f623..048bff7ca 100644
--- a/core/runtime/internal.odin
+++ b/core/runtime/internal.odin
@@ -103,7 +103,7 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
if data == nil {
return nil
}
- if len < 0 {
+ if len <= 0 {
return data
}
intrinsics.mem_zero(data, len)
@@ -111,22 +111,18 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
}
mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
- if src == nil {
- return dst
+ if src != nil && dst != src && len > 0 {
+ // NOTE(bill): This _must_ be implemented like C's memmove
+ intrinsics.mem_copy(dst, src, len)
}
-
- // NOTE(bill): This _must_ be implemented like C's memmove
- intrinsics.mem_copy(dst, src, len)
return dst
}
mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
- if src == nil {
- return dst
+ if src != nil && dst != src && len > 0 {
+ // NOTE(bill): This _must_ be implemented like C's memcpy
+ intrinsics.mem_copy_non_overlapping(dst, src, len)
}
-
- // NOTE(bill): This _must_ be implemented like C's memcpy
- intrinsics.mem_copy_non_overlapping(dst, src, len)
return dst
}
@@ -142,28 +138,38 @@ mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNM
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
}
-mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) {
- if size == 0 {
+mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+ if size == 0 || allocator.procedure == nil {
return nil, nil
}
- if allocator.procedure == nil {
- return nil, nil
- }
- data, err := allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
- return raw_data(data), err
+ return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
}
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
- if ptr == nil {
- return .None
- }
- if allocator.procedure == nil {
- return .None
+ if ptr == nil || allocator.procedure == nil {
+ return nil
}
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc)
return err
}
+mem_free_with_size :: #force_inline proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+ if ptr == nil || allocator.procedure == nil {
+ return nil
+ }
+ _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
+ return err
+}
+
+mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+ if bytes == nil || allocator.procedure == nil {
+ return nil
+ }
+ _, err := allocator.procedure(allocator.data, .Free, 0, 0, raw_data(bytes), len(bytes), loc)
+ return err
+}
+
+
mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
if allocator.procedure != nil {
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc)
@@ -171,21 +177,34 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
return
}
-mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (new_ptr: rawptr, err: Allocator_Error) {
- new_data: []byte
- switch {
- case allocator.procedure == nil:
- return
- case new_size == 0:
- new_data, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc)
- case ptr == nil:
- new_data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
- case:
- new_data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
- }
- new_ptr = raw_data(new_data)
- return
+mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+ if allocator.procedure == nil {
+ return nil, nil
+ }
+ if new_size == 0 {
+ if ptr != nil {
+ _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
+ return nil, err
+ }
+ return nil, nil
+ } else if ptr == nil {
+ return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
+ } else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
+ return ([^]byte)(ptr)[:old_size], nil
+ }
+
+ data, err := allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
+ if err == .Mode_Not_Implemented {
+ data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
+ if err != nil {
+ return data, err
+ }
+ copy(data, ([^]byte)(ptr)[:old_size])
+ _, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
+ }
+ return data, err
}
+
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
switch {
case n == 0: return true
@@ -341,7 +360,12 @@ string_eq :: proc "contextless" (lhs, rhs: string) -> bool {
string_cmp :: proc "contextless" (a, b: string) -> int {
x := transmute(Raw_String)a
y := transmute(Raw_String)b
- return memory_compare(x.data, y.data, min(x.len, y.len))
+
+ ret := memory_compare(x.data, y.data, min(x.len, y.len))
+ if ret == 0 && x.len != y.len {
+ return -1 if x.len < y.len else +1
+ }
+ return ret
}
string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) }
diff --git a/core/runtime/print.odin b/core/runtime/print.odin
index 89c196fc2..959dad3a9 100644
--- a/core/runtime/print.odin
+++ b/core/runtime/print.odin
@@ -228,6 +228,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
case Type_Info_Multi_Pointer:
print_string("[^]")
print_type(info.elem)
+ case Type_Info_Soa_Pointer:
+ print_string("#soa ^")
+ print_type(info.elem)
case Type_Info_Procedure:
print_string("proc")
if info.params == nil {
diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin
index 782efa773..510abcbb9 100644
--- a/core/runtime/procs.odin
+++ b/core/runtime/procs.odin
@@ -6,7 +6,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
@(private="file")
@(default_calling_convention="stdcall")
foreign lib {
- RtlMoveMemory :: proc(dst, src: rawptr, length: int) ---
+ RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
}
@@ -40,24 +40,34 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
@(link_name="memmove", linkage="strong", require)
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
- if dst != src {
- d, s := ([^]byte)(dst), ([^]byte)(src)
+ d, s := ([^]byte)(dst), ([^]byte)(src)
+ if d == s || len == 0 {
+ return dst
+ }
+ if d > s && uintptr(d)-uintptr(s) < uintptr(len) {
for i := len-1; i >= 0; i -= 1 {
d[i] = s[i]
}
+ return dst
}
- return dst
-
+
+ if s > d && uintptr(s)-uintptr(d) < uintptr(len) {
+ for i := 0; i < len; i += 1 {
+ d[i] = s[i]
+ }
+ return dst
+ }
+ return memcpy(dst, src, len)
}
@(link_name="memcpy", linkage="strong", require)
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
- if dst != src {
- d, s := ([^]byte)(dst), ([^]byte)(src)
- for i := len-1; i >= 0; i -= 1 {
+ d, s := ([^]byte)(dst), ([^]byte)(src)
+ if d != s {
+ for i := 0; i < len; i += 1 {
d[i] = s[i]
}
}
- return dst
+ return d
}
} else {
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index 440cf643f..e76a5599d 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -14,14 +14,14 @@ _ :: mem
Turn a pointer and a length into a slice.
*/
from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
- return ([^]T)(ptr)[:count]
+ return ([^]T)(ptr)[:count]
}
/*
Turn a pointer and a length into a byte slice.
*/
bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
- return ([^]byte)(ptr)[:byte_count]
+ return ([^]byte)(ptr)[:byte_count]
}
/*
diff --git a/core/sort/sort.odin b/core/sort/sort.odin
index 2ce74294d..a2b4d7ea0 100644
--- a/core/sort/sort.odin
+++ b/core/sort/sort.odin
@@ -684,5 +684,10 @@ compare_f64s :: proc(a, b: f64) -> int {
compare_strings :: proc(a, b: string) -> int {
x := transmute(mem.Raw_String)a
y := transmute(mem.Raw_String)b
- return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len))
+
+ ret := mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len))
+ if ret == 0 && x.len != y.len {
+ return -1 if x.len < y.len else +1
+ }
+ return ret
}
diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin
index 65161a820..c2d48be7a 100644
--- a/core/strconv/strconv.odin
+++ b/core/strconv/strconv.odin
@@ -2,11 +2,13 @@ package strconv
import "core:unicode/utf8"
-parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
+parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
switch s {
case "1", "t", "T", "true", "TRUE", "True":
+ if n != nil { n^ = len(s) }
return true, true
case "0", "f", "F", "false", "FALSE", "False":
+ if n != nil { n^ = len(s) }
return false, true
}
return
@@ -32,10 +34,13 @@ _digit_value :: proc(r: rune) -> int {
// n, ok := strconv.parse_i64_of_base("-1234eeee", 10);
// assert(n == -1234 && ok);
// ```
-parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) {
+parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok: bool) {
assert(base <= 16, "base must be 1-16")
s := str
+
+ defer if n != nil { n^ = len(str)-len(s) }
+
if s == "" {
return
}
@@ -87,8 +92,9 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) {
// n, ok = strconv.parse_i64_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok);
// ```
-parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, ok: bool) {
+parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: bool) {
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
@@ -155,9 +161,10 @@ parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}
// n, ok = strconv.parse_u64_of_base("5678eeee", 16);
// assert(n == 0x5678eeee && ok);
// ```
-parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) {
+parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok: bool) {
assert(base <= 16, "base must be 1-16")
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
@@ -198,8 +205,9 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) {
// n, ok = strconv.parse_u64_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok);
// ```
-parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) {
+parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: bool) {
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
@@ -259,11 +267,11 @@ parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}
// n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base
// assert(n == 0xffff && ok);
// ```
-parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) {
+parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) {
v: i64 = ---
switch base {
- case 0: v, ok = parse_i64_maybe_prefixed(s)
- case: v, ok = parse_i64_of_base(s, base)
+ case 0: v, ok = parse_i64_maybe_prefixed(s, n)
+ case: v, ok = parse_i64_of_base(s, base, n)
}
value = int(v)
return
@@ -289,11 +297,11 @@ parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) {
// n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base
// assert(n == 0xffff && ok);
// ```
-parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
+parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: bool) {
v: u64 = ---
switch base {
- case 0: v, ok = parse_u64_maybe_prefixed(s)
- case: v, ok = parse_u64_of_base(s, base)
+ case 0: v, ok = parse_u64_maybe_prefixed(s, n)
+ case: v, ok = parse_u64_of_base(s, base, n)
}
value = uint(v)
return
@@ -309,10 +317,11 @@ parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
// n, ok := strconv.parse_i128_of_base("-1234eeee", 10);
// assert(n == -1234 && ok);
// ```
-parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
+parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i128, ok: bool) {
assert(base <= 16, "base must be 1-16")
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
@@ -364,8 +373,9 @@ parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
// n, ok = strconv.parse_i128_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok);
// ```
-parse_i128_maybe_prefixed :: proc(str: string) -> (value: i128, ok: bool) {
+parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, ok: bool) {
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
@@ -432,9 +442,10 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
// n, ok = strconv.parse_u128_of_base("5678eeee", 16);
// assert(n == 0x5678eeee && ok);
// ```
-parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
+parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u128, ok: bool) {
assert(base <= 16, "base must be 1-16")
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
@@ -475,8 +486,9 @@ parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
// n, ok = strconv.parse_u128_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok);
// ```
-parse_u128_maybe_prefixed :: proc(str: string) -> (value: u128, ok: bool) {
+parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, ok: bool) {
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
@@ -535,9 +547,9 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
// n, ok = strconv.parse_f32("12.34");
// assert(n == 12.34 && ok);
// ```
-parse_f32 :: proc(s: string) -> (value: f32, ok: bool) {
+parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
v: f64 = ---
- v, ok = parse_f64(s)
+ v, ok = parse_f64(s, n)
return f32(v), ok
}
@@ -553,8 +565,9 @@ parse_f32 :: proc(s: string) -> (value: f32, ok: bool) {
// n, ok = strconv.parse_f32("12.34");
// assert(n == 12.34 && ok);
// ```
-parse_f64 :: proc(str: string) -> (value: f64, ok: bool) {
+parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
s := str
+ defer if n != nil { n^ = len(str)-len(s) }
if s == "" {
return
}
diff --git a/core/strings/intern.odin b/core/strings/intern.odin
index 1e9577e61..5e9193a0d 100644
--- a/core/strings/intern.odin
+++ b/core/strings/intern.odin
@@ -31,31 +31,31 @@ intern_destroy :: proc(m: ^Intern) {
// returns the `text` string from the intern map - gets set if it didnt exist yet
// the returned string lives as long as the map entry lives
-intern_get :: proc(m: ^Intern, text: string) -> string {
- entry := _intern_get_entry(m, text)
- #no_bounds_check return string(entry.str[:entry.len])
+intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
+ entry := _intern_get_entry(m, text) or_return
+ #no_bounds_check return string(entry.str[:entry.len]), nil
}
// returns the `text` cstring from the intern map - gets set if it didnt exist yet
// the returned cstring lives as long as the map entry lives
-intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring {
- entry := _intern_get_entry(m, text)
- return cstring(&entry.str[0])
+intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
+ entry := _intern_get_entry(m, text) or_return
+ return cstring(&entry.str[0]), nil
}
// looks up wether the `text` string exists in the map, returns the entry
// sets & allocates the entry if it wasnt set yet
-_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check {
+_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
if prev, ok := m.entries[text]; ok {
- return prev
+ return prev, nil
}
if m.allocator.procedure == nil {
m.allocator = context.allocator
}
entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
- ptr, _ := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator)
- new_entry := (^Intern_Entry)(ptr)
+ bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
+ new_entry = (^Intern_Entry)(raw_data(bytes))
new_entry.len = len(text)
copy(new_entry.str[:new_entry.len], text)
@@ -63,5 +63,5 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_
key := string(new_entry.str[:new_entry.len])
m.entries[key] = new_entry
- return new_entry
+ return new_entry, nil
}
diff --git a/core/sync/futex_windows.odin b/core/sync/futex_windows.odin
index ce662ba9e..ba6292742 100644
--- a/core/sync/futex_windows.odin
+++ b/core/sync/futex_windows.odin
@@ -15,18 +15,40 @@ foreign import Ntdll "system:Ntdll.lib"
@(default_calling_convention="stdcall")
foreign Ntdll {
RtlWaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> i32 ---
+ RtlNtStatusToDosError :: proc(status: i32) -> u32 ---
+ SetLastError :: proc(err: u32) ---
}
+
+/*
+ NOTE(bill, 2022-08-17)
+ WaitOnAddress is implemented on top of RtlWaitOnAddress
+ BUT requires taking the return value of it and if it is non-zero
+ converting that status to a DOS error and then SetLastError
+ If this is not done, then things don't work as expected when
+ and error occurs
+
+ GODDAMN MICROSOFT!
+*/
+CustomWaitOnAddress :: proc "stdcall" (Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> bool {
+ status := RtlWaitOnAddress(Address, CompareAddress, AddressSize, Timeout)
+ if status != 0 {
+ SetLastError(RtlNtStatusToDosError(status))
+ }
+ return status == 0
+}
+
+
_futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
expect := expect
- return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), nil)
+ return CustomWaitOnAddress(f, &expect, size_of(expect), nil)
}
_futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool {
expect := expect
// NOTE(bill): for some bizarre reason, this has be a negative number
timeout := -i64(duration / 100)
- return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), &timeout)
+ return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
}
_futex_signal :: proc(f: ^Futex) {
diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin
index d611e33f0..8ce3ca3cb 100644
--- a/core/sys/unix/syscalls_linux.odin
+++ b/core/sys/unix/syscalls_linux.odin
@@ -1568,7 +1568,7 @@ sys_gettid :: proc "contextless" () -> int {
}
sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
- return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags))
+ return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags))
}
sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
@@ -1622,7 +1622,7 @@ sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
low := uintptr(offset & 0xFFFFFFFF)
high := uintptr(offset >> 32)
result: i64
- res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
+ res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
return res if res < 0 else result
}
}
@@ -1748,7 +1748,7 @@ sys_unlink :: proc "contextless" (path: cstring) -> int {
}
sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int {
- return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag))
+ return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flag)))
}
sys_rmdir :: proc "contextless" (path: cstring) -> int {
diff --git a/core/sys/valgrind/callgrind.odin b/core/sys/valgrind/callgrind.odin
new file mode 100644
index 000000000..1396f82ad
--- /dev/null
+++ b/core/sys/valgrind/callgrind.odin
@@ -0,0 +1,63 @@
+//+build amd64
+package sys_valgrind
+
+import "core:intrinsics"
+
+Callgrind_Client_Request :: enum uintptr {
+ Dump_Stats = 'C'<<24 | 'T'<<16,
+ Zero_Stats,
+ Toggle_Collect,
+ Dump_Stats_At,
+ Start_Instrumentation,
+ Stop_Instrumentation,
+}
+
+@(require_results)
+callgrind_client_request_expr :: proc "c" (default: uintptr, request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
+ return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
+}
+callgrind_client_request_stmt :: proc "c" (request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) {
+ _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
+}
+
+// Dump current state of cost centres, and zero them afterwards.
+dump_stats :: proc "c" () {
+ callgrind_client_request_stmt(.Dump_Stats, 0, 0, 0, 0, 0)
+}
+
+// Zero cost centres
+zero_stats :: proc "c" () {
+ callgrind_client_request_stmt(.Zero_Stats, 0, 0, 0, 0, 0)
+}
+
+// Toggles collection state.
+// The collection state specifies whether the happening of events should be noted or
+// if they are to be ignored. Events are noted by increment of counters in a cost centre.
+toggle_collect :: proc "c" () {
+ callgrind_client_request_stmt(.Toggle_Collect, 0, 0, 0, 0, 0)
+}
+
+// Dump current state of cost centres, and zero them afterwards.
+// The argument is appended to a string stating the reason which triggered
+// the dump. This string is written as a description field into the
+// profile data dump.
+dump_stats_at :: proc "c" (pos_str: rawptr) {
+ callgrind_client_request_stmt(.Dump_Stats_At, uintptr(pos_str), 0, 0, 0, 0)
+}
+
+// Start full callgrind instrumentation if not already switched on.
+// When cache simulation is done, it will flush the simulated cache;
+// this will lead to an artificial cache warmup phase afterwards with
+// cache misses which would not have happened in reality.
+start_instrumentation :: proc "c" () {
+ callgrind_client_request_stmt(.Start_Instrumentation, 0, 0, 0, 0, 0)
+}
+
+// Stop full callgrind instrumentation if not already switched off.
+// This flushes Valgrinds translation cache, and does no additional instrumentation
+// afterwards, which effectivly will run at the same speed as the "none" tool (ie. at minimal slowdown).
+// Use this to bypass Callgrind aggregation for uninteresting code parts.
+// To start Callgrind in this mode to ignore the setup phase, use the option "--instr-atstart=no".
+stop_instrumentation :: proc "c" () {
+ callgrind_client_request_stmt(.Stop_Instrumentation, 0, 0, 0, 0, 0)
+} \ No newline at end of file
diff --git a/core/sys/valgrind/memcheck.odin b/core/sys/valgrind/memcheck.odin
new file mode 100644
index 000000000..99c65272a
--- /dev/null
+++ b/core/sys/valgrind/memcheck.odin
@@ -0,0 +1,169 @@
+//+build amd64
+package sys_valgrind
+
+import "core:intrinsics"
+
+Mem_Check_Client_Request :: enum uintptr {
+ Make_Mem_No_Access = 'M'<<24 | 'C'<<16,
+ Make_Mem_Undefined,
+ Make_Mem_Defined,
+ Discard,
+ Check_Mem_Is_Addressable,
+ Check_Mem_Is_Defined,
+ Do_Leak_Check,
+ Count_Leaks,
+ Get_Vbits,
+ Set_Vbits,
+ Create_Block,
+ Make_Mem_Defined_If_Addressable,
+ Count_Leak_Blocks,
+ Enable_Addr_Error_Reporting_In_Range,
+ Disable_Addr_Error_Reporting_In_Range,
+}
+
+@(require_results)
+mem_check_client_request_expr :: proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
+ return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
+}
+mem_check_client_request_stmt :: proc "c" (request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) {
+ _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
+}
+
+// Mark memory at `raw_data(qzz)` as unaddressable for `len(qzz)` bytes.
+// Returns true when run on Valgrind and false otherwise.
+make_mem_no_access :: proc "c" (qzz: []byte) -> bool {
+ return 0 != mem_check_client_request_expr(0, .Make_Mem_No_Access, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+// Mark memory at `raw_data(qzz)` as addressable but undefined for `len(qzz)` bytes.
+// Returns true when run on Valgrind and false otherwise.
+make_mem_undefined :: proc "c" (qzz: []byte) -> bool {
+ return 0 != mem_check_client_request_expr(0, .Make_Mem_Undefined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+// Mark memory at `raw_data(qzz)` as addressable for `len(qzz)` bytes.
+// Returns true when run on Valgrind and false otherwise.
+make_mem_defined :: proc "c" (qzz: []byte) -> bool {
+ return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+
+// Check that memory at `raw_data(qzz)` is addressable for `len(qzz)` bytes.
+// If suitable addressibility is not established, Valgrind prints an error
+// message and returns the address of the first offending byte.
+// Otherwise it returns zero.
+check_mem_is_addressable :: proc "c" (qzz: []byte) -> uintptr {
+ return mem_check_client_request_expr(0, .Check_Mem_Is_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+// Check that memory at `raw_data(qzz)` is addressable and defined for `len(qzz)` bytes.
+// If suitable addressibility and definedness are not established,
+// Valgrind prints an error message and returns the address of the first
+// offending byte. Otherwise it returns zero.
+check_mem_is_defined :: proc "c" (qzz: []byte) -> uintptr {
+ return mem_check_client_request_expr(0, .Check_Mem_Is_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+
+// Similar to `make_mem_defined(qzz)` except that addressability is not altered:
+// bytes which are addressable are marked as defined, but those which
+// are not addressable are left unchanged.
+// Returns true when run on Valgrind and false otherwise.
+make_mem_defined_if_addressable :: proc "c" (qzz: []byte) -> bool {
+ return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined_If_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+
+// Create a block-description handle.
+// The description is an ascii string which is included in any messages
+// pertaining to addresses within the specified memory range.
+// Has no other effect on the properties of the memory range.
+create_block :: proc "c" (qzz: []u8, desc: cstring) -> bool {
+ return 0 != mem_check_client_request_expr(0, .Create_Block, uintptr(raw_data(qzz)), uintptr(len(qzz)), uintptr(rawptr(desc)), 0, 0)
+}
+// Discard a block-description-handle. Returns true for an invalid handle, false for a valid handle.
+discard :: proc "c" (blk_index: uintptr) -> bool {
+ return 0 != mem_check_client_request_expr(0, .Discard, 0, blk_index, 0, 0, 0)
+}
+
+
+// Do a full memory leak check (like `--leak-check=full`) mid-execution.
+leak_check :: proc "c" () {
+ mem_check_client_request_stmt(.Do_Leak_Check, 0, 0, 0, 0, 0)
+}
+// Same as `leak_check()` but only showing the entries for which there was an increase
+// in leaked bytes or leaked nr of blocks since the previous leak search.
+added_leak_check :: proc "c" () {
+ mem_check_client_request_stmt(.Do_Leak_Check, 0, 1, 0, 0, 0)
+}
+// Same as `added_leak_check()` but showing entries with increased or decreased
+// leaked bytes/blocks since previous leak search.
+changed_leak_check :: proc "c" () {
+ mem_check_client_request_stmt(.Do_Leak_Check, 0, 2, 0, 0, 0)
+}
+// Do a summary memory leak check (like `--leak-check=summary`) mid-execution.
+quick_leak_check :: proc "c" () {
+ mem_check_client_request_stmt(.Do_Leak_Check, 1, 0, 0, 0, 0)
+}
+
+Count_Result :: struct {
+ leaked: uint,
+ dubious: uint,
+ reachable: uint,
+ suppressed: uint,
+}
+
+count_leaks :: proc "c" () -> (res: Count_Result) {
+ mem_check_client_request_stmt(
+ .Count_Leaks,
+ uintptr(&res.leaked),
+ uintptr(&res.dubious),
+ uintptr(&res.reachable),
+ uintptr(&res.suppressed),
+ 0,
+ )
+ return
+}
+
+count_leak_blocks :: proc "c" () -> (res: Count_Result) {
+ mem_check_client_request_stmt(
+ .Count_Leak_Blocks,
+ uintptr(&res.leaked),
+ uintptr(&res.dubious),
+ uintptr(&res.reachable),
+ uintptr(&res.suppressed),
+ 0,
+ )
+ return
+}
+
+// Get the validity data for addresses zza and copy it
+// into the provided zzvbits array. Return values:
+// 0 - if not running on valgrind
+// 1 - success
+// 2 - [previously indicated unaligned arrays; these are now allowed]
+// 3 - if any parts of zzsrc/zzvbits are not addressable.
+// The metadata is not copied in cases 0, 2 or 3 so it should be
+// impossible to segfault your system by using this call.
+get_vbits :: proc(zza, zzvbits: []byte) -> u8 {
+ // assert requires a `context` thus these procedures cannot `proc "c"`
+ assert(len(zzvbits) >= len(zza)/8)
+ return u8(mem_check_client_request_expr(0, .Get_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
+}
+
+// Set the validity data for addresses zza, copying it
+// from the provided zzvbits array. Return values:
+// 0 - if not running on valgrind
+// 1 - success
+// 2 - [previously indicated unaligned arrays; these are now allowed]
+// 3 - if any parts of zza/zzvbits are not addressable.
+// The metadata is not copied in cases 0, 2 or 3 so it should be
+// impossible to segfault your system by using this call.
+set_vbits :: proc(zzvbits, zza: []byte) -> u8 {
+ // assert requires a `context` thus these procedures cannot `proc "c"`
+ assert(len(zzvbits) >= len(zza)/8)
+ return u8(mem_check_client_request_expr(0, .Set_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
+}
+
+// (Re-)enable reporting of addressing errors in the specified address range.
+enable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
+ return mem_check_client_request_expr(0, .Enable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+// Disable reporting of addressing errors in the specified address range.
+disable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
+ return mem_check_client_request_expr(0, .Disable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+} \ No newline at end of file
diff --git a/core/sys/valgrind/valgrind.odin b/core/sys/valgrind/valgrind.odin
new file mode 100644
index 000000000..2fb95588f
--- /dev/null
+++ b/core/sys/valgrind/valgrind.odin
@@ -0,0 +1,182 @@
+//+build amd64
+package sys_valgrind
+
+import "core:intrinsics"
+
+Client_Request :: enum uintptr {
+ Running_On_Valgrind = 4097,
+ Discard_Translations = 4098,
+ Client_Call0 = 4353,
+ Client_Call1 = 4354,
+ Client_Call2 = 4355,
+ Client_Call3 = 4356,
+ Count_Errors = 4609,
+ Gdb_Monitor_Command = 4610,
+ Malloc_Like_Block = 4865,
+ Resize_Inplace_Block = 4875,
+ Free_Like_Block = 4866,
+ Create_Mem_Pool = 4867,
+ Destroy_Mem_Pool = 4868,
+ Mem_Pool_Alloc = 4869,
+ Mem_Pool_Free = 4870,
+ Mem_Pool_Trim = 4871,
+ Move_Mem_Pool = 4872,
+ Mem_Pool_Change = 4873,
+ Mem_Pool_Exists = 4874,
+ Printf = 5121,
+ Printf_Backtrace = 5122,
+ Printf_Valist_By_Ref = 5123,
+ Printf_Backtrace_Valist_By_Ref = 5124,
+ Stack_Register = 5377,
+ Stack_Deregister = 5378,
+ Stack_Change = 5379,
+ Load_Pdb_Debug_Info = 5633,
+ Map_Ip_To_Src_Loc = 5889,
+ Change_Err_Disablement = 6145,
+ Vex_Init_For_Iri = 6401,
+ Inner_Threads = 6402,
+}
+
+@(require_results)
+client_request_expr :: proc "c" (default: uintptr, request: Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
+ return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
+}
+client_request_stmt :: proc "c" (request: Client_Request, a0, a1, a2, a3, a4: uintptr) {
+ _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
+}
+
+// Returns the number of Valgrinds this code is running under
+// 0 - running natively
+// 1 - running under Valgrind
+// 2 - running under Valgrind which is running under another Valgrind
+running_on_valgrind :: proc "c" () -> uintptr {
+ return client_request_expr(0, .Running_On_Valgrind, 0, 0, 0, 0, 0)
+}
+
+// Discard translation of code in the slice qzz. Useful if you are debugging a JIT-er or some such,
+// since it provides a way to make sure valgrind will retranslate the invalidated area.
+discard_translations :: proc "c" (qzz: []byte) {
+ client_request_stmt(.Discard_Translations, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+
+non_simd_call0 :: proc "c" (p: proc "c" (uintptr) -> uintptr) -> uintptr {
+ return client_request_expr(0, .Client_Call0, uintptr(rawptr(p)), 0, 0, 0, 0)
+}
+non_simd_call1 :: proc "c" (p: proc "c" (uintptr, uintptr) -> uintptr, a0: uintptr) -> uintptr {
+ return client_request_expr(0, .Client_Call1, uintptr(rawptr(p)), a0, 0, 0, 0)
+}
+non_simd_call2 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr) -> uintptr, a0, a1: uintptr) -> uintptr {
+ return client_request_expr(0, .Client_Call2, uintptr(rawptr(p)), a0, a1, 0, 0)
+}
+non_simd_call3 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr, uintptr) -> uintptr, a0, a1, a2: uintptr) -> uintptr {
+ return client_request_expr(0, .Client_Call3, uintptr(rawptr(p)), a0, a1, a2, 0)
+}
+
+// Counts the number of errors that have been recorded by a tool.
+count_errrors :: proc "c" () -> uint {
+ return uint(client_request_expr(0, .Count_Errors, 0, 0, 0, 0, 0))
+}
+
+monitor_command :: proc "c" (command: cstring) -> bool {
+ return 0 != client_request_expr(0, .Gdb_Monitor_Command, uintptr(rawptr(command)), 0, 0, 0, 0)
+}
+
+
+malloc_like_block :: proc "c" (mem: []byte, rz_b: uintptr, is_zeroed: bool) {
+ client_request_stmt(.Malloc_Like_Block, uintptr(raw_data(mem)), uintptr(len(mem)), rz_b, uintptr(is_zeroed), 0)
+}
+resize_inplace_block :: proc "c" (old_mem: []byte, new_size: uint, rz_b: uintptr) {
+ client_request_stmt(.Resize_Inplace_Block, uintptr(raw_data(old_mem)), uintptr(len(old_mem)), uintptr(new_size), rz_b, 0)
+}
+free_like_block :: proc "c" (addr: rawptr, rz_b: uintptr) {
+ client_request_stmt(.Free_Like_Block, uintptr(addr), rz_b, 0, 0, 0)
+}
+
+Mem_Pool_Flags :: distinct bit_set[Mem_Pool_Flag; uintptr]
+Mem_Pool_Flag :: enum uintptr {
+ Auto_Free = 0,
+ Meta_Pool = 1,
+}
+
+// Create a memory pool.
+create_mem_pool :: proc "c" (pool: rawptr, rz_b: uintptr, is_zeroed: bool, flags: Mem_Pool_Flags) {
+ client_request_stmt(.Create_Mem_Pool, uintptr(pool), rz_b, uintptr(is_zeroed), transmute(uintptr)flags, 0)
+}
+// Destroy a memory pool.
+destroy_mem_pool :: proc "c" (pool: rawptr) {
+ client_request_stmt(.Destroy_Mem_Pool, uintptr(pool), 0, 0, 0, 0)
+}
+// Associate a section of memory with a memory pool.
+mem_pool_alloc :: proc "c" (pool: rawptr, mem: []byte) {
+ client_request_stmt(.Mem_Pool_Alloc, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0)
+}
+// Disassociate a section of memory from a memory pool.
+mem_pool_free :: proc "c" (pool: rawptr, addr: rawptr) {
+ client_request_stmt(.Mem_Pool_Free, uintptr(pool), uintptr(addr), 0, 0, 0)
+}
+// Disassociate parts of a section of memory outside a particular range.
+mem_pool_trim :: proc "c" (pool: rawptr, mem: []byte) {
+ client_request_stmt(.Mem_Pool_Trim, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0)
+}
+// Resize and/or move a section of memory associated with a memory pool.
+move_mem_pool :: proc "c" (pool_a, pool_b: rawptr) {
+ client_request_stmt(.Move_Mem_Pool, uintptr(pool_a), uintptr(pool_b), 0, 0, 0)
+}
+// Resize and/or move a section of memory associated with a memory pool.
+mem_pool_change :: proc "c" (pool: rawptr, addr_a: rawptr, mem: []byte) {
+ client_request_stmt(.Mem_Pool_Change, uintptr(pool), uintptr(addr_a), uintptr(raw_data(mem)), uintptr(len(mem)), 0)
+}
+// Return true if a memory pool exists
+mem_pool_exists :: proc "c" (pool: rawptr) -> bool {
+ return 0 != client_request_expr(0, .Mem_Pool_Exists, uintptr(pool), 0, 0, 0, 0)
+}
+
+
+// Mark a section of memory as being a stack. Returns a stack id.
+stack_register :: proc "c" (stack: []byte) -> (stack_id: uintptr) {
+ ptr := uintptr(raw_data(stack))
+ return client_request_expr(0, .Stack_Register, ptr, ptr+uintptr(len(stack)), 0, 0, 0)
+}
+
+// Unmark a section of memory associated with a stack id as being a stack.
+stack_deregister :: proc "c" (id: uintptr) {
+ client_request_stmt(.Stack_Deregister, id, 0, 0, 0, 0)
+}
+
+// Change the start and end address of the stack id with the `new_stack` slice.
+stack_change :: proc "c" (id: uint, new_stack: []byte) {
+ ptr := uintptr(raw_data(new_stack))
+ client_request_stmt(.Stack_Change, uintptr(id), ptr, ptr + uintptr(len(new_stack)), 0, 0)
+}
+
+
+// Disable error reporting for the current thread/
+// It behaves in a stack-like way, meaning you can safely call this multiple times
+// given that `enable_error_reporting()` is called the same number of times to
+// re-enable the error reporting.
+// The first call of this macro disables reporting.
+// Subsequent calls have no effect except to increase the number of `enable_error_reporting()`
+// calls needed to re-enable reporting.
+// Child threads do not inherit this setting from their parents;
+// they are always created with reporting enabled.
+disable_error_reporting :: proc "c" () {
+ client_request_stmt(.Change_Err_Disablement, 1, 0, 0, 0, 0)
+}
+// Re-enable error reporting
+enable_error_reporting :: proc "c" () {
+ client_request_stmt(.Change_Err_Disablement, ~uintptr(0), 0, 0, 0, 0)
+}
+
+
+inner_threads :: proc "c" (qzz: rawptr) {
+ client_request_stmt(.Inner_Threads, uintptr(qzz), 0, 0, 0, 0)
+}
+
+
+// Map a code address to a source file name and line number.
+// `buf64` must point to a 64-byte buffer in the caller's address space.
+// The result will be dumped in there and is guaranteed to be zero terminated.
+// If no info is found, the first byte is set to zero.
+map_ip_to_src_loc :: proc "c" (addr: rawptr, buf64: ^[64]byte) -> uintptr {
+ return client_request_expr(0, .Map_Ip_To_Src_Loc, uintptr(addr), uintptr(buf64), 0, 0, 0)
+} \ No newline at end of file
diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin
index 2e2a99617..d4f0d19cf 100644
--- a/core/sys/wasm/wasi/wasi_api.odin
+++ b/core/sys/wasm/wasi/wasi_api.odin
@@ -1148,6 +1148,156 @@ foreign wasi {
*/
how: sdflags_t,
) -> errno_t ---
+
+
+ /**
+ * Return a description of the given preopened file descriptor.
+ */
+ fd_prestat_dir_name :: proc(
+ fd: fd_t,
+ /**
+ * A buffer into which to write the preopened directory name.
+ */
+ path: string,
+ ) -> errno_t ---
+ /**
+ * Create a directory.
+ * Note: This is similar to `mkdirat` in POSIX.
+ */
+ path_create_directory :: proc(
+ fd: fd_t,
+ /**
+ * The path at which to create the directory.
+ */
+ path: string,
+ ) -> errno_t ---
+ /**
+ * Adjust the timestamps of a file or directory.
+ * Note: This is similar to `utimensat` in POSIX.
+ */
+ path_filestat_set_times :: proc(
+ fd: fd_t,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ flags: lookupflags_t,
+ /**
+ * The path of the file or directory to operate on.
+ */
+ path: string,
+ /**
+ * The desired values of the data access timestamp.
+ */
+ atim: timestamp_t,
+ /**
+ * The desired values of the data modification timestamp.
+ */
+ mtim: timestamp_t,
+ /**
+ * A bitmask indicating which timestamps to adjust.
+ */
+ fst_flags: fstflags_t,
+ ) -> errno_t ---
+ /**
+ * Remove a directory.
+ * Return `errno::notempty` if the directory is not empty.
+ * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
+ */
+ path_remove_directory :: proc(
+ fd: fd_t,
+ /**
+ * The path to a directory to remove.
+ */
+ path: string,
+ ) -> errno_t ---
+ /**
+ * Create a hard link.
+ * Note: This is similar to `linkat` in POSIX.
+ */
+ path_link :: proc(
+ old_fd: fd_t,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ old_flags: lookupflags_t,
+ /**
+ * The source path from which to link.
+ */
+ old_path: string,
+ /**
+ * The working directory at which the resolution of the new path starts.
+ */
+ new_fd: fd_t,
+ /**
+ * The destination path at which to create the hard link.
+ */
+ new_path: string,
+ ) -> errno_t ---
+
+ /**
+ * Rename a file or directory.
+ * Note: This is similar to `renameat` in POSIX.
+ */
+ path_rename :: proc(
+ fd: fd_t,
+ /**
+ * The source path of the file or directory to rename.
+ */
+ old_path: string,
+ /**
+ * The working directory at which the resolution of the new path starts.
+ */
+ new_fd: fd_t,
+ /**
+ * The destination path to which to rename the file or directory.
+ */
+ new_path: string,
+ ) -> errno_t ---
+
+ /**
+ * Create a symbolic link.
+ * Note: This is similar to `symlinkat` in POSIX.
+ */
+ path_symlink :: proc(
+ /**
+ * The contents of the symbolic link.
+ */
+ old_path: string,
+ fd: fd_t,
+ /**
+ * The destination path at which to create the symbolic link.
+ */
+ new_path: string,
+ ) -> errno_t ---
+
+ /**
+ * Unlink a file.
+ * Return `errno::isdir` if the path refers to a directory.
+ * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
+ */
+ path_unlink_file :: proc(
+ fd: fd_t,
+ /**
+ * The path to a file to unlink.
+ */
+ path: string,
+ ) -> errno_t ---
+
+ /**
+ * Write high-quality random data into a buffer.
+ * This function blocks when the implementation is unable to immediately
+ * provide sufficient high-quality random data.
+ * This function may execute slowly, so when large mounts of random data are
+ * required, it's advisable to use this function to seed a pseudo-random
+ * number generator, rather than to provide the random data directly.
+ */
+ random_get :: proc(
+ /**
+ * The buffer to fill with random data.
+ */
+ buf: []u8,
+ ) -> errno_t ---
+
}
/**
@@ -1250,7 +1400,7 @@ fd_pread :: proc "c" (
*/
offset: filesize_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n)
+ err = wasi_fd_pread(fd, iovs, offset, &n)
return
}
/**
@@ -1281,7 +1431,7 @@ fd_pwrite :: proc "c" (
*/
offset: filesize_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n)
+ err = wasi_fd_pwrite(fd, iovs, offset, &n)
return
}
/**
@@ -1297,7 +1447,7 @@ fd_read :: proc "c" (
*/
iovs: []iovec_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n)
+ err = wasi_fd_read(fd, iovs, &n)
return
}
/**
@@ -1324,7 +1474,7 @@ fd_readdir :: proc "c" (
*/
cookie: dircookie_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n)
+ err = wasi_fd_readdir(fd, buf, cookie, &n)
return
}
/**
@@ -1370,7 +1520,7 @@ fd_write :: proc "c" (
*/
iovs: []ciovec_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n)
+ err = wasi_fd_write(fd, iovs, &n)
return
}
/**
@@ -1390,7 +1540,7 @@ path_filestat_get :: proc "c" (
*/
path: string,
) -> (offset: filestat_t, err: errno_t) {
- err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset)
+ err = wasi_path_filestat_get(fd, flags, path, &offset)
return
}
/**
@@ -1432,7 +1582,7 @@ path_open :: proc "c" (
fs_rights_inheriting: rights_t,
fdflags: fdflags_t,
) -> (file: fd_t, err: errno_t) {
- err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
+ err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
return
}
/**
@@ -1452,7 +1602,7 @@ path_readlink :: proc "c" (
*/
buf: []u8,
) -> (n: size_t, err: errno_t) {
- err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n)
+ err = wasi_path_readlink(fd, path, buf, &n)
return
}
/**
@@ -1495,7 +1645,7 @@ sock_recv :: proc "c" (
*/
ri_flags: riflags_t,
) -> (n: size_t, flags: roflags_t, err: errno_t) {
- err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags)
+ err = wasi_sock_recv(fd, ri_data, ri_flags, &n, &flags)
return
}
/**
@@ -1516,172 +1666,11 @@ sock_send :: proc "c" (
*/
si_flags: siflags_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n)
+ err = wasi_sock_send(fd, si_data, si_flags, &n)
return
}
-/**
- * Return a description of the given preopened file descriptor.
- */
-fd_prestat_dir_name :: proc(
- fd: fd_t,
- /**
- * A buffer into which to write the preopened directory name.
- */
- path: string,
-) -> errno_t {
- return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path))
-}
-/**
- * Create a directory.
- * Note: This is similar to `mkdirat` in POSIX.
- */
-path_create_directory :: proc(
- fd: fd_t,
- /**
- * The path at which to create the directory.
- */
- path: string,
-) -> errno_t {
- return wasm_path_create_directory(fd, raw_data(path), len(path))
-}
-/**
- * Adjust the timestamps of a file or directory.
- * Note: This is similar to `utimensat` in POSIX.
- */
-path_filestat_set_times :: proc(
- fd: fd_t,
- /**
- * Flags determining the method of how the path is resolved.
- */
- flags: lookupflags_t,
- /**
- * The path of the file or directory to operate on.
- */
- path: string,
- /**
- * The desired values of the data access timestamp.
- */
- atim: timestamp_t,
- /**
- * The desired values of the data modification timestamp.
- */
- mtim: timestamp_t,
- /**
- * A bitmask indicating which timestamps to adjust.
- */
- fst_flags: fstflags_t,
-) -> errno_t {
- return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags)
-}
-/**
- * Remove a directory.
- * Return `errno::notempty` if the directory is not empty.
- * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
- */
-path_remove_directory :: proc(
- fd: fd_t,
- /**
- * The path to a directory to remove.
- */
- path: string,
-) -> errno_t {
- return wasm_path_remove_directory(fd, raw_data(path), len(path))
-}
-/**
- * Create a hard link.
- * Note: This is similar to `linkat` in POSIX.
- */
-path_link :: proc(
- old_fd: fd_t,
- /**
- * Flags determining the method of how the path is resolved.
- */
- old_flags: lookupflags_t,
- /**
- * The source path from which to link.
- */
- old_path: string,
- /**
- * The working directory at which the resolution of the new path starts.
- */
- new_fd: fd_t,
- /**
- * The destination path at which to create the hard link.
- */
- new_path: string,
-) -> errno_t {
- return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
-}
-/**
- * Rename a file or directory.
- * Note: This is similar to `renameat` in POSIX.
- */
-path_rename :: proc(
- fd: fd_t,
- /**
- * The source path of the file or directory to rename.
- */
- old_path: string,
- /**
- * The working directory at which the resolution of the new path starts.
- */
- new_fd: fd_t,
- /**
- * The destination path to which to rename the file or directory.
- */
- new_path: string,
-) -> errno_t {
- return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
-}
-/**
- * Create a symbolic link.
- * Note: This is similar to `symlinkat` in POSIX.
- */
-path_symlink :: proc(
- /**
- * The contents of the symbolic link.
- */
- old_path: string,
- fd: fd_t,
- /**
- * The destination path at which to create the symbolic link.
- */
- new_path: string,
-) -> errno_t {
- return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path))
-}
-/**
- * Unlink a file.
- * Return `errno::isdir` if the path refers to a directory.
- * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
- */
-path_unlink_file :: proc(
- fd: fd_t,
- /**
- * The path to a file to unlink.
- */
- path: string,
-) -> errno_t {
- return wasm_path_unlink_file(fd, raw_data(path), len(path))
-}
-/**
- * Write high-quality random data into a buffer.
- * This function blocks when the implementation is unable to immediately
- * provide sufficient high-quality random data.
- * This function may execute slowly, so when large mounts of random data are
- * required, it's advisable to use this function to seed a pseudo-random
- * number generator, rather than to provide the random data directly.
- */
-random_get :: proc(
- /**
- * The buffer to fill with random data.
- */
- buf: []u8,
-) -> errno_t {
- return wasm_random_get(raw_data(buf), len(buf))
-}
@@ -1722,8 +1711,7 @@ foreign wasi {
@(link_name="fd_pread")
wasi_fd_pread :: proc(
fd: fd_t,
- iovs: [^]iovec_t,
- iovs_len: size_t,
+ iovs: []iovec_t,
offset: filesize_t,
retptr0: ^size_t,
) -> errno_t ---
@@ -1735,23 +1723,20 @@ foreign wasi {
@(link_name="fd_pwrite")
wasi_fd_pwrite :: proc(
fd: fd_t,
- iovs: [^]ciovec_t,
- iovs_len: size_t,
+ iovs: []ciovec_t,
offset: filesize_t,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="fd_read")
wasi_fd_read :: proc(
fd: fd_t,
- iovs: [^]iovec_t,
- iovs_len: size_t,
+ iovs: []iovec_t,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="fd_readdir")
wasi_fd_readdir :: proc(
fd: fd_t,
- buf: [^]u8,
- buf_len: size_t,
+ buf: []u8,
cookie: dircookie_t,
retptr0: ^size_t,
) -> errno_t ---
@@ -1770,8 +1755,7 @@ foreign wasi {
@(link_name="fd_write")
wasi_fd_write :: proc(
fd: fd_t,
- iovs: [^]ciovec_t,
- iovs_len: size_t,
+ iovs: []ciovec_t,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="path_filestat_get")
@@ -1781,16 +1765,14 @@ foreign wasi {
/**
* The path of the file or directory to inspect.
*/
- path: [^]u8,
- path_len: size_t,
+ path: string,
retptr0: ^filestat_t,
) -> errno_t ---
@(link_name="path_open")
wasi_path_open :: proc(
fd: fd_t,
dirflags: lookupflags_t,
- path: [^]u8,
- path_len: size_t,
+ path: string,
oflags: oflags_t,
fs_rights_base: rights_t,
fs_rights_inheriting: rights_t,
@@ -1800,10 +1782,8 @@ foreign wasi {
@(link_name="path_readlink")
wasi_path_readlink :: proc(
fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- buf: [^]u8,
- buf_len: size_t,
+ path: string,
+ buf: []u8,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="poll_oneoff")
@@ -1816,8 +1796,7 @@ foreign wasi {
@(link_name="sock_recv")
wasi_sock_recv :: proc(
fd: fd_t,
- ri_data: [^]iovec_t,
- ri_data_len: size_t,
+ ri_data: []iovec_t,
ri_flags: riflags_t,
retptr0: ^size_t,
retptr1: ^roflags_t,
@@ -1825,75 +1804,8 @@ foreign wasi {
@(link_name="sock_send")
wasi_sock_send :: proc(
fd: fd_t,
- si_data: [^]ciovec_t,
- si_data_len: size_t,
+ si_data: []ciovec_t,
si_flags: siflags_t,
retptr0: ^size_t,
) -> errno_t ---
- @(link_name="fd_prestat_dir_name")
- wasm_fd_prestat_dir_name :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_create_directory")
- wasm_path_create_directory :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_filestat_set_times")
- wasm_path_filestat_set_times :: proc(
- fd: fd_t,
- flags: lookupflags_t,
- path: [^]u8,
- path_len: size_t,
- atim: timestamp_t,
- mtim: timestamp_t,
- fst_flags: fstflags_t,
- ) -> errno_t ---
- @(link_name="path_remove_directory")
- wasm_path_remove_directory :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_link")
- wasm_path_link :: proc(
- old_fd: fd_t,
- old_flags: lookupflags_t,
- old_path: [^]u8,
- old_path_len: size_t,
- new_fd: fd_t,
- new_path: [^]u8,
- new_path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_rename")
- wasm_path_rename :: proc(
- fd: fd_t,
- old_path: [^]u8,
- old_path_len: size_t,
- new_fd: fd_t,
- new_path: [^]u8,
- new_path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_symlink")
- wasm_path_symlink :: proc(
- old_path: [^]u8,
- old_path_len: size_t,
- fd: fd_t,
- new_path: [^]u8,
- new_path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_unlink_file")
- wasm_path_unlink_file :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="random_get")
- wasm_random_get :: proc(
- buf: [^]u8,
- buf_len: size_t,
- ) -> errno_t ---
}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 284936852..f055b6908 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -290,10 +290,10 @@ foreign kernel32 {
InitializeSRWLock :: proc(SRWLock: ^SRWLOCK) ---
AcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) ---
- TryAcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) -> BOOL ---
+ TryAcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) -> BOOLEAN ---
ReleaseSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) ---
AcquireSRWLockShared :: proc(SRWLock: ^SRWLOCK) ---
- TryAcquireSRWLockShared :: proc(SRWLock: ^SRWLOCK) -> BOOL ---
+ TryAcquireSRWLockShared :: proc(SRWLock: ^SRWLOCK) -> BOOLEAN ---
ReleaseSRWLockShared :: proc(SRWLock: ^SRWLOCK) ---
InitializeConditionVariable :: proc(ConditionVariable: ^CONDITION_VARIABLE) ---
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
index 47de354b6..4b3d3e68e 100644
--- a/core/sys/windows/user32.odin
+++ b/core/sys/windows/user32.odin
@@ -18,6 +18,7 @@ foreign user32 {
RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
+ UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
CreateWindowExW :: proc(
dwExStyle: DWORD,
@@ -41,6 +42,7 @@ foreign user32 {
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
GetForegroundWindow :: proc() -> HWND ---
+ UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
GetActiveWindow :: proc() -> HWND ---
@@ -94,6 +96,7 @@ foreign user32 {
GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
+ AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL ---
SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL ---
@@ -136,7 +139,19 @@ foreign user32 {
SetCursor :: proc(hCursor: HCURSOR) -> HCURSOR ---
EnumDisplaySettingsW :: proc(lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: ^DEVMODEW) -> BOOL ---
-
+
+ MonitorFromPoint :: proc(pt: POINT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
+ MonitorFromRect :: proc(lprc: LPRECT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
+ MonitorFromWindow :: proc(hwnd: HWND, dwFlags: Monitor_From_Flags) -> HMONITOR ---
+ EnumDisplayMonitors :: proc(hdc: HDC, lprcClip: LPRECT, lpfnEnum: Monitor_Enum_Proc, dwData: LPARAM) -> BOOL ---
+
+ SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT ---
+ GetThreadDpiAwarenessContext :: proc() -> DPI_AWARENESS_CONTEXT ---
+ GetWindowDpiAwarenessContext :: proc(hwnd: HWND) -> DPI_AWARENESS_CONTEXT ---
+ GetDpiFromDpiAwarenessContext :: proc(value: DPI_AWARENESS_CONTEXT) -> UINT ---
+ GetDpiForWindow :: proc(hwnd: HWND) -> UINT ---
+ SetProcessDpiAwarenessContext :: proc(value: DPI_AWARENESS_CONTEXT) -> BOOL ---
+
BroadcastSystemMessageW :: proc(
flags: DWORD,
lpInfo: LPDWORD,
@@ -220,7 +235,7 @@ when ODIN_ARCH == .amd64 {
SetClassLongPtrW :: SetClassLongW
GetWindowLongPtrW :: GetWindowLongW
- SetWindowLongPtrW :: GetWindowLongW
+ SetWindowLongPtrW :: SetWindowLongW
}
GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int {
@@ -246,3 +261,19 @@ GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD
MAKEINTRESOURCEW :: #force_inline proc "contextless" (#any_int i: int) -> LPWSTR {
return cast(LPWSTR)uintptr(WORD(i))
}
+
+Monitor_From_Flags :: enum DWORD {
+ MONITOR_DEFAULTTONULL = 0x00000000, // Returns NULL
+ MONITOR_DEFAULTTOPRIMARY = 0x00000001, // Returns a handle to the primary display monitor
+ MONITOR_DEFAULTTONEAREST = 0x00000002, // Returns a handle to the display monitor that is nearest to the window
+}
+
+Monitor_Enum_Proc :: #type proc "stdcall" (HMONITOR, HDC, LPRECT, LPARAM) -> BOOL
+
+USER_DEFAULT_SCREEN_DPI :: 96
+DPI_AWARENESS_CONTEXT :: distinct HANDLE
+DPI_AWARENESS_CONTEXT_UNAWARE :: DPI_AWARENESS_CONTEXT(~uintptr(0)) // -1
+DPI_AWARENESS_CONTEXT_SYSTEM_AWARE :: DPI_AWARENESS_CONTEXT(~uintptr(1)) // -2
+DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE :: DPI_AWARENESS_CONTEXT(~uintptr(2)) // -3
+DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 :: DPI_AWARENESS_CONTEXT(~uintptr(3)) // -4
+DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED :: DPI_AWARENESS_CONTEXT(~uintptr(4)) // -5
diff --git a/core/sys/windows/wgl.odin b/core/sys/windows/wgl.odin
index 689a41dea..150358d01 100644
--- a/core/sys/windows/wgl.odin
+++ b/core/sys/windows/wgl.odin
@@ -62,7 +62,7 @@ GetExtensionsStringARBType :: #type proc "c" (HDC) -> cstring
// Procedures
wglCreateContextAttribsARB: CreateContextAttribsARBType
wglChoosePixelFormatARB: ChoosePixelFormatARBType
- wglSwapIntervalExt: SwapIntervalEXTType
+ wglSwapIntervalEXT: SwapIntervalEXTType
wglGetExtensionsStringARB: GetExtensionsStringARBType
diff --git a/core/sys/windows/wglext.odin b/core/sys/windows/wglext.odin
new file mode 100644
index 000000000..0c4b51d65
--- /dev/null
+++ b/core/sys/windows/wglext.odin
@@ -0,0 +1,147 @@
+// +build windows
+package sys_windows
+
+// WGL_ARB_buffer_region
+WGL_FRONT_COLOR_BUFFER_BIT_ARB :: 0x00000001
+WGL_BACK_COLOR_BUFFER_BIT_ARB :: 0x00000002
+WGL_DEPTH_BUFFER_BIT_ARB :: 0x00000004
+WGL_STENCIL_BUFFER_BIT_ARB :: 0x00000008
+
+wglCreateBufferRegionARBType :: #type proc "c" (hDC: HDC, iLayerPlane: c_int, uType: UINT) -> HANDLE
+wglDeleteBufferRegionARBType :: #type proc "c" (hRegion: HANDLE)
+wglSaveBufferRegionARBType :: #type proc "c" (hRegion: HANDLE, x: c_int, y: c_int, width: c_int, height: c_int) -> BOOL
+wglRestoreBufferRegionARBType :: #type proc "c" (hRegion: HANDLE, x: c_int, y: c_int, width: c_int, height: c_int, xSrc: c_int, ySrc: c_int) -> BOOL
+
+// wglCreateBufferRegionARB: wglCreateBufferRegionARBType
+// wglDeleteBufferRegionARB: wglDeleteBufferRegionARBType
+// wglSaveBufferRegionARB: wglSaveBufferRegionARBType
+// wglRestoreBufferRegionARB: wglRestoreBufferRegionARBType
+
+// WGL_ARB_context_flush_control
+WGL_CONTEXT_RELEASE_BEHAVIOR_ARB :: 0x2097
+WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB :: 0
+WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB :: 0x2098
+
+// WGL_ARB_create_context
+WGL_CONTEXT_DEBUG_BIT_ARB :: 0x0001
+WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002
+WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091
+WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092
+WGL_CONTEXT_LAYER_PLANE_ARB :: 0x2093
+WGL_CONTEXT_FLAGS_ARB :: 0x2094
+ERROR_INVALID_VERSION_ARB :: 0x2095
+
+// WGL_ARB_create_context_no_error
+WGL_CONTEXT_OPENGL_NO_ERROR_ARB :: 0x31B3
+
+// WGL_ARB_create_context_profile
+WGL_CONTEXT_PROFILE_MASK_ARB :: 0x9126
+WGL_CONTEXT_CORE_PROFILE_BIT_ARB :: 0x0001
+WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x0002
+ERROR_INVALID_PROFILE_ARB :: 0x2096
+
+// WGL_ARB_create_context_robustness
+WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB :: 0x00000004
+WGL_LOSE_CONTEXT_ON_RESET_ARB :: 0x8252
+WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB :: 0x8256
+WGL_NO_RESET_NOTIFICATION_ARB :: 0x8261
+
+// WGL_ARB_framebuffer_sRGB
+WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB :: 0x20A9
+
+// WGL_ARB_make_current_read
+ERROR_INVALID_PIXEL_TYPE_ARB :: 0x2043
+ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB :: 0x2054
+
+wglMakeContextCurrentARBType :: #type proc "c" (hDrawDC: HDC, hReadDC:HDC, hglrc: HGLRC) -> BOOL
+wglGetCurrentReadDCARBType :: #type proc "c" () -> HDC
+
+// wglMakeContextCurrentARB: wglMakeContextCurrentARBType
+// wglGetCurrentReadDCARB: wglGetCurrentReadDCARBType
+
+// WGL_ARB_multisample
+WGL_SAMPLE_BUFFERS_ARB :: 0x2041
+WGL_SAMPLES_ARB :: 0x2042
+
+// WGL_ARB_pbuffer
+HPBUFFERARB :: distinct rawptr
+WGL_DRAW_TO_PBUFFER_ARB :: 0x202D
+WGL_MAX_PBUFFER_PIXELS_ARB :: 0x202E
+WGL_MAX_PBUFFER_WIDTH_ARB :: 0x202F
+WGL_MAX_PBUFFER_HEIGHT_ARB :: 0x2030
+WGL_PBUFFER_LARGEST_ARB :: 0x2033
+WGL_PBUFFER_WIDTH_ARB :: 0x2034
+WGL_PBUFFER_HEIGHT_ARB :: 0x2035
+WGL_PBUFFER_LOST_ARB :: 0x2036
+
+wglCreatePbufferARBType :: #type proc "c" (hDC: HDC, iPixelFormat, iWidth, iHeight: c_int, piAttribList: [^]c_int) -> HPBUFFERARB
+wglGetPbufferDCARBType :: #type proc "c" (hPbuffer: HPBUFFERARB) -> HDC
+wglReleasePbufferDCARBType :: #type proc "c" (hPbuffer: HPBUFFERARB, hDC: HDC) -> c_int
+wglDestroyPbufferARBType :: #type proc "c" (hPbuffer: HPBUFFERARB) -> BOOL
+wglQueryPbufferARBType :: #type proc "c" (hPbuffer: HPBUFFERARB, iAttribute: c_int, piValue: ^c_int) -> BOOL
+
+// wglCreatePbufferARB: wglCreatePbufferARBType
+// wglGetPbufferDCARB: wglGetPbufferDCARBType
+// wglReleasePbufferDCARB: wglReleasePbufferDCARBType
+// wglDestroyPbufferARB: wglDestroyPbufferARBType
+// wglQueryPbufferARB: wglQueryPbufferARBType
+
+// WGL_ARB_pixel_format
+WGL_NUMBER_PIXEL_FORMATS_ARB :: 0x2000
+WGL_DRAW_TO_WINDOW_ARB :: 0x2001
+WGL_DRAW_TO_BITMAP_ARB :: 0x2002
+WGL_ACCELERATION_ARB :: 0x2003
+WGL_NEED_PALETTE_ARB :: 0x2004
+WGL_NEED_SYSTEM_PALETTE_ARB :: 0x2005
+WGL_SWAP_LAYER_BUFFERS_ARB :: 0x2006
+WGL_SWAP_METHOD_ARB :: 0x2007
+WGL_NUMBER_OVERLAYS_ARB :: 0x2008
+WGL_NUMBER_UNDERLAYS_ARB :: 0x2009
+WGL_TRANSPARENT_ARB :: 0x200A
+WGL_TRANSPARENT_RED_VALUE_ARB :: 0x2037
+WGL_TRANSPARENT_GREEN_VALUE_ARB :: 0x2038
+WGL_TRANSPARENT_BLUE_VALUE_ARB :: 0x2039
+WGL_TRANSPARENT_ALPHA_VALUE_ARB :: 0x203A
+WGL_TRANSPARENT_INDEX_VALUE_ARB :: 0x203B
+WGL_SHARE_DEPTH_ARB :: 0x200C
+WGL_SHARE_STENCIL_ARB :: 0x200D
+WGL_SHARE_ACCUM_ARB :: 0x200E
+WGL_SUPPORT_GDI_ARB :: 0x200F
+WGL_SUPPORT_OPENGL_ARB :: 0x2010
+WGL_DOUBLE_BUFFER_ARB :: 0x2011
+WGL_STEREO_ARB :: 0x2012
+WGL_PIXEL_TYPE_ARB :: 0x2013
+WGL_COLOR_BITS_ARB :: 0x2014
+WGL_RED_BITS_ARB :: 0x2015
+WGL_RED_SHIFT_ARB :: 0x2016
+WGL_GREEN_BITS_ARB :: 0x2017
+WGL_GREEN_SHIFT_ARB :: 0x2018
+WGL_BLUE_BITS_ARB :: 0x2019
+WGL_BLUE_SHIFT_ARB :: 0x201A
+WGL_ALPHA_BITS_ARB :: 0x201B
+WGL_ALPHA_SHIFT_ARB :: 0x201C
+WGL_ACCUM_BITS_ARB :: 0x201D
+WGL_ACCUM_RED_BITS_ARB :: 0x201E
+WGL_ACCUM_GREEN_BITS_ARB :: 0x201F
+WGL_ACCUM_BLUE_BITS_ARB :: 0x2020
+WGL_ACCUM_ALPHA_BITS_ARB :: 0x2021
+WGL_DEPTH_BITS_ARB :: 0x2022
+WGL_STENCIL_BITS_ARB :: 0x2023
+WGL_AUX_BUFFERS_ARB :: 0x2024
+WGL_NO_ACCELERATION_ARB :: 0x2025
+WGL_GENERIC_ACCELERATION_ARB :: 0x2026
+WGL_FULL_ACCELERATION_ARB :: 0x2027
+WGL_SWAP_EXCHANGE_ARB :: 0x2028
+WGL_SWAP_COPY_ARB :: 0x2029
+WGL_SWAP_UNDEFINED_ARB :: 0x202A
+WGL_TYPE_RGBA_ARB :: 0x202B
+WGL_TYPE_COLORINDEX_ARB :: 0x202C
+
+wglGetPixelFormatAttribivARBType :: #type proc "c" (hdc: HDC, iPixelFormat, iLayerPlane: c_int, nAttributes: UINT, piAttributes: [^]c_int, piValues: [^]c_int) -> BOOL
+wglGetPixelFormatAttribfvARBType :: #type proc "c" (hdc: HDC, iPixelFormat, iLayerPlane: c_int, nAttributes: UINT, piAttributes: [^]c_int, pfValues: [^]f32) -> BOOL
+
+// wglGetPixelFormatAttribivARB: wglGetPixelFormatAttribivARBType
+// wglGetPixelFormatAttribfvARB: wglGetPixelFormatAttribfvARBType
+
+// WGL_ARB_pixel_format_float
+WGL_TYPE_RGBA_FLOAT_ARB :: 0x21A0
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin
index d6b883916..7ce9e545c 100644
--- a/core/sys/windows/window_messages.odin
+++ b/core/sys/windows/window_messages.odin
@@ -153,6 +153,7 @@ BM_CLICK :: 0x00f5
BM_GETIMAGE :: 0x00f6
BM_SETIMAGE :: 0x00f7
BM_SETDONTCLICK :: 0x00f8
+WM_INPUT_DEVICE_CHANGE :: 0x00fe
WM_INPUT :: 0x00ff
WM_KEYDOWN :: 0x0100
WM_KEYFIRST :: 0x0100
@@ -165,6 +166,7 @@ WM_SYSCHAR :: 0x0106
WM_SYSDEADCHAR :: 0x0107
WM_UNICHAR :: 0x0109
WM_KEYLAST :: 0x0109
+UNICODE_NOCHAR :: 0xFFFF
WM_WNT_CONVERTREQUESTEX :: 0x0109
WM_CONVERTREQUEST :: 0x010a
WM_CONVERTRESULT :: 0x010b
@@ -279,6 +281,27 @@ WM_ENTERSIZEMOVE :: 0x0231
WM_EXITSIZEMOVE :: 0x0232
WM_DROPFILES :: 0x0233
WM_MDIREFRESHMENU :: 0x0234
+WM_POINTERDEVICECHANGE :: 0x0238
+WM_POINTERDEVICEINRANGE :: 0x0239
+WM_POINTERDEVICEOUTOFRANGE :: 0x023a
+WM_TOUCH :: 0x0240
+WM_NCPOINTERUPDATE :: 0x0241
+WM_NCPOINTERDOWN :: 0x0242
+WM_NCPOINTERUP :: 0x0243
+WM_POINTERUPDATE :: 0x0245
+WM_POINTERDOWN :: 0x0246
+WM_POINTERUP :: 0x0247
+WM_POINTERENTER :: 0x0249
+WM_POINTERLEAVE :: 0x024a
+WM_POINTERACTIVATE :: 0x024b
+WM_POINTERCAPTURECHANGED :: 0x024c
+WM_TOUCHHITTESTING :: 0x024d
+WM_POINTERWHEEL :: 0x024e
+WM_POINTERHWHEEL :: 0x024f
+DM_POINTERHITTEST :: 0x0250
+WM_POINTERROUTEDTO :: 0x0251
+WM_POINTERROUTEDAWAY :: 0x0252
+WM_POINTERROUTEDRELEASED :: 0x0253
WM_IME_REPORT :: 0x0280
WM_IME_SETCONTEXT :: 0x0281
WM_IME_NOTIFY :: 0x0282
@@ -295,6 +318,13 @@ WM_NCMOUSEHOVER :: 0x02a0
WM_MOUSEHOVER :: 0x02a1
WM_NCMOUSELEAVE :: 0x02a2
WM_MOUSELEAVE :: 0x02a3
+WM_WTSSESSION_CHANGE :: 0x02b1
+WM_TABLET_FIRST :: 0x02c0
+WM_TABLET_LAST :: 0x02df
+WM_DPICHANGED :: 0x02e0
+WM_DPICHANGED_BEFOREPARENT :: 0x02e2
+WM_DPICHANGED_AFTERPARENT :: 0x02e3
+WM_GETDPISCALEDSIZE :: 0x02e4
WM_CUT :: 0x0300
WM_COPY :: 0x0301
WM_PASTE :: 0x0302
@@ -317,6 +347,15 @@ WM_HOTKEY :: 0x0312
WM_PRINT :: 0x0317
WM_PRINTCLIENT :: 0x0318
WM_APPCOMMAND :: 0x0319
+WM_THEMECHANGED :: 0x031A
+WM_CLIPBOARDUPDATE :: 0x031D
+WM_DWMCOMPOSITIONCHANGED :: 0x031E
+WM_DWMNCRENDERINGCHANGED :: 0x031F
+WM_DWMCOLORIZATIONCOLORCHANGED:: 0x0320
+WM_DWMWINDOWMAXIMIZEDCHANGE :: 0x0321
+WM_DWMSENDICONICTHUMBNAIL :: 0x0323
+WM_DWMSENDICONICLIVEPREVIEWBITMAP :: 0x0326
+WM_GETTITLEBARINFOEX :: 0x033F
WM_HANDHELDFIRST :: 0x0358
WM_HANDHELDLAST :: 0x035f
WM_AFXFIRST :: 0x0360
diff --git a/core/text/i18n/gettext.odin b/core/text/i18n/gettext.odin
index d99ec1c9b..d5537a19c 100644
--- a/core/text/i18n/gettext.odin
+++ b/core/text/i18n/gettext.odin
@@ -99,14 +99,14 @@ parse_mo_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, plur
}
for k in keys {
- interned_key := strings.intern_get(&translation.intern, string(k))
+ interned_key, _ := strings.intern_get(&translation.intern, string(k))
interned_vals := make([]string, len(keys))
last_val: string
i := 0
for v in vals {
- interned_vals[i] = strings.intern_get(&translation.intern, string(v))
+ interned_vals[i], _ = strings.intern_get(&translation.intern, string(v))
last_val = interned_vals[i]
i += 1
}
diff --git a/core/text/i18n/qt_linguist.odin b/core/text/i18n/qt_linguist.odin
index 036a89eeb..e7c1f9974 100644
--- a/core/text/i18n/qt_linguist.odin
+++ b/core/text/i18n/qt_linguist.odin
@@ -59,9 +59,9 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
return translation, .TS_File_Expected_Context_Name,
}
- section_name := strings.intern_get(&translation.intern, "")
+ section_name, _ := strings.intern_get(&translation.intern, "")
if !options.merge_sections {
- section_name = strings.intern_get(&translation.intern, ts.elements[section_name_id].value)
+ section_name, _ = strings.intern_get(&translation.intern, ts.elements[section_name_id].value)
}
if section_name not_in translation.k_v {
@@ -92,8 +92,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
return translation, .TS_File_Expected_Translation
}
- source := strings.intern_get(&translation.intern, ts.elements[source_id].value)
- xlat := strings.intern_get(&translation.intern, ts.elements[translation_id].value)
+ source, _ := strings.intern_get(&translation.intern, ts.elements[source_id].value)
+ xlat, _ := strings.intern_get(&translation.intern, ts.elements[translation_id].value)
if source in section {
return translation, .Duplicate_Key
@@ -124,7 +124,7 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
if !numerus_found {
break
}
- numerus := strings.intern_get(&translation.intern, ts.elements[numerus_id].value)
+ numerus, _ := strings.intern_get(&translation.intern, ts.elements[numerus_id].value)
section[source][num_plurals] = numerus
num_plurals += 1
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 65da09df0..3f6be3c48 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -116,6 +116,7 @@ struct TargetMetrics {
TargetArchKind arch;
isize word_size;
isize max_align;
+ isize max_simd_align;
String target_triplet;
String target_data_layout;
TargetABIKind abi;
@@ -204,7 +205,7 @@ enum BuildPath : u8 {
BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
BuildPath_RES, // Output Path for .res file, generated from previous.
- BuildPath_Win_SDK_Root, // windows_sdk_root
+ BuildPath_Win_SDK_Bin_Path, // windows_sdk_bin_path
BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
BuildPath_VS_EXE, // vs_exe_path
@@ -228,14 +229,16 @@ struct BuildContext {
bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
bool ODIN_FOREIGN_ERROR_PROCEDURES;
+ bool ODIN_VALGRIND_SUPPORT;
ErrorPosStyle ODIN_ERROR_POS_STYLE;
TargetEndianKind endian_kind;
// In bytes
- i64 word_size; // Size of a pointer, must be >= 4
- i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
+ i64 word_size; // Size of a pointer, must be >= 4
+ i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
+ i64 max_simd_align; // max alignment, must be >= 1 (and typically >= word_size)
CommandKind command_kind;
String command;
@@ -338,15 +341,13 @@ bool global_ignore_warnings(void) {
gb_global TargetMetrics target_windows_i386 = {
TargetOs_windows,
TargetArch_i386,
- 4,
- 8,
+ 4, 4, 8,
str_lit("i386-pc-windows-msvc"),
};
gb_global TargetMetrics target_windows_amd64 = {
TargetOs_windows,
TargetArch_amd64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("x86_64-pc-windows-msvc"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
@@ -354,24 +355,21 @@ gb_global TargetMetrics target_windows_amd64 = {
gb_global TargetMetrics target_linux_i386 = {
TargetOs_linux,
TargetArch_i386,
- 4,
- 8,
+ 4, 4, 8,
str_lit("i386-pc-linux-gnu"),
};
gb_global TargetMetrics target_linux_amd64 = {
TargetOs_linux,
TargetArch_amd64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("x86_64-pc-linux-gnu"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
gb_global TargetMetrics target_linux_arm64 = {
TargetOs_linux,
TargetArch_arm64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("aarch64-linux-elf"),
str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
};
@@ -379,8 +377,7 @@ gb_global TargetMetrics target_linux_arm64 = {
gb_global TargetMetrics target_linux_arm32 = {
TargetOs_linux,
TargetArch_arm32,
- 4,
- 8,
+ 4, 4, 8,
str_lit("arm-linux-gnu"),
str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
};
@@ -388,8 +385,7 @@ gb_global TargetMetrics target_linux_arm32 = {
gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
TargetArch_amd64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("x86_64-apple-darwin"),
str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
};
@@ -397,8 +393,7 @@ gb_global TargetMetrics target_darwin_amd64 = {
gb_global TargetMetrics target_darwin_arm64 = {
TargetOs_darwin,
TargetArch_arm64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("arm64-apple-macosx11.0.0"),
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
};
@@ -406,16 +401,14 @@ gb_global TargetMetrics target_darwin_arm64 = {
gb_global TargetMetrics target_freebsd_i386 = {
TargetOs_freebsd,
TargetArch_i386,
- 4,
- 8,
+ 4, 4, 8,
str_lit("i386-unknown-freebsd-elf"),
};
gb_global TargetMetrics target_freebsd_amd64 = {
TargetOs_freebsd,
TargetArch_amd64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("x86_64-unknown-freebsd-elf"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
@@ -423,8 +416,7 @@ gb_global TargetMetrics target_freebsd_amd64 = {
gb_global TargetMetrics target_openbsd_amd64 = {
TargetOs_openbsd,
TargetArch_amd64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("x86_64-unknown-openbsd-elf"),
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
};
@@ -432,62 +424,48 @@ gb_global TargetMetrics target_openbsd_amd64 = {
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("x86_64-pc-none-elf"),
};
+
gb_global TargetMetrics target_freestanding_wasm32 = {
TargetOs_freestanding,
TargetArch_wasm32,
- 4,
- 8,
+ 4, 8, 16,
str_lit("wasm32-freestanding-js"),
- str_lit(""),
+ str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
gb_global TargetMetrics target_js_wasm32 = {
TargetOs_js,
TargetArch_wasm32,
- 4,
- 8,
+ 4, 8, 16,
str_lit("wasm32-js-js"),
- str_lit(""),
-};
-
-gb_global TargetMetrics target_js_wasm64 = {
- TargetOs_js,
- TargetArch_wasm64,
- 8,
- 16,
- str_lit("wasm64-js-js"),
- str_lit(""),
+ str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
gb_global TargetMetrics target_wasi_wasm32 = {
TargetOs_wasi,
TargetArch_wasm32,
- 4,
- 8,
+ 4, 8, 16,
str_lit("wasm32-wasi-js"),
- str_lit(""),
+ str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
-// gb_global TargetMetrics target_freestanding_wasm64 = {
-// TargetOs_freestanding,
-// TargetArch_wasm64,
-// 8,
-// 16,
-// str_lit("wasm64-freestanding-js"),
-// str_lit(""),
-// };
+gb_global TargetMetrics target_js_wasm64 = {
+ TargetOs_js,
+ TargetArch_wasm64,
+ 8, 8, 16,
+ str_lit("wasm64-js-js"),
+ str_lit(""),
+};
gb_global TargetMetrics target_freestanding_amd64_sysv = {
TargetOs_freestanding,
TargetArch_amd64,
- 8,
- 16,
+ 8, 8, 16,
str_lit("x86_64-pc-none-gnu"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
TargetABI_SysV,
@@ -516,7 +494,7 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
{ str_lit("js_wasm32"), &target_js_wasm32 },
- { str_lit("js_wasm64"), &target_js_wasm64 },
+ // { str_lit("js_wasm64"), &target_js_wasm64 },
{ str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
};
@@ -1083,14 +1061,16 @@ void init_build_context(TargetMetrics *cross_target) {
GB_ASSERT(metrics->arch != TargetArch_Invalid);
GB_ASSERT(metrics->word_size > 1);
GB_ASSERT(metrics->max_align > 1);
+ GB_ASSERT(metrics->max_simd_align > 1);
bc->metrics = *metrics;
- bc->ODIN_OS = target_os_names[metrics->os];
- bc->ODIN_ARCH = target_arch_names[metrics->arch];
- bc->endian_kind = target_endians[metrics->arch];
- bc->word_size = metrics->word_size;
- bc->max_align = metrics->max_align;
+ bc->ODIN_OS = target_os_names[metrics->os];
+ bc->ODIN_ARCH = target_arch_names[metrics->arch];
+ bc->endian_kind = target_endians[metrics->arch];
+ bc->word_size = metrics->word_size;
+ bc->max_align = metrics->max_align;
+ bc->max_simd_align = metrics->max_simd_align;
bc->link_flags = str_lit(" ");
#if defined(DEFAULT_TO_THREADED_CHECKER)
@@ -1190,6 +1170,8 @@ void init_build_context(TargetMetrics *cross_target) {
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
+ bc->ODIN_VALGRIND_SUPPORT = is_arch_x86() && build_context.metrics.os != TargetOs_windows;
+
#undef LINK_FLAG_X64
#undef LINK_FLAG_386
}
@@ -1336,7 +1318,7 @@ bool init_build_paths(String init_filename) {
if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
- Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
+ Find_Result find_result = find_visual_studio_and_windows_sdk();
defer (mc_free_all());
if (find_result.windows_sdk_version == 0) {
@@ -1357,8 +1339,8 @@ bool init_build_paths(String init_filename) {
if (find_result.windows_sdk_um_library_path.len > 0) {
GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
- if (find_result.windows_sdk_root.len > 0) {
- bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
+ if (find_result.windows_sdk_bin_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_Bin_Path] = path_from_string(ha, find_result.windows_sdk_bin_path);
}
if (find_result.windows_sdk_um_library_path.len > 0) {
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 8108604ba..e55a2e024 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -1074,6 +1074,505 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call
return false;
}
+bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_) {
+ ast_node(ce, CallExpr, call);
+ ast_node(bd, BasicDirective, ce->proc);
+ String builtin_name = bd->name.string;
+
+ String base_dir = dir_from_path(get_file_path_string(call->file_id));
+
+ BlockingMutex *ignore_mutex = nullptr;
+ String path = {};
+ bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
+ gb_unused(ok);
+
+
+ MUTEX_GUARD(&c->info->load_file_mutex);
+
+ gbFileError file_error = gbFileError_None;
+ String data = {};
+
+ LoadFileCache **cache_ptr = string_map_get(&c->info->load_file_cache, path);
+ LoadFileCache *cache = cache_ptr ? *cache_ptr : nullptr;
+ if (cache) {
+ file_error = cache->file_error;
+ data = cache->data;
+ }
+ defer ({
+ if (cache == nullptr) {
+ LoadFileCache *new_cache = gb_alloc_item(permanent_allocator(), LoadFileCache);
+ new_cache->path = path;
+ new_cache->data = data;
+ new_cache->file_error = file_error;
+ string_map_init(&new_cache->hashes, heap_allocator(), 32);
+ string_map_set(&c->info->load_file_cache, path, new_cache);
+ if (cache_) *cache_ = new_cache;
+ } else {
+ cache->data = data;
+ cache->file_error = file_error;
+ if (cache_) *cache_ = cache;
+ }
+ });
+
+ char *c_str = alloc_cstring(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), c_str));
+
+ gbFile f = {};
+ if (cache == nullptr) {
+ file_error = gb_file_open(&f, c_str);
+ }
+ defer (gb_file_close(&f));
+
+ switch (file_error) {
+ default:
+ case gbFileError_Invalid:
+ if (err_on_not_found) {
+ error(ce->proc, "Failed to `#%.*s` file: %s; invalid file or cannot be found", LIT(builtin_name), c_str);
+ }
+ call->state_flags |= StateFlag_DirectiveWasFalse;
+ return false;
+ case gbFileError_NotExists:
+ if (err_on_not_found) {
+ error(ce->proc, "Failed to `#%.*s` file: %s; file cannot be found", LIT(builtin_name), c_str);
+ }
+ call->state_flags |= StateFlag_DirectiveWasFalse;
+ return false;
+ case gbFileError_Permission:
+ if (err_on_not_found) {
+ error(ce->proc, "Failed to `#%.*s` file: %s; file permissions problem", LIT(builtin_name), c_str);
+ }
+ call->state_flags |= StateFlag_DirectiveWasFalse;
+ return false;
+ case gbFileError_None:
+ // Okay
+ break;
+ }
+
+ if (cache == nullptr) {
+ isize file_size = cast(isize)gb_file_size(&f);
+ if (file_size > 0) {
+ u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1);
+ gb_file_read_at(&f, ptr, file_size, 0);
+ ptr[file_size] = '\0';
+ data.text = ptr;
+ data.len = file_size;
+ }
+ }
+
+ return true;
+}
+
+
+bool is_valid_type_for_load(Type *type) {
+ if (type == t_invalid) {
+ return false;
+ } else if (is_type_string(type)) {
+ return true;
+ } else if (is_type_slice(type) /*|| is_type_array(type) || is_type_enumerated_array(type)*/) {
+ Type *elem = nullptr;
+ Type *bt = base_type(type);
+ if (bt->kind == Type_Slice) {
+ elem = bt->Slice.elem;
+ } else if (bt->kind == Type_Array) {
+ elem = bt->Array.elem;
+ } else if (bt->kind == Type_EnumeratedArray) {
+ elem = bt->EnumeratedArray.elem;
+ }
+ GB_ASSERT(elem != nullptr);
+ return is_type_load_safe(elem);
+ }
+ return false;
+}
+
+LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) {
+ ast_node(ce, CallExpr, call);
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ GB_ASSERT(name == "load");
+
+ if (ce->args.count != 1 && ce->args.count != 2) {
+ if (ce->args.count == 0) {
+ error(ce->close, "'#%.*s' expects 1 or 2 arguments, got 0", LIT(name));
+ } else {
+ error(ce->args[0], "'#%.*s' expects 1 or 2 arguments, got %td", LIT(name), ce->args.count);
+ }
+
+ return LoadDirective_Error;
+ }
+
+ Ast *arg = ce->args[0];
+ Operand o = {};
+ check_expr(c, &o, arg);
+ if (o.mode != Addressing_Constant) {
+ error(arg, "'#%.*s' expected a constant string argument", LIT(name));
+ return LoadDirective_Error;
+ }
+
+ if (!is_type_string(o.type)) {
+ gbString str = type_to_string(o.type);
+ error(arg, "'#%.*s' expected a constant string, got %s", LIT(name), str);
+ gb_string_free(str);
+ return LoadDirective_Error;
+ }
+
+ GB_ASSERT(o.value.kind == ExactValue_String);
+
+ operand->type = t_u8_slice;
+ if (ce->args.count == 1) {
+ if (type_hint && is_valid_type_for_load(type_hint)) {
+ operand->type = type_hint;
+ }
+ } else if (ce->args.count == 2) {
+ Ast *arg_type = ce->args[1];
+ Type *type = check_type(c, arg_type);
+ if (type != nullptr) {
+ if (is_valid_type_for_load(type)) {
+ operand->type = type;
+ } else {
+ gbString type_str = type_to_string(type);
+ error(arg_type, "'#%.*s' invalid type, expected a string, or slice of simple types, got %s", LIT(name), type_str);
+ gb_string_free(type_str);
+ }
+ }
+ } else {
+ GB_PANIC("unreachable");
+ }
+ operand->mode = Addressing_Constant;
+
+ LoadFileCache *cache = nullptr;
+ if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache)) {
+ operand->value = exact_value_string(cache->data);
+ return LoadDirective_Success;
+ }
+ return LoadDirective_NotFound;
+
+}
+
+
+bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) {
+ ast_node(ce, CallExpr, call);
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ if (name == "location") {
+ if (ce->args.count > 1) {
+ error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count);
+ }
+ if (ce->args.count > 0) {
+ Ast *arg = ce->args[0];
+ Entity *e = nullptr;
+ Operand o = {};
+ if (arg->kind == Ast_Ident) {
+ e = check_ident(c, &o, arg, nullptr, nullptr, true);
+ } else if (arg->kind == Ast_SelectorExpr) {
+ e = check_selector(c, &o, arg, nullptr);
+ }
+ if (e == nullptr) {
+ error(ce->args[0], "'#location' expected a valid entity name");
+ }
+ }
+
+ operand->type = t_source_code_location;
+ operand->mode = Addressing_Value;
+ } else if (name == "load") {
+ return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success;
+ } else if (name == "load_hash") {
+ if (ce->args.count != 2) {
+ if (ce->args.count == 0) {
+ error(ce->close, "'#load_hash' expects 2 argument, got 0");
+ } else {
+ error(ce->args[0], "'#load_hash' expects 2 argument, got %td", ce->args.count);
+ }
+ return false;
+ }
+
+ Ast *arg0 = ce->args[0];
+ Ast *arg1 = ce->args[1];
+ Operand o = {};
+ check_expr(c, &o, arg0);
+ if (o.mode != Addressing_Constant) {
+ error(arg0, "'#load_hash' expected a constant string argument");
+ return false;
+ }
+
+ if (!is_type_string(o.type)) {
+ gbString str = type_to_string(o.type);
+ error(arg0, "'#load_hash' expected a constant string, got %s", str);
+ gb_string_free(str);
+ return false;
+ }
+
+ Operand o_hash = {};
+ check_expr(c, &o_hash, arg1);
+ if (o_hash.mode != Addressing_Constant) {
+ error(arg1, "'#load_hash' expected a constant string argument");
+ return false;
+ }
+
+ if (!is_type_string(o_hash.type)) {
+ gbString str = type_to_string(o.type);
+ error(arg1, "'#load_hash' expected a constant string, got %s", str);
+ gb_string_free(str);
+ return false;
+ }
+ gbAllocator a = heap_allocator();
+
+ GB_ASSERT(o.value.kind == ExactValue_String);
+ GB_ASSERT(o_hash.value.kind == ExactValue_String);
+
+ String original_string = o.value.value_string;
+ String hash_kind = o_hash.value.value_string;
+
+ String supported_hashes[] = {
+ str_lit("adler32"),
+ str_lit("crc32"),
+ str_lit("crc64"),
+ str_lit("fnv32"),
+ str_lit("fnv64"),
+ str_lit("fnv32a"),
+ str_lit("fnv64a"),
+ str_lit("murmur32"),
+ str_lit("murmur64"),
+ };
+
+ bool hash_found = false;
+ for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
+ if (supported_hashes[i] == hash_kind) {
+ hash_found = true;
+ break;
+ }
+ }
+ if (!hash_found) {
+ ERROR_BLOCK();
+ error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind));
+ error_line("\tAvailable hash kinds:\n");
+ for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
+ error_line("\t%.*s\n", LIT(supported_hashes[i]));
+ }
+ return false;
+ }
+
+ LoadFileCache *cache = nullptr;
+ if (cache_load_file_directive(c, call, original_string, true, &cache)) {
+ MUTEX_GUARD(&c->info->load_file_mutex);
+ // TODO(bill): make these procedures fast :P
+ u64 hash_value = 0;
+ u64 *hash_value_ptr = string_map_get(&cache->hashes, hash_kind);
+ if (hash_value_ptr) {
+ hash_value = *hash_value_ptr;
+ } else {
+ u8 *data = cache->data.text;
+ isize file_size = cache->data.len;
+ if (hash_kind == "adler32") {
+ hash_value = gb_adler32(data, file_size);
+ } else if (hash_kind == "crc32") {
+ hash_value = gb_crc32(data, file_size);
+ } else if (hash_kind == "crc64") {
+ hash_value = gb_crc64(data, file_size);
+ } else if (hash_kind == "fnv32") {
+ hash_value = gb_fnv32(data, file_size);
+ } else if (hash_kind == "fnv64") {
+ hash_value = gb_fnv64(data, file_size);
+ } else if (hash_kind == "fnv32a") {
+ hash_value = fnv32a(data, file_size);
+ } else if (hash_kind == "fnv64a") {
+ hash_value = fnv64a(data, file_size);
+ } else if (hash_kind == "murmur32") {
+ hash_value = gb_murmur32(data, file_size);
+ } else if (hash_kind == "murmur64") {
+ hash_value = gb_murmur64(data, file_size);
+ } else {
+ compiler_error("unhandled hash kind: %.*s", LIT(hash_kind));
+ }
+ string_map_set(&cache->hashes, hash_kind, hash_value);
+ }
+
+ operand->type = t_untyped_integer;
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_u64(hash_value);
+ return true;
+ }
+ return false;
+ } else if (name == "load_or") {
+ warning(call, "'#load_or' is deprecated in favour of '#load(path) or_else default'");
+
+ if (ce->args.count != 2) {
+ if (ce->args.count == 0) {
+ error(ce->close, "'#load_or' expects 2 arguments, got 0");
+ } else {
+ error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
+ }
+ return false;
+ }
+
+ Ast *arg = ce->args[0];
+ Operand o = {};
+ check_expr(c, &o, arg);
+ if (o.mode != Addressing_Constant) {
+ error(arg, "'#load_or' expected a constant string argument");
+ return false;
+ }
+
+ if (!is_type_string(o.type)) {
+ gbString str = type_to_string(o.type);
+ error(arg, "'#load_or' expected a constant string, got %s", str);
+ gb_string_free(str);
+ return false;
+ }
+
+ Ast *default_arg = ce->args[1];
+ Operand default_op = {};
+ check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice);
+ if (default_op.mode != Addressing_Constant) {
+ error(arg, "'#load_or' expected a constant '[]byte' argument");
+ return false;
+ }
+
+ if (!are_types_identical(base_type(default_op.type), t_u8_slice)) {
+ gbString str = type_to_string(default_op.type);
+ error(arg, "'#load_or' expected a constant '[]byte', got %s", str);
+ gb_string_free(str);
+ return false;
+ }
+ GB_ASSERT(o.value.kind == ExactValue_String);
+ String original_string = o.value.value_string;
+
+ operand->type = t_u8_slice;
+ operand->mode = Addressing_Constant;
+ LoadFileCache *cache = nullptr;
+ if (cache_load_file_directive(c, call, original_string, false, &cache)) {
+ operand->value = exact_value_string(cache->data);
+ } else {
+ operand->value = default_op.value;
+ }
+ } else if (name == "assert") {
+ if (ce->args.count != 1 && ce->args.count != 2) {
+ error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
+ return false;
+ }
+ if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
+ gbString str = expr_to_string(ce->args[0]);
+ error(call, "'%s' is not a constant boolean", str);
+ gb_string_free(str);
+ return false;
+ }
+ if (ce->args.count == 2) {
+ Ast *arg = unparen_expr(ce->args[1]);
+ if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
+ gbString str = expr_to_string(arg);
+ error(call, "'%s' is not a constant string", str);
+ gb_string_free(str);
+ return false;
+ }
+ }
+
+ if (!operand->value.value_bool) {
+ gbString arg1 = expr_to_string(ce->args[0]);
+ gbString arg2 = {};
+
+ if (ce->args.count == 1) {
+ error(call, "Compile time assertion: %s", arg1);
+ } else {
+ arg2 = expr_to_string(ce->args[1]);
+ error(call, "Compile time assertion: %s (%s)", arg1, arg2);
+ }
+
+ if (c->proc_name != "") {
+ gbString str = type_to_string(c->curr_proc_sig);
+ error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
+ gb_string_free(str);
+ }
+
+ gb_string_free(arg1);
+ if (ce->args.count == 2) {
+ gb_string_free(arg2);
+ }
+ }
+
+ operand->type = t_untyped_bool;
+ operand->mode = Addressing_Constant;
+ } else if (name == "panic") {
+ if (ce->args.count != 1) {
+ error(call, "'#panic' expects 1 argument, got %td", ce->args.count);
+ return false;
+ }
+ if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) {
+ gbString str = expr_to_string(ce->args[0]);
+ error(call, "'%s' is not a constant string", str);
+ gb_string_free(str);
+ return false;
+ }
+ error(call, "Compile time panic: %.*s", LIT(operand->value.value_string));
+ if (c->proc_name != "") {
+ gbString str = type_to_string(c->curr_proc_sig);
+ error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
+ gb_string_free(str);
+ }
+ operand->type = t_invalid;
+ operand->mode = Addressing_NoValue;
+ } else if (name == "defined") {
+ if (ce->args.count != 1) {
+ error(call, "'#defined' expects 1 argument, got %td", ce->args.count);
+ return false;
+ }
+ Ast *arg = unparen_expr(ce->args[0]);
+ if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) {
+ error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind]));
+ return false;
+ }
+
+ if (c->curr_proc_decl == nullptr) {
+ error(call, "'#defined' is only allowed within a procedure, prefer the replacement '#config(NAME, default_value)'");
+ return false;
+ }
+
+ bool is_defined = check_identifier_exists(c->scope, arg);
+ gb_unused(is_defined);
+ operand->type = t_untyped_bool;
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_bool(false);
+
+ } else if (name == "config") {
+ if (ce->args.count != 2) {
+ error(call, "'#config' expects 2 argument, got %td", ce->args.count);
+ return false;
+ }
+ Ast *arg = unparen_expr(ce->args[0]);
+ if (arg == nullptr || arg->kind != Ast_Ident) {
+ error(call, "'#config' expects an identifier, got %.*s", LIT(ast_strings[arg->kind]));
+ return false;
+ }
+
+ Ast *def_arg = unparen_expr(ce->args[1]);
+
+ Operand def = {};
+ check_expr(c, &def, def_arg);
+ if (def.mode != Addressing_Constant) {
+ error(def_arg, "'#config' default value must be a constant");
+ return false;
+ }
+
+ String name = arg->Ident.token.string;
+
+
+ operand->type = def.type;
+ operand->mode = def.mode;
+ operand->value = def.value;
+
+ Entity *found = scope_lookup_current(config_pkg->scope, name);
+ if (found != nullptr) {
+ if (found->kind != Entity_Constant) {
+ error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name));
+ } else {
+ operand->type = found->type;
+ operand->mode = Addressing_Constant;
+ operand->value = found->Constant.value;
+ }
+ }
+ } else {
+ error(call, "Unknown directive call: #%.*s", LIT(name));
+ }
+ return true;
+}
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
@@ -1186,458 +1685,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
mpmc_enqueue(&c->info->intrinsics_entry_point_usage, call);
break;
- case BuiltinProc_DIRECTIVE: {
- ast_node(bd, BasicDirective, ce->proc);
- String name = bd->name.string;
- if (name == "location") {
- if (ce->args.count > 1) {
- error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count);
- }
- if (ce->args.count > 0) {
- Ast *arg = ce->args[0];
- Entity *e = nullptr;
- Operand o = {};
- if (arg->kind == Ast_Ident) {
- e = check_ident(c, &o, arg, nullptr, nullptr, true);
- } else if (arg->kind == Ast_SelectorExpr) {
- e = check_selector(c, &o, arg, nullptr);
- }
- if (e == nullptr) {
- error(ce->args[0], "'#location' expected a valid entity name");
- }
- }
-
- operand->type = t_source_code_location;
- operand->mode = Addressing_Value;
- } else if (name == "load") {
- if (ce->args.count != 1) {
- if (ce->args.count == 0) {
- error(ce->close, "'#load' expects 1 argument, got 0");
- } else {
- error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count);
- }
-
- return false;
- }
-
- Ast *arg = ce->args[0];
- Operand o = {};
- check_expr(c, &o, arg);
- if (o.mode != Addressing_Constant) {
- error(arg, "'#load' expected a constant string argument");
- return false;
- }
-
- if (!is_type_string(o.type)) {
- gbString str = type_to_string(o.type);
- error(arg, "'#load' expected a constant string, got %s", str);
- gb_string_free(str);
- return false;
- }
-
- gbAllocator a = heap_allocator();
-
- GB_ASSERT(o.value.kind == ExactValue_String);
- String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id));
- String original_string = o.value.value_string;
-
-
- BlockingMutex *ignore_mutex = nullptr;
- String path = {};
- bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
- gb_unused(ok);
-
- char *c_str = alloc_cstring(a, path);
- defer (gb_free(a, c_str));
-
-
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- default:
- case gbFileError_Invalid:
- error(ce->proc, "Failed to `#load` file: %s; invalid file or cannot be found", c_str);
- return false;
- case gbFileError_NotExists:
- error(ce->proc, "Failed to `#load` file: %s; file cannot be found", c_str);
- return false;
- case gbFileError_Permission:
- error(ce->proc, "Failed to `#load` file: %s; file permissions problem", c_str);
- return false;
- case gbFileError_None:
- // Okay
- break;
- }
-
- String result = {};
- isize file_size = cast(isize)gb_file_size(&f);
- if (file_size > 0) {
- u8 *data = cast(u8 *)gb_alloc(a, file_size+1);
- gb_file_read_at(&f, data, file_size, 0);
- data[file_size] = '\0';
- result.text = data;
- result.len = file_size;
- }
-
- operand->type = t_u8_slice;
- operand->mode = Addressing_Constant;
- operand->value = exact_value_string(result);
-
- } else if (name == "load_hash") {
- if (ce->args.count != 2) {
- if (ce->args.count == 0) {
- error(ce->close, "'#load_hash' expects 2 argument, got 0");
- } else {
- error(ce->args[0], "'#load_hash' expects 2 argument, got %td", ce->args.count);
- }
- return false;
- }
-
- Ast *arg0 = ce->args[0];
- Ast *arg1 = ce->args[1];
- Operand o = {};
- check_expr(c, &o, arg0);
- if (o.mode != Addressing_Constant) {
- error(arg0, "'#load_hash' expected a constant string argument");
- return false;
- }
-
- if (!is_type_string(o.type)) {
- gbString str = type_to_string(o.type);
- error(arg0, "'#load_hash' expected a constant string, got %s", str);
- gb_string_free(str);
- return false;
- }
-
- Operand o_hash = {};
- check_expr(c, &o_hash, arg1);
- if (o_hash.mode != Addressing_Constant) {
- error(arg1, "'#load_hash' expected a constant string argument");
- return false;
- }
-
- if (!is_type_string(o_hash.type)) {
- gbString str = type_to_string(o.type);
- error(arg1, "'#load_hash' expected a constant string, got %s", str);
- gb_string_free(str);
- return false;
- }
-
-
- gbAllocator a = heap_allocator();
-
- GB_ASSERT(o.value.kind == ExactValue_String);
- GB_ASSERT(o_hash.value.kind == ExactValue_String);
-
- String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id));
- String original_string = o.value.value_string;
- String hash_kind = o_hash.value.value_string;
-
- String supported_hashes[] = {
- str_lit("adler32"),
- str_lit("crc32"),
- str_lit("crc64"),
- str_lit("fnv32"),
- str_lit("fnv64"),
- str_lit("fnv32a"),
- str_lit("fnv64a"),
- str_lit("murmur32"),
- str_lit("murmur64"),
- };
-
- bool hash_found = false;
- for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
- if (supported_hashes[i] == hash_kind) {
- hash_found = true;
- break;
- }
- }
- if (!hash_found) {
- ERROR_BLOCK();
- error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind));
- error_line("\tAvailable hash kinds:\n");
- for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
- error_line("\t%.*s\n", LIT(supported_hashes[i]));
- }
- return false;
- }
-
-
- BlockingMutex *ignore_mutex = nullptr;
- String path = {};
- bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
- gb_unused(ok);
-
- char *c_str = alloc_cstring(a, path);
- defer (gb_free(a, c_str));
-
-
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- default:
- case gbFileError_Invalid:
- error(ce->proc, "Failed to `#load_hash` file: %s; invalid file or cannot be found", c_str);
- return false;
- case gbFileError_NotExists:
- error(ce->proc, "Failed to `#load_hash` file: %s; file cannot be found", c_str);
- return false;
- case gbFileError_Permission:
- error(ce->proc, "Failed to `#load_hash` file: %s; file permissions problem", c_str);
- return false;
- case gbFileError_None:
- // Okay
- break;
- }
-
- // TODO(bill): make these procedures fast :P
-
- u64 hash_value = 0;
- String result = {};
- isize file_size = cast(isize)gb_file_size(&f);
- if (file_size > 0) {
- u8 *data = cast(u8 *)gb_alloc(a, file_size);
- gb_file_read_at(&f, data, file_size, 0);
- if (hash_kind == "adler32") {
- hash_value = gb_adler32(data, file_size);
- } else if (hash_kind == "crc32") {
- hash_value = gb_crc32(data, file_size);
- } else if (hash_kind == "crc64") {
- hash_value = gb_crc64(data, file_size);
- } else if (hash_kind == "fnv32") {
- hash_value = gb_fnv32(data, file_size);
- } else if (hash_kind == "fnv64") {
- hash_value = gb_fnv64(data, file_size);
- } else if (hash_kind == "fnv32a") {
- hash_value = fnv32a(data, file_size);
- } else if (hash_kind == "fnv64a") {
- hash_value = fnv64a(data, file_size);
- } else if (hash_kind == "murmur32") {
- hash_value = gb_murmur32(data, file_size);
- } else if (hash_kind == "murmur64") {
- hash_value = gb_murmur64(data, file_size);
- } else {
- compiler_error("unhandled hash kind: %.*s", LIT(hash_kind));
- }
- gb_free(a, data);
- }
-
- operand->type = t_untyped_integer;
- operand->mode = Addressing_Constant;
- operand->value = exact_value_u64(hash_value);
-
- } else if (name == "load_or") {
- if (ce->args.count != 2) {
- if (ce->args.count == 0) {
- error(ce->close, "'#load_or' expects 2 arguments, got 0");
- } else {
- error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
- }
- return false;
- }
-
- Ast *arg = ce->args[0];
- Operand o = {};
- check_expr(c, &o, arg);
- if (o.mode != Addressing_Constant) {
- error(arg, "'#load_or' expected a constant string argument");
- return false;
- }
-
- if (!is_type_string(o.type)) {
- gbString str = type_to_string(o.type);
- error(arg, "'#load_or' expected a constant string, got %s", str);
- gb_string_free(str);
- return false;
- }
-
- Ast *default_arg = ce->args[1];
- Operand default_op = {};
- check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice);
- if (default_op.mode != Addressing_Constant) {
- error(arg, "'#load_or' expected a constant '[]byte' argument");
- return false;
- }
-
- if (!are_types_identical(base_type(default_op.type), t_u8_slice)) {
- gbString str = type_to_string(default_op.type);
- error(arg, "'#load_or' expected a constant '[]byte', got %s", str);
- gb_string_free(str);
- return false;
- }
-
- gbAllocator a = heap_allocator();
-
- GB_ASSERT(o.value.kind == ExactValue_String);
- String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id));
- String original_string = o.value.value_string;
-
-
- BlockingMutex *ignore_mutex = nullptr;
- String path = {};
- bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
- gb_unused(ok);
-
- char *c_str = alloc_cstring(a, path);
- defer (gb_free(a, c_str));
-
-
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- operand->type = t_u8_slice;
- operand->mode = Addressing_Constant;
- if (file_err == gbFileError_None) {
- String result = {};
- isize file_size = cast(isize)gb_file_size(&f);
- if (file_size > 0) {
- u8 *data = cast(u8 *)gb_alloc(a, file_size+1);
- gb_file_read_at(&f, data, file_size, 0);
- data[file_size] = '\0';
- result.text = data;
- result.len = file_size;
- }
-
- operand->value = exact_value_string(result);
- } else {
- operand->value = default_op.value;
- }
-
- } else if (name == "assert") {
- if (ce->args.count != 1 && ce->args.count != 2) {
- error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
- return false;
- }
- if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
- gbString str = expr_to_string(ce->args[0]);
- error(call, "'%s' is not a constant boolean", str);
- gb_string_free(str);
- return false;
- }
- if (ce->args.count == 2) {
- Ast *arg = unparen_expr(ce->args[1]);
- if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
- gbString str = expr_to_string(arg);
- error(call, "'%s' is not a constant string", str);
- gb_string_free(str);
- return false;
- }
- }
-
- if (!operand->value.value_bool) {
- gbString arg1 = expr_to_string(ce->args[0]);
- gbString arg2 = {};
-
- if (ce->args.count == 1) {
- error(call, "Compile time assertion: %s", arg1);
- } else {
- arg2 = expr_to_string(ce->args[1]);
- error(call, "Compile time assertion: %s (%s)", arg1, arg2);
- }
-
- if (c->proc_name != "") {
- gbString str = type_to_string(c->curr_proc_sig);
- error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
- gb_string_free(str);
- }
-
- gb_string_free(arg1);
- if (ce->args.count == 2) {
- gb_string_free(arg2);
- }
- }
-
- operand->type = t_untyped_bool;
- operand->mode = Addressing_Constant;
- } else if (name == "panic") {
- if (ce->args.count != 1) {
- error(call, "'#panic' expects 1 argument, got %td", ce->args.count);
- return false;
- }
- if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) {
- gbString str = expr_to_string(ce->args[0]);
- error(call, "'%s' is not a constant string", str);
- gb_string_free(str);
- return false;
- }
- error(call, "Compile time panic: %.*s", LIT(operand->value.value_string));
- if (c->proc_name != "") {
- gbString str = type_to_string(c->curr_proc_sig);
- error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
- gb_string_free(str);
- }
- operand->type = t_invalid;
- operand->mode = Addressing_NoValue;
- } else if (name == "defined") {
- if (ce->args.count != 1) {
- error(call, "'#defined' expects 1 argument, got %td", ce->args.count);
- return false;
- }
- Ast *arg = unparen_expr(ce->args[0]);
- if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) {
- error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind]));
- return false;
- }
-
- if (c->curr_proc_decl == nullptr) {
- error(call, "'#defined' is only allowed within a procedure, prefer the replacement '#config(NAME, default_value)'");
- return false;
- }
-
- bool is_defined = check_identifier_exists(c->scope, arg);
- gb_unused(is_defined);
- operand->type = t_untyped_bool;
- operand->mode = Addressing_Constant;
- operand->value = exact_value_bool(false);
-
- } else if (name == "config") {
- if (ce->args.count != 2) {
- error(call, "'#config' expects 2 argument, got %td", ce->args.count);
- return false;
- }
- Ast *arg = unparen_expr(ce->args[0]);
- if (arg == nullptr || arg->kind != Ast_Ident) {
- error(call, "'#config' expects an identifier, got %.*s", LIT(ast_strings[arg->kind]));
- return false;
- }
-
- Ast *def_arg = unparen_expr(ce->args[1]);
-
- Operand def = {};
- check_expr(c, &def, def_arg);
- if (def.mode != Addressing_Constant) {
- error(def_arg, "'#config' default value must be a constant");
- return false;
- }
-
- String name = arg->Ident.token.string;
-
-
- operand->type = def.type;
- operand->mode = def.mode;
- operand->value = def.value;
-
- Entity *found = scope_lookup_current(config_pkg->scope, name);
- if (found != nullptr) {
- if (found->kind != Entity_Constant) {
- error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name));
- } else {
- operand->type = found->type;
- operand->mode = Addressing_Constant;
- operand->value = found->Constant.value;
- }
- }
- } else {
- error(call, "Unknown directive call: #%.*s", LIT(name));
- }
-
- break;
- }
+ case BuiltinProc_DIRECTIVE:
+ return check_builtin_procedure_directive(c, operand, call, type_hint);
case BuiltinProc_len:
check_expr_or_type(c, operand, ce->args[0]);
@@ -3569,6 +3618,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_NoValue;
break;
+ case BuiltinProc_unreachable:
case BuiltinProc_trap:
case BuiltinProc_debug_trap:
operand->mode = Addressing_NoValue;
@@ -4473,7 +4523,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (x.mode != Addressing_Invalid) {
convert_to_typed(c, &x, t_uintptr);
}
- if (!is_type_uintptr(operand->type)) {
+ convert_to_typed(c, &x, t_uintptr);
+ if (!is_type_uintptr(x.type)) {
gbString t = type_to_string(x.type);
error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t);
gb_string_free(t);
@@ -5338,6 +5389,41 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
break;
+ case BuiltinProc_valgrind_client_request:
+ {
+ if (!is_arch_x86()) {
+ error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
+ return false;
+ }
+
+ enum {ARG_COUNT = 7};
+ GB_ASSERT(builtin_procs[BuiltinProc_valgrind_client_request].arg_count == ARG_COUNT);
+
+ Operand operands[ARG_COUNT] = {};
+ for (isize i = 0; i < ARG_COUNT; i++) {
+ Operand *op = &operands[i];
+ check_expr_with_type_hint(c, op, ce->args[i], t_uintptr);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+ convert_to_typed(c, op, t_uintptr);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+ if (!are_types_identical(op->type, t_uintptr)) {
+ gbString str = type_to_string(op->type);
+ error(op->expr, "'%.*s' expected a uintptr, got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ return false;
+ }
+ }
+
+ operand->type = t_uintptr;
+ operand->mode = Addressing_Value;
+ operand->value = {};
+ return true;
+ }
+
}
return true;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 86280b6cb..9d043e60a 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -320,7 +320,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
} else if (is_type_any(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'any'");
is_distinct = false;
- } else if (is_type_simd_vector(e->type)) {
+ } else if (is_type_simd_vector(e->type) || is_type_soa_pointer(e->type)) {
gbString str = type_to_string(e->type);
error(init_expr, "'distinct' cannot be applied to '%s'", str);
gb_string_free(str);
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index cf9f2f751..9c2d20781 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -119,6 +119,29 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name
void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
+bool is_diverging_expr(Ast *expr);
+
+
+enum LoadDirectiveResult {
+ LoadDirective_Success = 0,
+ LoadDirective_Error = 1,
+ LoadDirective_NotFound = 2,
+};
+
+bool is_load_directive_call(Ast *call) {
+ call = unparen_expr(call);
+ if (call->kind != Ast_CallExpr) {
+ return false;
+ }
+ ast_node(ce, CallExpr, call);
+ if (ce->proc->kind != Ast_BasicDirective) {
+ return false;
+ }
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ return name == "load";
+}
+LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found);
void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
auto results = did_you_mean_results(d);
@@ -795,6 +818,10 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
if (is_type_matrix(dst)) {
+ if (are_types_identical(src, dst)) {
+ return 5;
+ }
+
Type *dst_elem = base_array_type(dst);
i64 distance = check_distance_between_types(c, operand, dst_elem);
if (distance >= 0) {
@@ -2051,7 +2078,7 @@ bool check_is_not_addressable(CheckerContext *c, Operand *o) {
return false;
}
- return o->mode != Addressing_Variable;
+ return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable;
}
void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
@@ -2068,9 +2095,6 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str);
} else {
switch (o->mode) {
- case Addressing_SoaVariable:
- error(op, "Cannot take the pointer address of '%s' as it is an indirect index of an SOA struct", str);
- break;
case Addressing_Constant:
error(op, "Cannot take the pointer address of '%s' which is a constant", str);
break;
@@ -2098,7 +2122,19 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
return;
}
- o->type = alloc_type_pointer(o->type);
+ if (o->mode == Addressing_SoaVariable) {
+ ast_node(ue, UnaryExpr, node);
+ if (ast_node_expect(ue->expr, Ast_IndexExpr)) {
+ ast_node(ie, IndexExpr, ue->expr);
+ Type *soa_type = type_of_expr(ie->expr);
+ GB_ASSERT(is_type_soa_struct(soa_type));
+ o->type = alloc_type_soa_pointer(soa_type);
+ } else {
+ o->type = alloc_type_pointer(o->type);
+ }
+ } else {
+ o->type = alloc_type_pointer(o->type);
+ }
switch (o->mode) {
case Addressing_OptionalOk:
@@ -2495,8 +2531,17 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
x->expr->tav.is_lhs = true;
}
x->mode = Addressing_Value;
- if (type_hint && is_type_integer(type_hint)) {
- x->type = type_hint;
+ if (type_hint) {
+ if (is_type_integer(type_hint)) {
+ x->type = type_hint;
+ } else {
+ gbString x_str = expr_to_string(x->expr);
+ gbString to_type = type_to_string(type_hint);
+ error(node, "Conversion of shifted operand '%s' to '%s' is not allowed", x_str, to_type);
+ gb_string_free(x_str);
+ gb_string_free(to_type);
+ x->mode = Addressing_Invalid;
+ }
}
// x->value = x_val;
return;
@@ -2512,7 +2557,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
// TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
if (!is_type_integer(x->type)) {
- gbString err_str = expr_to_string(y->expr);
+ gbString err_str = expr_to_string(x->expr);
error(node, "Shift operand '%s' must be an integer", err_str);
gb_string_free(err_str);
x->mode = Addressing_Invalid;
@@ -7388,9 +7433,59 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type
String name = oe->token.string;
Ast *arg = oe->x;
Ast *default_value = oe->y;
-
Operand x = {};
Operand y = {};
+
+ // NOTE(bill, 2022-08-11): edge case to handle #load(path) or_else default
+ if (is_load_directive_call(arg)) {
+ LoadDirectiveResult res = check_load_directive(c, &x, arg, type_hint, false);
+
+ // Allow for chaining of '#load(path) or_else #load(path)'
+ if (!(is_load_directive_call(default_value) && res == LoadDirective_Success)) {
+ bool y_is_diverging = false;
+ check_expr_base(c, &y, default_value, x.type);
+ switch (y.mode) {
+ case Addressing_NoValue:
+ if (is_diverging_expr(y.expr)) {
+ // Allow
+ y.mode = Addressing_Value;
+ y_is_diverging = true;
+ } else {
+ error_operand_no_value(&y);
+ y.mode = Addressing_Invalid;
+ }
+ break;
+ case Addressing_Type:
+ error_operand_not_expression(&y);
+ y.mode = Addressing_Invalid;
+ break;
+ }
+
+ if (y.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ if (!y_is_diverging) {
+ check_assignment(c, &y, x.type, name);
+ if (y.mode != Addressing_Constant) {
+ error(y.expr, "expected a constant expression on the right-hand side of 'or_else' in conjuction with '#load'");
+ }
+ }
+ }
+
+ if (res == LoadDirective_Success) {
+ *o = x;
+ } else {
+ *o = y;
+ }
+ o->expr = node;
+
+ return Expr_Expr;
+ }
+
check_multi_expr_with_type_hint(c, &x, arg, type_hint);
if (x.mode == Addressing_Invalid) {
o->mode = Addressing_Value;
@@ -7398,9 +7493,25 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type
o->expr = node;
return Expr_Expr;
}
+ bool y_is_diverging = false;
+ check_expr_base(c, &y, default_value, x.type);
+ switch (y.mode) {
+ case Addressing_NoValue:
+ if (is_diverging_expr(y.expr)) {
+ // Allow
+ y.mode = Addressing_Value;
+ y_is_diverging = true;
+ } else {
+ error_operand_no_value(&y);
+ y.mode = Addressing_Invalid;
+ }
+ break;
+ case Addressing_Type:
+ error_operand_not_expression(&y);
+ y.mode = Addressing_Invalid;
+ break;
+ }
- check_multi_expr_with_type_hint(c, &y, default_value, x.type);
- error_operand_no_value(&y);
if (y.mode == Addressing_Invalid) {
o->mode = Addressing_Value;
o->type = t_invalid;
@@ -7414,7 +7525,9 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type
add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
if (left_type != nullptr) {
- check_assignment(c, &y, left_type, name);
+ if (!y_is_diverging) {
+ check_assignment(c, &y, left_type, name);
+ }
} else {
check_or_else_expr_no_value_error(c, name, x, type_hint);
}
@@ -9358,6 +9471,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (t->kind == Type_Pointer && !is_type_empty_union(t->Pointer.elem)) {
o->mode = Addressing_Variable;
o->type = t->Pointer.elem;
+ } else if (t->kind == Type_SoaPointer) {
+ o->mode = Addressing_SoaVariable;
+ o->type = type_deref(t);
} else if (t->kind == Type_RelativePointer) {
if (o->mode != Addressing_Variable) {
gbString str = expr_to_string(o->expr);
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index a6f6f1a7d..d741ceabf 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -1,8 +1,5 @@
-bool is_diverging_stmt(Ast *stmt) {
- if (stmt->kind != Ast_ExprStmt) {
- return false;
- }
- Ast *expr = unparen_expr(stmt->ExprStmt.expr);
+bool is_diverging_expr(Ast *expr) {
+ expr = unparen_expr(expr);
if (expr->kind != Ast_CallExpr) {
return false;
}
@@ -26,6 +23,12 @@ bool is_diverging_stmt(Ast *stmt) {
t = base_type(t);
return t != nullptr && t->kind == Type_Proc && t->Proc.diverging;
}
+bool is_diverging_stmt(Ast *stmt) {
+ if (stmt->kind != Ast_ExprStmt) {
+ return false;
+ }
+ return is_diverging_expr(stmt->ExprStmt.expr);
+}
bool contains_deferred_call(Ast *node) {
if (node->viral_state_flags & ViralStateFlag_ContainsDeferredProcedure) {
@@ -1393,6 +1396,23 @@ bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) {
return id != BuiltinProc_Invalid;
}
+bool check_expr_is_stack_variable(Ast *expr) {
+ expr = unparen_expr(expr);
+ Entity *e = entity_of_node(expr);
+ if (e && e->kind == Entity_Variable) {
+ if (e->flags & (EntityFlag_Static|EntityFlag_Using)) {
+ // okay
+ } else if (e->Variable.thread_local_model.len != 0) {
+ // okay
+ } else if (e->scope) {
+ if ((e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Type)) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
@@ -1444,6 +1464,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
AstSelectorCallExpr *se = &expr->SelectorCallExpr;
ast_node(ce, CallExpr, se->call);
Type *t = base_type(type_of_expr(ce->proc));
+ if (t == nullptr) {
+ gbString expr_str = expr_to_string(ce->proc);
+ error(node, "'%s' is not a value field nor procedure", expr_str);
+ gb_string_free(expr_str);
+ return;
+ }
if (t->kind == Type_Proc) {
do_require = t->Proc.require_results;
} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
@@ -1675,6 +1701,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (is_type_untyped(o->type)) {
update_untyped_expr_type(ctx, o->expr, e->type, true);
}
+
+
+ // NOTE(bill): This is very basic escape analysis
+ // This needs to be improved tremendously, and a lot of it done during the
+ // middle-end (or LLVM side) to improve checks and error messages
+ Ast *expr = unparen_expr(o->expr);
+ if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
+ Ast *x = unparen_expr(expr->UnaryExpr.expr);
+ if (x->kind == Ast_CompoundLit) {
+ error(expr, "Cannot return the address to a stack value from a procedure");
+ } else if (x->kind == Ast_IndexExpr) {
+ Ast *array = x->IndexExpr.expr;
+ if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) {
+ gbString t = type_to_string(type_of_expr(array));
+ error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t);
+ gb_string_free(t);
+ }
+ } else {
+ if (check_expr_is_stack_variable(x)) {
+ error(expr, "Cannot return the address to a stack variable from a procedure");
+ }
+ }
+ }
}
}
case_end;
diff --git a/src/check_type.cpp b/src/check_type.cpp
index dea523599..da0a9706b 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -695,11 +695,6 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
break;
- case UnionType_maybe:
- if (variants.count != 1) {
- error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
- }
- break;
}
if (ut->align != nullptr) {
@@ -2698,9 +2693,12 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
case_ast_node(ue, UnaryExpr, e);
switch (ue->op.kind) {
case Token_Pointer:
- *type = alloc_type_pointer(check_type(ctx, ue->expr));
- set_base_type(named_type, *type);
- return true;
+ {
+ Type *elem = check_type(ctx, ue->expr);
+ *type = alloc_type_pointer(elem);
+ set_base_type(named_type, *type);
+ return true;
+ }
}
case_end;
@@ -2726,7 +2724,24 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
elem = o.type;
}
- *type = alloc_type_pointer(elem);
+ if (pt->tag != nullptr) {
+ GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
+ String name = pt->tag->BasicDirective.name.string;
+ if (name == "soa") {
+ // TODO(bill): generic #soa pointers
+ if (is_type_soa_struct(elem)) {
+ *type = alloc_type_soa_pointer(elem);
+ } else {
+ error(pt->tag, "#soa pointers require an #soa record type as the element");
+ *type = alloc_type_pointer(elem);
+ }
+ } else {
+ error(pt->tag, "Invalid tag applied to pointer, got #%.*s", LIT(name));
+ *type = alloc_type_pointer(elem);
+ }
+ } else {
+ *type = alloc_type_pointer(elem);
+ }
set_base_type(named_type, *type);
return true;
case_end;
diff --git a/src/checker.cpp b/src/checker.cpp
index 874839ece..a7470a4c9 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -1037,6 +1037,9 @@ void init_universal(void) {
add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti);
+ add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
+
+
// Builtin Procedures
for (isize i = 0; i < gb_count_of(builtin_procs); i++) {
@@ -1170,6 +1173,8 @@ void init_checker_info(CheckerInfo *i) {
mutex_init(&i->objc_types_mutex);
map_init(&i->objc_msgSend_types, a);
+ mutex_init(&i->load_file_mutex);
+ string_map_init(&i->load_file_cache, a);
}
void destroy_checker_info(CheckerInfo *i) {
@@ -1205,6 +1210,8 @@ void destroy_checker_info(CheckerInfo *i) {
mutex_destroy(&i->objc_types_mutex);
map_destroy(&i->objc_msgSend_types);
+ mutex_init(&i->load_file_mutex);
+ string_map_destroy(&i->load_file_cache);
}
CheckerContext make_checker_context(Checker *c) {
@@ -1947,6 +1954,11 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->Matrix.elem);
break;
+ case Type_SoaPointer:
+ add_type_info_type_internal(c, bt->SoaPointer.elem);
+ break;
+
+
default:
GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind);
break;
@@ -2164,6 +2176,10 @@ void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->Matrix.elem);
break;
+ case Type_SoaPointer:
+ add_min_dep_type_info(c, bt->SoaPointer.elem);
+ break;
+
default:
GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind]));
break;
@@ -2756,6 +2772,7 @@ void init_core_type_info(Checker *c) {
t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
t_type_info_relative_slice = find_core_type(c, str_lit("Type_Info_Relative_Slice"));
t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix"));
+ t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
t_type_info_named_ptr = alloc_type_pointer(t_type_info_named);
t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer);
@@ -2784,6 +2801,7 @@ void init_core_type_info(Checker *c) {
t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
t_type_info_relative_slice_ptr = alloc_type_pointer(t_type_info_relative_slice);
t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix);
+ t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer);
}
void init_mem_allocator(Checker *c) {
diff --git a/src/checker.hpp b/src/checker.hpp
index f11a00532..badcd93d5 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -287,6 +287,12 @@ struct ObjcMsgData {
ObjcMsgKind kind;
Type *proc_type;
};
+struct LoadFileCache {
+ String path;
+ gbFileError file_error;
+ String data;
+ StringMap<u64> hashes;
+};
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
@@ -363,6 +369,9 @@ struct CheckerInfo {
BlockingMutex objc_types_mutex;
PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
+
+ BlockingMutex load_file_mutex;
+ StringMap<LoadFileCache *> load_file_cache;
};
struct CheckerContext {
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 05f256775..717422df1 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -40,6 +40,8 @@ enum BuiltinProcId {
BuiltinProc_hadamard_product,
BuiltinProc_matrix_flatten,
+ BuiltinProc_unreachable,
+
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
// "Intrinsics"
@@ -289,6 +291,8 @@ BuiltinProc__type_end,
BuiltinProc_wasm_memory_atomic_wait32,
BuiltinProc_wasm_memory_atomic_notify32,
+ BuiltinProc_valgrind_client_request,
+
BuiltinProc_COUNT,
};
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -330,6 +334,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("matrix_flatten"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
+ {STR_LIT("unreachable"), 0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true},
+
{STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
@@ -341,7 +347,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/true},
+ {STR_LIT("trap"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics, /*diverging*/true},
{STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false},
{STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -434,7 +440,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_abs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_min"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_max"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -578,4 +584,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_atomic_wait32"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_atomic_notify32"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("valgrind_client_request"), 7, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index ee32d0e05..b13b8b364 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -83,6 +83,7 @@ enum OdinDocTypeKind : u32 {
OdinDocType_RelativeSlice = 21,
OdinDocType_MultiPointer = 22,
OdinDocType_Matrix = 23,
+ OdinDocType_SoaPointer = 24,
};
enum OdinDocTypeFlag_Basic : u32 {
@@ -98,7 +99,6 @@ enum OdinDocTypeFlag_Struct : u32 {
enum OdinDocTypeFlag_Union : u32 {
OdinDocTypeFlag_Union_polymorphic = 1<<0,
OdinDocTypeFlag_Union_no_nil = 1<<1,
- OdinDocTypeFlag_Union_maybe = 1<<2,
OdinDocTypeFlag_Union_shared_nil = 1<<3,
};
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 2f531a45c..1b8e1fc34 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -532,6 +532,10 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.kind = OdinDocType_MultiPointer;
doc_type.types = odin_doc_type_as_slice(w, type->MultiPointer.elem);
break;
+ case Type_SoaPointer:
+ doc_type.kind = OdinDocType_SoaPointer;
+ doc_type.types = odin_doc_type_as_slice(w, type->SoaPointer.elem);
+ break;
case Type_Array:
doc_type.kind = OdinDocType_Array;
doc_type.elem_count_len = 1;
diff --git a/src/gb/gb.h b/src/gb/gb.h
index d09c7618b..90f2fd15a 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -90,7 +90,7 @@ extern "C" {
#error This operating system is not supported
#endif
-#if defined(GB_SYSTEM_OPENBSD)
+#if defined(GB_SYSTEM_UNIX)
#include <sys/wait.h>
#endif
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index b22a839b3..4bdc31077 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -62,7 +62,7 @@ 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) {
+LLVMTypeRef lb_function_type_to_llvm_raw(lbFunctionType *ft, bool is_var_arg) {
unsigned arg_count = cast(unsigned)ft->args.count;
unsigned offset = 0;
@@ -108,10 +108,16 @@ LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) {
}
unsigned total_arg_count = arg_index;
LLVMTypeRef func_type = LLVMFunctionType(ret, args, total_arg_count, is_var_arg);
- return LLVMPointerType(func_type, 0);
+ return func_type;
}
+// LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) {
+// LLVMTypeRef func_type = lb_function_type_to_llvm_raw(ft, 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;
@@ -217,7 +223,7 @@ i64 lb_sizeof(LLVMTypeRef type) {
break;
case LLVMArrayTypeKind:
{
- LLVMTypeRef elem = LLVMGetElementType(type);
+ LLVMTypeRef elem = OdinLLVMGetArrayElementType(type);
i64 elem_size = lb_sizeof(elem);
i64 count = LLVMGetArrayLength(type);
i64 size = count * elem_size;
@@ -229,7 +235,7 @@ i64 lb_sizeof(LLVMTypeRef type) {
return 8;
case LLVMVectorTypeKind:
{
- LLVMTypeRef elem = LLVMGetElementType(type);
+ LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
i64 elem_size = lb_sizeof(elem);
i64 count = LLVMGetVectorSize(type);
i64 size = count * elem_size;
@@ -277,18 +283,18 @@ i64 lb_alignof(LLVMTypeRef type) {
}
break;
case LLVMArrayTypeKind:
- return lb_alignof(LLVMGetElementType(type));
+ return lb_alignof(OdinLLVMGetArrayElementType(type));
case LLVMX86_MMXTypeKind:
return 8;
case LLVMVectorTypeKind:
{
// TODO(bill): This appears to be correct but LLVM isn't necessarily "great" with regards to documentation
- LLVMTypeRef elem = LLVMGetElementType(type);
+ LLVMTypeRef elem = OdinLLVMGetVectorElementType(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);
+ return gb_clamp(next_pow2(size), 1, build_context.max_simd_align);
}
}
@@ -787,7 +793,7 @@ namespace lbAbiAmd64SysV {
case LLVMArrayTypeKind:
{
i64 len = LLVMGetArrayLength(t);
- LLVMTypeRef elem = LLVMGetElementType(t);
+ LLVMTypeRef elem = OdinLLVMGetArrayElementType(t);
i64 elem_sz = lb_sizeof(elem);
for (i64 i = 0; i < len; i++) {
classify_with(elem, cls, ix, off + i*elem_sz);
@@ -797,7 +803,7 @@ namespace lbAbiAmd64SysV {
case LLVMVectorTypeKind:
{
i64 len = LLVMGetVectorSize(t);
- LLVMTypeRef elem = LLVMGetElementType(t);
+ LLVMTypeRef elem = OdinLLVMGetVectorElementType(t);
i64 elem_sz = lb_sizeof(elem);
LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
RegClass reg = RegClass_NoClass;
@@ -907,7 +913,7 @@ namespace lbAbiArm64 {
if (len == 0) {
return false;
}
- LLVMTypeRef elem = LLVMGetElementType(type);
+ LLVMTypeRef elem = OdinLLVMGetArrayElementType(type);
LLVMTypeRef base_type = nullptr;
unsigned member_count = 0;
if (is_homogenous_aggregate(c, elem, &base_type, &member_count)) {
@@ -1123,7 +1129,7 @@ namespace lbAbiWasm {
}
if (sz <= MAX_DIRECT_STRUCT_SIZE) {
if (kind == LLVMArrayTypeKind) {
- if (is_basic_register_type(LLVMGetElementType(type))) {
+ if (is_basic_register_type(OdinLLVMGetArrayElementType(type))) {
return true;
}
} else if (kind == LLVMStructTypeKind) {
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index cf7389ec1..6ee1541d6 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -739,11 +739,11 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
lb_begin_procedure_body(p);
if (startup_type_info) {
- LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+ LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, startup_type_info->type), startup_type_info->value, nullptr, 0, "");
}
if (objc_names) {
- LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, "");
+ LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
}
for_array(i, global_variables) {
@@ -762,7 +762,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
if (init_expr != nullptr) {
lbValue init = lb_build_expr(p, init_expr);
if (init.value == nullptr) {
- LLVMTypeRef global_type = LLVMGetElementType(LLVMTypeOf(var->var.value));
+ LLVMTypeRef global_type = llvm_addr_type(p->module, var->var);
if (is_type_untyped_undef(init.type)) {
// LLVMSetInitializer(var->var.value, LLVMGetUndef(global_type));
LLVMSetInitializer(var->var.value, LLVMConstNull(global_type));
@@ -805,8 +805,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
lb_emit_store(p, ti, lb_type_info(main_module, var_type));
} else {
- LLVMTypeRef pvt = LLVMTypeOf(var->var.value);
- LLVMTypeRef vt = LLVMGetElementType(pvt);
+ LLVMTypeRef vt = llvm_addr_type(p->module, var->var);
lbValue src0 = lb_emit_conv(p, var->init, t);
LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt);
LLVMValueRef dst = var->var.value;
@@ -933,7 +932,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
GB_ASSERT(LLVMIsConstant(vals[1]));
GB_ASSERT(LLVMIsConstant(vals[2]));
- LLVMValueRef dst = LLVMConstInBoundsGEP(all_tests_array.value, indices, gb_count_of(indices));
+ LLVMValueRef dst = LLVMConstInBoundsGEP2(llvm_addr_type(m, all_tests_array), all_tests_array.value, indices, gb_count_of(indices));
LLVMValueRef src = llvm_const_named_struct(m, t_Internal_Test, vals, gb_count_of(vals));
LLVMBuildStore(p->builder, src, dst);
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index a09286d0b..79f0f37e7 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -42,6 +42,18 @@
#define ODIN_LLVM_MINIMUM_VERSION_12 0
#endif
+#if LLVM_VERSION_MAJOR > 13 || (LLVM_VERSION_MAJOR == 13 && LLVM_VERSION_MINOR >= 0 && LLVM_VERSION_PATCH > 0)
+#define ODIN_LLVM_MINIMUM_VERSION_13 1
+#else
+#define ODIN_LLVM_MINIMUM_VERSION_13 0
+#endif
+
+#if LLVM_VERSION_MAJOR > 14 || (LLVM_VERSION_MAJOR == 14 && LLVM_VERSION_MINOR >= 0 && LLVM_VERSION_PATCH > 0)
+#define ODIN_LLVM_MINIMUM_VERSION_14 1
+#else
+#define ODIN_LLVM_MINIMUM_VERSION_14 0
+#endif
+
struct lbProcedure;
struct lbValue {
@@ -115,6 +127,7 @@ struct lbModule {
AstPackage *pkg; // associated
PtrMap<Type *, LLVMTypeRef> types;
+ PtrMap<Type *, LLVMTypeRef> func_raw_types;
PtrMap<void *, lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *
i32 internal_type_level;
@@ -299,7 +312,11 @@ struct lbProcedure {
-
+#if !ODIN_LLVM_MINIMUM_VERSION_14
+#define LLVMConstGEP2(Ty__, ConstantVal__, ConstantIndices__, NumIndices__) LLVMConstGEP(ConstantVal__, ConstantIndices__, NumIndices__)
+#define LLVMConstInBoundsGEP2(Ty__, ConstantVal__, ConstantIndices__, NumIndices__) LLVMConstInBoundsGEP(ConstantVal__, ConstantIndices__, NumIndices__)
+#define LLVMBuildPtrDiff2(Builder__, Ty__, LHS__, RHS__, Name__) LLVMBuildPtrDiff(Builder__, LHS__, RHS__, Name__)
+#endif
bool lb_init_generator(lbGenerator *gen, Checker *c);
@@ -314,7 +331,8 @@ lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_b
void lb_end_procedure(lbProcedure *p);
-LLVMTypeRef lb_type(lbModule *m, Type *type);
+LLVMTypeRef lb_type(lbModule *m, Type *type);
+LLVMTypeRef llvm_get_element_type(LLVMTypeRef type);
lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append=false);
@@ -327,7 +345,7 @@ lbValue lb_const_int(lbModule *m, Type *type, u64 value);
lbAddr lb_addr(lbValue addr);
Type *lb_addr_type(lbAddr const &addr);
-LLVMTypeRef lb_addr_lb_type(lbAddr const &addr);
+LLVMTypeRef llvm_addr_type(lbModule *module, lbValue addr_val);
void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value);
lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr);
lbValue lb_emit_load(lbProcedure *p, lbValue v);
@@ -339,8 +357,9 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr);
lbAddr lb_build_addr(lbProcedure *p, Ast *expr);
void lb_build_stmt_list(lbProcedure *p, Array<Ast *> const &stmts);
-lbValue lb_build_gep(lbProcedure *p, lbValue const &value, i32 index) ;
-
+lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index);
+lbValue lb_emit_epi(lbModule *m, lbValue const &value, isize index);
+lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index);
lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index);
lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index);
lbValue lb_emit_array_epi(lbProcedure *p, lbValue value, isize index);
@@ -480,6 +499,7 @@ LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align);
LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask);
+LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count);
void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile);
@@ -488,6 +508,9 @@ i64 lb_max_zero_init_size(void) {
return cast(i64)(4*build_context.word_size);
}
+LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
+LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
+
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
#define LB_TYPE_INFO_DATA_NAME "__$type_info_data"
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 24b2bc3a2..2d14070e2 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -10,11 +10,12 @@ bool lb_is_const(lbValue value) {
return false;
}
-
bool lb_is_const_or_global(lbValue value) {
if (lb_is_const(value)) {
return true;
}
+ // TODO remove use of LLVMGetElementType
+ #if 0
if (LLVMGetValueKind(value.value) == LLVMGlobalVariableValueKind) {
LLVMTypeRef t = LLVMGetElementType(LLVMTypeOf(value.value));
if (!lb_is_type_kind(t, LLVMPointerTypeKind)) {
@@ -23,6 +24,7 @@ bool lb_is_const_or_global(lbValue value) {
LLVMTypeRef elem = LLVMGetElementType(t);
return lb_is_type_kind(elem, LLVMFunctionTypeKind);
}
+ #endif
return false;
}
@@ -389,8 +391,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
if (is_type_slice(type)) {
if (value.kind == ExactValue_String) {
- GB_ASSERT(is_type_u8_slice(type));
- res.value = lb_find_or_add_entity_string_byte_slice(m, value.value_string).value;
+ GB_ASSERT(is_type_slice(type));
+ res.value = lb_find_or_add_entity_string_byte_slice_with_type(m, value.value_string, original_type).value;
return res;
} else {
ast_node(cl, CompoundLit, value.value_compound);
@@ -418,7 +420,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
{
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
- LLVMValueRef ptr = LLVMBuildInBoundsGEP(p->builder, array_data, indices, 2, "");
+ LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, "");
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
lbAddr slice = lb_add_local_generated(p, type, false);
lb_fill_slice(p, slice, {ptr, alloc_type_pointer(elem)}, {len, t_int});
@@ -445,7 +447,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
{
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
- LLVMValueRef ptr = LLVMConstInBoundsGEP(array_data, indices, 2);
+ LLVMValueRef ptr = LLVMConstInBoundsGEP2(lb_type(m, t), array_data, indices, 2);
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
LLVMValueRef values[2] = {ptr, len};
@@ -1007,7 +1009,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
for (isize i = 0; i < value_count; i++) {
LLVMValueRef val = old_values[i];
if (!LLVMIsConstant(val)) {
- LLVMValueRef dst = LLVMBuildStructGEP(p->builder, v.addr.value, cast(unsigned)i, "");
+ LLVMValueRef dst = LLVMBuildStructGEP2(p->builder, llvm_addr_type(p->module, v.addr), v.addr.value, cast(unsigned)i, "");
LLVMBuildStore(p->builder, val, dst);
}
}
@@ -1041,7 +1043,6 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
i64 v = big_int_to_i64(&tav.value.value_integer);
i64 lower = type->BitSet.lower;
u64 index = cast(u64)(v-lower);
- gb_printf_err("index: %llu\n", index);
BigInt bit = {};
big_int_from_u64(&bit, index);
big_int_shl(&bit, &one, &bit);
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index b28470770..7d81d1407 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -137,7 +137,7 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
lbAddr res_addr = lb_add_local(p, type, nullptr, false, 0, true);
lbValue res = lb_addr_get_ptr(p, res_addr);
- bool inline_array_arith = type_size_of(type) <= build_context.max_align;
+ bool inline_array_arith = lb_can_try_to_inline_array_arith(type);
i32 count = cast(i32)get_array_type_count(tl);
@@ -243,8 +243,9 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
LLVMValueRef v1 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 1, ""), "");
lbAddr addr = lb_add_local_generated(p, x.type, false);
- LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP(p->builder, addr.addr.value, 0, ""));
- LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP(p->builder, addr.addr.value, 1, ""));
+ LLVMTypeRef type = llvm_addr_type(p->module, addr.addr);
+ LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, ""));
+ LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, ""));
return lb_addr_load(p, addr);
} else if (is_type_quaternion(x.type)) {
@@ -254,10 +255,11 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
LLVMValueRef v3 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 3, ""), "");
lbAddr addr = lb_add_local_generated(p, x.type, false);
- LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP(p->builder, addr.addr.value, 0, ""));
- LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP(p->builder, addr.addr.value, 1, ""));
- LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, ""));
- LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, ""));
+ LLVMTypeRef type = llvm_addr_type(p->module, addr.addr);
+ LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, ""));
+ LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, ""));
+ LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 2, ""));
+ LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 3, ""));
return lb_addr_load(p, addr);
} else if (is_type_simd_vector(x.type)) {
Type *elem = base_array_type(x.type);
@@ -434,7 +436,7 @@ lbValue lb_emit_arith_array(lbProcedure *p, TokenKind op, lbValue lhs, lbValue r
return direct_vector_res;
}
- bool inline_array_arith = type_size_of(type) <= build_context.max_align;
+ bool inline_array_arith = lb_can_try_to_inline_array_arith(type);
if (inline_array_arith) {
auto dst_ptrs = slice_make<lbValue>(temporary_allocator(), n);
@@ -543,7 +545,7 @@ LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) {
#if 1
LLVMValueRef ptr = lb_address_from_load_or_generate_local(p, matrix).value;
LLVMValueRef matrix_vector_ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(total_matrix_type, 0), "");
- LLVMValueRef matrix_vector = LLVMBuildLoad(p->builder, matrix_vector_ptr, "");
+ LLVMValueRef matrix_vector = LLVMBuildLoad2(p->builder, total_matrix_type, matrix_vector_ptr, "");
LLVMSetAlignment(matrix_vector, cast(unsigned)type_align_of(mt));
return matrix_vector;
#else
@@ -555,7 +557,7 @@ LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) {
LLVMValueRef lb_matrix_trimmed_vector_mask(lbProcedure *p, Type *mt) {
mt = base_type(mt);
GB_ASSERT(mt->kind == Type_Matrix);
-
+
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
unsigned column_count = cast(unsigned)mt->Matrix.column_count;
@@ -567,23 +569,23 @@ LLVMValueRef lb_matrix_trimmed_vector_mask(lbProcedure *p, Type *mt) {
mask_elems[mask_elems_index++] = lb_const_int(p->module, t_u32, offset).value;
}
}
-
+
LLVMValueRef mask = LLVMConstVector(mask_elems.data, cast(unsigned)mask_elems.count);
return mask;
}
LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
LLVMValueRef vector = lb_matrix_to_vector(p, m);
-
+
Type *mt = base_type(m.type);
GB_ASSERT(mt->kind == Type_Matrix);
-
+
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
if (stride == row_count) {
return vector;
}
-
+
LLVMValueRef mask = lb_matrix_trimmed_vector_mask(p, mt);
LLVMValueRef trimmed_vector = llvm_basic_shuffle(p, vector, mask);
return trimmed_vector;
@@ -619,28 +621,28 @@ lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
}
Type *mt = base_type(m.type);
GB_ASSERT(mt->kind == Type_Matrix);
-
+
if (lb_is_matrix_simdable(mt)) {
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
unsigned column_count = cast(unsigned)mt->Matrix.column_count;
-
+
auto rows = slice_make<LLVMValueRef>(permanent_allocator(), row_count);
auto mask_elems = slice_make<LLVMValueRef>(permanent_allocator(), column_count);
-
+
LLVMValueRef vector = lb_matrix_to_vector(p, m);
for (unsigned i = 0; i < row_count; i++) {
for (unsigned j = 0; j < column_count; j++) {
unsigned offset = stride*j + i;
mask_elems[j] = lb_const_int(p->module, t_u32, offset).value;
}
-
+
// transpose mask
LLVMValueRef mask = LLVMConstVector(mask_elems.data, column_count);
LLVMValueRef row = llvm_basic_shuffle(p, vector, mask);
rows[i] = row;
}
-
+
lbAddr res = lb_add_local_generated(p, type, true);
for_array(i, rows) {
LLVMValueRef row = rows[i];
@@ -649,12 +651,12 @@ lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(LLVMTypeOf(row), 0), "");
LLVMBuildStore(p->builder, row, ptr);
}
-
+
return lb_addr_load(p, res);
}
-
+
lbAddr res = lb_add_local_generated(p, type, true);
-
+
i64 row_count = mt->Matrix.row_count;
i64 column_count = mt->Matrix.column_count;
for (i64 j = 0; j < column_count; j++) {
@@ -672,10 +674,10 @@ lbValue lb_matrix_cast_vector_to_type(lbProcedure *p, LLVMValueRef vector, Type
LLVMValueRef res_ptr = res.addr.value;
unsigned alignment = cast(unsigned)gb_max(type_align_of(type), lb_alignof(LLVMTypeOf(vector)));
LLVMSetAlignment(res_ptr, alignment);
-
+
res_ptr = LLVMBuildPointerCast(p->builder, res_ptr, LLVMPointerType(LLVMTypeOf(vector), 0), "");
LLVMBuildStore(p->builder, vector, res_ptr);
-
+
return lb_addr_load(p, res);
}
@@ -687,14 +689,14 @@ lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type) {
}
Type *mt = base_type(m.type);
GB_ASSERT(mt->kind == Type_Matrix);
-
+
if (lb_is_matrix_simdable(mt)) {
LLVMValueRef vector = lb_matrix_to_trimmed_vector(p, m);
return lb_matrix_cast_vector_to_type(p, vector, type);
}
-
+
lbAddr res = lb_add_local_generated(p, type, true);
-
+
i64 row_count = mt->Matrix.row_count;
i64 column_count = mt->Matrix.column_count;
for (i64 j = 0; j < column_count; j++) {
@@ -715,17 +717,17 @@ lbValue lb_emit_outer_product(lbProcedure *p, lbValue a, lbValue b, Type *type)
GB_ASSERT(mt->kind == Type_Matrix);
GB_ASSERT(at->kind == Type_Array);
GB_ASSERT(bt->kind == Type_Array);
-
-
+
+
i64 row_count = mt->Matrix.row_count;
i64 column_count = mt->Matrix.column_count;
-
+
GB_ASSERT(row_count == at->Array.count);
GB_ASSERT(column_count == bt->Array.count);
-
-
+
+
lbAddr res = lb_add_local_generated(p, type, true);
-
+
for (i64 j = 0; j < column_count; j++) {
for (i64 i = 0; i < row_count; i++) {
lbValue x = lb_emit_struct_ev(p, a, cast(i32)i);
@@ -741,51 +743,51 @@ lbValue lb_emit_outer_product(lbProcedure *p, lbValue a, lbValue b, Type *type)
lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
// TODO(bill): Handle edge case for f16 types on x86(-64) platforms
-
+
Type *xt = base_type(lhs.type);
Type *yt = base_type(rhs.type);
-
+
GB_ASSERT(is_type_matrix(type));
GB_ASSERT(is_type_matrix(xt));
GB_ASSERT(is_type_matrix(yt));
GB_ASSERT(xt->Matrix.column_count == yt->Matrix.row_count);
GB_ASSERT(are_types_identical(xt->Matrix.elem, yt->Matrix.elem));
-
+
Type *elem = xt->Matrix.elem;
-
+
unsigned outer_rows = cast(unsigned)xt->Matrix.row_count;
unsigned inner = cast(unsigned)xt->Matrix.column_count;
unsigned outer_columns = cast(unsigned)yt->Matrix.column_count;
-
+
if (lb_is_matrix_simdable(xt)) {
unsigned x_stride = cast(unsigned)matrix_type_stride_in_elems(xt);
unsigned y_stride = cast(unsigned)matrix_type_stride_in_elems(yt);
-
+
auto x_rows = slice_make<LLVMValueRef>(permanent_allocator(), outer_rows);
auto y_columns = slice_make<LLVMValueRef>(permanent_allocator(), outer_columns);
-
+
LLVMValueRef x_vector = lb_matrix_to_vector(p, lhs);
LLVMValueRef y_vector = lb_matrix_to_vector(p, rhs);
-
+
auto mask_elems = slice_make<LLVMValueRef>(permanent_allocator(), inner);
for (unsigned i = 0; i < outer_rows; i++) {
for (unsigned j = 0; j < inner; j++) {
unsigned offset = x_stride*j + i;
mask_elems[j] = lb_const_int(p->module, t_u32, offset).value;
}
-
+
// transpose mask
LLVMValueRef mask = LLVMConstVector(mask_elems.data, inner);
LLVMValueRef row = llvm_basic_shuffle(p, x_vector, mask);
x_rows[i] = row;
}
-
+
for (unsigned i = 0; i < outer_columns; i++) {
LLVMValueRef mask = llvm_mask_iota(p->module, y_stride*i, inner);
LLVMValueRef column = llvm_basic_shuffle(p, y_vector, mask);
y_columns[i] = column;
}
-
+
lbAddr res = lb_add_local_generated(p, type, true);
for_array(i, x_rows) {
LLVMValueRef x_row = x_rows[i];
@@ -795,15 +797,15 @@ lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type)
lbValue dst = lb_emit_matrix_epi(p, res.addr, i, j);
LLVMBuildStore(p->builder, elem, dst.value);
}
- }
+ }
return lb_addr_load(p, res);
}
-
+
{
lbAddr res = lb_add_local_generated(p, type, true);
-
+
auto inners = slice_make<lbValue[2]>(permanent_allocator(), inner);
-
+
for (unsigned j = 0; j < outer_columns; j++) {
for (unsigned i = 0; i < outer_rows; i++) {
lbValue dst = lb_emit_matrix_epi(p, res.addr, i, j);
@@ -811,7 +813,7 @@ lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type)
inners[k][0] = lb_emit_matrix_ev(p, lhs, i, k);
inners[k][1] = lb_emit_matrix_ev(p, rhs, k, j);
}
-
+
lbValue sum = lb_const_nil(p->module, elem);
for (unsigned k = 0; k < inner; k++) {
lbValue a = inners[k][0];
@@ -821,51 +823,51 @@ lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type)
lb_emit_store(p, dst, sum);
}
}
-
+
return lb_addr_load(p, res);
}
}
lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
// TODO(bill): Handle edge case for f16 types on x86(-64) platforms
-
+
Type *mt = base_type(lhs.type);
Type *vt = base_type(rhs.type);
-
+
GB_ASSERT(is_type_matrix(mt));
GB_ASSERT(is_type_array_like(vt));
-
+
i64 vector_count = get_array_type_count(vt);
-
+
GB_ASSERT(mt->Matrix.column_count == vector_count);
GB_ASSERT(are_types_identical(mt->Matrix.elem, base_array_type(vt)));
-
+
Type *elem = mt->Matrix.elem;
-
+
if (lb_is_matrix_simdable(mt)) {
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
-
+
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
unsigned column_count = cast(unsigned)mt->Matrix.column_count;
auto m_columns = slice_make<LLVMValueRef>(permanent_allocator(), column_count);
auto v_rows = slice_make<LLVMValueRef>(permanent_allocator(), column_count);
-
- LLVMValueRef matrix_vector = lb_matrix_to_vector(p, lhs);
-
+
+ LLVMValueRef matrix_vector = lb_matrix_to_vector(p, lhs);
+
for (unsigned column_index = 0; column_index < column_count; column_index++) {
LLVMValueRef mask = llvm_mask_iota(p->module, stride*column_index, row_count);
LLVMValueRef column = llvm_basic_shuffle(p, matrix_vector, mask);
m_columns[column_index] = column;
}
-
+
for (unsigned row_index = 0; row_index < column_count; row_index++) {
LLVMValueRef value = lb_emit_struct_ev(p, rhs, row_index).value;
LLVMValueRef row = llvm_vector_broadcast(p, value, row_count);
v_rows[row_index] = row;
}
-
+
GB_ASSERT(column_count > 0);
-
+
LLVMValueRef vector = nullptr;
for (i64 i = 0; i < column_count; i++) {
if (i == 0) {
@@ -874,51 +876,51 @@ lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbValue rhs, Type
vector = llvm_vector_mul_add(p, m_columns[i], v_rows[i], vector);
}
}
-
+
return lb_matrix_cast_vector_to_type(p, vector, type);
}
-
+
lbAddr res = lb_add_local_generated(p, type, true);
-
+
for (i64 i = 0; i < mt->Matrix.row_count; i++) {
for (i64 j = 0; j < mt->Matrix.column_count; j++) {
lbValue dst = lb_emit_matrix_epi(p, res.addr, i, 0);
lbValue d0 = lb_emit_load(p, dst);
-
+
lbValue a = lb_emit_matrix_ev(p, lhs, i, j);
lbValue b = lb_emit_struct_ev(p, rhs, cast(i32)j);
lbValue c = lb_emit_mul_add(p, a, b, d0, elem);
lb_emit_store(p, dst, c);
}
}
-
+
return lb_addr_load(p, res);
}
lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
// TODO(bill): Handle edge case for f16 types on x86(-64) platforms
-
+
Type *mt = base_type(rhs.type);
Type *vt = base_type(lhs.type);
-
+
GB_ASSERT(is_type_matrix(mt));
GB_ASSERT(is_type_array_like(vt));
-
+
i64 vector_count = get_array_type_count(vt);
-
+
GB_ASSERT(vector_count == mt->Matrix.row_count);
GB_ASSERT(are_types_identical(mt->Matrix.elem, base_array_type(vt)));
-
+
Type *elem = mt->Matrix.elem;
-
+
if (lb_is_matrix_simdable(mt)) {
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
-
+
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
unsigned column_count = cast(unsigned)mt->Matrix.column_count; gb_unused(column_count);
auto m_columns = slice_make<LLVMValueRef>(permanent_allocator(), row_count);
auto v_rows = slice_make<LLVMValueRef>(permanent_allocator(), row_count);
-
+
LLVMValueRef matrix_vector = lb_matrix_to_vector(p, rhs);
auto mask_elems = slice_make<LLVMValueRef>(permanent_allocator(), column_count);
@@ -927,21 +929,21 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
unsigned offset = row_index + column_index*stride;
mask_elems[column_index] = lb_const_int(p->module, t_u32, offset).value;
}
-
+
// transpose mask
LLVMValueRef mask = LLVMConstVector(mask_elems.data, column_count);
LLVMValueRef column = llvm_basic_shuffle(p, matrix_vector, mask);
m_columns[row_index] = column;
}
-
+
for (unsigned column_index = 0; column_index < row_count; column_index++) {
LLVMValueRef value = lb_emit_struct_ev(p, lhs, column_index).value;
LLVMValueRef row = llvm_vector_broadcast(p, value, column_count);
v_rows[column_index] = row;
}
-
+
GB_ASSERT(row_count > 0);
-
+
LLVMValueRef vector = nullptr;
for (i64 i = 0; i < row_count; i++) {
if (i == 0) {
@@ -955,27 +957,27 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
LLVMValueRef res_ptr = res.addr.value;
unsigned alignment = cast(unsigned)gb_max(type_align_of(type), lb_alignof(LLVMTypeOf(vector)));
LLVMSetAlignment(res_ptr, alignment);
-
+
res_ptr = LLVMBuildPointerCast(p->builder, res_ptr, LLVMPointerType(LLVMTypeOf(vector), 0), "");
LLVMBuildStore(p->builder, vector, res_ptr);
-
+
return lb_addr_load(p, res);
}
-
+
lbAddr res = lb_add_local_generated(p, type, true);
-
+
for (i64 j = 0; j < mt->Matrix.column_count; j++) {
for (i64 k = 0; k < mt->Matrix.row_count; k++) {
lbValue dst = lb_emit_matrix_epi(p, res.addr, 0, j);
lbValue d0 = lb_emit_load(p, dst);
-
+
lbValue a = lb_emit_struct_ev(p, lhs, cast(i32)k);
lbValue b = lb_emit_matrix_ev(p, rhs, k, j);
lbValue c = lb_emit_mul_add(p, a, b, d0, elem);
lb_emit_store(p, dst, c);
}
}
-
+
return lb_addr_load(p, res);
}
@@ -984,12 +986,12 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) {
GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type));
-
-
+
+
if (op == Token_Mul && !component_wise) {
Type *xt = base_type(lhs.type);
Type *yt = base_type(rhs.type);
-
+
if (xt->kind == Type_Matrix) {
if (yt->kind == Type_Matrix) {
return lb_emit_matrix_mul(p, lhs, rhs, type);
@@ -1000,17 +1002,17 @@ lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue
GB_ASSERT(yt->kind == Type_Matrix);
return lb_emit_vector_mul_matrix(p, lhs, rhs, type);
}
-
+
} else {
if (is_type_matrix(lhs.type)) {
rhs = lb_emit_conv(p, rhs, lhs.type);
} else {
lhs = lb_emit_conv(p, lhs, rhs.type);
}
-
+
Type *xt = base_type(lhs.type);
Type *yt = base_type(rhs.type);
-
+
GB_ASSERT_MSG(are_types_identical(xt, yt), "%s %.*s %s", type_to_string(lhs.type), LIT(token_strings[op]), type_to_string(rhs.type));
GB_ASSERT(xt->kind == Type_Matrix);
// element-wise arithmetic
@@ -1019,8 +1021,8 @@ lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue
lbValue array_rhs = rhs;
Type *array_type = alloc_type_array(xt->Matrix.elem, matrix_type_total_internal_elems(xt));
GB_ASSERT(type_size_of(array_type) == type_size_of(xt));
-
- array_lhs.type = array_type;
+
+ array_lhs.type = array_type;
array_rhs.type = array_type;
if (token_is_comparison(op)) {
@@ -1033,7 +1035,7 @@ lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue
}
}
-
+
GB_PANIC("TODO: lb_emit_arith_matrix");
return {};
@@ -1314,13 +1316,13 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
ast_node(be, BinaryExpr, expr);
TypeAndValue tv = type_and_value_of_expr(expr);
-
+
if (is_type_matrix(be->left->tav.type) || is_type_matrix(be->right->tav.type)) {
lbValue left = lb_build_expr(p, be->left);
lbValue right = lb_build_expr(p, be->right);
return lb_emit_arith_matrix(p, be->op.kind, left, right, default_type(tv.type));
}
-
+
switch (be->op.kind) {
case Token_Add:
@@ -1690,7 +1692,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
return res;
}
-
+
if (is_type_complex(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
@@ -1775,12 +1777,9 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
lbValue res = {};
res = lb_emit_conv(p, value, platform_src_type);
res = lb_emit_conv(p, res, platform_dst_type);
- if (is_type_different_to_arch_endianness(dst)) {
- res = lb_emit_byte_swap(p, res, platform_dst_type);
- }
return lb_emit_conv(p, res, t);
}
-
+
if (is_type_integer_128bit(dst)) {
auto args = array_make<lbValue>(temporary_allocator(), 1);
args[0] = value;
@@ -2053,10 +2052,10 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
return lb_addr_load(p, v);
}
-
+
if (is_type_matrix(dst) && !is_type_matrix(src)) {
GB_ASSERT_MSG(dst->Matrix.row_count == dst->Matrix.column_count, "%s <- %s", type_to_string(dst), type_to_string(src));
-
+
Type *elem = base_array_type(dst);
lbValue e = lb_emit_conv(p, value, elem);
lbAddr v = lb_add_local_generated(p, t, false);
@@ -2065,16 +2064,16 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
lbValue ptr = lb_emit_matrix_epi(p, v.addr, j, j);
lb_emit_store(p, ptr, e);
}
-
-
+
+
return lb_addr_load(p, v);
}
-
+
if (is_type_matrix(dst) && is_type_matrix(src)) {
GB_ASSERT(dst->kind == Type_Matrix);
GB_ASSERT(src->kind == Type_Matrix);
lbAddr v = lb_add_local_generated(p, t, true);
-
+
if (is_matrix_square(dst) && is_matrix_square(dst)) {
for (i64 j = 0; j < dst->Matrix.column_count; j++) {
for (i64 i = 0; i < dst->Matrix.row_count; i++) {
@@ -2093,15 +2092,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
i64 dst_count = dst->Matrix.row_count*dst->Matrix.column_count;
i64 src_count = src->Matrix.row_count*src->Matrix.column_count;
GB_ASSERT(dst_count == src_count);
-
+
lbValue pdst = v.addr;
lbValue psrc = lb_address_from_load_or_generate_local(p, value);
-
+
bool same_elem_base_types = are_types_identical(
base_type(dst->Matrix.elem),
base_type(src->Matrix.elem)
);
-
+
if (same_elem_base_types && type_size_of(dst) == type_size_of(src)) {
lb_mem_copy_overlapping(p, v.addr, psrc, lb_const_int(p->module, t_int, type_size_of(dst)));
} else {
@@ -2115,9 +2114,9 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
}
return lb_addr_load(p, v);
- }
-
-
+ }
+
+
if (is_type_any(dst)) {
if (is_type_untyped_nil(src)) {
@@ -2270,6 +2269,9 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
}
+ a = core_type(left.type);
+ b = core_type(right.type);
+
if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
Type *tl = base_type(a);
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
@@ -2301,7 +2303,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
cmp_op = Token_And;
}
- bool inline_array_arith = type_size_of(tl) <= build_context.max_align;
+ bool inline_array_arith = lb_can_try_to_inline_array_arith(tl);
i32 count = 0;
switch (tl->kind) {
case Type_Array: count = cast(i32)tl->Array.count; break;
@@ -2721,7 +2723,7 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
unsigned indices[2] = {0, 0};
lbValue hashes_data = lb_emit_struct_ep(p, map_ptr, 0);
lbValue hashes_data_ptr_ptr = lb_emit_struct_ep(p, hashes_data, 0);
- LLVMValueRef hashes_data_ptr = LLVMBuildLoad(p->builder, hashes_data_ptr_ptr.value, "");
+ LLVMValueRef hashes_data_ptr = LLVMBuildLoad2(p->builder, llvm_addr_type(p->module, hashes_data_ptr_ptr), hashes_data_ptr_ptr.value, "");
if (op_kind == Token_CmpEq) {
res.value = LLVMBuildIsNull(p->builder, hashes_data_ptr, "");
@@ -2800,7 +2802,15 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
return {};
}
+lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) {
+ lbAddr v = lb_add_local_generated(p, type, false);
+ lbValue ptr = lb_emit_struct_ep(p, v.addr, 0);
+ lbValue idx = lb_emit_struct_ep(p, v.addr, 1);
+ lb_emit_store(p, ptr, addr);
+ lb_emit_store(p, idx, index);
+ return lb_addr_load(p, v);
+}
lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
ast_node(ue, UnaryExpr, expr);
@@ -2839,7 +2849,17 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
lb_emit_store(p, gep1, ok);
return lb_addr_load(p, res);
- } if (ue_expr->kind == Ast_CompoundLit) {
+ } else if (is_type_soa_pointer(tv.type)) {
+ ast_node(ie, IndexExpr, ue_expr);
+ lbValue addr = lb_build_addr_ptr(p, ie->expr);
+ lbValue index = lb_build_expr(p, ie->index);
+
+ if (!build_context.no_bounds_check) {
+ // TODO(bill): soa bounds checking
+ }
+
+ return lb_make_soa_pointer(p, tv.type, addr, index);
+ } else if (ue_expr->kind == Ast_CompoundLit) {
lbValue v = lb_build_expr(p, ue->expr);
Type *type = v.type;
@@ -3322,7 +3342,7 @@ lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
default: GB_PANIC("Unhandled inline asm dialect"); break;
}
- LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, t));
+ LLVMTypeRef func_type = lb_type_internal_for_procedures_raw(p->module, t);
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, asm_string, constraints_string, ia->has_side_effects, ia->has_side_effects, dialect);
GB_ASSERT(the_asm != nullptr);
return {the_asm, t};
@@ -3468,6 +3488,901 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
return addr;
}
+void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice<Ast *> const &elems, Array<lbCompoundLitElemTempData> *temp_data, Type *compound_type) {
+ Type *bt = base_type(compound_type);
+ Type *et = nullptr;
+ switch (bt->kind) {
+ case Type_Array: et = bt->Array.elem; break;
+ case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
+ case Type_Slice: et = bt->Slice.elem; break;
+ case Type_BitSet: et = bt->BitSet.elem; break;
+ case Type_DynamicArray: et = bt->DynamicArray.elem; break;
+ case Type_SimdVector: et = bt->SimdVector.elem; break;
+ case Type_Matrix: et = bt->Matrix.elem; break;
+ }
+ GB_ASSERT(et != nullptr);
+
+
+ // NOTE(bill): Separate value, gep, store into their own chunks
+ for_array(i, elems) {
+ Ast *elem = elems[i];
+ if (elem->kind == Ast_FieldValue) {
+ ast_node(fv, FieldValue, elem);
+ if (lb_is_elem_const(fv->value, et)) {
+ continue;
+ }
+ if (is_ast_range(fv->field)) {
+ ast_node(ie, BinaryExpr, fv->field);
+ TypeAndValue lo_tav = ie->left->tav;
+ TypeAndValue hi_tav = ie->right->tav;
+ GB_ASSERT(lo_tav.mode == Addressing_Constant);
+ GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+ TokenKind op = ie->op.kind;
+ i64 lo = exact_value_to_i64(lo_tav.value);
+ i64 hi = exact_value_to_i64(hi_tav.value);
+ if (op != Token_RangeHalf) {
+ hi += 1;
+ }
+
+ lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et);
+
+ GB_ASSERT((hi-lo) > 0);
+
+ if (bt->kind == Type_Matrix) {
+ for (i64 k = lo; k < hi; k++) {
+ lbCompoundLitElemTempData data = {};
+ data.value = value;
+
+ data.elem_index = matrix_row_major_index_to_offset(bt, k);
+ array_add(temp_data, data);
+ }
+ } else {
+ enum {MAX_ELEMENT_AMOUNT = 32};
+ if ((hi-lo) <= MAX_ELEMENT_AMOUNT) {
+ for (i64 k = lo; k < hi; k++) {
+ lbCompoundLitElemTempData data = {};
+ data.value = value;
+ data.elem_index = k;
+ array_add(temp_data, data);
+ }
+ } else {
+ lbCompoundLitElemTempData data = {};
+ data.value = value;
+ data.elem_index = lo;
+ data.elem_length = hi-lo;
+ array_add(temp_data, data);
+ }
+ }
+ } else {
+ auto tav = fv->field->tav;
+ GB_ASSERT(tav.mode == Addressing_Constant);
+ i64 index = exact_value_to_i64(tav.value);
+
+ lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et);
+ GB_ASSERT(!is_type_tuple(value.type));
+
+ lbCompoundLitElemTempData data = {};
+ data.value = value;
+ data.expr = fv->value;
+ if (bt->kind == Type_Matrix) {
+ data.elem_index = matrix_row_major_index_to_offset(bt, index);
+ } else {
+ data.elem_index = index;
+ }
+ array_add(temp_data, data);
+ }
+
+ } else {
+ if (bt->kind != Type_DynamicArray && lb_is_elem_const(elem, et)) {
+ continue;
+ }
+
+ lbValue field_expr = lb_build_expr(p, elem);
+ GB_ASSERT(!is_type_tuple(field_expr.type));
+
+ lbValue ev = lb_emit_conv(p, field_expr, et);
+
+ lbCompoundLitElemTempData data = {};
+ data.value = ev;
+ if (bt->kind == Type_Matrix) {
+ data.elem_index = matrix_row_major_index_to_offset(bt, i);
+ } else {
+ data.elem_index = i;
+ }
+ array_add(temp_data, data);
+ }
+ }
+}
+void lb_build_addr_compound_lit_assign_array(lbProcedure *p, Array<lbCompoundLitElemTempData> const &temp_data) {
+ for_array(i, temp_data) {
+ auto td = temp_data[i];
+ if (td.value.value != nullptr) {
+ if (td.elem_length > 0) {
+ auto loop_data = lb_loop_start(p, cast(isize)td.elem_length, t_i32);
+ {
+ lbValue dst = td.gep;
+ dst = lb_emit_ptr_offset(p, dst, loop_data.idx);
+ lb_emit_store(p, dst, td.value);
+ }
+ lb_loop_end(p, loop_data);
+ } else {
+ lb_emit_store(p, td.gep, td.value);
+ }
+ }
+ }
+}
+
+lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
+ ast_node(ie, IndexExpr, expr);
+
+ Type *t = base_type(type_of_expr(ie->expr));
+
+ bool deref = is_type_pointer(t);
+ t = base_type(type_deref(t));
+ if (is_type_soa_struct(t)) {
+ // SOA STRUCTURES!!!!
+ lbValue val = lb_build_addr_ptr(p, ie->expr);
+ if (deref) {
+ val = lb_emit_load(p, val);
+ }
+
+ lbValue index = lb_build_expr(p, ie->index);
+ return lb_addr_soa_variable(val, index, ie->index);
+ }
+
+ if (ie->expr->tav.mode == Addressing_SoaVariable) {
+ // SOA Structures for slices/dynamic arrays
+ GB_ASSERT(is_type_pointer(type_of_expr(ie->expr)));
+
+ lbValue field = lb_build_expr(p, ie->expr);
+ lbValue index = lb_build_expr(p, ie->index);
+
+
+ if (!build_context.no_bounds_check) {
+ // TODO HACK(bill): Clean up this hack to get the length for bounds checking
+ // GB_ASSERT(LLVMIsALoadInst(field.value));
+
+ // lbValue a = {};
+ // a.value = LLVMGetOperand(field.value, 0);
+ // a.type = alloc_type_pointer(field.type);
+
+ // irInstr *b = &a->Instr;
+ // GB_ASSERT(b->kind == irInstr_StructElementPtr);
+ // lbValue base_struct = b->StructElementPtr.address;
+
+ // GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct))));
+ // lbValue len = ir_soa_struct_len(p, base_struct);
+ // lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ }
+ lbValue val = lb_emit_ptr_offset(p, field, index);
+ return lb_addr(val);
+ }
+
+ GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
+
+ if (is_type_map(t)) {
+ lbAddr map_addr = lb_build_addr(p, ie->expr);
+ lbValue map_val = lb_addr_load(p, map_addr);
+ if (deref) {
+ map_val = lb_emit_load(p, map_val);
+ }
+
+ lbValue key = lb_build_expr(p, ie->index);
+ key = lb_emit_conv(p, key, t->Map.key);
+
+ Type *result_type = type_of_expr(expr);
+ lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
+ return lb_addr_map(map_ptr, key, t, result_type);
+ }
+
+ switch (t->kind) {
+ case Type_Array: {
+ lbValue array = {};
+ array = lb_build_addr_ptr(p, ie->expr);
+ if (deref) {
+ array = lb_emit_load(p, array);
+ }
+ lbValue index = lb_build_expr(p, ie->index);
+ index = lb_emit_conv(p, index, t_int);
+ lbValue elem = lb_emit_array_ep(p, array, index);
+
+ auto index_tv = type_and_value_of_expr(ie->index);
+ if (index_tv.mode != Addressing_Constant) {
+ lbValue len = lb_const_int(p->module, t_int, t->Array.count);
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ }
+ return lb_addr(elem);
+ }
+
+ case Type_EnumeratedArray: {
+ lbValue array = {};
+ array = lb_build_addr_ptr(p, ie->expr);
+ if (deref) {
+ array = lb_emit_load(p, array);
+ }
+
+ Type *index_type = t->EnumeratedArray.index;
+
+ auto index_tv = type_and_value_of_expr(ie->index);
+
+ lbValue index = {};
+ if (compare_exact_values(Token_NotEq, *t->EnumeratedArray.min_value, exact_value_i64(0))) {
+ if (index_tv.mode == Addressing_Constant) {
+ ExactValue idx = exact_value_sub(index_tv.value, *t->EnumeratedArray.min_value);
+ index = lb_const_value(p->module, index_type, idx);
+ } else {
+ index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+ index = lb_emit_arith(p, Token_Sub, index, lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value), index_type);
+ }
+ } else {
+ index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+ }
+
+ lbValue elem = lb_emit_array_ep(p, array, index);
+
+ if (index_tv.mode != Addressing_Constant) {
+ lbValue len = lb_const_int(p->module, t_int, t->EnumeratedArray.count);
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ }
+ return lb_addr(elem);
+ }
+
+ case Type_Slice: {
+ lbValue slice = {};
+ slice = lb_build_expr(p, ie->expr);
+ if (deref) {
+ slice = lb_emit_load(p, slice);
+ }
+ lbValue elem = lb_slice_elem(p, slice);
+ lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+ lbValue len = lb_slice_len(p, slice);
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ lbValue v = lb_emit_ptr_offset(p, elem, index);
+ return lb_addr(v);
+ }
+
+ case Type_MultiPointer: {
+ lbValue multi_ptr = {};
+ multi_ptr = lb_build_expr(p, ie->expr);
+ if (deref) {
+ multi_ptr = lb_emit_load(p, multi_ptr);
+ }
+ lbValue index = lb_build_expr(p, ie->index);
+ lbValue v = {};
+
+ LLVMValueRef indices[1] = {index.value};
+ v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, t->MultiPointer.elem), multi_ptr.value, indices, 1, "foo");
+ v.type = alloc_type_pointer(t->MultiPointer.elem);
+ return lb_addr(v);
+ }
+
+ case Type_RelativeSlice: {
+ lbAddr slice_addr = {};
+ if (deref) {
+ slice_addr = lb_addr(lb_build_expr(p, ie->expr));
+ } else {
+ slice_addr = lb_build_addr(p, ie->expr);
+ }
+ lbValue slice = lb_addr_load(p, slice_addr);
+
+ lbValue elem = lb_slice_elem(p, slice);
+ lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+ lbValue len = lb_slice_len(p, slice);
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ lbValue v = lb_emit_ptr_offset(p, elem, index);
+ return lb_addr(v);
+ }
+
+ case Type_DynamicArray: {
+ lbValue dynamic_array = {};
+ dynamic_array = lb_build_expr(p, ie->expr);
+ if (deref) {
+ dynamic_array = lb_emit_load(p, dynamic_array);
+ }
+ lbValue elem = lb_dynamic_array_elem(p, dynamic_array);
+ lbValue len = lb_dynamic_array_len(p, dynamic_array);
+ lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ lbValue v = lb_emit_ptr_offset(p, elem, index);
+ return lb_addr(v);
+ }
+
+ case Type_Matrix: {
+ lbValue matrix = {};
+ matrix = lb_build_addr_ptr(p, ie->expr);
+ if (deref) {
+ matrix = lb_emit_load(p, matrix);
+ }
+ lbValue index = lb_build_expr(p, ie->index);
+ index = lb_emit_conv(p, index, t_int);
+ lbValue elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index);
+ elem = lb_emit_conv(p, elem, alloc_type_pointer(type_of_expr(expr)));
+
+ auto index_tv = type_and_value_of_expr(ie->index);
+ if (index_tv.mode != Addressing_Constant) {
+ lbValue len = lb_const_int(p->module, t_int, t->Matrix.column_count);
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ }
+ return lb_addr(elem);
+ }
+
+
+ case Type_Basic: { // Basic_string
+ lbValue str;
+ lbValue elem;
+ lbValue len;
+ lbValue index;
+
+ str = lb_build_expr(p, ie->expr);
+ if (deref) {
+ str = lb_emit_load(p, str);
+ }
+ elem = lb_string_elem(p, str);
+ len = lb_string_len(p, str);
+
+ index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+
+ return lb_addr(lb_emit_ptr_offset(p, elem, index));
+ }
+ }
+ return {};
+}
+
+
+lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
+ ast_node(se, SliceExpr, expr);
+
+ lbValue low = lb_const_int(p->module, t_int, 0);
+ lbValue high = {};
+
+ if (se->low != nullptr) {
+ low = lb_correct_endianness(p, lb_build_expr(p, se->low));
+ }
+ if (se->high != nullptr) {
+ high = lb_correct_endianness(p, lb_build_expr(p, se->high));
+ }
+
+ bool no_indices = se->low == nullptr && se->high == nullptr;
+
+ lbAddr addr = lb_build_addr(p, se->expr);
+ lbValue base = lb_addr_load(p, addr);
+ Type *type = base_type(base.type);
+
+ if (is_type_pointer(type)) {
+ type = base_type(type_deref(type));
+ addr = lb_addr(base);
+ base = lb_addr_load(p, addr);
+ }
+
+ switch (type->kind) {
+ case Type_Slice: {
+ Type *slice_type = type;
+ lbValue len = lb_slice_len(p, base);
+ if (high.value == nullptr) high = len;
+
+ if (!no_indices) {
+ lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+ }
+
+ lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+ lbAddr slice = lb_add_local_generated(p, slice_type, false);
+ lb_fill_slice(p, slice, elem, new_len);
+ return slice;
+ }
+
+ case Type_RelativeSlice:
+ GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the lb_addr_load");
+ break;
+
+ case Type_DynamicArray: {
+ Type *elem_type = type->DynamicArray.elem;
+ Type *slice_type = alloc_type_slice(elem_type);
+
+ lbValue len = lb_dynamic_array_len(p, base);
+ if (high.value == nullptr) high = len;
+
+ if (!no_indices) {
+ lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+ }
+
+ lbValue elem = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+ lbAddr slice = lb_add_local_generated(p, slice_type, false);
+ lb_fill_slice(p, slice, elem, new_len);
+ return slice;
+ }
+
+ case Type_MultiPointer: {
+ lbAddr res = lb_add_local_generated(p, type_of_expr(expr), false);
+ if (se->high == nullptr) {
+ lbValue offset = base;
+ LLVMValueRef indices[1] = {low.value};
+ offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, "");
+ lb_addr_store(p, res, offset);
+ } else {
+ low = lb_emit_conv(p, low, t_int);
+ high = lb_emit_conv(p, high, t_int);
+
+ lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
+
+ LLVMValueRef indices[1] = {low.value};
+ LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, "");
+ LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, "");
+
+ LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value;
+ LLVMValueRef gep1 = lb_emit_struct_ep(p, res.addr, 1).value;
+ LLVMBuildStore(p->builder, ptr, gep0);
+ LLVMBuildStore(p->builder, len, gep1);
+ }
+ return res;
+ }
+
+ case Type_Array: {
+ Type *slice_type = alloc_type_slice(type->Array.elem);
+ lbValue len = lb_const_int(p->module, t_int, type->Array.count);
+
+ if (high.value == nullptr) high = len;
+
+ bool low_const = type_and_value_of_expr(se->low).mode == Addressing_Constant;
+ bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant;
+
+ if (!low_const || !high_const) {
+ if (!no_indices) {
+ lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+ }
+ }
+ lbValue elem = lb_emit_ptr_offset(p, lb_array_elem(p, lb_addr_get_ptr(p, addr)), low);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+ lbAddr slice = lb_add_local_generated(p, slice_type, false);
+ lb_fill_slice(p, slice, elem, new_len);
+ return slice;
+ }
+
+ case Type_Basic: {
+ GB_ASSERT(type == t_string);
+ lbValue len = lb_string_len(p, base);
+ if (high.value == nullptr) high = len;
+
+ if (!no_indices) {
+ lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+ }
+
+ lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+ lbAddr str = lb_add_local_generated(p, t_string, false);
+ lb_fill_string(p, str, elem, new_len);
+ return str;
+ }
+
+
+ case Type_Struct:
+ if (is_type_soa_struct(type)) {
+ lbValue len = lb_soa_struct_len(p, lb_addr_get_ptr(p, addr));
+ if (high.value == nullptr) high = len;
+
+ if (!no_indices) {
+ lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+ }
+ #if 1
+
+ lbAddr dst = lb_add_local_generated(p, type_of_expr(expr), true);
+ if (type->Struct.soa_kind == StructSoa_Fixed) {
+ i32 field_count = cast(i32)type->Struct.fields.count;
+ for (i32 i = 0; i < field_count; i++) {
+ lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+ lbValue field_src = lb_emit_struct_ep(p, lb_addr_get_ptr(p, addr), i);
+ field_src = lb_emit_array_ep(p, field_src, low);
+ lb_emit_store(p, field_dst, field_src);
+ }
+
+ lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+ lb_emit_store(p, len_dst, new_len);
+ } else if (type->Struct.soa_kind == StructSoa_Slice) {
+ if (no_indices) {
+ lb_addr_store(p, dst, base);
+ } else {
+ i32 field_count = cast(i32)type->Struct.fields.count - 1;
+ for (i32 i = 0; i < field_count; i++) {
+ lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+ lbValue field_src = lb_emit_struct_ev(p, base, i);
+ field_src = lb_emit_ptr_offset(p, field_src, low);
+ lb_emit_store(p, field_dst, field_src);
+ }
+
+
+ lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+ lb_emit_store(p, len_dst, new_len);
+ }
+ } else if (type->Struct.soa_kind == StructSoa_Dynamic) {
+ i32 field_count = cast(i32)type->Struct.fields.count - 3;
+ for (i32 i = 0; i < field_count; i++) {
+ lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+ lbValue field_src = lb_emit_struct_ev(p, base, i);
+ field_src = lb_emit_ptr_offset(p, field_src, low);
+ lb_emit_store(p, field_dst, field_src);
+ }
+
+
+ lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+ lb_emit_store(p, len_dst, new_len);
+ }
+
+ return dst;
+ #endif
+ }
+ break;
+
+ }
+
+ GB_PANIC("Unknown slicable type");
+ return {};
+}
+
+
+lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
+ ast_node(cl, CompoundLit, expr);
+
+ Type *type = type_of_expr(expr);
+ Type *bt = base_type(type);
+
+ lbAddr v = lb_add_local_generated(p, type, true);
+
+ Type *et = nullptr;
+ switch (bt->kind) {
+ case Type_Array: et = bt->Array.elem; break;
+ case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
+ case Type_Slice: et = bt->Slice.elem; break;
+ case Type_BitSet: et = bt->BitSet.elem; break;
+ case Type_SimdVector: et = bt->SimdVector.elem; break;
+ case Type_Matrix: et = bt->Matrix.elem; break;
+ }
+
+ String proc_name = {};
+ if (p->entity) {
+ proc_name = p->entity->token.string;
+ }
+ TokenPos pos = ast_token(expr).pos;
+
+ switch (bt->kind) {
+ default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
+
+ case Type_Struct: {
+ // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment.
+ // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR
+ bool is_raw_union = is_type_raw_union(bt);
+ GB_ASSERT(is_type_struct(bt) || is_raw_union);
+ TypeStruct *st = &bt->Struct;
+ if (cl->elems.count > 0) {
+ lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+ lbValue comp_lit_ptr = lb_addr_get_ptr(p, v);
+
+ for_array(field_index, cl->elems) {
+ Ast *elem = cl->elems[field_index];
+
+ lbValue field_expr = {};
+ Entity *field = nullptr;
+ isize index = field_index;
+
+ if (elem->kind == Ast_FieldValue) {
+ ast_node(fv, FieldValue, elem);
+ String name = fv->field->Ident.token.string;
+ Selection sel = lookup_field(bt, name, false);
+ index = sel.index[0];
+ elem = fv->value;
+ TypeAndValue tav = type_and_value_of_expr(elem);
+ } else {
+ TypeAndValue tav = type_and_value_of_expr(elem);
+ Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index);
+ index = sel.index[0];
+ }
+
+ field = st->fields[index];
+ Type *ft = field->type;
+ if (!is_raw_union && !is_type_typeid(ft) && lb_is_elem_const(elem, ft)) {
+ continue;
+ }
+
+ field_expr = lb_build_expr(p, elem);
+
+ lbValue gep = {};
+ if (is_raw_union) {
+ gep = lb_emit_conv(p, comp_lit_ptr, alloc_type_pointer(ft));
+ } else {
+ gep = lb_emit_struct_ep(p, comp_lit_ptr, cast(i32)index);
+ }
+
+ Type *fet = field_expr.type;
+ GB_ASSERT(fet->kind != Type_Tuple);
+
+ // HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
+ if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
+ GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
+
+ lb_emit_store_union_variant(p, gep, field_expr, fet);
+ } else {
+ lbValue fv = lb_emit_conv(p, field_expr, ft);
+ lb_emit_store(p, gep, fv);
+ }
+ }
+ }
+ break;
+ }
+
+ case Type_Map: {
+ if (cl->elems.count == 0) {
+ break;
+ }
+ GB_ASSERT(!build_context.no_dynamic_literals);
+ {
+ auto args = array_make<lbValue>(permanent_allocator(), 3);
+ args[0] = lb_gen_map_header(p, v.addr, type);
+ args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count);
+ args[2] = lb_emit_source_code_location(p, proc_name, pos);
+ lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
+ }
+ for_array(field_index, cl->elems) {
+ Ast *elem = cl->elems[field_index];
+ ast_node(fv, FieldValue, elem);
+
+ lbValue key = lb_build_expr(p, fv->field);
+ lbValue value = lb_build_expr(p, fv->value);
+ lb_insert_dynamic_map_key_and_value(p, v, type, key, value, elem);
+ }
+ break;
+ }
+
+ case Type_Array: {
+ 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<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+ lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type);
+
+ lbValue dst_ptr = lb_addr_get_ptr(p, v);
+ for_array(i, temp_data) {
+ i32 index = cast(i32)(temp_data[i].elem_index);
+ temp_data[i].gep = lb_emit_array_epi(p, dst_ptr, index);
+ }
+
+ lb_build_addr_compound_lit_assign_array(p, temp_data);
+ }
+ break;
+ }
+ case Type_EnumeratedArray: {
+ 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<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+ lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type);
+
+ lbValue dst_ptr = lb_addr_get_ptr(p, v);
+ i64 index_offset = exact_value_to_i64(*bt->EnumeratedArray.min_value);
+ for_array(i, temp_data) {
+ i32 index = cast(i32)(temp_data[i].elem_index - index_offset);
+ temp_data[i].gep = lb_emit_array_epi(p, dst_ptr, index);
+ }
+
+ lb_build_addr_compound_lit_assign_array(p, temp_data);
+ }
+ break;
+ }
+ case Type_Slice: {
+ if (cl->elems.count > 0) {
+ lbValue slice = lb_const_value(p->module, type, exact_value_compound(expr));
+
+ lbValue data = lb_slice_elem(p, slice);
+
+ auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+ lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type);
+
+ for_array(i, temp_data) {
+ temp_data[i].gep = lb_emit_ptr_offset(p, data, lb_const_int(p->module, t_int, temp_data[i].elem_index));
+ }
+
+ lb_build_addr_compound_lit_assign_array(p, temp_data);
+
+ {
+ lbValue count = {};
+ count.type = t_int;
+
+ if (lb_is_const(slice)) {
+ unsigned indices[1] = {1};
+ count.value = LLVMConstExtractValue(slice.value, indices, gb_count_of(indices));
+ } else {
+ count.value = LLVMBuildExtractValue(p->builder, slice.value, 1, "");
+ }
+ lb_fill_slice(p, v, data, count);
+ }
+ }
+ break;
+ }
+
+ case Type_DynamicArray: {
+ if (cl->elems.count == 0) {
+ break;
+ }
+ GB_ASSERT(!build_context.no_dynamic_literals);
+
+ Type *et = bt->DynamicArray.elem;
+ 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<lbValue>(permanent_allocator(), 5);
+ args[0] = lb_emit_conv(p, lb_addr_get_ptr(p, v), t_rawptr);
+ args[1] = size;
+ args[2] = align;
+ args[3] = lb_const_int(p->module, t_int, item_count);
+ args[4] = lb_emit_source_code_location(p, proc_name, pos);
+ lb_emit_runtime_call(p, "__dynamic_array_reserve", args);
+ }
+
+ lbValue items = lb_generate_local_array(p, et, item_count);
+
+ auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+ lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type);
+
+ for_array(i, temp_data) {
+ temp_data[i].gep = lb_emit_array_epi(p, items, temp_data[i].elem_index);
+ }
+ lb_build_addr_compound_lit_assign_array(p, temp_data);
+
+ {
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
+ args[0] = lb_emit_conv(p, v.addr, t_rawptr);
+ args[1] = size;
+ args[2] = align;
+ args[3] = lb_emit_conv(p, items, t_rawptr);
+ args[4] = lb_const_int(p->module, t_int, item_count);
+ args[5] = lb_emit_source_code_location(p, proc_name, pos);
+ lb_emit_runtime_call(p, "__dynamic_array_append", args);
+ }
+ break;
+ }
+
+ case Type_Basic: {
+ GB_ASSERT(is_type_any(bt));
+ if (cl->elems.count > 0) {
+ lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+ String field_names[2] = {
+ str_lit("data"),
+ str_lit("id"),
+ };
+ Type *field_types[2] = {
+ t_rawptr,
+ t_typeid,
+ };
+
+ for_array(field_index, cl->elems) {
+ Ast *elem = cl->elems[field_index];
+
+ lbValue field_expr = {};
+ isize index = field_index;
+
+ if (elem->kind == Ast_FieldValue) {
+ ast_node(fv, FieldValue, elem);
+ Selection sel = lookup_field(bt, fv->field->Ident.token.string, false);
+ index = sel.index[0];
+ elem = fv->value;
+ } else {
+ TypeAndValue tav = type_and_value_of_expr(elem);
+ Selection sel = lookup_field(bt, field_names[field_index], false);
+ index = sel.index[0];
+ }
+
+ field_expr = lb_build_expr(p, elem);
+
+ GB_ASSERT(field_expr.type->kind != Type_Tuple);
+
+ Type *ft = field_types[index];
+ lbValue fv = lb_emit_conv(p, field_expr, ft);
+ lbValue gep = lb_emit_struct_ep(p, lb_addr_get_ptr(p, v), cast(i32)index);
+ lb_emit_store(p, gep, fv);
+ }
+ }
+
+ break;
+ }
+
+ case Type_BitSet: {
+ i64 sz = type_size_of(type);
+ if (cl->elems.count > 0 && sz > 0) {
+ lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+
+ lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower));
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ GB_ASSERT(elem->kind != Ast_FieldValue);
+
+ if (lb_is_elem_const(elem, et)) {
+ continue;
+ }
+
+ lbValue expr = lb_build_expr(p, elem);
+ GB_ASSERT(expr.type->kind != Type_Tuple);
+
+ Type *it = bit_set_to_int(bt);
+ lbValue one = lb_const_value(p->module, it, exact_value_i64(1));
+ lbValue e = lb_emit_conv(p, expr, it);
+ e = lb_emit_arith(p, Token_Sub, e, lower, it);
+ e = lb_emit_arith(p, Token_Shl, one, e, it);
+
+ lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it);
+ lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it);
+ new_value = lb_emit_transmute(p, new_value, type);
+ lb_addr_store(p, v, new_value);
+ }
+ }
+ break;
+ }
+
+ case Type_Matrix: {
+ 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<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+ lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type);
+
+ lbValue dst_ptr = lb_addr_get_ptr(p, v);
+ for_array(i, temp_data) {
+ temp_data[i].gep = lb_emit_array_epi(p, dst_ptr, temp_data[i].elem_index);
+ }
+
+ lb_build_addr_compound_lit_assign_array(p, temp_data);
+ }
+ break;
+ }
+
+ case Type_SimdVector: {
+ if (cl->elems.count > 0) {
+ lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr));
+ defer (lb_addr_store(p, v, vector_value));
+
+ auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+ lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type);
+
+ // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector`
+ // might be a better option
+ for_array(i, temp_data) {
+ auto td = temp_data[i];
+ if (td.value.value != nullptr) {
+ if (td.elem_length > 0) {
+ for (i64 k = 0; k < td.elem_length; k++) {
+ LLVMValueRef index = lb_const_int(p->module, t_u32, td.elem_index + k).value;
+ vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, td.value.value, index, "");
+ }
+ } else {
+ LLVMValueRef index = lb_const_int(p->module, t_u32, td.elem_index).value;
+ vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, td.value.value, index, "");
+
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return v;
+}
+
lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
switch (expr->kind) {
@@ -3601,6 +4516,7 @@ lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
// NOTE(bill): just patch the index in place
sel.index[0] = addr.swizzle.indices[sel.index[0]];
}
+
lbValue a = lb_addr_get_ptr(p, addr);
a = lb_emit_deep_field_gep(p, a, sel);
return lb_addr(a);
@@ -3652,225 +4568,15 @@ lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(ie, IndexExpr, expr);
- Type *t = base_type(type_of_expr(ie->expr));
-
- bool deref = is_type_pointer(t);
- t = base_type(type_deref(t));
- if (is_type_soa_struct(t)) {
- // SOA STRUCTURES!!!!
- lbValue val = lb_build_addr_ptr(p, ie->expr);
- if (deref) {
- val = lb_emit_load(p, val);
- }
-
- lbValue index = lb_build_expr(p, ie->index);
- return lb_addr_soa_variable(val, index, ie->index);
- }
-
- if (ie->expr->tav.mode == Addressing_SoaVariable) {
- // SOA Structures for slices/dynamic arrays
- GB_ASSERT(is_type_pointer(type_of_expr(ie->expr)));
-
- lbValue field = lb_build_expr(p, ie->expr);
- lbValue index = lb_build_expr(p, ie->index);
-
-
- if (!build_context.no_bounds_check) {
- // TODO HACK(bill): Clean up this hack to get the length for bounds checking
- // GB_ASSERT(LLVMIsALoadInst(field.value));
-
- // lbValue a = {};
- // a.value = LLVMGetOperand(field.value, 0);
- // a.type = alloc_type_pointer(field.type);
-
- // irInstr *b = &a->Instr;
- // GB_ASSERT(b->kind == irInstr_StructElementPtr);
- // lbValue base_struct = b->StructElementPtr.address;
-
- // GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct))));
- // lbValue len = ir_soa_struct_len(p, base_struct);
- // lb_emit_bounds_check(p, ast_token(ie->index), index, len);
- }
- lbValue val = lb_emit_ptr_offset(p, field, index);
- return lb_addr(val);
- }
-
- GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
-
- if (is_type_map(t)) {
- lbAddr map_addr = lb_build_addr(p, ie->expr);
- lbValue map_val = lb_addr_load(p, map_addr);
- if (deref) {
- map_val = lb_emit_load(p, map_val);
- }
-
- lbValue key = lb_build_expr(p, ie->index);
- key = lb_emit_conv(p, key, t->Map.key);
-
- Type *result_type = type_of_expr(expr);
- lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
- return lb_addr_map(map_ptr, key, t, result_type);
- }
-
- switch (t->kind) {
- case Type_Array: {
- lbValue array = {};
- array = lb_build_addr_ptr(p, ie->expr);
- if (deref) {
- array = lb_emit_load(p, array);
- }
- lbValue index = lb_build_expr(p, ie->index);
- index = lb_emit_conv(p, index, t_int);
- lbValue elem = lb_emit_array_ep(p, array, index);
-
- auto index_tv = type_and_value_of_expr(ie->index);
- if (index_tv.mode != Addressing_Constant) {
- lbValue len = lb_const_int(p->module, t_int, t->Array.count);
- lb_emit_bounds_check(p, ast_token(ie->index), index, len);
- }
- return lb_addr(elem);
- }
-
- case Type_EnumeratedArray: {
- lbValue array = {};
- array = lb_build_addr_ptr(p, ie->expr);
- if (deref) {
- array = lb_emit_load(p, array);
- }
-
- Type *index_type = t->EnumeratedArray.index;
-
- auto index_tv = type_and_value_of_expr(ie->index);
-
- lbValue index = {};
- if (compare_exact_values(Token_NotEq, *t->EnumeratedArray.min_value, exact_value_i64(0))) {
- if (index_tv.mode == Addressing_Constant) {
- ExactValue idx = exact_value_sub(index_tv.value, *t->EnumeratedArray.min_value);
- index = lb_const_value(p->module, index_type, idx);
- } else {
- index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
- index = lb_emit_arith(p, Token_Sub, index, lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value), index_type);
- }
- } else {
- index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
- }
-
- lbValue elem = lb_emit_array_ep(p, array, index);
-
- if (index_tv.mode != Addressing_Constant) {
- lbValue len = lb_const_int(p->module, t_int, t->EnumeratedArray.count);
- lb_emit_bounds_check(p, ast_token(ie->index), index, len);
- }
- return lb_addr(elem);
- }
-
- case Type_Slice: {
- lbValue slice = {};
- slice = lb_build_expr(p, ie->expr);
- if (deref) {
- slice = lb_emit_load(p, slice);
- }
- lbValue elem = lb_slice_elem(p, slice);
- lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
- lbValue len = lb_slice_len(p, slice);
- lb_emit_bounds_check(p, ast_token(ie->index), index, len);
- lbValue v = lb_emit_ptr_offset(p, elem, index);
- return lb_addr(v);
- }
-
- case Type_MultiPointer: {
- lbValue multi_ptr = {};
- multi_ptr = lb_build_expr(p, ie->expr);
- if (deref) {
- multi_ptr = lb_emit_load(p, multi_ptr);
- }
- lbValue index = lb_build_expr(p, ie->index);
- lbValue v = {};
-
- LLVMValueRef indices[1] = {index.value};
- v.value = LLVMBuildGEP(p->builder, multi_ptr.value, indices, 1, "");
- v.type = alloc_type_pointer(t->MultiPointer.elem);
- return lb_addr(v);
- }
-
- case Type_RelativeSlice: {
- lbAddr slice_addr = {};
- if (deref) {
- slice_addr = lb_addr(lb_build_expr(p, ie->expr));
- } else {
- slice_addr = lb_build_addr(p, ie->expr);
- }
- lbValue slice = lb_addr_load(p, slice_addr);
-
- lbValue elem = lb_slice_elem(p, slice);
- lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
- lbValue len = lb_slice_len(p, slice);
- lb_emit_bounds_check(p, ast_token(ie->index), index, len);
- lbValue v = lb_emit_ptr_offset(p, elem, index);
- return lb_addr(v);
- }
-
- case Type_DynamicArray: {
- lbValue dynamic_array = {};
- dynamic_array = lb_build_expr(p, ie->expr);
- if (deref) {
- dynamic_array = lb_emit_load(p, dynamic_array);
- }
- lbValue elem = lb_dynamic_array_elem(p, dynamic_array);
- lbValue len = lb_dynamic_array_len(p, dynamic_array);
- lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
- lb_emit_bounds_check(p, ast_token(ie->index), index, len);
- lbValue v = lb_emit_ptr_offset(p, elem, index);
- return lb_addr(v);
- }
-
- case Type_Matrix: {
- lbValue matrix = {};
- matrix = lb_build_addr_ptr(p, ie->expr);
- if (deref) {
- matrix = lb_emit_load(p, matrix);
- }
- lbValue index = lb_build_expr(p, ie->index);
- index = lb_emit_conv(p, index, t_int);
- lbValue elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index);
- elem = lb_emit_conv(p, elem, alloc_type_pointer(type_of_expr(expr)));
-
- auto index_tv = type_and_value_of_expr(ie->index);
- if (index_tv.mode != Addressing_Constant) {
- lbValue len = lb_const_int(p->module, t_int, t->Matrix.column_count);
- lb_emit_bounds_check(p, ast_token(ie->index), index, len);
- }
- return lb_addr(elem);
- }
-
-
- case Type_Basic: { // Basic_string
- lbValue str;
- lbValue elem;
- lbValue len;
- lbValue index;
-
- str = lb_build_expr(p, ie->expr);
- if (deref) {
- str = lb_emit_load(p, str);
- }
- elem = lb_string_elem(p, str);
- len = lb_string_len(p, str);
-
- index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
- lb_emit_bounds_check(p, ast_token(ie->index), index, len);
-
- return lb_addr(lb_emit_ptr_offset(p, elem, index));
- }
- }
+ return lb_build_addr_index_expr(p, expr);
case_end;
-
+
case_ast_node(ie, MatrixIndexExpr, expr);
Type *t = base_type(type_of_expr(ie->expr));
bool deref = is_type_pointer(t);
t = base_type(type_deref(t));
-
+
lbValue m = {};
m = lb_build_addr_ptr(p, ie->expr);
if (deref) {
@@ -3890,210 +4596,25 @@ lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
lb_emit_matrix_bounds_check(p, ast_token(ie->row_index), row_index, column_index, row_count, column_count);
}
return lb_addr(elem);
-
-
- case_end;
- case_ast_node(se, SliceExpr, expr);
-
- lbValue low = lb_const_int(p->module, t_int, 0);
- lbValue high = {};
-
- if (se->low != nullptr) {
- low = lb_correct_endianness(p, lb_build_expr(p, se->low));
- }
- if (se->high != nullptr) {
- high = lb_correct_endianness(p, lb_build_expr(p, se->high));
- }
-
- bool no_indices = se->low == nullptr && se->high == nullptr;
-
- lbAddr addr = lb_build_addr(p, se->expr);
- lbValue base = lb_addr_load(p, addr);
- Type *type = base_type(base.type);
-
- if (is_type_pointer(type)) {
- type = base_type(type_deref(type));
- addr = lb_addr(base);
- base = lb_addr_load(p, addr);
- }
-
- switch (type->kind) {
- case Type_Slice: {
- Type *slice_type = type;
- lbValue len = lb_slice_len(p, base);
- if (high.value == nullptr) high = len;
-
- if (!no_indices) {
- lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
- }
-
- lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low);
- lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
- lbAddr slice = lb_add_local_generated(p, slice_type, false);
- lb_fill_slice(p, slice, elem, new_len);
- return slice;
- }
-
- case Type_RelativeSlice:
- GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the lb_addr_load");
- break;
-
- case Type_DynamicArray: {
- Type *elem_type = type->DynamicArray.elem;
- Type *slice_type = alloc_type_slice(elem_type);
-
- lbValue len = lb_dynamic_array_len(p, base);
- if (high.value == nullptr) high = len;
-
- if (!no_indices) {
- lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
- }
-
- lbValue elem = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low);
- lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
-
- lbAddr slice = lb_add_local_generated(p, slice_type, false);
- lb_fill_slice(p, slice, elem, new_len);
- return slice;
- }
-
- case Type_MultiPointer: {
- lbAddr res = lb_add_local_generated(p, type_of_expr(expr), false);
- if (se->high == nullptr) {
- lbValue offset = base;
- LLVMValueRef indices[1] = {low.value};
- offset.value = LLVMBuildGEP(p->builder, offset.value, indices, 1, "");
- lb_addr_store(p, res, offset);
- } else {
- low = lb_emit_conv(p, low, t_int);
- high = lb_emit_conv(p, high, t_int);
-
- lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
-
- LLVMValueRef indices[1] = {low.value};
- LLVMValueRef ptr = LLVMBuildGEP(p->builder, base.value, indices, 1, "");
- LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, "");
-
- LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value;
- LLVMValueRef gep1 = lb_emit_struct_ep(p, res.addr, 1).value;
- LLVMBuildStore(p->builder, ptr, gep0);
- LLVMBuildStore(p->builder, len, gep1);
- }
- return res;
- }
-
- case Type_Array: {
- Type *slice_type = alloc_type_slice(type->Array.elem);
- lbValue len = lb_const_int(p->module, t_int, type->Array.count);
-
- if (high.value == nullptr) high = len;
-
- bool low_const = type_and_value_of_expr(se->low).mode == Addressing_Constant;
- bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant;
-
- if (!low_const || !high_const) {
- if (!no_indices) {
- lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
- }
- }
- lbValue elem = lb_emit_ptr_offset(p, lb_array_elem(p, lb_addr_get_ptr(p, addr)), low);
- lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
-
- lbAddr slice = lb_add_local_generated(p, slice_type, false);
- lb_fill_slice(p, slice, elem, new_len);
- return slice;
- }
-
- case Type_Basic: {
- GB_ASSERT(type == t_string);
- lbValue len = lb_string_len(p, base);
- if (high.value == nullptr) high = len;
-
- if (!no_indices) {
- lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
- }
-
- lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
- lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
-
- lbAddr str = lb_add_local_generated(p, t_string, false);
- lb_fill_string(p, str, elem, new_len);
- return str;
- }
-
-
- case Type_Struct:
- if (is_type_soa_struct(type)) {
- lbValue len = lb_soa_struct_len(p, lb_addr_get_ptr(p, addr));
- if (high.value == nullptr) high = len;
-
- if (!no_indices) {
- lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
- }
- #if 1
-
- lbAddr dst = lb_add_local_generated(p, type_of_expr(expr), true);
- if (type->Struct.soa_kind == StructSoa_Fixed) {
- i32 field_count = cast(i32)type->Struct.fields.count;
- for (i32 i = 0; i < field_count; i++) {
- lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
- lbValue field_src = lb_emit_struct_ep(p, lb_addr_get_ptr(p, addr), i);
- field_src = lb_emit_array_ep(p, field_src, low);
- lb_emit_store(p, field_dst, field_src);
- }
-
- lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
- lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
- lb_emit_store(p, len_dst, new_len);
- } else if (type->Struct.soa_kind == StructSoa_Slice) {
- if (no_indices) {
- lb_addr_store(p, dst, base);
- } else {
- i32 field_count = cast(i32)type->Struct.fields.count - 1;
- for (i32 i = 0; i < field_count; i++) {
- lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
- lbValue field_src = lb_emit_struct_ev(p, base, i);
- field_src = lb_emit_ptr_offset(p, field_src, low);
- lb_emit_store(p, field_dst, field_src);
- }
-
-
- lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
- lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
- lb_emit_store(p, len_dst, new_len);
- }
- } else if (type->Struct.soa_kind == StructSoa_Dynamic) {
- i32 field_count = cast(i32)type->Struct.fields.count - 3;
- for (i32 i = 0; i < field_count; i++) {
- lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
- lbValue field_src = lb_emit_struct_ev(p, base, i);
- field_src = lb_emit_ptr_offset(p, field_src, low);
- lb_emit_store(p, field_dst, field_src);
- }
-
-
- lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
- lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
- lb_emit_store(p, len_dst, new_len);
- }
-
- return dst;
- #endif
- }
- break;
-
- }
+ case_end;
- GB_PANIC("Unknown slicable type");
+ case_ast_node(se, SliceExpr, expr);
+ return lb_build_addr_slice_expr(p, expr);
case_end;
case_ast_node(de, DerefExpr, expr);
- if (is_type_relative_pointer(type_of_expr(de->expr))) {
+ Type *t = type_of_expr(de->expr);
+ if (is_type_relative_pointer(t)) {
lbAddr addr = lb_build_addr(p, de->expr);
addr.relative.deref = true;
- return addr;\
+ return addr;
+ } else if (is_type_soa_pointer(t)) {
+ lbValue value = lb_build_expr(p, de->expr);
+ lbValue ptr = lb_emit_struct_ev(p, value, 0);
+ lbValue idx = lb_emit_struct_ev(p, value, 1);
+ return lb_addr_soa_variable(ptr, idx, nullptr);
}
lbValue addr = lb_build_expr(p, de->expr);
return lb_addr(addr);
@@ -4129,747 +4650,7 @@ lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(cl, CompoundLit, expr);
- Type *type = type_of_expr(expr);
- Type *bt = base_type(type);
-
- lbAddr v = lb_add_local_generated(p, type, true);
-
- Type *et = nullptr;
- switch (bt->kind) {
- case Type_Array: et = bt->Array.elem; break;
- case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
- case Type_Slice: et = bt->Slice.elem; break;
- case Type_BitSet: et = bt->BitSet.elem; break;
- case Type_SimdVector: et = bt->SimdVector.elem; break;
- case Type_Matrix: et = bt->Matrix.elem; break;
- }
-
- String proc_name = {};
- if (p->entity) {
- proc_name = p->entity->token.string;
- }
- TokenPos pos = ast_token(expr).pos;
-
- switch (bt->kind) {
- default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
-
- case Type_Struct: {
- // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment.
- // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR
- bool is_raw_union = is_type_raw_union(bt);
- GB_ASSERT(is_type_struct(bt) || is_raw_union);
- TypeStruct *st = &bt->Struct;
- if (cl->elems.count > 0) {
- lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
- lbValue comp_lit_ptr = lb_addr_get_ptr(p, v);
-
- for_array(field_index, cl->elems) {
- Ast *elem = cl->elems[field_index];
-
- lbValue field_expr = {};
- Entity *field = nullptr;
- isize index = field_index;
-
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
- String name = fv->field->Ident.token.string;
- Selection sel = lookup_field(bt, name, false);
- index = sel.index[0];
- elem = fv->value;
- TypeAndValue tav = type_and_value_of_expr(elem);
- } else {
- TypeAndValue tav = type_and_value_of_expr(elem);
- Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index);
- index = sel.index[0];
- }
-
- field = st->fields[index];
- Type *ft = field->type;
- if (!is_raw_union && !is_type_typeid(ft) && lb_is_elem_const(elem, ft)) {
- continue;
- }
-
- field_expr = lb_build_expr(p, elem);
-
- lbValue gep = {};
- if (is_raw_union) {
- gep = lb_emit_conv(p, comp_lit_ptr, alloc_type_pointer(ft));
- } else {
- gep = lb_emit_struct_ep(p, comp_lit_ptr, cast(i32)index);
- }
-
- Type *fet = field_expr.type;
- GB_ASSERT(fet->kind != Type_Tuple);
-
- // HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
- if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
- GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
-
- lb_emit_store_union_variant(p, gep, field_expr, fet);
- } else {
- lbValue fv = lb_emit_conv(p, field_expr, ft);
- lb_emit_store(p, gep, fv);
- }
- }
- }
- break;
- }
-
- case Type_Map: {
- if (cl->elems.count == 0) {
- break;
- }
- {
- auto args = array_make<lbValue>(permanent_allocator(), 3);
- args[0] = lb_gen_map_header(p, v.addr, type);
- args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count);
- args[2] = lb_emit_source_code_location(p, proc_name, pos);
- lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
- }
- for_array(field_index, cl->elems) {
- Ast *elem = cl->elems[field_index];
- ast_node(fv, FieldValue, elem);
-
- lbValue key = lb_build_expr(p, fv->field);
- lbValue value = lb_build_expr(p, fv->value);
- lb_insert_dynamic_map_key_and_value(p, v, type, key, value, elem);
- }
- break;
- }
-
- case Type_Array: {
- 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<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
-
- // NOTE(bill): Separate value, gep, store into their own chunks
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
- if (lb_is_elem_const(fv->value, et)) {
- continue;
- }
- if (is_ast_range(fv->field)) {
- ast_node(ie, BinaryExpr, fv->field);
- TypeAndValue lo_tav = ie->left->tav;
- TypeAndValue hi_tav = ie->right->tav;
- GB_ASSERT(lo_tav.mode == Addressing_Constant);
- GB_ASSERT(hi_tav.mode == Addressing_Constant);
-
- TokenKind op = ie->op.kind;
- i64 lo = exact_value_to_i64(lo_tav.value);
- i64 hi = exact_value_to_i64(hi_tav.value);
- if (op != Token_RangeHalf) {
- hi += 1;
- }
-
- lbValue value = lb_build_expr(p, fv->value);
-
- for (i64 k = lo; k < hi; k++) {
- lbCompoundLitElemTempData data = {};
- data.value = value;
- data.elem_index = cast(i32)k;
- array_add(&temp_data, data);
- }
-
- } else {
- auto tav = fv->field->tav;
- GB_ASSERT(tav.mode == Addressing_Constant);
- i64 index = exact_value_to_i64(tav.value);
-
- lbValue value = lb_build_expr(p, fv->value);
- lbCompoundLitElemTempData data = {};
- data.value = lb_emit_conv(p, value, et);
- data.expr = fv->value;
- data.elem_index = cast(i32)index;
- array_add(&temp_data, data);
- }
-
- } else {
- if (lb_is_elem_const(elem, et)) {
- continue;
- }
- lbCompoundLitElemTempData data = {};
- data.expr = elem;
- data.elem_index = cast(i32)i;
- array_add(&temp_data, data);
- }
- }
-
- for_array(i, temp_data) {
- temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), temp_data[i].elem_index);
- }
-
- for_array(i, temp_data) {
- lbValue field_expr = temp_data[i].value;
- Ast *expr = temp_data[i].expr;
-
- auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr);
-
- if (field_expr.value == nullptr) {
- field_expr = lb_build_expr(p, expr);
- }
- Type *t = field_expr.type;
- GB_ASSERT(t->kind != Type_Tuple);
- lbValue ev = lb_emit_conv(p, field_expr, et);
-
- if (!p->copy_elision_hint.used) {
- temp_data[i].value = ev;
- }
-
- lb_reset_copy_elision_hint(p, prev_hint);
- }
-
- for_array(i, temp_data) {
- if (temp_data[i].value.value != nullptr) {
- lb_emit_store(p, temp_data[i].gep, temp_data[i].value);
- }
- }
- }
- break;
- }
- case Type_EnumeratedArray: {
- 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<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
-
- // NOTE(bill): Separate value, gep, store into their own chunks
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
- if (lb_is_elem_const(fv->value, et)) {
- continue;
- }
- if (is_ast_range(fv->field)) {
- ast_node(ie, BinaryExpr, fv->field);
- TypeAndValue lo_tav = ie->left->tav;
- TypeAndValue hi_tav = ie->right->tav;
- GB_ASSERT(lo_tav.mode == Addressing_Constant);
- GB_ASSERT(hi_tav.mode == Addressing_Constant);
-
- TokenKind op = ie->op.kind;
- i64 lo = exact_value_to_i64(lo_tav.value);
- i64 hi = exact_value_to_i64(hi_tav.value);
- if (op != Token_RangeHalf) {
- hi += 1;
- }
-
- lbValue value = lb_build_expr(p, fv->value);
-
- for (i64 k = lo; k < hi; k++) {
- lbCompoundLitElemTempData data = {};
- data.value = value;
- data.elem_index = cast(i32)k;
- array_add(&temp_data, data);
- }
-
- } else {
- auto tav = fv->field->tav;
- GB_ASSERT(tav.mode == Addressing_Constant);
- i64 index = exact_value_to_i64(tav.value);
-
- lbValue value = lb_build_expr(p, fv->value);
- lbCompoundLitElemTempData data = {};
- data.value = lb_emit_conv(p, value, et);
- data.expr = fv->value;
- data.elem_index = cast(i32)index;
- array_add(&temp_data, data);
- }
-
- } else {
- if (lb_is_elem_const(elem, et)) {
- continue;
- }
- lbCompoundLitElemTempData data = {};
- data.expr = elem;
- data.elem_index = cast(i32)i;
- array_add(&temp_data, data);
- }
- }
-
-
- i32 index_offset = cast(i32)exact_value_to_i64(*bt->EnumeratedArray.min_value);
-
- for_array(i, temp_data) {
- i32 index = temp_data[i].elem_index - index_offset;
- temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), index);
- }
-
- for_array(i, temp_data) {
- lbValue field_expr = temp_data[i].value;
- Ast *expr = temp_data[i].expr;
-
- auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr);
-
- if (field_expr.value == nullptr) {
- field_expr = lb_build_expr(p, expr);
- }
- Type *t = field_expr.type;
- GB_ASSERT(t->kind != Type_Tuple);
- lbValue ev = lb_emit_conv(p, field_expr, et);
-
- if (!p->copy_elision_hint.used) {
- temp_data[i].value = ev;
- }
-
- lb_reset_copy_elision_hint(p, prev_hint);
- }
-
- for_array(i, temp_data) {
- if (temp_data[i].value.value != nullptr) {
- lb_emit_store(p, temp_data[i].gep, temp_data[i].value);
- }
- }
- }
- break;
- }
- case Type_Slice: {
- if (cl->elems.count > 0) {
- lbValue slice = lb_const_value(p->module, type, exact_value_compound(expr));
-
- lbValue data = lb_slice_elem(p, slice);
-
- auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
-
- if (lb_is_elem_const(fv->value, et)) {
- continue;
- }
-
- if (is_ast_range(fv->field)) {
- ast_node(ie, BinaryExpr, fv->field);
- TypeAndValue lo_tav = ie->left->tav;
- TypeAndValue hi_tav = ie->right->tav;
- GB_ASSERT(lo_tav.mode == Addressing_Constant);
- GB_ASSERT(hi_tav.mode == Addressing_Constant);
-
- TokenKind op = ie->op.kind;
- i64 lo = exact_value_to_i64(lo_tav.value);
- i64 hi = exact_value_to_i64(hi_tav.value);
- if (op != Token_RangeHalf) {
- hi += 1;
- }
-
- lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et);
-
- for (i64 k = lo; k < hi; k++) {
- lbCompoundLitElemTempData data = {};
- data.value = value;
- data.elem_index = cast(i32)k;
- array_add(&temp_data, data);
- }
-
- } else {
- GB_ASSERT(fv->field->tav.mode == Addressing_Constant);
- i64 index = exact_value_to_i64(fv->field->tav.value);
-
- lbValue field_expr = lb_build_expr(p, fv->value);
- GB_ASSERT(!is_type_tuple(field_expr.type));
-
- lbValue ev = lb_emit_conv(p, field_expr, et);
-
- lbCompoundLitElemTempData data = {};
- data.value = ev;
- data.elem_index = cast(i32)index;
- array_add(&temp_data, data);
- }
- } else {
- if (lb_is_elem_const(elem, et)) {
- continue;
- }
- lbValue field_expr = lb_build_expr(p, elem);
- GB_ASSERT(!is_type_tuple(field_expr.type));
-
- lbValue ev = lb_emit_conv(p, field_expr, et);
-
- lbCompoundLitElemTempData data = {};
- data.value = ev;
- data.elem_index = cast(i32)i;
- array_add(&temp_data, data);
- }
- }
-
- for_array(i, temp_data) {
- temp_data[i].gep = lb_emit_ptr_offset(p, data, lb_const_int(p->module, t_int, temp_data[i].elem_index));
- }
-
- for_array(i, temp_data) {
- lb_emit_store(p, temp_data[i].gep, temp_data[i].value);
- }
-
- {
- lbValue count = {};
- count.type = t_int;
-
- if (lb_is_const(slice)) {
- unsigned indices[1] = {1};
- count.value = LLVMConstExtractValue(slice.value, indices, gb_count_of(indices));
- } else {
- count.value = LLVMBuildExtractValue(p->builder, slice.value, 1, "");
- }
- lb_fill_slice(p, v, data, count);
- }
- }
- break;
- }
-
- case Type_DynamicArray: {
- if (cl->elems.count == 0) {
- break;
- }
- Type *et = bt->DynamicArray.elem;
- 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<lbValue>(permanent_allocator(), 5);
- args[0] = lb_emit_conv(p, lb_addr_get_ptr(p, v), t_rawptr);
- args[1] = size;
- args[2] = align;
- args[3] = lb_const_int(p->module, t_int, 2*item_count); // TODO(bill): Is this too much waste?
- args[4] = lb_emit_source_code_location(p, proc_name, pos);
- lb_emit_runtime_call(p, "__dynamic_array_reserve", args);
- }
-
- lbValue items = lb_generate_local_array(p, et, item_count);
- // lbValue items = lb_generate_global_array(p->module, et, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
- if (is_ast_range(fv->field)) {
- ast_node(ie, BinaryExpr, fv->field);
- TypeAndValue lo_tav = ie->left->tav;
- TypeAndValue hi_tav = ie->right->tav;
- GB_ASSERT(lo_tav.mode == Addressing_Constant);
- GB_ASSERT(hi_tav.mode == Addressing_Constant);
-
- TokenKind op = ie->op.kind;
- i64 lo = exact_value_to_i64(lo_tav.value);
- i64 hi = exact_value_to_i64(hi_tav.value);
- if (op != Token_RangeHalf) {
- hi += 1;
- }
-
- lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et);
-
- for (i64 k = lo; k < hi; k++) {
- lbValue ep = lb_emit_array_epi(p, items, cast(i32)k);
- lb_emit_store(p, ep, value);
- }
- } else {
- GB_ASSERT(fv->field->tav.mode == Addressing_Constant);
-
- i64 field_index = exact_value_to_i64(fv->field->tav.value);
-
- lbValue ev = lb_build_expr(p, fv->value);
- lbValue value = lb_emit_conv(p, ev, et);
- lbValue ep = lb_emit_array_epi(p, items, cast(i32)field_index);
- lb_emit_store(p, ep, value);
- }
- } else {
- lbValue value = lb_emit_conv(p, lb_build_expr(p, elem), et);
- lbValue ep = lb_emit_array_epi(p, items, cast(i32)i);
- lb_emit_store(p, ep, value);
- }
- }
-
- {
- auto args = array_make<lbValue>(permanent_allocator(), 6);
- args[0] = lb_emit_conv(p, v.addr, t_rawptr);
- args[1] = size;
- args[2] = align;
- args[3] = lb_emit_conv(p, items, t_rawptr);
- args[4] = lb_const_int(p->module, t_int, item_count);
- args[5] = lb_emit_source_code_location(p, proc_name, pos);
- lb_emit_runtime_call(p, "__dynamic_array_append", args);
- }
- break;
- }
-
- case Type_Basic: {
- GB_ASSERT(is_type_any(bt));
- if (cl->elems.count > 0) {
- lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
- String field_names[2] = {
- str_lit("data"),
- str_lit("id"),
- };
- Type *field_types[2] = {
- t_rawptr,
- t_typeid,
- };
-
- for_array(field_index, cl->elems) {
- Ast *elem = cl->elems[field_index];
-
- lbValue field_expr = {};
- isize index = field_index;
-
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
- Selection sel = lookup_field(bt, fv->field->Ident.token.string, false);
- index = sel.index[0];
- elem = fv->value;
- } else {
- TypeAndValue tav = type_and_value_of_expr(elem);
- Selection sel = lookup_field(bt, field_names[field_index], false);
- index = sel.index[0];
- }
-
- field_expr = lb_build_expr(p, elem);
-
- GB_ASSERT(field_expr.type->kind != Type_Tuple);
-
- Type *ft = field_types[index];
- lbValue fv = lb_emit_conv(p, field_expr, ft);
- lbValue gep = lb_emit_struct_ep(p, lb_addr_get_ptr(p, v), cast(i32)index);
- lb_emit_store(p, gep, fv);
- }
- }
-
- break;
- }
-
- case Type_BitSet: {
- i64 sz = type_size_of(type);
- if (cl->elems.count > 0 && sz > 0) {
- lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
-
- lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower));
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- GB_ASSERT(elem->kind != Ast_FieldValue);
-
- if (lb_is_elem_const(elem, et)) {
- continue;
- }
-
- lbValue expr = lb_build_expr(p, elem);
- GB_ASSERT(expr.type->kind != Type_Tuple);
-
- Type *it = bit_set_to_int(bt);
- lbValue one = lb_const_value(p->module, it, exact_value_i64(1));
- lbValue e = lb_emit_conv(p, expr, it);
- e = lb_emit_arith(p, Token_Sub, e, lower, it);
- e = lb_emit_arith(p, Token_Shl, one, e, it);
-
- lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it);
- lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it);
- new_value = lb_emit_transmute(p, new_value, type);
- lb_addr_store(p, v, new_value);
- }
- }
- break;
- }
-
- case Type_Matrix: {
- 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<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
-
- // NOTE(bill): Separate value, gep, store into their own chunks
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
-
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
- if (lb_is_elem_const(fv->value, et)) {
- continue;
- }
- if (is_ast_range(fv->field)) {
- ast_node(ie, BinaryExpr, fv->field);
- TypeAndValue lo_tav = ie->left->tav;
- TypeAndValue hi_tav = ie->right->tav;
- GB_ASSERT(lo_tav.mode == Addressing_Constant);
- GB_ASSERT(hi_tav.mode == Addressing_Constant);
-
- TokenKind op = ie->op.kind;
- i64 lo = exact_value_to_i64(lo_tav.value);
- i64 hi = exact_value_to_i64(hi_tav.value);
- if (op != Token_RangeHalf) {
- hi += 1;
- }
-
- lbValue value = lb_build_expr(p, fv->value);
-
- for (i64 k = lo; k < hi; k++) {
- lbCompoundLitElemTempData data = {};
- data.value = value;
-
- data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, k);
- array_add(&temp_data, data);
- }
-
- } else {
- auto tav = fv->field->tav;
- GB_ASSERT(tav.mode == Addressing_Constant);
- i64 index = exact_value_to_i64(tav.value);
-
- lbValue value = lb_build_expr(p, fv->value);
- lbCompoundLitElemTempData data = {};
- data.value = lb_emit_conv(p, value, et);
- data.expr = fv->value;
-
- data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, index);
- array_add(&temp_data, data);
- }
-
- } else {
- if (lb_is_elem_const(elem, et)) {
- continue;
- }
- lbCompoundLitElemTempData data = {};
- data.expr = elem;
- data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, i);
- array_add(&temp_data, data);
- }
- }
-
- for_array(i, temp_data) {
- temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), temp_data[i].elem_index);
- }
-
- for_array(i, temp_data) {
- lbValue field_expr = temp_data[i].value;
- Ast *expr = temp_data[i].expr;
-
- auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr);
-
- if (field_expr.value == nullptr) {
- field_expr = lb_build_expr(p, expr);
- }
- Type *t = field_expr.type;
- GB_ASSERT(t->kind != Type_Tuple);
- lbValue ev = lb_emit_conv(p, field_expr, et);
-
- if (!p->copy_elision_hint.used) {
- temp_data[i].value = ev;
- }
-
- lb_reset_copy_elision_hint(p, prev_hint);
- }
-
- for_array(i, temp_data) {
- if (temp_data[i].value.value != nullptr) {
- lb_emit_store(p, temp_data[i].gep, temp_data[i].value);
- }
- }
- }
- break;
- }
-
- case Type_SimdVector: {
- if (cl->elems.count > 0) {
- lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr));
- defer (lb_addr_store(p, v, vector_value));
-
- auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
-
- // NOTE(bill): Separate value, store into their own chunks
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, elem);
- if (lb_is_elem_const(fv->value, et)) {
- continue;
- }
- if (is_ast_range(fv->field)) {
- ast_node(ie, BinaryExpr, fv->field);
- TypeAndValue lo_tav = ie->left->tav;
- TypeAndValue hi_tav = ie->right->tav;
- GB_ASSERT(lo_tav.mode == Addressing_Constant);
- GB_ASSERT(hi_tav.mode == Addressing_Constant);
-
- TokenKind op = ie->op.kind;
- i64 lo = exact_value_to_i64(lo_tav.value);
- i64 hi = exact_value_to_i64(hi_tav.value);
- if (op != Token_RangeHalf) {
- hi += 1;
- }
-
- lbValue value = lb_build_expr(p, fv->value);
-
- for (i64 k = lo; k < hi; k++) {
- lbCompoundLitElemTempData data = {};
- data.value = value;
- data.elem_index = cast(i32)k;
- array_add(&temp_data, data);
- }
-
- } else {
- auto tav = fv->field->tav;
- GB_ASSERT(tav.mode == Addressing_Constant);
- i64 index = exact_value_to_i64(tav.value);
-
- lbValue value = lb_build_expr(p, fv->value);
- lbCompoundLitElemTempData data = {};
- data.value = lb_emit_conv(p, value, et);
- data.expr = fv->value;
- data.elem_index = cast(i32)index;
- array_add(&temp_data, data);
- }
-
- } else {
- if (lb_is_elem_const(elem, et)) {
- continue;
- }
- lbCompoundLitElemTempData data = {};
- data.expr = elem;
- data.elem_index = cast(i32)i;
- array_add(&temp_data, data);
- }
- }
-
-
- for_array(i, temp_data) {
- lbValue field_expr = temp_data[i].value;
- Ast *expr = temp_data[i].expr;
-
- auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr);
-
- if (field_expr.value == nullptr) {
- field_expr = lb_build_expr(p, expr);
- }
- Type *t = field_expr.type;
- GB_ASSERT(t->kind != Type_Tuple);
- lbValue ev = lb_emit_conv(p, field_expr, et);
-
- if (!p->copy_elision_hint.used) {
- temp_data[i].value = ev;
- }
-
- lb_reset_copy_elision_hint(p, prev_hint);
- }
-
-
- // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector`
- // might be a better option
-
- for_array(i, temp_data) {
- if (temp_data[i].value.value != nullptr) {
- LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value;
- vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, "");
- }
- }
- }
- break;
- }
- }
-
- return v;
+ return lb_build_addr_compound_lit(p, expr);
case_end;
case_ast_node(tc, TypeCast, expr);
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index b61439238..6f98458fa 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -56,6 +56,7 @@ void lb_init_module(lbModule *m, Checker *c) {
gbAllocator a = heap_allocator();
map_init(&m->types, a);
+ map_init(&m->func_raw_types, a);
map_init(&m->struct_field_remapping, a);
map_init(&m->values, a);
map_init(&m->soa_values, a);
@@ -174,7 +175,8 @@ struct lbLoopData {
struct lbCompoundLitElemTempData {
Ast * expr;
lbValue value;
- i32 elem_index;
+ i64 elem_index;
+ i64 elem_length;
lbValue gep;
};
@@ -211,6 +213,45 @@ void lb_loop_end(lbProcedure *p, lbLoopData const &data) {
}
+// This emits a GEP at 0, index
+lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index) {
+ GB_ASSERT(is_type_pointer(value.type));
+ Type *type = type_deref(value.type);
+
+ LLVMValueRef indices[2] = {
+ LLVMConstInt(lb_type(p->module, t_int), 0, false),
+ LLVMConstInt(lb_type(p->module, t_int), cast(unsigned long long)index, false),
+ };
+ LLVMTypeRef llvm_type = lb_type(p->module, type);
+ lbValue res = {};
+ Type *ptr = base_array_type(type);
+ res.type = alloc_type_pointer(ptr);
+ if (LLVMIsConstant(value.value)) {
+ res.value = LLVMConstGEP2(llvm_type, value.value, indices, gb_count_of(indices));
+ } else {
+ res.value = LLVMBuildGEP2(p->builder, llvm_type, value.value, indices, gb_count_of(indices), "");
+ }
+ return res;
+}
+// This emits a GEP at 0, index
+lbValue lb_emit_epi(lbModule *m, lbValue const &value, isize index) {
+ GB_ASSERT(is_type_pointer(value.type));
+ GB_ASSERT(LLVMIsConstant(value.value));
+ Type *type = type_deref(value.type);
+
+ LLVMValueRef indices[2] = {
+ LLVMConstInt(lb_type(m, t_int), 0, false),
+ LLVMConstInt(lb_type(m, t_int), cast(unsigned long long)index, false),
+ };
+ lbValue res = {};
+ Type *ptr = base_array_type(type);
+ res.type = alloc_type_pointer(ptr);
+ res.value = LLVMConstGEP2(lb_type(m, type), value.value, indices, gb_count_of(indices));
+ return res;
+}
+
+
+
LLVMValueRef llvm_zero(lbModule *m) {
return LLVMConstInt(lb_type(m, t_int), 0, false);
}
@@ -341,9 +382,6 @@ Type *lb_addr_type(lbAddr const &addr) {
}
return type_deref(addr.addr.type);
}
-LLVMTypeRef lb_addr_lb_type(lbAddr const &addr) {
- return LLVMGetElementType(LLVMTypeOf(addr.addr.value));
-}
lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
if (addr.addr.value == nullptr) {
@@ -530,6 +568,13 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu
}
}
+unsigned lb_try_get_alignment(LLVMValueRef addr_ptr, unsigned default_alignment) {
+ if (LLVMIsAGlobalValue(addr_ptr) || LLVMIsAAllocaInst(addr_ptr) || LLVMIsALoadInst(addr_ptr)) {
+ return LLVMGetAlignment(addr_ptr);
+ }
+ return default_alignment;
+}
+
bool lb_try_update_alignment(LLVMValueRef addr_ptr, unsigned alignment) {
if (LLVMIsAGlobalValue(addr_ptr) || LLVMIsAAllocaInst(addr_ptr) || LLVMIsALoadInst(addr_ptr)) {
if (LLVMGetAlignment(addr_ptr) < alignment) {
@@ -546,6 +591,9 @@ bool lb_try_update_alignment(lbValue ptr, unsigned alignment) {
return lb_try_update_alignment(ptr.value, alignment);
}
+bool lb_can_try_to_inline_array_arith(Type *t) {
+ return type_size_of(t) <= build_context.max_simd_align;
+}
bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vector_type_) {
Type *array_type = base_type(type_deref(ptr.type));
@@ -554,7 +602,7 @@ bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vector_type_) {
Type *elem_type = base_array_type(array_type);
// TODO(bill): Determine what is the correct limit for doing vector arithmetic
- if (type_size_of(array_type) <= build_context.max_align &&
+ if (lb_can_try_to_inline_array_arith(array_type) &&
is_type_valid_vector_elem(elem_type)) {
// Try to treat it like a vector if possible
bool possible = false;
@@ -854,8 +902,12 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
Type *a = type_deref(ptr.type);
if (LLVMIsNull(value.value)) {
- LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value));
- if (lb_sizeof(src_t) <= lb_max_zero_init_size()) {
+ LLVMTypeRef src_t = llvm_addr_type(p->module, ptr);
+ if (is_type_proc(a)) {
+ LLVMTypeRef rawptr_type = lb_type(p->module, t_rawptr);
+ LLVMTypeRef rawptr_ptr_type = LLVMPointerType(rawptr_type, 0);
+ LLVMBuildStore(p->builder, LLVMConstNull(rawptr_type), LLVMBuildBitCast(p->builder, ptr.value, rawptr_ptr_type, ""));
+ } else if (lb_sizeof(src_t) <= lb_max_zero_init_size()) {
LLVMBuildStore(p->builder, LLVMConstNull(src_t), ptr.value);
} else {
lb_mem_zero_ptr(p, ptr.value, a, 1);
@@ -873,25 +925,46 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
enum {MAX_STORE_SIZE = 64};
- if (LLVMIsALoadInst(value.value) && lb_sizeof(LLVMTypeOf(value.value)) > MAX_STORE_SIZE) {
- LLVMValueRef dst_ptr = ptr.value;
- LLVMValueRef src_ptr = LLVMGetOperand(value.value, 0);
- src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), "");
-
- LLVMBuildMemMove(p->builder,
- dst_ptr, 1,
- src_ptr, 1,
- LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
- return;
+ if (lb_sizeof(LLVMTypeOf(value.value)) > MAX_STORE_SIZE) {
+ if (LLVMIsALoadInst(value.value)) {
+ LLVMValueRef dst_ptr = ptr.value;
+ LLVMValueRef src_ptr_original = LLVMGetOperand(value.value, 0);
+ LLVMValueRef src_ptr = LLVMBuildPointerCast(p->builder, src_ptr_original, LLVMTypeOf(dst_ptr), "");
+
+ LLVMBuildMemMove(p->builder,
+ dst_ptr, lb_try_get_alignment(dst_ptr, 1),
+ src_ptr, lb_try_get_alignment(src_ptr_original, 1),
+ LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
+ return;
+ } else if (LLVMIsConstant(value.value)) {
+ lbAddr addr = lb_add_global_generated(p->module, value.type, value, nullptr);
+ LLVMValueRef global_data = addr.addr.value;
+ // make it truly private data
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
+ LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
+ LLVMSetGlobalConstant(global_data, true);
+
+ LLVMValueRef dst_ptr = ptr.value;
+ LLVMValueRef src_ptr = global_data;
+ src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), "");
+
+ LLVMBuildMemMove(p->builder,
+ dst_ptr, lb_try_get_alignment(dst_ptr, 1),
+ src_ptr, lb_try_get_alignment(global_data, 1),
+ LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
+ return;
+ }
}
if (lb_is_type_proc_recursive(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);
+ LLVMTypeRef rawptr_type = lb_type(p->module, t_rawptr);
+ LLVMTypeRef rawptr_ptr_type = LLVMPointerType(rawptr_type, 0);
+ LLVMBuildStore(p->builder,
+ LLVMBuildPointerCast(p->builder, value.value, rawptr_type, ""),
+ LLVMBuildPointerCast(p->builder, ptr.value, rawptr_ptr_type, ""));
} else {
Type *ca = core_type(a);
if (ca->kind == Type_Basic || ca->kind == Type_Proc) {
@@ -904,8 +977,8 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
}
}
-LLVMTypeRef llvm_addr_type(lbValue addr_val) {
- return LLVMGetElementType(LLVMTypeOf(addr_val.value));
+LLVMTypeRef llvm_addr_type(lbModule *module, lbValue addr_val) {
+ return lb_type(module, type_deref(addr_val.type));
}
lbValue lb_emit_load(lbProcedure *p, lbValue value) {
@@ -914,12 +987,18 @@ lbValue lb_emit_load(lbProcedure *p, lbValue value) {
Type *vt = base_type(value.type);
GB_ASSERT(vt->kind == Type_MultiPointer);
Type *t = vt->MultiPointer.elem;
- LLVMValueRef v = LLVMBuildLoad2(p->builder, llvm_addr_type(value), value.value, "");
+ LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, "");
return lbValue{v, t};
+ } else if (is_type_soa_pointer(value.type)) {
+ lbValue ptr = lb_emit_struct_ev(p, value, 0);
+ lbValue idx = lb_emit_struct_ev(p, value, 1);
+ lbAddr addr = lb_addr_soa_variable(ptr, idx, nullptr);
+ return lb_addr_load(p, addr);
}
+
GB_ASSERT(is_type_pointer(value.type));
Type *t = type_deref(value.type);
- LLVMValueRef v = LLVMBuildLoad2(p->builder, llvm_addr_type(value), value.value, "");
+ LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, "");
return lbValue{v, t};
}
@@ -1184,12 +1263,12 @@ lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
Type *tag_type = union_tag_type(ut);
- LLVMTypeRef uvt = LLVMGetElementType(LLVMTypeOf(u.value));
+ LLVMTypeRef uvt = llvm_addr_type(p->module, u);
unsigned element_count = LLVMCountStructElementTypes(uvt);
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
lbValue tag_ptr = {};
- tag_ptr.value = LLVMBuildStructGEP(p->builder, u.value, 1, "");
+ tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, u.value, 1, "");
tag_ptr.type = alloc_type_pointer(tag_type);
return tag_ptr;
}
@@ -1413,6 +1492,116 @@ String lb_get_entity_name(lbModule *m, Entity *e, String default_name) {
}
+LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
+ Type *original_type = type;
+ type = base_type(original_type);
+ GB_ASSERT(type->kind == Type_Proc);
+
+ LLVMTypeRef *found = map_get(&m->func_raw_types, type);
+ if (found) {
+ return *found;
+ }
+
+ 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 (e->flags & EntityFlag_CVarArg) {
+ continue;
+ }
+ param_count += 1;
+ }
+ }
+ m->internal_type_level += 1;
+ defer (m->internal_type_level -= 1);
+
+ LLVMTypeRef ret = nullptr;
+ LLVMTypeRef *params = gb_alloc_array(permanent_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);
+ }
+ }
+ }
+
+ unsigned 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;
+ }
+ if (e->flags & EntityFlag_CVarArg) {
+ continue;
+ }
+ Type *e_type = reduce_tuple_to_single_type(e->type);
+
+ LLVMTypeRef param_type = nullptr;
+ if (e->flags & EntityFlag_ByPtr) {
+ param_type = lb_type(m, alloc_type_pointer(e_type));
+ } else if (is_type_boolean(e_type) &&
+ type_size_of(e_type) <= 1) {
+ param_type = LLVMInt1TypeInContext(m->ctx);
+ } else {
+ 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_rawptr);
+ }
+ GB_ASSERT(param_index == param_count);
+
+ lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention);
+ {
+ for_array(j, ft->args) {
+ auto arg = ft->args[j];
+ GB_ASSERT_MSG(LLVMGetTypeContext(arg.type) == ft->ctx,
+ "\n\t%s %td/%td"
+ "\n\tArgTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p",
+ LLVMPrintTypeToString(arg.type),
+ j, ft->args.count,
+ LLVMGetTypeContext(arg.type), ft->ctx, LLVMGetGlobalContext());
+ }
+ GB_ASSERT_MSG(LLVMGetTypeContext(ft->ret.type) == ft->ctx,
+ "\n\t%s"
+ "\n\tRetTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p",
+ LLVMPrintTypeToString(ft->ret.type),
+ LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext());
+ }
+
+ map_set(&m->function_type_map, type, ft);
+ LLVMTypeRef new_abi_fn_type = lb_function_type_to_llvm_raw(ft, type->Proc.c_vararg);
+
+ GB_ASSERT_MSG(LLVMGetTypeContext(new_abi_fn_type) == m->ctx,
+ "\n\tFuncTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p",
+ LLVMGetTypeContext(new_abi_fn_type), m->ctx, LLVMGetGlobalContext());
+
+ map_set(&m->func_raw_types, type, new_abi_fn_type);
+
+ return new_abi_fn_type;
+
+}
LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMContextRef ctx = m->ctx;
i64 size = type_size_of(type); // Check size
@@ -1916,103 +2105,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
if (m->internal_type_level > 1) { // 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;
- }
-
- 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 (e->flags & EntityFlag_CVarArg) {
- continue;
- }
- param_count += 1;
- }
- }
- m->internal_type_level += 1;
- defer (m->internal_type_level -= 1);
-
- LLVMTypeRef ret = nullptr;
- LLVMTypeRef *params = gb_alloc_array(permanent_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);
- }
- }
- }
-
- unsigned 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;
- }
- if (e->flags & EntityFlag_CVarArg) {
- continue;
- }
- Type *e_type = reduce_tuple_to_single_type(e->type);
-
- LLVMTypeRef param_type = nullptr;
- if (e->flags & EntityFlag_ByPtr) {
- param_type = lb_type(m, alloc_type_pointer(e_type));
- } else if (is_type_boolean(e_type) &&
- type_size_of(e_type) <= 1) {
- param_type = LLVMInt1TypeInContext(m->ctx);
- } else {
- 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_rawptr);
- }
- GB_ASSERT(param_index == param_count);
-
- lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention);
- {
- for_array(j, ft->args) {
- auto arg = ft->args[j];
- GB_ASSERT_MSG(LLVMGetTypeContext(arg.type) == ft->ctx,
- "\n\t%s %td/%td"
- "\n\tArgTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p",
- LLVMPrintTypeToString(arg.type),
- j, ft->args.count,
- LLVMGetTypeContext(arg.type), ft->ctx, LLVMGetGlobalContext());
- }
- GB_ASSERT_MSG(LLVMGetTypeContext(ft->ret.type) == ft->ctx,
- "\n\t%s"
- "\n\tRetTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p",
- LLVMPrintTypeToString(ft->ret.type),
- LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext());
- }
-
- map_set(&m->function_type_map, 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);
-
- GB_ASSERT_MSG(LLVMGetTypeContext(new_abi_fn_type) == m->ctx,
- "\n\tFuncTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p",
- LLVMGetTypeContext(new_abi_fn_type), m->ctx, LLVMGetGlobalContext());
-
- return new_abi_fn_ptr_type;
+ LLVMTypeRef proc_raw_type = lb_type_internal_for_procedures_raw(m, type);
+ return LLVMPointerType(proc_raw_type, 0);
}
break;
@@ -2055,6 +2149,15 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
m->internal_type_level += 1;
return t;
}
+
+ case Type_SoaPointer:
+ {
+ unsigned field_count = 2;
+ LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count);
+ fields[0] = LLVMPointerType(lb_type(m, type->Pointer.elem), 0);
+ fields[1] = LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.word_size);
+ return LLVMStructTypeInContext(ctx, fields, field_count, false);
+ }
}
@@ -2259,6 +2362,17 @@ void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *fals
}
+gb_inline LLVMTypeRef OdinLLVMGetInternalElementType(LLVMTypeRef type) {
+ return LLVMGetElementType(type);
+}
+LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type) {
+ GB_ASSERT(lb_is_type_kind(type, LLVMArrayTypeKind));
+ return OdinLLVMGetInternalElementType(type);
+}
+LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type) {
+ GB_ASSERT(lb_is_type_kind(type, LLVMVectorTypeKind));
+ return OdinLLVMGetInternalElementType(type);
+}
LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRef dst_type) {
@@ -2329,7 +2443,7 @@ general_end:;
if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) {
LLVMValueRef val_ptr = LLVMGetOperand(val, 0);
val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), "");
- LLVMValueRef loaded_val = LLVMBuildLoad(p->builder, val_ptr, "");
+ LLVMValueRef loaded_val = LLVMBuildLoad2(p->builder, dst_type, val_ptr, "");
// LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align));
@@ -2345,7 +2459,7 @@ general_end:;
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), "");
LLVMBuildStore(p->builder, val, nptr);
- return LLVMBuildLoad(p->builder, ptr, "");
+ return LLVMBuildLoad2(p->builder, dst_type, ptr, "");
}
}
@@ -2371,14 +2485,15 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
len -= 1;
- LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
+ LLVMTypeRef type = LLVMTypeOf(data);
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetAlignment(global_data, 1);
LLVMSetGlobalConstant(global_data, true);
- LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
+ LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
return ptr;
}
@@ -2416,7 +2531,8 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
len -= 1;
}
- LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
+ LLVMTypeRef type = LLVMTypeOf(data);
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
@@ -2425,7 +2541,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
- ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
+ ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
} else {
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
}
@@ -2437,7 +2553,55 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
res.type = t_u8_slice;
return res;
}
+lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *m, String const &str, Type *slice_type) {
+ GB_ASSERT(is_type_slice(slice_type));
+ LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
+ LLVMValueRef data = LLVMConstStringInContext(m->ctx,
+ cast(char const *)str.text,
+ cast(unsigned)str.len,
+ false);
+
+
+ char *name = nullptr;
+ {
+ isize max_len = 7+8+1;
+ name = gb_alloc_array(permanent_allocator(), char, max_len);
+ u32 id = m->gen->global_array_index.fetch_add(1);
+ isize len = gb_snprintf(name, max_len, "csbs$%x", id);
+ len -= 1;
+ }
+ LLVMTypeRef type = LLVMTypeOf(data);
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
+ LLVMSetInitializer(global_data, data);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
+ LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
+ LLVMSetAlignment(global_data, 1);
+ LLVMSetGlobalConstant(global_data, true);
+
+ i64 data_len = str.len;
+ LLVMValueRef ptr = nullptr;
+ if (data_len != 0) {
+ ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
+ } else {
+ ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
+ }
+ if (!is_type_u8_slice(slice_type)) {
+ Type *bt = base_type(slice_type);
+ Type *elem = bt->Slice.elem;
+ i64 sz = type_size_of(elem);
+ GB_ASSERT(sz > 0);
+ ptr = LLVMConstPointerCast(ptr, lb_type(m, alloc_type_pointer(elem)));
+ data_len /= sz;
+ }
+
+ LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), data_len, true);
+ LLVMValueRef values[2] = {ptr, len};
+ lbValue res = {};
+ res.value = llvm_const_named_struct(m, slice_type, values, 2);
+ res.type = slice_type;
+ return res;
+}
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 6b80b21d6..e2f51b868 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -62,7 +62,9 @@ void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimiz
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddMergedLoadStoreMotionPass(fpm);
LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
- LLVMAddEarlyCSEPass(fpm);
+ if (!build_context.ODIN_DEBUG) {
+ LLVMAddEarlyCSEPass(fpm);
+ }
}
}
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 0b0c0794b..f85d8397c 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -1,3 +1,13 @@
+
+LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count)
+{
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, type_count);
+ LLVMTypeRef call_type = LLVMIntrinsicGetType(p->module->ctx, id, types, type_count);
+ return LLVMBuildCall2(p->builder, call_type, ip, args, arg_count, "");
+}
+
void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
dst = lb_emit_conv(p, dst, t_rawptr);
src = lb_emit_conv(p, src, t_rawptr);
@@ -10,23 +20,23 @@ void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue l
name = "llvm.memmove.inline";
}
}
-
LLVMTypeRef types[3] = {
lb_type(p->module, t_rawptr),
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
- LLVMValueRef args[4] = {};
- args[0] = dst.value;
- args[1] = src.value;
- args[2] = len.value;
- args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile);
- LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ LLVMValueRef args[4] = {
+ dst.value,
+ src.value,
+ len.value,
+ LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile)
+ };
+
+ lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
}
+
+
+
void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
dst = lb_emit_conv(p, dst, t_rawptr);
src = lb_emit_conv(p, src, t_rawptr);
@@ -45,16 +55,14 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
- LLVMValueRef args[4] = {};
- args[0] = dst.value;
- args[1] = src.value;
- args[2] = len.value;
- args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile);
- LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+
+ LLVMValueRef args[4] = {
+ dst.value,
+ src.value,
+ len.value,
+ LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile) };
+
+ lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
}
@@ -121,8 +129,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
}
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);
+ LLVMTypeRef func_type = lb_get_procedure_raw_type(m, p->type);
p->value = LLVMAddFunction(m->mod, c_link_name, func_type);
@@ -141,34 +148,30 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
lb_add_attribute_to_proc(m, p->value, "noredzone");
}
- if (build_context.optimization_level == 0 && build_context.ODIN_DEBUG) {
+
+ switch (p->inlining) {
+ case ProcInlining_inline:
+ lb_add_attribute_to_proc(m, p->value, "alwaysinline");
+ break;
+ case ProcInlining_no_inline:
lb_add_attribute_to_proc(m, p->value, "noinline");
- lb_add_attribute_to_proc(m, p->value, "optnone");
- } else {
- switch (p->inlining) {
- case ProcInlining_inline:
- lb_add_attribute_to_proc(m, p->value, "alwaysinline");
- break;
- case ProcInlining_no_inline:
- lb_add_attribute_to_proc(m, p->value, "noinline");
- break;
- }
+ break;
+ }
- switch (entity->Procedure.optimization_mode) {
- case ProcedureOptimizationMode_None:
- lb_add_attribute_to_proc(m, p->value, "optnone");
- break;
- case ProcedureOptimizationMode_Minimal:
- lb_add_attribute_to_proc(m, p->value, "optnone");
- break;
- case ProcedureOptimizationMode_Size:
- lb_add_attribute_to_proc(m, p->value, "optsize");
- break;
- case ProcedureOptimizationMode_Speed:
- // TODO(bill): handle this correctly
- lb_add_attribute_to_proc(m, p->value, "optsize");
- break;
- }
+ switch (entity->Procedure.optimization_mode) {
+ case ProcedureOptimizationMode_None:
+ lb_add_attribute_to_proc(m, p->value, "optnone");
+ break;
+ case ProcedureOptimizationMode_Minimal:
+ lb_add_attribute_to_proc(m, p->value, "optnone");
+ break;
+ case ProcedureOptimizationMode_Size:
+ lb_add_attribute_to_proc(m, p->value, "optsize");
+ break;
+ case ProcedureOptimizationMode_Speed:
+ // TODO(bill): handle this correctly
+ lb_add_attribute_to_proc(m, p->value, "optsize");
+ break;
}
if (!entity->Procedure.target_feature_disabled &&
@@ -346,8 +349,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
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);
+ LLVMTypeRef func_type = lb_get_procedure_raw_type(m, p->type);
p->value = LLVMAddFunction(m->mod, c_link_name, func_type);
@@ -745,12 +747,12 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
GB_ASSERT(curr_block != p->decl_block->block);
{
- LLVMTypeRef ftp = lb_type(p->module, value.type);
+ LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, value.type);
+ LLVMTypeRef ftp = LLVMPointerType(fnp, 0);
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));
{
@@ -1264,13 +1266,8 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
}
args[args_count++] = arg0.value;
-
LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
- res.value = LLVMBuildCall(p->builder, ip, args, cast(unsigned)args_count, "");
+ res.value = lb_call_intrinsic(p, name, args, cast(unsigned)args_count, types, gb_count_of(types));
return res;
}
case BuiltinProc_simd_reduce_min:
@@ -1303,15 +1300,11 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
case BuiltinProc_simd_reduce_or: name = "llvm.vector.reduce.or"; break;
case BuiltinProc_simd_reduce_xor: name = "llvm.vector.reduce.xor"; break;
}
- LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[1] = {};
- args[0] = arg0.value;
+ LLVMTypeRef types[1] = { lb_type(p->module, arg0.type) };
+ LLVMValueRef args[1] = { arg0.value };
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
return res;
}
@@ -1360,15 +1353,10 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
case BuiltinProc_simd_nearest: name = "llvm.nearbyint"; break;
}
- LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+ LLVMTypeRef types[1] = { lb_type(p->module, arg0.type) };
+ LLVMValueRef args[1] = { arg0.value };
- LLVMValueRef args[1] = {};
- args[0] = arg0.value;
-
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
return res;
}
@@ -1432,15 +1420,10 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
}
LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[2] = {};
- args[0] = arg0.value;
- args[1] = arg1.value;
+ LLVMValueRef args[2] = { arg0.value, arg1.value };
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
return res;
}
@@ -1851,6 +1834,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return lb_emit_matrix_flatten(p, m, tv.type);
}
+ case BuiltinProc_unreachable:
+ lb_emit_unreachable(p);
+ return {};
+
+
// "Intrinsics"
case BuiltinProc_alloca:
@@ -1898,11 +1886,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_trap: name = "llvm.trap"; break;
}
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
-
- LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+ lb_call_intrinsic(p, name, nullptr, 0, nullptr, 0);
if (id == BuiltinProc_trap) {
LLVMBuildUnreachable(p->builder);
}
@@ -1922,11 +1906,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
} else {
char const *name = "llvm.readcyclecounter";
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
-
- res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+ res.value = lb_call_intrinsic(p, name, nullptr, 0, nullptr, 0);
}
return res;
}
@@ -1981,16 +1961,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
}
LLVMTypeRef types[1] = {lb_type(p->module, type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[2] = {};
- args[0] = x.value;
- args[1] = y.value;
+ LLVMValueRef args[2] = { x.value, y.value };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
if (is_type_tuple(main_type)) {
Type *res_type = nullptr;
@@ -2017,15 +1992,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
char const *name = "llvm.sqrt";
LLVMTypeRef types[1] = {lb_type(p->module, type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[1] = {};
- args[0] = x.value;
+ LLVMValueRef args[1] = { x.value };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = type;
return res;
}
@@ -2040,17 +2011,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
char const *name = "llvm.fma";
LLVMTypeRef types[1] = {lb_type(p->module, type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[3] = {};
- args[0] = x.value;
- args[1] = y.value;
- args[2] = z.value;
+ LLVMValueRef args[3] = { x.value, y.value, z.value };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = type;
return res;
}
@@ -2102,15 +2067,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lbValue ptr = lb_build_expr(p, ce->args[0]);
lbValue len = lb_build_expr(p, ce->args[1]);
len = lb_emit_conv(p, len, t_int);
-
- LLVMValueRef indices[1] = {
- len.value,
- };
-
- lbValue res = {};
- res.type = tv.type;
- res.value = LLVMBuildGEP(p->builder, ptr.value, indices, gb_count_of(indices), "");
- return res;
+ return lb_emit_ptr_offset(p, ptr, len);
}
case BuiltinProc_ptr_sub:
{
@@ -2118,7 +2075,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lbValue ptr1 = lb_build_expr(p, ce->args[1]);
LLVMTypeRef type_int = lb_type(p->module, t_int);
- LLVMValueRef diff = LLVMBuildPtrDiff(p->builder, ptr0.value, ptr1.value, "");
+ LLVMValueRef diff = LLVMBuildPtrDiff2(p->builder, lb_type(p->module, ptr0.type), ptr0.value, ptr1.value, "");
diff = LLVMBuildIntCast2(p->builder, diff, type_int, /*signed*/true, "");
lbValue res = {};
@@ -2169,7 +2126,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
- LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
+ LLVMValueRef instr = LLVMBuildLoad2(p->builder, lb_type(p->module, type_deref(dst.type)), dst.value, "");
switch (id) {
case BuiltinProc_non_temporal_load:
{
@@ -2343,18 +2300,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_ASSERT(name != nullptr);
LLVMTypeRef types[1] = {lb_type(p->module, platform_type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
lbValue res = {};
- LLVMValueRef args[3] = {};
- args[0] = x.value;
- args[1] = y.value;
- args[2] = scale.value;
+ LLVMValueRef args[3] = {
+ x.value,
+ y.value,
+ scale.value };
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = platform_type;
return lb_emit_conv(p, res, tv.type);
}
@@ -2368,17 +2321,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
char const *name = "llvm.expect";
LLVMTypeRef types[1] = {lb_type(p->module, t)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
lbValue res = {};
+ LLVMValueRef args[2] = { x.value, y.value };
- LLVMValueRef args[2] = {};
- args[0] = x.value;
- args[1] = y.value;
-
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = t;
return lb_emit_conv(p, res, t);
}
@@ -2414,9 +2360,6 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
char const *name = "llvm.prefetch";
LLVMTypeRef types[1] = {lb_type(p->module, t_rawptr)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMTypeRef llvm_i32 = lb_type(p->module, t_i32);
LLVMValueRef args[4] = {};
@@ -2426,7 +2369,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
args[3] = LLVMConstInt(llvm_i32, cache, false);
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = nullptr;
return res;
}
@@ -2672,7 +2615,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
len -= 1;
}
- LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name);
+ LLVMTypeRef type = LLVMTypeOf(array);
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, array);
LLVMSetLinkage(global_data, LLVMInternalLinkage);
@@ -2684,7 +2628,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
};
lbValue res = {};
res.type = tv.type;
- res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), "");
+ res.value = LLVMBuildInBoundsGEP2(p->builder, type, global_data, indices, gb_count_of(indices), "");
return res;
}
@@ -2695,9 +2639,6 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMTypeRef types[1] = {
lb_type(p->module, t_uintptr),
};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[2] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
@@ -2705,7 +2646,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lbValue res = {};
res.type = tv.type;
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
return res;
}
case BuiltinProc_wasm_memory_size:
@@ -2714,16 +2655,13 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMTypeRef types[1] = {
lb_type(p->module, t_uintptr),
};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[1] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
lbValue res = {};
res.type = tv.type;
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
return res;
}
@@ -2733,9 +2671,6 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMTypeRef types[1] = {
lb_type(p->module, t_u32),
};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types));
Type *t_u32_ptr = alloc_type_pointer(t_u32);
@@ -2746,7 +2681,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lbValue res = {};
res.type = tv.type;
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
return res;
}
@@ -2756,19 +2691,16 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMTypeRef types[1] = {
lb_type(p->module, t_u32),
};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types));
Type *t_u32_ptr = alloc_type_pointer(t_u32);
- LLVMValueRef args[2] = {};
- args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value;
- args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
+ LLVMValueRef args[2] = {
+ lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value,
+ lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value };
lbValue res = {};
res.type = tv.type;
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
return res;
}
@@ -2777,7 +2709,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
{
Type *param_types[2] = {t_u32, t_u32};
Type *type = alloc_type_proc_from_types(param_types, gb_count_of(param_types), tv.type, false, ProcCC_None);
- LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
+ LLVMTypeRef func_type = lb_get_procedure_raw_type(p->module, type);
LLVMValueRef the_asm = llvm_get_inline_asm(
func_type,
str_lit("cpuid"),
@@ -2797,7 +2729,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_x86_xgetbv:
{
Type *type = alloc_type_proc_from_types(&t_u32, 1, tv.type, false, ProcCC_None);
- LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
+ LLVMTypeRef func_type = lb_get_procedure_raw_type(p->module, type);
LLVMValueRef the_asm = llvm_get_inline_asm(
func_type,
str_lit("xgetbv"),
@@ -2813,6 +2745,55 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), "");
return res;
}
+
+ case BuiltinProc_valgrind_client_request:
+ {
+ lbValue args[7] = {};
+ for (isize i = 0; i < 7; i++) {
+ args[i] = lb_emit_conv(p, lb_build_expr(p, ce->args[i]), t_uintptr);
+ }
+ if (!build_context.ODIN_VALGRIND_SUPPORT) {
+ return args[0];
+ }
+ lbValue array = lb_generate_local_array(p, t_uintptr, 6, false);
+ for (isize i = 0; i < 6; i++) {
+ lbValue gep = lb_emit_array_epi(p, array, i);
+ lb_emit_store(p, gep, args[i+1]);
+ }
+
+ switch (build_context.metrics.arch) {
+ case TargetArch_amd64:
+ {
+ Type *param_types[2] = {};
+ param_types[0] = t_uintptr;
+ param_types[1] = array.type;
+
+ Type *type = alloc_type_proc_from_types(param_types, gb_count_of(param_types), t_uintptr, false, ProcCC_None);
+ LLVMTypeRef func_type = lb_get_procedure_raw_type(p->module, type);
+ LLVMValueRef the_asm = llvm_get_inline_asm(
+ func_type,
+ str_lit("rolq $3, %rdi; rolq $13, %rdi\n rolq $61, %rdi; rolq $51, %rdi\n xchgq %rbx, %rbx"),
+ str_lit("={rdx},{rdx},{rax},cc,memory"),
+ true
+ );
+
+ LLVMValueRef asm_args[2] = {};
+ asm_args[0] = args[0].value;
+ asm_args[1] = array.value;
+
+ lbValue res = {};
+ res.type = t_uintptr;
+ res.value = LLVMBuildCall2(p->builder, func_type, the_asm, asm_args, gb_count_of(asm_args), "");
+ return res;
+ }
+ break;
+ default:
+ GB_PANIC("Unsupported architecture: %.*s", LIT(target_arch_names[build_context.metrics.arch]));
+ break;
+ }
+
+ }
+
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index f131bb3db..175c4c537 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -458,15 +458,6 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
val1_type = type_of_expr(rs->vals[1]);
}
- if (val0_type != nullptr) {
- Entity *e = entity_of_node(rs->vals[0]);
- lb_add_local(p, e->type, e, true);
- }
- if (val1_type != nullptr) {
- Entity *e = entity_of_node(rs->vals[1]);
- lb_add_local(p, e->type, e, true);
- }
-
TokenKind op = Token_Lt;
switch (node->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break;
@@ -478,10 +469,22 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lbValue lower = lb_build_expr(p, node->left);
lbValue upper = {}; // initialized each time in the loop
- lbAddr value = lb_add_local_generated(p, val0_type ? val0_type : lower.type, false);
+ lbAddr value;
+ if (val0_type != nullptr) {
+ Entity *e = entity_of_node(rs->vals[0]);
+ value = lb_add_local(p, val0_type, e, false);
+ } else {
+ value = lb_add_local_generated(p, lower.type, false);
+ }
lb_addr_store(p, value, lower);
- lbAddr index = lb_add_local_generated(p, t_int, false);
+ lbAddr index;
+ if (val1_type != nullptr) {
+ Entity *e = entity_of_node(rs->vals[1]);
+ index = lb_add_local(p, val1_type, e, false);
+ } else {
+ index = lb_add_local_generated(p, t_int, false);
+ }
lb_addr_store(p, index, lb_const_int(m, t_int, 0));
lbBlock *loop = lb_create_block(p, "for.interval.loop");
@@ -1793,7 +1796,7 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
lbValue rhs = lb_emit_conv(p, value, lhs_type);
- bool inline_array_arith = type_size_of(array_type) <= build_context.max_align;
+ bool inline_array_arith = lb_can_try_to_inline_array_arith(array_type);
if (lhs.kind == lbAddr_Swizzle) {
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index 2e7b2788a..d424fa5b2 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -57,6 +57,7 @@ lbValue lb_typeid(lbModule *m, Type *type) {
case Type_SimdVector: kind = Typeid_Simd_Vector; break;
case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
case Type_RelativeSlice: kind = Typeid_Relative_Slice; break;
+ case Type_SoaPointer: kind = Typeid_SoaPointer; break;
}
if (is_type_cstring(type)) {
@@ -97,34 +98,12 @@ lbValue lb_type_info(lbModule *m, Type *type) {
isize index = lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
- LLVMTypeRef it = lb_type(m, t_int);
- LLVMValueRef indices[2] = {
- LLVMConstInt(it, 0, false),
- LLVMConstInt(it, index, true),
- };
-
- lbValue value = {};
- value.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices));
- value.type = t_type_info_ptr;
- return value;
+ lbValue data = lb_global_type_info_data_ptr(m);
+ return lb_emit_array_epi(m, data, index);
}
-lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
- GB_ASSERT(!build_context.disallow_rtti);
-
- i32 index = cast(i32)lb_type_info_index(m->info, type);
- GB_ASSERT(index >= 0);
- // gb_printf_err("%d %s\n", index, type_to_string(type));
-
- LLVMValueRef indices[2] = {
- LLVMConstInt(lb_type(m, t_int), 0, false),
- LLVMConstInt(lb_type(m, t_int), index, false),
- };
-
- lbValue res = {};
- res.type = t_type_info_ptr;
- res.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, cast(unsigned)gb_count_of(indices));
- return res;
+LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) {
+ return lb_type_internal_for_procedures_raw(m, type);
}
@@ -178,10 +157,10 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
LLVMValueRef values[2] = {
- LLVMConstInBoundsGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices)),
+ LLVMConstInBoundsGEP2(lb_type(m, lb_global_type_info_data_entity->type), lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices)),
LLVMConstInt(lb_type(m, t_int), type->Array.count, true),
};
- LLVMValueRef slice = llvm_const_named_struct_internal(llvm_addr_type(global_type_table), values, gb_count_of(values));
+ LLVMValueRef slice = llvm_const_named_struct_internal(lb_type(m, type_deref(global_type_table.type)), values, gb_count_of(values));
LLVMSetInitializer(global_type_table.value, slice);
}
@@ -260,7 +239,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
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,
+ lb_type_info(m, t->Named.base).value,
pkg_name,
loc.value
};
@@ -419,7 +398,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_Pointer: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
- lbValue gep = lb_get_type_info_ptr(m, t->Pointer.elem);
+ lbValue gep = lb_type_info(m, t->Pointer.elem);
LLVMValueRef vals[1] = {
gep.value,
@@ -433,7 +412,21 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
case Type_MultiPointer: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_multi_pointer_ptr);
- lbValue gep = lb_get_type_info_ptr(m, t->MultiPointer.elem);
+ lbValue gep = lb_type_info(m, t->MultiPointer.elem);
+
+ LLVMValueRef vals[1] = {
+ gep.value,
+ };
+
+ lbValue res = {};
+ res.type = type_deref(tag.type);
+ res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
+ lb_emit_store(p, tag, res);
+ break;
+ }
+ case Type_SoaPointer: {
+ tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_soa_pointer_ptr);
+ lbValue gep = lb_type_info(m, t->SoaPointer.elem);
LLVMValueRef vals[1] = {
gep.value,
@@ -450,7 +443,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
i64 ez = type_size_of(t->Array.elem);
LLVMValueRef vals[3] = {
- lb_get_type_info_ptr(m, t->Array.elem).value,
+ lb_type_info(m, t->Array.elem).value,
lb_const_int(m, t_int, ez).value,
lb_const_int(m, t_int, t->Array.count).value,
};
@@ -465,8 +458,8 @@ 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_enumerated_array_ptr);
LLVMValueRef vals[7] = {
- lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
- lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
+ lb_type_info(m, t->EnumeratedArray.elem).value,
+ lb_type_info(m, t->EnumeratedArray.index).value,
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
lb_const_int(m, t_int, t->EnumeratedArray.count).value,
@@ -497,7 +490,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr);
LLVMValueRef vals[2] = {
- lb_get_type_info_ptr(m, t->DynamicArray.elem).value,
+ lb_type_info(m, t->DynamicArray.elem).value,
lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value,
};
@@ -511,7 +504,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr);
LLVMValueRef vals[2] = {
- lb_get_type_info_ptr(m, t->Slice.elem).value,
+ lb_type_info(m, t->Slice.elem).value,
lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value,
};
@@ -527,10 +520,10 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr));
LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr));
if (t->Proc.params != nullptr) {
- params = lb_get_type_info_ptr(m, t->Proc.params).value;
+ params = lb_type_info(m, t->Proc.params).value;
}
if (t->Proc.results != nullptr) {
- results = lb_get_type_info_ptr(m, t->Proc.results).value;
+ results = lb_type_info(m, t->Proc.results).value;
}
LLVMValueRef vals[4] = {
@@ -649,7 +642,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
// NOTE(bill): Zeroth is nil so ignore it
for (isize variant_index = 0; variant_index < variant_count; variant_index++) {
Type *vt = t->Union.variants[variant_index];
- lbValue tip = lb_get_type_info_ptr(m, vt);
+ lbValue tip = lb_type_info(m, vt);
lbValue index = lb_const_int(m, t_int, variant_index);
lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
@@ -737,7 +730,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
for (isize source_index = 0; source_index < count; source_index++) {
// TODO(bill): Order fields in source order not layout order
Entity *f = t->Struct.fields[source_index];
- lbValue tip = lb_get_type_info_ptr(m, f->type);
+ lbValue tip = lb_type_info(m, f->type);
i64 foffset = 0;
if (!t->Struct.is_raw_union) {
GB_ASSERT(t->Struct.offsets != nullptr);
@@ -794,11 +787,11 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
init_map_internal_types(t);
- lbValue gst = lb_get_type_info_ptr(m, t->Map.generated_struct_type);
+ lbValue gst = lb_type_info(m, t->Map.generated_struct_type);
LLVMValueRef vals[5] = {
- lb_get_type_info_ptr(m, t->Map.key).value,
- lb_get_type_info_ptr(m, t->Map.value).value,
+ lb_type_info(m, t->Map.key).value,
+ lb_type_info(m, t->Map.value).value,
gst.value,
lb_get_equal_proc_for_type(m, t->Map.key).value,
lb_get_hasher_proc_for_type(m, t->Map.key).value
@@ -819,13 +812,13 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
LLVMValueRef vals[4] = {
- lb_get_type_info_ptr(m, t->BitSet.elem).value,
+ lb_type_info(m, t->BitSet.elem).value,
LLVMConstNull(lb_type(m, t_type_info_ptr)),
lb_const_int(m, t_i64, t->BitSet.lower).value,
lb_const_int(m, t_i64, t->BitSet.upper).value,
};
if (t->BitSet.underlying != nullptr) {
- vals[1] =lb_get_type_info_ptr(m, t->BitSet.underlying).value;
+ vals[1] =lb_type_info(m, t->BitSet.underlying).value;
}
lbValue res = {};
@@ -841,7 +834,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
LLVMValueRef vals[3] = {};
- vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
+ vals[0] = lb_type_info(m, t->SimdVector.elem).value;
vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
@@ -856,8 +849,8 @@ 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_relative_pointer_ptr);
LLVMValueRef vals[2] = {
- lb_get_type_info_ptr(m, t->RelativePointer.pointer_type).value,
- lb_get_type_info_ptr(m, t->RelativePointer.base_integer).value,
+ lb_type_info(m, t->RelativePointer.pointer_type).value,
+ lb_type_info(m, t->RelativePointer.base_integer).value,
};
lbValue res = {};
@@ -870,8 +863,8 @@ 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_relative_slice_ptr);
LLVMValueRef vals[2] = {
- lb_get_type_info_ptr(m, t->RelativeSlice.slice_type).value,
- lb_get_type_info_ptr(m, t->RelativeSlice.base_integer).value,
+ lb_type_info(m, t->RelativeSlice.slice_type).value,
+ lb_type_info(m, t->RelativeSlice.base_integer).value,
};
lbValue res = {};
@@ -886,7 +879,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
i64 ez = type_size_of(t->Matrix.elem);
LLVMValueRef vals[5] = {
- lb_get_type_info_ptr(m, t->Matrix.elem).value,
+ lb_type_info(m, t->Matrix.elem).value,
lb_const_int(m, t_int, ez).value,
lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value,
lb_const_int(m, t_int, t->Matrix.row_count).value,
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 88ec2f22c..7163f1d9e 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -39,6 +39,13 @@ bool lb_is_type_aggregate(Type *t) {
return false;
}
+void lb_emit_unreachable(lbProcedure *p) {
+ LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+ if (instr == nullptr || !lb_is_instr_terminating(instr)) {
+ lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
+ LLVMBuildUnreachable(p->builder);
+ }
+}
lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
Type *src = core_type(value.type);
@@ -73,9 +80,6 @@ LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValu
lb_type(p->module, t_int)
};
if (true || is_inlinable) {
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[4] = {};
args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
@@ -83,16 +87,18 @@ LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValu
args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
- return LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
} else {
- LLVMValueRef ip = lb_lookup_runtime_procedure(p->module, str_lit("memset")).value;
+ lbValue pr = lb_lookup_runtime_procedure(p->module, str_lit("memset"));
LLVMValueRef args[3] = {};
args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
args[1] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), 0, false);
args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
- return LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ // We always get the function pointer type rather than the function and there is apparently no way around that?
+ LLVMTypeRef type = lb_type_internal_for_procedures_raw(p->module, pr.type);
+ return LLVMBuildCall2(p->builder, type, pr.value, args, gb_count_of(args), "");
}
}
@@ -346,44 +352,65 @@ lbValue lb_emit_try_has_value(lbProcedure *p, lbValue rhs) {
lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue const &tv) {
+ if (arg->state_flags & StateFlag_DirectiveWasFalse) {
+ return lb_build_expr(p, else_expr);
+ }
+
lbValue lhs = {};
lbValue rhs = {};
lb_emit_try_lhs_rhs(p, arg, tv, &lhs, &rhs);
- LLVMValueRef incoming_values[2] = {};
- LLVMBasicBlockRef incoming_blocks[2] = {};
-
GB_ASSERT(else_expr != nullptr);
- lbBlock *then = lb_create_block(p, "or_else.then");
- lbBlock *done = lb_create_block(p, "or_else.done"); // NOTE(bill): Append later
- lbBlock *else_ = lb_create_block(p, "or_else.else");
-
- lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
- lb_start_block(p, then);
Type *type = default_type(tv.type);
- incoming_values[0] = lb_emit_conv(p, lhs, type).value;
+ if (is_diverging_expr(else_expr)) {
+ lbBlock *then = lb_create_block(p, "or_else.then");
+ lbBlock *else_ = lb_create_block(p, "or_else.else");
- lb_emit_jump(p, done);
- lb_start_block(p, else_);
+ lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
+ // NOTE(bill): else block needs to be straight afterwards to make sure that the actual value is used
+ // from the then block
+ lb_start_block(p, else_);
- incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, else_expr), type).value;
+ lb_build_expr(p, else_expr);
+ lb_emit_unreachable(p); // add just in case
- lb_emit_jump(p, done);
- lb_start_block(p, done);
+ lb_start_block(p, then);
+ return lb_emit_conv(p, lhs, type);
+ } else {
+ LLVMValueRef incoming_values[2] = {};
+ LLVMBasicBlockRef incoming_blocks[2] = {};
- lbValue res = {};
- res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
- res.type = type;
+ lbBlock *then = lb_create_block(p, "or_else.then");
+ lbBlock *done = lb_create_block(p, "or_else.done"); // NOTE(bill): Append later
+ lbBlock *else_ = lb_create_block(p, "or_else.else");
- GB_ASSERT(p->curr_block->preds.count >= 2);
- incoming_blocks[0] = p->curr_block->preds[0]->block;
- incoming_blocks[1] = p->curr_block->preds[1]->block;
+ lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
+ lb_start_block(p, then);
- LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2);
+ incoming_values[0] = lb_emit_conv(p, lhs, type).value;
- return res;
+ lb_emit_jump(p, done);
+ lb_start_block(p, else_);
+
+ incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, else_expr), type).value;
+
+ lb_emit_jump(p, done);
+ lb_start_block(p, done);
+
+ lbValue res = {};
+ res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
+ res.type = type;
+
+ GB_ASSERT(p->curr_block->preds.count >= 2);
+ incoming_blocks[0] = p->curr_block->preds[0]->block;
+ incoming_blocks[1] = p->curr_block->preds[1]->block;
+
+ LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2);
+
+ return res;
+ }
}
void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
@@ -460,15 +487,11 @@ lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
char const *name = "llvm.bswap";
LLVMTypeRef types[1] = {lb_type(p->module, value.type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[1] = {};
- args[0] = value.value;
+ LLVMValueRef args[1] = { value.value };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = value.type;
if (is_type_float(original_type)) {
@@ -486,15 +509,10 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) {
char const *name = "llvm.ctpop";
LLVMTypeRef types[1] = {lb_type(p->module, type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
- LLVMValueRef args[1] = {};
- args[0] = x.value;
+ LLVMValueRef args[1] = { x.value };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = type;
return res;
}
@@ -515,16 +533,13 @@ lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type) {
char const *name = "llvm.cttz";
LLVMTypeRef types[1] = {lb_type(p->module, type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[2] = {};
- args[0] = x.value;
- args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx));
+ LLVMValueRef args[2] = {
+ x.value,
+ LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx)) };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = type;
return res;
}
@@ -534,16 +549,13 @@ lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type) {
char const *name = "llvm.ctlz";
LLVMTypeRef types[1] = {lb_type(p->module, type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[2] = {};
- args[0] = x.value;
- args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx));
+ LLVMValueRef args[2] = {
+ x.value,
+ LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx)) };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = type;
return res;
}
@@ -555,15 +567,11 @@ lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type) {
char const *name = "llvm.bitreverse";
LLVMTypeRef types[1] = {lb_type(p->module, type)};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[1] = {};
- args[0] = x.value;
+ LLVMValueRef args[1] = { x.value };
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
res.type = type;
return res;
}
@@ -984,6 +992,11 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
case 0: result_type = t->RelativeSlice.base_integer; break;
case 1: result_type = t->RelativeSlice.base_integer; break;
}
+ } else if (is_type_soa_pointer(t)) {
+ switch (index) {
+ case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
+ case 1: result_type = t_int; break;
+ }
} else {
GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(s.type), index);
}
@@ -994,15 +1007,16 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
index = lb_convert_struct_index(p->module, t, index);
if (lb_is_const(s)) {
+ // NOTE(bill): this cannot be replaced with lb_emit_epi
lbModule *m = p->module;
lbValue res = {};
LLVMValueRef indices[2] = {llvm_zero(m), LLVMConstInt(lb_type(m, t_i32), index, false)};
- res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
+ res.value = LLVMConstGEP2(lb_type(m, type_deref(s.type)), s.value, indices, gb_count_of(indices));
res.type = alloc_type_pointer(result_type);
return res;
} else {
lbValue res = {};
- LLVMTypeRef st = LLVMGetElementType(LLVMTypeOf(s.value));
+ LLVMTypeRef st = lb_type(p->module, type_deref(s.type));
// gb_printf_err("%s\n", type_to_string(s.type));
// gb_printf_err("%s\n", LLVMPrintTypeToString(LLVMTypeOf(s.value)));
// gb_printf_err("%d\n", index);
@@ -1010,7 +1024,7 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
unsigned count = LLVMCountStructElementTypes(st);
GB_ASSERT_MSG(count >= cast(unsigned)index, "%u %d %d", count, index, original_index);
- res.value = LLVMBuildStructGEP(p->builder, s.value, cast(unsigned)index, "");
+ res.value = LLVMBuildStructGEP2(p->builder, st, s.value, cast(unsigned)index, "");
res.type = alloc_type_pointer(result_type);
return res;
}
@@ -1114,6 +1128,13 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
result_type = t->Array.elem;
break;
+ case Type_SoaPointer:
+ switch (index) {
+ case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
+ case 1: result_type = t_int; break;
+ }
+ break;
+
default:
GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(s.type), index);
break;
@@ -1141,7 +1162,28 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) {
}
type = core_type(type);
- if (is_type_quaternion(type)) {
+ if (type->kind == Type_SoaPointer) {
+ lbValue addr = lb_emit_struct_ep(p, e, 0);
+ lbValue index = lb_emit_struct_ep(p, e, 1);
+ addr = lb_emit_load(p, addr);
+ index = lb_emit_load(p, index);
+
+ i32 first_index = sel.index[0];
+ Selection sub_sel = sel;
+ sub_sel.index.data += 1;
+ sub_sel.index.count -= 1;
+
+ lbValue arr = lb_emit_struct_ep(p, addr, first_index);
+
+ Type *t = base_type(type_deref(addr.type));
+ GB_ASSERT(is_type_soa_struct(t));
+
+ if (t->Struct.soa_kind == StructSoa_Fixed) {
+ e = lb_emit_array_ep(p, arr, index);
+ } else {
+ e = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index);
+ }
+ } else if (is_type_quaternion(type)) {
e = lb_emit_struct_ep(p, e, index);
} else if (is_type_raw_union(type)) {
type = get_struct_field_type(type, index);
@@ -1216,7 +1258,12 @@ lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index) {
Type *ptr = base_array_type(st);
lbValue res = {};
- res.value = LLVMBuildGEP(p->builder, s.value, indices, 2, "");
+
+ if (LLVMIsConstant(s.value) && LLVMIsConstant(index.value)) {
+ res.value = LLVMConstGEP2(lb_type(p->module, st), s.value, indices, gb_count_of(indices));
+ } else {
+ res.value = LLVMBuildGEP2(p->builder, lb_type(p->module, st), s.value, indices, gb_count_of(indices), "");
+ }
res.type = alloc_type_pointer(ptr);
return res;
}
@@ -1226,24 +1273,16 @@ lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
GB_ASSERT(is_type_pointer(t));
Type *st = base_type(type_deref(t));
GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st));
-
GB_ASSERT(0 <= index);
- Type *ptr = base_array_type(st);
-
-
- LLVMValueRef indices[2] = {
- LLVMConstInt(lb_type(p->module, t_int), 0, false),
- LLVMConstInt(lb_type(p->module, t_int), cast(unsigned)index, false),
- };
-
- lbValue res = {};
- if (lb_is_const(s)) {
- res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
- } else {
- res.value = LLVMBuildGEP(p->builder, s.value, indices, gb_count_of(indices), "");
- }
- res.type = alloc_type_pointer(ptr);
- return res;
+ return lb_emit_epi(p, s, index);
+}
+lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index) {
+ Type *t = s.type;
+ GB_ASSERT(is_type_pointer(t));
+ Type *st = base_type(type_deref(t));
+ GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st));
+ GB_ASSERT(0 <= index);
+ return lb_emit_epi(m, s, index);
}
lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
@@ -1251,11 +1290,12 @@ lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
LLVMValueRef indices[1] = {index.value};
lbValue res = {};
res.type = ptr.type;
+ LLVMTypeRef type = lb_type(p->module, type_deref(res.type, true));
if (lb_is_const(ptr) && lb_is_const(index)) {
- res.value = LLVMConstGEP(ptr.value, indices, 1);
+ res.value = LLVMConstGEP2(type, ptr.value, indices, 1);
} else {
- res.value = LLVMBuildGEP(p->builder, ptr.value, indices, 1, "");
+ res.value = LLVMBuildGEP2(p->builder, type, ptr.value, indices, 1, "");
}
return res;
}
@@ -1264,63 +1304,18 @@ lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column) {
Type *t = s.type;
GB_ASSERT(is_type_pointer(t));
Type *mt = base_type(type_deref(t));
-
- Type *ptr = base_array_type(mt);
-
if (column == 0) {
GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt));
-
- LLVMValueRef indices[2] = {
- LLVMConstInt(lb_type(p->module, t_int), 0, false),
- LLVMConstInt(lb_type(p->module, t_int), cast(unsigned)row, false),
- };
-
- lbValue res = {};
- if (lb_is_const(s)) {
- res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
- } else {
- res.value = LLVMBuildGEP(p->builder, s.value, indices, gb_count_of(indices), "");
- }
-
- Type *ptr = base_array_type(mt);
- res.type = alloc_type_pointer(ptr);
- return res;
+ return lb_emit_epi(p, s, row);
} else if (row == 0 && is_type_array_like(mt)) {
- LLVMValueRef indices[2] = {
- LLVMConstInt(lb_type(p->module, t_int), 0, false),
- LLVMConstInt(lb_type(p->module, t_int), cast(unsigned)column, false),
- };
-
- lbValue res = {};
- if (lb_is_const(s)) {
- res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
- } else {
- res.value = LLVMBuildGEP(p->builder, s.value, indices, gb_count_of(indices), "");
- }
-
- Type *ptr = base_array_type(mt);
- res.type = alloc_type_pointer(ptr);
- return res;
+ return lb_emit_epi(p, s, column);
}
GB_ASSERT_MSG(is_type_matrix(mt), "%s", type_to_string(mt));
isize offset = matrix_indices_to_offset(mt, row, column);
-
- LLVMValueRef indices[2] = {
- LLVMConstInt(lb_type(p->module, t_int), 0, false),
- LLVMConstInt(lb_type(p->module, t_int), cast(unsigned)offset, false),
- };
-
- lbValue res = {};
- if (lb_is_const(s)) {
- res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
- } else {
- res.value = LLVMBuildGEP(p->builder, s.value, indices, gb_count_of(indices), "");
- }
- res.type = alloc_type_pointer(ptr);
- return res;
+ return lb_emit_epi(p, s, offset);
}
lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column) {
@@ -1343,11 +1338,12 @@ lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column
index,
};
+ LLVMTypeRef type = lb_type(p->module, mt);
lbValue res = {};
if (lb_is_const(s)) {
- res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
+ res.value = LLVMConstGEP2(type, s.value, indices, gb_count_of(indices));
} else {
- res.value = LLVMBuildGEP(p->builder, s.value, indices, gb_count_of(indices), "");
+ res.value = LLVMBuildGEP2(p->builder, type, s.value, indices, gb_count_of(indices), "");
}
res.type = alloc_type_pointer(ptr);
return res;
@@ -1551,18 +1547,12 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t
if (is_possible) {
char const *name = "llvm.fma";
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
-
- LLVMTypeRef types[1] = {};
- types[0] = lb_type(m, t);
-
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, types, gb_count_of(types));
- LLVMValueRef values[3] = {};
- values[0] = a.value;
- values[1] = b.value;
- values[2] = c.value;
- LLVMValueRef call = LLVMBuildCall(p->builder, ip, values, gb_count_of(values), "");
+ LLVMTypeRef types[1] = { lb_type(m, t) };
+ LLVMValueRef values[3] = {
+ a.value,
+ b.value,
+ c.value };
+ LLVMValueRef call = lb_call_intrinsic(p, name, values, gb_count_of(values), types, gb_count_of(types));
return {call, t};
} else {
lbValue x = lb_emit_arith(p, Token_Mul, a, b, t);
@@ -1661,7 +1651,7 @@ LLVMValueRef llvm_vector_expand_to_power_of_two(lbProcedure *p, LLVMValueRef val
LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
LLVMTypeRef type = LLVMTypeOf(value);
GB_ASSERT(LLVMGetTypeKind(type) == LLVMVectorTypeKind);
- LLVMTypeRef elem = LLVMGetElementType(type);
+ LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
unsigned len = LLVMGetVectorSize(type);
if (len == 0) {
return LLVMConstNull(type);
@@ -1691,15 +1681,9 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
if (id != 0 && false) {
- LLVMTypeRef types[1] = {};
- types[0] = type;
-
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef values[2] = {};
- values[0] = LLVMConstNull(elem);
- values[1] = value;
- LLVMValueRef call = LLVMBuildCall(p->builder, ip, values+value_offset, value_count, "");
- return call;
+ LLVMTypeRef types[1] = { type };
+ LLVMValueRef values[2] = { LLVMConstNull(elem), value };
+ return lb_call_intrinsic(p, name, values + value_offset, value_count, types, gb_count_of(types));
}
// Manual reduce
@@ -1743,7 +1727,7 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
GB_ASSERT(LLVMTypeOf(a) == LLVMTypeOf(b));
- LLVMTypeRef elem = LLVMGetElementType(LLVMTypeOf(a));
+ LLVMTypeRef elem = OdinLLVMGetVectorElementType(LLVMTypeOf(a));
if (LLVMGetTypeKind(elem) == LLVMIntegerTypeKind) {
return LLVMBuildAdd(p->builder, a, b, "");
@@ -1754,7 +1738,7 @@ LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
LLVMValueRef llvm_vector_mul(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
GB_ASSERT(LLVMTypeOf(a) == LLVMTypeOf(b));
- LLVMTypeRef elem = LLVMGetElementType(LLVMTypeOf(a));
+ LLVMTypeRef elem = OdinLLVMGetVectorElementType(LLVMTypeOf(a));
if (LLVMGetTypeKind(elem) == LLVMIntegerTypeKind) {
return LLVMBuildMul(p->builder, a, b, "");
@@ -1768,14 +1752,13 @@ LLVMValueRef llvm_vector_dot(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
}
LLVMValueRef llvm_vector_mul_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b, LLVMValueRef c) {
- lbModule *m = p->module;
-
+
LLVMTypeRef t = LLVMTypeOf(a);
GB_ASSERT(t == LLVMTypeOf(b));
GB_ASSERT(t == LLVMTypeOf(c));
GB_ASSERT(LLVMGetTypeKind(t) == LLVMVectorTypeKind);
- LLVMTypeRef elem = LLVMGetElementType(t);
+ LLVMTypeRef elem = OdinLLVMGetVectorElementType(t);
bool is_possible = false;
@@ -1791,18 +1774,9 @@ LLVMValueRef llvm_vector_mul_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b,
if (is_possible) {
char const *name = "llvm.fmuladd";
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
-
- LLVMTypeRef types[1] = {};
- types[0] = t;
-
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, types, gb_count_of(types));
- LLVMValueRef values[3] = {};
- values[0] = a;
- values[1] = b;
- values[2] = c;
- LLVMValueRef call = LLVMBuildCall(p->builder, ip, values, gb_count_of(values), "");
+ LLVMTypeRef types[1] = { t };
+ LLVMValueRef values[3] = { a, b, c};
+ LLVMValueRef call = lb_call_intrinsic(p, name, values, gb_count_of(values), types, gb_count_of(types));
return call;
} else {
LLVMValueRef x = llvm_vector_mul(p, a, b);
@@ -1817,7 +1791,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin
cast(char *)clobbers.text, cast(size_t)clobbers.len,
has_side_effects, is_align_stack,
dialect
- #if LLVM_VERSION_MAJOR >= 13
+ #if LLVM_VERSION_MAJOR >= 13
, /*CanThrow*/false
#endif
);
diff --git a/src/main.cpp b/src/main.cpp
index beefec702..ef1b8dda1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -283,6 +283,9 @@ i32 linker_stage(lbGenerator *gen) {
String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
defer (gb_free(heap_allocator(), vs_exe_path.text));
+ String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
+ defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
+
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
if (build_context.has_resource) {
@@ -292,7 +295,8 @@ i32 linker_stage(lbGenerator *gen) {
defer (gb_free(heap_allocator(), res_path.text));
result = system_exec_command_line_app("msvc-link",
- "\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+ "\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+ LIT(windows_sdk_bin_path),
LIT(res_path),
LIT(rc_path)
);
@@ -1558,7 +1562,7 @@ bool parse_build_flags(Array<String> args) {
bad_flags = true;
break;
}
- build_context.resource_filepath = substring(path, 0, string_extension_position(path));
+ build_context.resource_filepath = path;
build_context.has_resource = true;
} else {
gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h
index 812513875..7d23f2557 100644
--- a/src/microsoft_craziness.h
+++ b/src/microsoft_craziness.h
@@ -50,18 +50,7 @@ gb_global gbAllocator mc_allocator = heap_allocator();
struct Find_Result {
int windows_sdk_version; // Zero if no Windows SDK found.
- wchar_t const *windows_sdk_root;
- wchar_t const *windows_sdk_um_library_path;
- wchar_t const *windows_sdk_ucrt_library_path;
-
- wchar_t const *vs_exe_path;
- wchar_t const *vs_library_path;
-};
-
-struct Find_Result_Utf8 {
- int windows_sdk_version; // Zero if no Windows SDK found.
-
- String windows_sdk_root;
+ String windows_sdk_bin_path;
String windows_sdk_um_library_path;
String windows_sdk_ucrt_library_path;
@@ -69,8 +58,6 @@ struct Find_Result_Utf8 {
String vs_library_path;
};
-Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8();
-
String mc_wstring_to_string(wchar_t const *str) {
return string16_to_string(mc_allocator, make_string16_c(str));
}
@@ -87,6 +74,10 @@ String mc_concat(String a, String b, String c) {
return concatenate3_strings(mc_allocator, a, b, c);
}
+String mc_concat(String a, String b, String c, String d) {
+ return concatenate4_strings(mc_allocator, a, b, c, d);
+}
+
String mc_get_env(String key) {
char const * value = gb_get_env((char const *)key.text, mc_allocator);
return make_string_c(value);
@@ -219,19 +210,19 @@ struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE I
// The beginning of the actual code that does things.
-struct Version_Data_Utf8 {
- i32 best_version[4]; // For Windows 8 versions, only two of these numbers are used.
+struct Version_Data {
+ i32 best_version[4];
String best_name;
};
-typedef void (*MC_Visit_Proc)(String short_name, String full_name, Version_Data_Utf8 *data);
-bool mc_visit_files(String dir_name, Version_Data_Utf8 *data, MC_Visit_Proc proc) {
+typedef void (*MC_Visit_Proc)(String short_name, String full_name, Version_Data *data);
+bool mc_visit_files(String dir_name, Version_Data *data, MC_Visit_Proc proc) {
// Visit everything in one folder (non-recursively). If it's a directory
// that doesn't start with ".", call the visit proc on it. The visit proc
// will see if the filename conforms to the expected versioning pattern.
- String wildcard_name = mc_concat(dir_name, str_lit("\\*"));
+ String wildcard_name = mc_concat(dir_name, str_lit("*"));
defer (mc_free(wildcard_name));
MC_Find_Data find_data;
@@ -242,7 +233,7 @@ bool mc_visit_files(String dir_name, Version_Data_Utf8 *data, MC_Visit_Proc proc
bool success = true;
while (success) {
if ((find_data.file_attributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.filename[0] != '.')) {
- String full_name = mc_concat(dir_name, str_lit("\\"), find_data.filename);
+ String full_name = mc_concat(dir_name, find_data.filename);
defer (mc_free(full_name));
proc(find_data.filename, full_name, data);
@@ -284,7 +275,7 @@ String find_windows_kit_root(HKEY key, String const version) {
return value;
}
-void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) {
+void win10_best(String short_name, String full_name, Version_Data *data) {
// Find the Windows 10 subdirectory with the highest version number.
int i0, i1, i2, i3;
@@ -304,11 +295,11 @@ void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) {
// we have to copy_string and free here because visit_files free's the full_name string
// after we execute this function, so Win*_Data would contain an invalid pointer.
- if (data->best_name.len > 0) mc_free(data->best_name);
+ if (data->best_name.len) mc_free(data->best_name);
data->best_name = copy_string(mc_allocator, full_name);
- if (data->best_name.len > 0) {
+ if (data->best_name.len) {
data->best_version[0] = i0;
data->best_version[1] = i1;
data->best_version[2] = i2;
@@ -316,34 +307,8 @@ void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) {
}
}
-void win8_best(String short_name, String full_name, Version_Data_Utf8 *data) {
- // Find the Windows 8 subdirectory with the highest version number.
-
- int i0, i1;
- auto success = sscanf_s((const char *const)short_name.text, "winv%d.%d", &i0, &i1);
- if (success < 2) return;
-
- if (i0 < data->best_version[0]) return;
- else if (i0 == data->best_version[0]) {
- if (i1 < data->best_version[1]) return;
- }
-
- // we have to copy_string and free here because visit_files free's the full_name string
- // after we execute this function, so Win*_Data would contain an invalid pointer.
- if (data->best_name.len > 0) mc_free(data->best_name);
- data->best_name = copy_string(mc_allocator, full_name);
-
- if (data->best_name.len > 0) {
- data->best_version[0] = i0;
- data->best_version[1] = i1;
- }
-}
-
-void find_windows_kit_root(Find_Result_Utf8 *result) {
- // Information about the Windows 10 and Windows 8 development kits
- // is stored in the same place in the registry. We open a key
- // to that place, first checking preferntially for a Windows 10 kit,
- // then, if that's not found, a Windows 8 kit.
+void find_windows_kit_paths(Find_Result *result) {
+ bool sdk_found = false;
HKEY main_key;
@@ -355,44 +320,42 @@ void find_windows_kit_root(Find_Result_Utf8 *result) {
// Look for a Windows 10 entry.
String windows10_root = find_windows_kit_root(main_key, str_lit("KitsRoot10"));
- if (windows10_root.len > 0) {
+ if (windows10_root.len) {
defer (mc_free(windows10_root));
- String windows10_lib = mc_concat(windows10_root, str_lit("Lib"));
+ String windows10_lib = mc_concat(windows10_root, str_lit("Lib\\"));
+ Version_Data data_lib = {0};
+ mc_visit_files(windows10_lib, &data_lib, win10_best);
defer (mc_free(windows10_lib));
+ defer (mc_free(data_lib.best_name));
+
+ String windows10_bin = mc_concat(windows10_root, str_lit("bin\\"));
+ Version_Data data_bin = {0};
+ mc_visit_files(windows10_bin, &data_bin, win10_best);
+ defer (mc_free(windows10_bin));
+ defer (mc_free(data_bin.best_name));
- Version_Data_Utf8 data = {0};
- mc_visit_files(windows10_lib, &data, win10_best);
- if (data.best_name.len > 0) {
- result->windows_sdk_version = 10;
- result->windows_sdk_root = mc_concat(data.best_name, str_lit("\\"));
- return;
+ if (data_lib.best_name.len && data_bin.best_name.len) {
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ result->windows_sdk_um_library_path = mc_concat(data_lib.best_name, str_lit("\\um\\x64\\"));
+ result->windows_sdk_ucrt_library_path = mc_concat(data_lib.best_name, str_lit("\\ucrt\\x64\\"));
+ result->windows_sdk_bin_path = mc_concat(data_bin.best_name, str_lit("\\x64\\"));
+ sdk_found = true;
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ result->windows_sdk_um_library_path = mc_concat(data_lib.best_name, str_lit("\\um\\x86\\"));
+ result->windows_sdk_ucrt_library_path = mc_concat(data_lib.best_name, str_lit("\\ucrt\\x86\\"));
+ result->windows_sdk_bin_path = mc_concat(data_bin.best_name, str_lit("\\x86\\"));
+ sdk_found = true;
+ }
}
- mc_free(data.best_name);
}
- // Look for a Windows 8 entry.
- String windows8_root = find_windows_kit_root(main_key, str_lit("KitsRoot81"));
-
- if (windows8_root.len > 0) {
- defer (mc_free(windows8_root));
-
- String windows8_lib = mc_concat(windows8_root, str_lit("Lib"));
- defer (mc_free(windows8_lib));
-
- Version_Data_Utf8 data = {0};
- mc_visit_files(windows8_lib, &data, win8_best);
- if (data.best_name.len > 0) {
- result->windows_sdk_version = 8;
- result->windows_sdk_root = mc_concat(data.best_name, str_lit("\\"));
- return;
- }
- mc_free(data.best_name);
+ if (sdk_found) {
+ result->windows_sdk_version = 10;
}
- // If we get here, we failed to find anything.
}
-bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result_Utf8 *result) {
+bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *result) {
// The name of this procedure is kind of cryptic. Its purpose is
// to fight through Microsoft craziness. The things that the fine
// Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER
@@ -555,54 +518,97 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result_Utf8
}
// NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both
-// official and portable installations (like mmozeiko's portable msvc script). This will only use
-// the first paths it finds, and won't overwrite any values that `result` already has.
-bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
+// official and portable installations (like mmozeiko's portable msvc script).
+void find_windows_kit_paths_from_env_vars(Find_Result *result) {
if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
- return false;
+ return;
}
- // We can find windows sdk using the following combination of env vars:
- // (UniversalCRTSdkDir or WindowsSdkDir) and (WindowsSDKLibVersion or WindowsSDKVersion)
- bool sdk_found = false;
+ // We can find windows sdk lib dir using the following combination of env vars:
+ // (WindowsSdkDir or UniversalCRTSdkDir) and (WindowsSDKVersion or WindowsSDKLibVersion)
+ bool sdk_lib_found = false;
+
+ // We can find windows sdk bin dir using the following combination of env vars:
+ // (WindowsSdkVerBinPath) or ((WindowsSdkBinPath or WindowsSdkDir or UniversalCRTSdkDir) and (WindowsSDKVersion || WindowsSDKLibVersion))
+ bool sdk_bin_found = false;
// These appear to be suitable env vars used by Visual Studio
String win_sdk_ver_env = mc_get_env(str_lit("WindowsSDKVersion"));
- String win_sdk_lib_env = mc_get_env(str_lit("WindowsSDKLibVersion"));
+ String win_sdk_lib_ver_env = mc_get_env(str_lit("WindowsSDKLibVersion"));
String win_sdk_dir_env = mc_get_env(str_lit("WindowsSdkDir"));
String crt_sdk_dir_env = mc_get_env(str_lit("UniversalCRTSdkDir"));
+ String win_sdk_bin_path_env = mc_get_env(str_lit("WindowsSdkBinPath"));
+ String win_sdk_ver_bin_path_env = mc_get_env(str_lit("WindowsSdkVerBinPath"));
defer ({
mc_free(win_sdk_ver_env);
- mc_free(win_sdk_lib_env);
+ mc_free(win_sdk_lib_ver_env);
mc_free(win_sdk_dir_env);
mc_free(crt_sdk_dir_env);
+ mc_free(win_sdk_bin_path_env);
+ mc_free(win_sdk_ver_bin_path_env);
});
+ if (win_sdk_ver_bin_path_env.len || ((win_sdk_bin_path_env.len || win_sdk_dir_env.len || crt_sdk_dir_env.len) && (win_sdk_ver_env.len || win_sdk_lib_ver_env.len))) {
+ String bin;
+ defer (mc_free(bin));
+
+ if (win_sdk_ver_bin_path_env.len) {
+ String dir = win_sdk_ver_bin_path_env;
+
+ // Add trailing '\' in case it was missing
+ bin = mc_concat(dir, dir[dir.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+ } else {
+ String dir = win_sdk_bin_path_env.len ? win_sdk_bin_path_env : win_sdk_dir_env.len ? win_sdk_dir_env : crt_sdk_dir_env;
+ String ver = win_sdk_ver_env.len ? win_sdk_ver_env : win_sdk_lib_ver_env;
+
+ // Add trailing '\' in case it was missing
+ dir = mc_concat(dir, dir[dir.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+ ver = mc_concat(ver, ver[ver.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+ defer (mc_free(dir));
+ defer (mc_free(ver));
+
+ // Append "bin" for win_sdk_dir_env and crt_sdk_dir_env
+ String dir_bin = mc_concat(dir, win_sdk_bin_path_env.len ? str_lit("") : str_lit("bin\\"));
+ defer (mc_free(dir_bin));
+
+ bin = mc_concat(dir_bin, ver);
+ }
+
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ result->windows_sdk_bin_path = mc_concat(bin, str_lit("x64\\"));
+ sdk_bin_found = true;
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ result->windows_sdk_bin_path = mc_concat(bin, str_lit("x86\\"));
+ sdk_bin_found = true;
+ }
+ }
+
// NOTE(WalterPlinge): If any combination is found, let's just assume they are correct
- if ((win_sdk_ver_env.len || win_sdk_lib_env.len) && (win_sdk_dir_env.len || crt_sdk_dir_env.len)) {
- //? Maybe we need to handle missing '\' at end of strings, so far it doesn't seem an issue
+ if ((win_sdk_ver_env.len || win_sdk_lib_ver_env.len) && (win_sdk_dir_env.len || crt_sdk_dir_env.len)) {
String dir = win_sdk_dir_env.len ? win_sdk_dir_env : crt_sdk_dir_env;
- String ver = win_sdk_ver_env.len ? win_sdk_ver_env : win_sdk_lib_env;
-
- // These have trailing '\' as we are just composing the path
- String um_dir = build_context.metrics.arch == TargetArch_amd64
- ? str_lit("um\\x64\\")
- : str_lit("um\\x86\\");
- String ucrt_dir = build_context.metrics.arch == TargetArch_amd64
- ? str_lit("ucrt\\x64\\")
- : str_lit("ucrt\\x86\\");
+ String ver = win_sdk_ver_env.len ? win_sdk_ver_env : win_sdk_lib_ver_env;
- result->windows_sdk_root = mc_concat(dir, str_lit("Lib\\"), ver);
- result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir);
- result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir);
+ // Add trailing '\' in case it was missing
+ dir = mc_concat(dir, dir[dir.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+ ver = mc_concat(ver, ver[ver.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+ defer (mc_free(dir));
+ defer (mc_free(ver));
- sdk_found = true;
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ result->windows_sdk_um_library_path = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("um\\x64\\"));
+ result->windows_sdk_ucrt_library_path = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("ucrt\\x64\\"));
+ sdk_lib_found = true;
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ result->windows_sdk_um_library_path = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("um\\x86\\"));
+ result->windows_sdk_ucrt_library_path = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("ucrt\\x86\\"));
+ sdk_lib_found = true;
+ }
}
// If we haven't found it yet, we can loop through LIB for specific folders
//? This may not be robust enough using `um\x64` and `ucrt\x64`
- if (!sdk_found) {
+ if (!sdk_lib_found) {
String lib = mc_get_env(str_lit("LIB"));
defer (mc_free(lib));
@@ -624,38 +630,30 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
continue;
}
hi = c;
- String dir = substring(lib, lo, hi);
defer (lo = hi + 1);
+ // Skip when there are two ;; in a row
+ if (lo == hi) {
+ continue;
+ }
+
+ String dir = substring(lib, lo, hi);
+
// Remove the last slash so we can match with the strings above
String end = dir[dir.len - 1] == '\\'
? substring(dir, 0, dir.len - 1)
: substring(dir, 0, dir.len);
- // Find one and we can make the other
if (string_ends_with(end, um_dir)) {
- result->windows_sdk_um_library_path = mc_concat(end, str_lit("\\"));
- break;
+ result->windows_sdk_um_library_path = mc_concat(end, str_lit("\\"));
} else if (string_ends_with(end, ucrt_dir)) {
result->windows_sdk_ucrt_library_path = mc_concat(end, str_lit("\\"));
- break;
}
- }
-
- // Get the root from the one we found, and make the other
- // NOTE(WalterPlinge): we need to copy the string so that we don't risk a double free
- if (result->windows_sdk_um_library_path.len > 0) {
- String root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len);
- result->windows_sdk_root = copy_string(mc_allocator, root);
- result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir, str_lit("\\"));
- } else if (result->windows_sdk_ucrt_library_path.len > 0) {
- String root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len);
- result->windows_sdk_root = copy_string(mc_allocator, root);
- result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir, str_lit("\\"));
- }
- if (result->windows_sdk_root.len > 0) {
- sdk_found = true;
+ if (result->windows_sdk_um_library_path.len && result->windows_sdk_ucrt_library_path.len) {
+ sdk_lib_found = true;
+ break;
+ }
}
}
}
@@ -663,33 +661,36 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
// NOTE(WalterPlinge): So far this function assumes it will only be called if MSVC was
// installed using mmozeiko's portable msvc script, which uses the windows 10 sdk.
// This may need to be changed later if it ends up causing problems.
- if (sdk_found && result->windows_sdk_version == 0) {
+ if (sdk_bin_found && sdk_lib_found) {
result->windows_sdk_version = 10;
}
+}
- bool vs_found = false;
-
- if (result->vs_exe_path.len > 0 && result->vs_library_path.len > 0) {
- vs_found = true;
+// NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both
+// official and portable installations (like mmozeiko's portable msvc script). This will only use
+// the first paths it finds, and won't overwrite any values that `result` already has.
+void find_visual_studio_paths_from_env_vars(Find_Result *result) {
+ if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
+ return;
}
- // We can find visual studio using VCToolsInstallDir
- if (!vs_found) {
- String vctid = mc_get_env(str_lit("VCToolsInstallDir"));
- defer (mc_free(vctid));
-
- if (vctid.len) {
- String exe = build_context.metrics.arch == TargetArch_amd64
- ? str_lit("bin\\Hostx64\\x64\\")
- : str_lit("bin\\Hostx86\\x86\\");
- String lib = build_context.metrics.arch == TargetArch_amd64
- ? str_lit("lib\\x64\\")
- : str_lit("lib\\x86\\");
+ bool vs_found = false;
- result->vs_exe_path = mc_concat(vctid, exe);
- result->vs_library_path = mc_concat(vctid, lib);
- vs_found = true;
- }
+ // We can find visual studio using VCToolsInstallDir
+ String vctid = mc_get_env(str_lit("VCToolsInstallDir"));
+ defer (mc_free(vctid));
+
+ if (vctid.len) {
+ String exe = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("bin\\Hostx64\\x64\\")
+ : str_lit("bin\\Hostx86\\x86\\");
+ String lib = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("lib\\x64\\")
+ : str_lit("lib\\x86\\");
+
+ result->vs_exe_path = mc_concat(vctid, exe);
+ result->vs_library_path = mc_concat(vctid, lib);
+ vs_found = true;
}
// If we haven't found it yet, we can loop through Path for specific folders
@@ -701,21 +702,32 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
String exe = build_context.metrics.arch == TargetArch_amd64
? str_lit("bin\\Hostx64\\x64")
: str_lit("bin\\Hostx86\\x86");
+ // The environment variable may have an uppercase X even though the folder is lowercase
+ String exe2 = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("bin\\HostX64\\x64")
+ : str_lit("bin\\HostX86\\x86");
String lib = build_context.metrics.arch == TargetArch_amd64
? str_lit("lib\\x64")
: str_lit("lib\\x86");
isize lo = {0};
isize hi = {0};
- for (isize c = 0; c < path.len; c += 1) {
- if (path[c] != ';') {
+ for (isize c = 0; c <= path.len; c += 1) {
+ if (c != path.len && path[c] != ';') {
continue;
}
hi = c;
- String dir = substring(path, lo, hi);
defer (lo = hi + 1);
+ // Skip when there are two ;; in a row
+ if (lo == hi) {
+ continue;
+ }
+
+ String dir = substring(path, lo, hi);
+
+ // Remove the last slash so we can match with the strings above
String end = dir[dir.len - 1] == '\\'
? substring(dir, 0, dir.len - 1)
: substring(dir, 0, dir.len);
@@ -726,7 +738,10 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
defer (mc_free(cl));
defer (mc_free(link));
- if (!string_ends_with(end, exe) || !gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) {
+ if (!string_ends_with(end, exe) && !string_ends_with(end, exe2)) {
+ continue;
+ }
+ if (!gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) {
continue;
}
@@ -735,42 +750,36 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
result->vs_library_path = mc_concat(root, lib, str_lit("\\"));
vs_found = true;
+ break;
}
}
}
-
- return sdk_found && vs_found;
}
-Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8() {
- Find_Result_Utf8 r = {};
- find_windows_kit_root(&r);
+Find_Result find_visual_studio_and_windows_sdk() {
+ Find_Result r = {};
+ find_windows_kit_paths(&r);
+ find_visual_studio_by_fighting_through_microsoft_craziness(&r);
- if (r.windows_sdk_root.len > 0) {
- if (build_context.metrics.arch == TargetArch_amd64) {
- r.windows_sdk_um_library_path = mc_concat(r.windows_sdk_root, str_lit("um\\x64\\"));
- r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x64\\"));
- } else if (build_context.metrics.arch == TargetArch_i386) {
- r.windows_sdk_um_library_path = mc_concat(r.windows_sdk_root, str_lit("um\\x86\\"));
- r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x86\\"));
- }
- }
+ bool sdk_found =
+ r.windows_sdk_bin_path.len &&
+ r.windows_sdk_um_library_path.len &&
+ r.windows_sdk_ucrt_library_path.len ;
- find_visual_studio_by_fighting_through_microsoft_craziness(&r);
+ bool vs_found =
+ r.vs_exe_path.len &&
+ r.vs_library_path.len ;
- bool all_found =
- r.windows_sdk_root.len > 0 &&
- r.windows_sdk_um_library_path.len > 0 &&
- r.windows_sdk_ucrt_library_path.len > 0 &&
- r.vs_exe_path.len > 0 &&
- r.vs_library_path.len > 0;
+ if (!sdk_found) {
+ find_windows_kit_paths_from_env_vars(&r);
+ }
- if (!all_found) {
- find_msvc_install_from_env_vars(&r);
+ if (!vs_found) {
+ find_visual_studio_paths_from_env_vars(&r);
}
#if 0
- printf("windows_sdk_root: %.*s\n", LIT(r.windows_sdk_root));
+ printf("windows_sdk_bin_path: %.*s\n", LIT(r.windows_sdk_bin_path));
printf("windows_sdk_um_library_path: %.*s\n", LIT(r.windows_sdk_um_library_path));
printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(r.windows_sdk_ucrt_library_path));
printf("vs_exe_path: %.*s\n", LIT(r.vs_exe_path));
diff --git a/src/parser.cpp b/src/parser.cpp
index 247255ce8..9a5531289 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -356,6 +356,7 @@ Ast *clone_ast(Ast *node) {
break;
case Ast_PointerType:
n->PointerType.type = clone_ast(n->PointerType.type);
+ n->PointerType.tag = clone_ast(n->PointerType.tag);
break;
case Ast_MultiPointerType:
n->MultiPointerType.type = clone_ast(n->MultiPointerType.type);
@@ -2167,10 +2168,11 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type);
switch (type->kind) {
- case Ast_ArrayType: type->ArrayType.tag = tag; break;
+ case Ast_ArrayType: type->ArrayType.tag = tag; break;
case Ast_DynamicArrayType: type->DynamicArrayType.tag = tag; break;
+ case Ast_PointerType: type->PointerType.tag = tag; break;
default:
- syntax_error(type, "Expected an array type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind]));
+ syntax_error(type, "Expected an array or pointer type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind]));
break;
}
return original_type;
@@ -2548,21 +2550,15 @@ Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "Invalid union tag '#%.*s'", LIT(tag.string));
}
}
- if (no_nil && maybe) {
- syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
- }
+
if (no_nil && shared_nil) {
syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together");
}
- if (shared_nil && maybe) {
- syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together");
- }
-
if (maybe) {
- union_kind = UnionType_maybe;
syntax_error(f->curr_token, "#maybe functionality has now been merged with standard 'union' functionality");
- } else if (no_nil) {
+ }
+ if (no_nil) {
union_kind = UnionType_no_nil;
} else if (shared_nil) {
union_kind = UnionType_shared_nil;
@@ -3550,49 +3546,34 @@ Ast *parse_var_type(AstFile *f, bool allow_ellipsis, bool allow_typeid_token) {
}
-enum FieldPrefixKind : i32 {
- FieldPrefix_Unknown = -1,
- FieldPrefix_Invalid = 0,
-
- FieldPrefix_using, // implies #subtype
- FieldPrefix_const,
- FieldPrefix_no_alias,
- FieldPrefix_c_vararg,
- FieldPrefix_auto_cast,
- FieldPrefix_any_int,
- FieldPrefix_subtype, // does not imply `using` semantics
- FieldPrefix_by_ptr,
-};
-
struct ParseFieldPrefixMapping {
String name;
TokenKind token_kind;
- FieldPrefixKind prefix;
FieldFlag flag;
};
gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
- {str_lit("using"), Token_using, FieldPrefix_using, FieldFlag_using},
- {str_lit("auto_cast"), Token_auto_cast, FieldPrefix_auto_cast, FieldFlag_auto_cast},
- {str_lit("no_alias"), Token_Hash, FieldPrefix_no_alias, FieldFlag_no_alias},
- {str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg},
- {str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const},
- {str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int},
- {str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype},
- {str_lit("by_ptr"), Token_Hash, FieldPrefix_by_ptr, FieldFlag_by_ptr},
+ {str_lit("using"), Token_using, FieldFlag_using},
+ {str_lit("auto_cast"), Token_auto_cast, FieldFlag_auto_cast},
+ {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
+ {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
+ {str_lit("const"), Token_Hash, FieldFlag_const},
+ {str_lit("any_int"), Token_Hash, FieldFlag_any_int},
+ {str_lit("subtype"), Token_Hash, FieldFlag_subtype},
+ {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
};
-FieldPrefixKind is_token_field_prefix(AstFile *f) {
+FieldFlag is_token_field_prefix(AstFile *f) {
switch (f->curr_token.kind) {
case Token_EOF:
- return FieldPrefix_Invalid;
+ return FieldFlag_Invalid;
case Token_using:
- return FieldPrefix_using;
+ return FieldFlag_using;
case Token_auto_cast:
- return FieldPrefix_auto_cast;
+ return FieldFlag_auto_cast;
case Token_Hash:
advance_token(f);
@@ -3602,33 +3583,33 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
auto const &mapping = parse_field_prefix_mappings[i];
if (mapping.token_kind == Token_Hash) {
if (f->curr_token.string == mapping.name) {
- return mapping.prefix;
+ return mapping.flag;
}
}
}
break;
}
- return FieldPrefix_Unknown;
+ return FieldFlag_Unknown;
}
- return FieldPrefix_Invalid;
+ return FieldFlag_Invalid;
}
u32 parse_field_prefixes(AstFile *f) {
i32 counts[gb_count_of(parse_field_prefix_mappings)] = {};
for (;;) {
- FieldPrefixKind kind = is_token_field_prefix(f);
- if (kind == FieldPrefix_Invalid) {
+ FieldFlag flag = is_token_field_prefix(f);
+ if (flag & FieldFlag_Invalid) {
break;
}
- if (kind == FieldPrefix_Unknown) {
+ if (flag & FieldFlag_Unknown) {
syntax_error(f->curr_token, "Unknown prefix kind '#%.*s'", LIT(f->curr_token.string));
advance_token(f);
continue;
}
for (i32 i = 0; i < gb_count_of(parse_field_prefix_mappings); i++) {
- if (parse_field_prefix_mappings[i].prefix == kind) {
+ if (parse_field_prefix_mappings[i].flag == flag) {
counts[i] += 1;
advance_token(f);
break;
@@ -3894,7 +3875,8 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
while (f->curr_token.kind != follow &&
- f->curr_token.kind != Token_EOF) {
+ f->curr_token.kind != Token_EOF &&
+ f->curr_token.kind != Token_Semicolon) {
CommentGroup *docs = f->lead_comment;
u32 set_flags = parse_field_prefixes(f);
Token tag = {};
@@ -3922,7 +3904,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
default_value = parse_expr(f, false);
if (!allow_default_parameters) {
syntax_error(f->curr_token, "Default parameters are only allowed for procedures");
- default_value = nullptr;
+ default_value = nullptr;
}
}
diff --git a/src/parser.hpp b/src/parser.hpp
index 3126e0a02..7433744e6 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -282,7 +282,8 @@ enum StateFlag : u8 {
StateFlag_type_assert = 1<<2,
StateFlag_no_type_assert = 1<<3,
- StateFlag_SelectorCallExpr = 1<<6,
+ StateFlag_SelectorCallExpr = 1<<5,
+ StateFlag_DirectiveWasFalse = 1<<6,
StateFlag_BeenHandled = 1<<7,
};
@@ -308,6 +309,10 @@ enum FieldFlag : u32 {
FieldFlag_Tags = 1<<10,
FieldFlag_Results = 1<<16,
+
+ FieldFlag_Unknown = 1u<<30,
+ FieldFlag_Invalid = 1u<<31,
+
// Parameter List Restrictions
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr,
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
@@ -335,7 +340,6 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
enum UnionTypeKind : u8 {
UnionType_Normal = 0,
- UnionType_maybe = 1, // removed
UnionType_no_nil = 2,
UnionType_shared_nil = 3,
};
@@ -647,7 +651,8 @@ AST_KIND(_TypeBegin, "", bool) \
}) \
AST_KIND(PointerType, "pointer type", struct { \
Token token; \
- Ast *type; \
+ Ast *type; \
+ Ast *tag; \
}) \
AST_KIND(RelativeType, "relative type", struct { \
Ast *tag; \
diff --git a/src/string.cpp b/src/string.cpp
index 44eccd2d2..bc55e370c 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -324,6 +324,16 @@ String concatenate3_strings(gbAllocator a, String const &x, String const &y, Str
data[len] = 0;
return make_string(data, len);
}
+String concatenate4_strings(gbAllocator a, String const &x, String const &y, String const &z, String const &w) {
+ isize len = x.len+y.len+z.len+w.len;
+ u8 *data = gb_alloc_array(a, u8, len+1);
+ gb_memmove(data, x.text, x.len);
+ gb_memmove(data+x.len, y.text, y.len);
+ gb_memmove(data+x.len+y.len, z.text, z.len);
+ gb_memmove(data+x.len+y.len+z.len, w.text, w.len);
+ data[len] = 0;
+ return make_string(data, len);
+}
String string_join_and_quote(gbAllocator a, Array<String> strings) {
if (!strings.count) {
diff --git a/src/types.cpp b/src/types.cpp
index 5f112ce09..b7cb4dd2c 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -278,7 +278,8 @@ struct TypeProc {
Type *generic_row_count; \
Type *generic_column_count; \
i64 stride_in_bytes; \
- })
+ }) \
+ TYPE_KIND(SoaPointer, struct { Type *elem; })
enum TypeKind {
@@ -350,6 +351,7 @@ enum Typeid_Kind : u8 {
Typeid_Relative_Pointer,
Typeid_Relative_Slice,
Typeid_Matrix,
+ Typeid_SoaPointer,
};
// IMPORTANT NOTE(bill): This must match the same as the in core.odin
@@ -644,6 +646,7 @@ gb_global Type *t_type_info_simd_vector = nullptr;
gb_global Type *t_type_info_relative_pointer = nullptr;
gb_global Type *t_type_info_relative_slice = nullptr;
gb_global Type *t_type_info_matrix = nullptr;
+gb_global Type *t_type_info_soa_pointer = nullptr;
gb_global Type *t_type_info_named_ptr = nullptr;
gb_global Type *t_type_info_integer_ptr = nullptr;
@@ -672,6 +675,7 @@ gb_global Type *t_type_info_simd_vector_ptr = nullptr;
gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
gb_global Type *t_type_info_relative_slice_ptr = nullptr;
gb_global Type *t_type_info_matrix_ptr = nullptr;
+gb_global Type *t_type_info_soa_pointer_ptr = nullptr;
gb_global Type *t_allocator = nullptr;
gb_global Type *t_allocator_ptr = nullptr;
@@ -735,6 +739,7 @@ Type * bit_set_to_int(Type *t);
bool are_types_identical(Type *x, Type *y);
bool is_type_pointer(Type *t);
+bool is_type_soa_pointer(Type *t);
bool is_type_proc(Type *t);
bool is_type_slice(Type *t);
bool is_type_integer(Type *t);
@@ -917,6 +922,13 @@ Type *alloc_type_multi_pointer(Type *elem) {
return t;
}
+Type *alloc_type_soa_pointer(Type *elem) {
+ Type *t = alloc_type(Type_SoaPointer);
+ t->SoaPointer.elem = elem;
+ return t;
+}
+
+
Type *alloc_type_array(Type *elem, i64 count, Type *generic_count = nullptr) {
if (generic_count != nullptr) {
Type *t = alloc_type(Type_Array);
@@ -1103,17 +1115,28 @@ Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_count=nullptr)
////////////////////////////////////////////////////////////////
-Type *type_deref(Type *t) {
+Type *type_deref(Type *t, bool allow_multi_pointer=false) {
if (t != nullptr) {
Type *bt = base_type(t);
if (bt == nullptr) {
return nullptr;
}
- if (bt->kind == Type_Pointer) {
+ switch (bt->kind) {
+ case Type_Pointer:
return bt->Pointer.elem;
- }
- if (bt->kind == Type_RelativePointer) {
+ case Type_RelativePointer:
return type_deref(bt->RelativePointer.pointer_type);
+ case Type_SoaPointer:
+ {
+ Type *elem = base_type(bt->SoaPointer.elem);
+ GB_ASSERT(elem->kind == Type_Struct && elem->Struct.soa_kind != StructSoa_None);
+ return elem->Struct.soa_elem;
+ }
+ case Type_MultiPointer:
+ if (allow_multi_pointer) {
+ return bt->MultiPointer.elem;
+ }
+ break;
}
}
return t;
@@ -1327,6 +1350,10 @@ bool is_type_pointer(Type *t) {
}
return t->kind == Type_Pointer;
}
+bool is_type_soa_pointer(Type *t) {
+ t = base_type(t);
+ return t->kind == Type_SoaPointer;
+}
bool is_type_multi_pointer(Type *t) {
t = base_type(t);
return t->kind == Type_MultiPointer;
@@ -1401,7 +1428,7 @@ i64 matrix_align_of(Type *t, struct TypePath *tp) {
}
GB_ASSERT(min_alignment >= elem_align);
- i64 align = gb_min(min_alignment, build_context.max_align);
+ i64 align = gb_min(min_alignment, build_context.max_simd_align);
return align;
}
@@ -1804,7 +1831,7 @@ bool is_type_dereferenceable(Type *t) {
if (is_type_rawptr(t)) {
return false;
}
- return is_type_pointer(t);
+ return is_type_pointer(t) || is_type_soa_pointer(t);
}
@@ -2079,6 +2106,9 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
case Type_Pointer:
return is_type_polymorphic(t->Pointer.elem, or_specialized);
+ case Type_SoaPointer:
+ return is_type_polymorphic(t->SoaPointer.elem, or_specialized);
+
case Type_EnumeratedArray:
if (is_type_polymorphic(t->EnumeratedArray.index, or_specialized)) {
return true;
@@ -2196,6 +2226,7 @@ bool type_has_nil(Type *t) {
case Type_Slice:
case Type_Proc:
case Type_Pointer:
+ case Type_SoaPointer:
case Type_MultiPointer:
case Type_DynamicArray:
case Type_Map:
@@ -2262,6 +2293,8 @@ bool is_type_comparable(Type *t) {
return true;
case Type_Pointer:
return true;
+ case Type_SoaPointer:
+ return true;
case Type_MultiPointer:
return true;
case Type_Enum:
@@ -2335,6 +2368,7 @@ bool is_type_simple_compare(Type *t) {
case Type_Pointer:
case Type_MultiPointer:
+ case Type_SoaPointer:
case Type_Proc:
case Type_BitSet:
return true;
@@ -2369,6 +2403,57 @@ bool is_type_simple_compare(Type *t) {
return false;
}
+bool is_type_load_safe(Type *type) {
+ GB_ASSERT(type != nullptr);
+ type = core_type(core_array_type(type));
+ switch (type->kind) {
+ case Type_Basic:
+ return (type->Basic.flags & (BasicFlag_Boolean|BasicFlag_Numeric|BasicFlag_Rune)) != 0;
+
+ case Type_BitSet:
+ if (type->BitSet.underlying) {
+ return is_type_load_safe(type->BitSet.underlying);
+ }
+ return true;
+
+ case Type_RelativePointer:
+ case Type_RelativeSlice:
+ return true;
+
+ case Type_Pointer:
+ case Type_MultiPointer:
+ case Type_Slice:
+ case Type_DynamicArray:
+ case Type_Proc:
+ case Type_SoaPointer:
+ return false;
+
+ case Type_Enum:
+ case Type_EnumeratedArray:
+ case Type_Array:
+ case Type_SimdVector:
+ case Type_Matrix:
+ GB_PANIC("should never be hit");
+ return false;
+
+ case Type_Struct:
+ for_array(i, type->Struct.fields) {
+ if (!is_type_load_safe(type->Struct.fields[i]->type)) {
+ return false;
+ }
+ }
+ return type_size_of(type) > 0;
+ case Type_Union:
+ for_array(i, type->Union.variants) {
+ if (!is_type_load_safe(type->Union.variants[i])) {
+ return false;
+ }
+ }
+ return type_size_of(type) > 0;
+ }
+ return false;
+}
+
String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
Type *prev_src = src;
// Type *prev_dst = dst;
@@ -2558,6 +2643,12 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
}
break;
+ case Type_SoaPointer:
+ if (y->kind == Type_SoaPointer) {
+ return are_types_identical(x->SoaPointer.elem, y->SoaPointer.elem);
+ }
+ break;
+
case Type_Named:
if (y->kind == Type_Named) {
return x->Named.type_name == y->Named.type_name;
@@ -3465,7 +3556,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_SimdVector: {
// IMPORTANT TODO(bill): Figure out the alignment of vector types
- return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2);
+ return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_simd_align*2);
}
case Type_Matrix:
@@ -3475,12 +3566,14 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
return type_align_of_internal(t->RelativePointer.base_integer, path);
case Type_RelativeSlice:
return type_align_of_internal(t->RelativeSlice.base_integer, path);
+
+ case Type_SoaPointer:
+ return build_context.word_size;
}
- // return gb_clamp(next_pow2(type_size_of(t)), 1, build_context.max_align);
// NOTE(bill): Things that are bigger than build_context.word_size, are actually comprised of smaller types
// TODO(bill): Is this correct for 128-bit types (integers)?
- return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.word_size);
+ return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
}
i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union) {
@@ -3580,6 +3673,9 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
case Type_MultiPointer:
return build_context.word_size;
+ case Type_SoaPointer:
+ return build_context.word_size*2;
+
case Type_Array: {
i64 count, align, size, alignment;
count = t->Array.count;
@@ -4017,6 +4113,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
str = write_type_to_string(str, type->Pointer.elem);
break;
+ case Type_SoaPointer:
+ str = gb_string_appendc(str, "#soa ^");
+ str = write_type_to_string(str, type->SoaPointer.elem);
+ break;
+
case Type_MultiPointer:
str = gb_string_appendc(str, "[^]");
str = write_type_to_string(str, type->Pointer.elem);
diff --git a/vendor/directx/d3d11/d3d11.odin b/vendor/directx/d3d11/d3d11.odin
index 2adb7925a..db1611bc4 100644
--- a/vendor/directx/d3d11/d3d11.odin
+++ b/vendor/directx/d3d11/d3d11.odin
@@ -3625,3 +3625,36 @@ IFunctionLinkingGraph_VTable :: struct {
GetLastError: proc "stdcall" (this: ^IFunctionLinkingGraph, ppErrorBuffer: ^^IBlob) -> HRESULT,
GenerateHlsl: proc "stdcall" (this: ^IFunctionLinkingGraph, uFlags: u32, ppBuffer: ^^IBlob) -> HRESULT,
}
+
+IDebug_UUID_STRING :: "79CF2233-7536-4948-9D36-1E4692DC5760"
+IDebug_UUID := &IID{0x79CF2233, 0x7536, 0x4948, {0x9D, 0x36, 0x1E, 0x46, 0x92, 0xDC, 0x57, 0x60}}
+
+IDebug :: struct #raw_union {
+ #subtype iunknown: IUnknown,
+ using id3d11debug_vtable: ^IDebug_VTable,
+}
+
+RLDO_FLAGS :: enum u32 { // TODO: make bit_set
+ SUMMARY = 0x1,
+ DETAIL = 0x2,
+ IGNORE_INTERNAL = 0x4,
+}
+
+DEBUG_FEATURE :: enum u32 { // TODO: make bit_set
+ FLUSH_PER_RENDER_OP = 0x1,
+ FINISH_PER_RENDER_OP = 0x2,
+ FEATURE_PRESENT_PER_RENDER_OP = 0x4,
+}
+
+IDebug_VTable :: struct {
+ using iunkown_vtable: IUnknown_VTable,
+ SetFeatureMask: proc "stdcall" (this: ^IDebug, mask: DEBUG_FEATURE) -> HRESULT,
+ GetFeatureMask: proc "stdcall" (this: ^IDebug) -> DEBUG_FEATURE,
+ SetPresentPerRenderOpDelay: proc "stdcall" (this: ^IDebug, Milliseconds: u32) -> HRESULT,
+ GetPresentPerRenderOpDelay: proc "stdcall" (this: ^IDebug) -> u32,
+ SetSwapChain: proc "stdcall" (this: ^IDebug, pSwapChain: ^dxgi.ISwapChain) -> HRESULT,
+ GetSwapChain: proc "stdcall" (this: ^IDebug, ppSwapChain: ^^dxgi.ISwapChain) -> HRESULT,
+ ValidateContext: proc "stdcall" (this: ^IDebug, pContext: ^IDeviceContext) -> HRESULT,
+ ReportLiveDeviceObjects: proc "stdcall" (this: ^IDebug, Flags: RLDO_FLAGS) -> HRESULT,
+ ValidateContextForDispatch: proc "stdcall" (this: ^IDebug, pContext: ^IDeviceContext) -> HRESULT,
+}
diff --git a/vendor/directx/dxgi/dxgi.odin b/vendor/directx/dxgi/dxgi.odin
index ae0cfd17a..24b85e9f3 100644
--- a/vendor/directx/dxgi/dxgi.odin
+++ b/vendor/directx/dxgi/dxgi.odin
@@ -38,10 +38,10 @@ IUnknown_VTable :: struct {
@(default_calling_convention="stdcall")
foreign dxgi {
- CreateDXGIFactory :: proc(riid: ^IID, ppFactory: rawptr) -> HRESULT ---
- CreateDXGIFactory1 :: proc(riid: ^IID, ppFactory: rawptr) -> HRESULT ---
- CreateDXGIFactory2 :: proc(Flags: u32, riid: ^IID, ppFactory: rawptr) -> HRESULT ---
- DXGIGetDebugInterface1 :: proc(Flags: u32, riid: ^IID, pDebug: rawptr) -> HRESULT ---
+ CreateDXGIFactory :: proc(riid: ^IID, ppFactory: ^rawptr) -> HRESULT ---
+ CreateDXGIFactory1 :: proc(riid: ^IID, ppFactory: ^rawptr) -> HRESULT ---
+ CreateDXGIFactory2 :: proc(Flags: u32, riid: ^IID, ppFactory: ^rawptr) -> HRESULT ---
+ DXGIGetDebugInterface1 :: proc(Flags: u32, riid: ^IID, pDebug: ^rawptr) -> HRESULT ---
}
STANDARD_MULTISAMPLE_QUALITY_PATTERN :: 0xffffffff
diff --git a/vendor/glfw/bindings/bindings.odin b/vendor/glfw/bindings/bindings.odin
index aa1578153..aea09e31d 100644
--- a/vendor/glfw/bindings/bindings.odin
+++ b/vendor/glfw/bindings/bindings.odin
@@ -13,6 +13,13 @@ when ODIN_OS == .Windows {
} else when ODIN_OS == .Linux {
// TODO: Add the billion-or-so static libs to link to in linux
foreign import glfw "system:glfw"
+} else when ODIN_OS == .Darwin {
+ foreign import glfw {
+ "../lib/darwin/libglfw3.a",
+ "system:Cocoa.framework",
+ "system:IOKit.framework",
+ "system:OpenGL.framework",
+ }
} else {
foreign import glfw "system:glfw"
}
diff --git a/vendor/glfw/constants.odin b/vendor/glfw/constants.odin
index 77cbd7309..245cfef52 100644
--- a/vendor/glfw/constants.odin
+++ b/vendor/glfw/constants.odin
@@ -4,7 +4,7 @@ package glfw
/* Versions */
VERSION_MAJOR :: 3
VERSION_MINOR :: 3
-VERSION_REVISION :: 4
+VERSION_REVISION :: 8
/* Booleans */
TRUE :: true
diff --git a/vendor/glfw/lib/darwin/libglfw3.a b/vendor/glfw/lib/darwin/libglfw3.a
new file mode 100644
index 000000000..77506567f
--- /dev/null
+++ b/vendor/glfw/lib/darwin/libglfw3.a
Binary files differ
diff --git a/vendor/glfw/lib/glfw3.dll b/vendor/glfw/lib/glfw3.dll
index 0359bb4f3..733404cf4 100644
--- a/vendor/glfw/lib/glfw3.dll
+++ b/vendor/glfw/lib/glfw3.dll
Binary files differ
diff --git a/vendor/glfw/lib/glfw3.lib b/vendor/glfw/lib/glfw3.lib
index e59770e65..2a52eb4eb 100644
--- a/vendor/glfw/lib/glfw3.lib
+++ b/vendor/glfw/lib/glfw3.lib
Binary files differ
diff --git a/vendor/glfw/lib/glfw3_mt.lib b/vendor/glfw/lib/glfw3_mt.lib
index 231030e92..30ba4adab 100644
--- a/vendor/glfw/lib/glfw3_mt.lib
+++ b/vendor/glfw/lib/glfw3_mt.lib
Binary files differ
diff --git a/vendor/glfw/lib/glfw3dll.lib b/vendor/glfw/lib/glfw3dll.lib
index cfcf1197b..e1b3f3efc 100644
--- a/vendor/glfw/lib/glfw3dll.lib
+++ b/vendor/glfw/lib/glfw3dll.lib
Binary files differ
diff --git a/vendor/glfw/native.odin b/vendor/glfw/native.odin
index 902b30656..8ed439a94 100644
--- a/vendor/glfw/native.odin
+++ b/vendor/glfw/native.odin
@@ -3,7 +3,7 @@ package glfw
when ODIN_OS == .Windows {
import win32 "core:sys/windows"
- foreign import glfw { "lib/glfw3.lib", "system:user32.lib", "system:gdi32.lib", "system:shell32.lib" }
+ foreign import glfw { "lib/glfw3_mt.lib", "system:user32.lib", "system:gdi32.lib", "system:shell32.lib" }
@(default_calling_convention="c", link_prefix="glfw")
foreign glfw {
diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin
index a694c83a4..1a7f81577 100644
--- a/vendor/raylib/raylib.odin
+++ b/vendor/raylib/raylib.odin
@@ -625,9 +625,13 @@ KeyboardKey :: enum c.int {
// Mouse buttons
MouseButton :: enum c.int {
- LEFT = 0,
- RIGHT = 1,
- MIDDLE = 2,
+ LEFT = 0, // Mouse button left
+ RIGHT = 1, // Mouse button right
+ MIDDLE = 2, // Mouse button middle (pressed wheel)
+ SIDE = 3, // Mouse button side (advanced mouse device)
+ EXTRA = 4, // Mouse button extra (advanced mouse device)
+ FORWARD = 5, // Mouse button fordward (advanced mouse device)
+ BACK = 6, // Mouse button back (advanced mouse device)
}
// Mouse cursor
diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin
index 362ec9315..5763e142a 100644
--- a/vendor/stb/image/stb_image_resize.odin
+++ b/vendor/stb/image/stb_image_resize.odin
@@ -185,4 +185,4 @@ foreign lib {
space: colorspace, alloc_context: rawptr,
s0, t0, s1, t1: f32) -> c.int ---
-} \ No newline at end of file
+}
diff --git a/vendor/stb/lib/darwin/libstb_image.a b/vendor/stb/lib/darwin/libstb_image.a
new file mode 100644
index 000000000..06ce44321
--- /dev/null
+++ b/vendor/stb/lib/darwin/libstb_image.a
Binary files differ
diff --git a/vendor/stb/lib/darwin/stb_image.a b/vendor/stb/lib/darwin/stb_image.a
new file mode 100644
index 000000000..1379d6f9e
--- /dev/null
+++ b/vendor/stb/lib/darwin/stb_image.a
Binary files differ
diff --git a/vendor/stb/lib/darwin/stb_image_resize.a b/vendor/stb/lib/darwin/stb_image_resize.a
new file mode 100644
index 000000000..f39c507a6
--- /dev/null
+++ b/vendor/stb/lib/darwin/stb_image_resize.a
Binary files differ
diff --git a/vendor/stb/lib/darwin/stb_image_write.a b/vendor/stb/lib/darwin/stb_image_write.a
new file mode 100644
index 000000000..bce02b33d
--- /dev/null
+++ b/vendor/stb/lib/darwin/stb_image_write.a
Binary files differ
diff --git a/vendor/stb/lib/darwin/stb_rect_pack.a b/vendor/stb/lib/darwin/stb_rect_pack.a
new file mode 100644
index 000000000..3b55ab802
--- /dev/null
+++ b/vendor/stb/lib/darwin/stb_rect_pack.a
Binary files differ
diff --git a/vendor/stb/lib/darwin/stb_truetype.a b/vendor/stb/lib/darwin/stb_truetype.a
new file mode 100644
index 000000000..c4a895b54
--- /dev/null
+++ b/vendor/stb/lib/darwin/stb_truetype.a
Binary files differ