aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2020-12-06 00:49:48 +0000
committerGitHub <noreply@github.com>2020-12-06 00:49:48 +0000
commitf0683c910231513db9adab83f7c2fca9dd8d2613 (patch)
tree2539634b5b71caf5148d8927c9298ba20bad5246
parent54fbdabc380905a925ab5e922749fa2b1ccb2621 (diff)
parentca4657fd31b9efc7ab52f7e1b6f4145d5ed28fb7 (diff)
Merge branch 'master' into parser-experiments
-rw-r--r--.github/workflows/nightly.yml15
-rw-r--r--Makefile2
-rw-r--r--PROPOSAL-PROCESS.md2
-rw-r--r--README.md2
-rwxr-xr-xbuild-m1.sh31
-rw-r--r--core/bufio/read_writer.odin68
-rw-r--r--core/bufio/reader.odin474
-rw-r--r--core/bufio/writer.odin255
-rw-r--r--core/bytes/buffer.odin335
-rw-r--r--core/bytes/reader.odin177
-rw-r--r--core/bytes/strings.odin1032
-rw-r--r--core/dynlib/lib.odin2
-rw-r--r--core/encoding/json/marshal.odin15
-rw-r--r--core/fmt/fmt.odin586
-rw-r--r--core/intrinsics/intrinsics.odin6
-rw-r--r--core/io/conv.odin200
-rw-r--r--core/io/io.odin504
-rw-r--r--core/io/multi.odin113
-rw-r--r--core/io/util.odin192
-rw-r--r--core/math/math.odin24
-rw-r--r--core/odin/ast/ast.odin15
-rw-r--r--core/odin/parser/parser.odin167
-rw-r--r--core/odin/tokenizer/token.odin25
-rw-r--r--core/odin/tokenizer/tokenizer.odin79
-rw-r--r--core/os/stream.odin69
-rw-r--r--core/path/filepath/path_windows.odin3
-rw-r--r--core/path/match.odin2
-rw-r--r--core/reflect/reflect.odin79
-rw-r--r--core/reflect/types.odin293
-rw-r--r--core/runtime/core.odin1314
-rw-r--r--core/runtime/core_builtin.odin838
-rw-r--r--core/runtime/dynamic_array_internal.odin100
-rw-r--r--core/runtime/dynamic_map_internal.odin394
-rw-r--r--core/runtime/error_checks.odin61
-rw-r--r--core/runtime/internal.odin41
-rw-r--r--core/runtime/internal_linux.odin135
-rw-r--r--core/runtime/internal_windows.odin184
-rw-r--r--core/runtime/print.odin2
-rw-r--r--core/runtime/procs_windows_amd64.odin11
-rw-r--r--core/slice/slice.odin43
-rw-r--r--core/slice/sort.odin8
-rw-r--r--core/strings/builder.odin257
-rw-r--r--core/strings/conversion.odin269
-rw-r--r--core/strings/reader.odin177
-rw-r--r--core/strings/strings.odin382
-rw-r--r--core/sync/channel.odin111
-rw-r--r--core/sys/cpu/cpu.odin35
-rw-r--r--core/sys/cpu/cpu_x86.odin67
-rw-r--r--core/sys/unix/pthread_darwin.odin18
-rw-r--r--core/sys/unix/pthread_freebsd.odin18
-rw-r--r--core/sys/unix/pthread_linux.odin18
-rw-r--r--core/sys/win32/kernel32.odin4
-rw-r--r--core/text/scanner/scanner.odin585
-rw-r--r--core/thread/thread.odin114
-rw-r--r--core/thread/thread_unix.odin5
-rw-r--r--core/thread/thread_windows.odin8
-rw-r--r--core/unicode/letter.odin1038
-rw-r--r--core/unicode/tables.odin1272
-rw-r--r--core/unicode/utf8/utf8.odin41
-rw-r--r--examples/demo/demo.odin6
-rw-r--r--src/array.cpp89
-rw-r--r--src/build_settings.cpp51
-rw-r--r--src/check_decl.cpp13
-rw-r--r--src/check_expr.cpp499
-rw-r--r--src/check_stmt.cpp35
-rw-r--r--src/check_type.cpp280
-rw-r--r--src/checker.cpp267
-rw-r--r--src/checker.hpp32
-rw-r--r--src/checker_builtin_procs.hpp6
-rw-r--r--src/common.cpp141
-rw-r--r--src/docs.cpp356
-rw-r--r--src/entity.cpp7
-rw-r--r--src/exact_value.cpp110
-rw-r--r--src/gb/gb.h105
-rw-r--r--src/ir.cpp520
-rw-r--r--src/ir_print.cpp65
-rw-r--r--src/llvm_abi.cpp959
-rw-r--r--src/llvm_backend.cpp2035
-rw-r--r--src/llvm_backend.hpp33
-rw-r--r--src/main.cpp561
-rw-r--r--src/parser.cpp284
-rw-r--r--src/parser.hpp81
-rw-r--r--src/ptr_set.cpp137
-rw-r--r--src/string.cpp1
-rw-r--r--src/tokenizer.cpp2
-rw-r--r--src/types.cpp292
86 files changed, 14282 insertions, 5002 deletions
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index d43e96b12..1b6d82eaf 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -1,6 +1,7 @@
name: Nightly
on:
+ workflow_dispatch:
schedule:
- cron: 0 20 * * *
@@ -50,7 +51,7 @@ jobs:
- name: (Linux) Download LLVM
run: sudo apt-get install llvm
- name: build odin
- run: make release
+ run: make nightly
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Copy artifacts
@@ -76,7 +77,7 @@ jobs:
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: build odin
- run: make release
+ run: make nightly
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Copy artifacts
@@ -96,10 +97,18 @@ jobs:
needs: [build_windows, build_macos, build_ubuntu]
steps:
- uses: actions/checkout@v1
+ - uses: actions/setup-python@v2
+ with:
+ python-version: '3.x'
- name: Install B2 CLI
shell: bash
- run: sudo pip install --upgrade b2
+ run: |
+ python -m pip install --upgrade pip
+ pip install --upgrade b2
+
+ - name: Display Python version
+ run: python -c "import sys; print(sys.version)"
- name: Download Windows artifacts
uses: actions/download-artifact@v1
diff --git a/Makefile b/Makefile
index 34e56c950..0be242aeb 100644
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,7 @@ release:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
nightly:
- $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 -march=native $(LDFLAGS) -o odin
+ $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
diff --git a/PROPOSAL-PROCESS.md b/PROPOSAL-PROCESS.md
index d82fc02dd..fa31c47d9 100644
--- a/PROPOSAL-PROCESS.md
+++ b/PROPOSAL-PROCESS.md
@@ -30,7 +30,7 @@ The proposal process is the process for reviewing a proposal and reaching a deci
* Accept proposal
* Decline proposal
-After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
+After the proposal is accepted or declined, implementation of the proposal proceeds in the same way as any other contribution to the project.
## Design Documents
diff --git a/README.md b/README.md
index ab92d6e46..23a969271 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ main :: proc() {
#### [Getting Started](https://odin-lang.org/docs/install)
-Instructions for downloading and install the Odin compiler and libraries.
+Instructions for downloading and installing the Odin compiler and libraries.
### Learning Odin
diff --git a/build-m1.sh b/build-m1.sh
new file mode 100755
index 000000000..502a340ad
--- /dev/null
+++ b/build-m1.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+release_mode=$1
+
+warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
+libraries="-pthread -ldl -lm -lstdc++"
+other_args="-DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM"
+compiler="clang"
+
+if [ -z "$release_mode" ]; then release_mode="0"; fi
+
+if [ "$release_mode" -eq "0" ]; then
+ other_args="${other_args} -g"
+fi
+if [ "$release_mode" -eq "1" ]; then
+ other_args="${other_args} -O3 -march=native"
+fi
+
+if [[ "$(uname)" == "Darwin" ]]; then
+
+ # Set compiler to clang on MacOS
+ # MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
+ compiler="clang"
+
+ other_args="${other_args} -liconv"
+elif [[ "$(uname)" == "FreeBSD" ]]; then
+ compiler="clang"
+fi
+
+${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin \
+ && ./odin run examples/demo/demo.odin -llvm-api
diff --git a/core/bufio/read_writer.odin b/core/bufio/read_writer.odin
new file mode 100644
index 000000000..f60e17d2d
--- /dev/null
+++ b/core/bufio/read_writer.odin
@@ -0,0 +1,68 @@
+package bufio
+
+import "core:io"
+
+// Read_Writer stores pointers to a Reader and a Writer
+Read_Writer :: struct {
+ r: ^Reader,
+ w: ^Writer,
+}
+
+
+read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
+ rw.r, rw.w = r, w;
+}
+
+read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
+ s.stream_data = rw;
+ s.stream_vtable = _read_writer_vtable;
+ return;
+}
+
+@(private)
+_read_writer_vtable := &io.Stream_VTable{
+ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Read_Writer)(s.stream_data).r;
+ return reader_read(b, p);
+ },
+ impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
+ b := (^Read_Writer)(s.stream_data).r;
+ return reader_read_byte(b);
+ },
+ impl_unread_byte = proc(s: io.Stream) -> io.Error {
+ b := (^Read_Writer)(s.stream_data).r;
+ return reader_unread_byte(b);
+ },
+ impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
+ b := (^Read_Writer)(s.stream_data).r;
+ return reader_read_rune(b);
+ },
+ impl_unread_rune = proc(s: io.Stream) -> io.Error {
+ b := (^Read_Writer)(s.stream_data).r;
+ return reader_unread_rune(b);
+ },
+ impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+ b := (^Read_Writer)(s.stream_data).r;
+ return reader_write_to(b, w);
+ },
+ impl_flush = proc(s: io.Stream) -> io.Error {
+ b := (^Read_Writer)(s.stream_data).w;
+ return writer_flush(b);
+ },
+ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Read_Writer)(s.stream_data).w;
+ return writer_write(b, p);
+ },
+ impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+ b := (^Read_Writer)(s.stream_data).w;
+ return writer_write_byte(b, c);
+ },
+ impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
+ b := (^Read_Writer)(s.stream_data).w;
+ return writer_write_rune(b, r);
+ },
+ impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
+ b := (^Read_Writer)(s.stream_data).w;
+ return writer_read_from(b, r);
+ },
+};
diff --git a/core/bufio/reader.odin b/core/bufio/reader.odin
new file mode 100644
index 000000000..f35706f9b
--- /dev/null
+++ b/core/bufio/reader.odin
@@ -0,0 +1,474 @@
+package bufio
+
+import "core:io"
+import "core:mem"
+import "core:unicode/utf8"
+import "core:bytes"
+
+// Reader is a buffered wrapper for an io.Reader
+Reader :: struct {
+ buf: []byte,
+ buf_allocator: mem.Allocator,
+
+ rd: io.Reader, // reader
+ r, w: int, // read and write positions for buf
+
+ err: io.Error,
+
+ last_byte: int, // last byte read, invalid is -1
+ last_rune_size: int, // size of last rune read, invalid is -1
+}
+
+
+DEFAULT_BUF_SIZE :: 4096;
+
+@(private)
+MIN_READ_BUFFER_SIZE :: 16;
+@(private)
+MAX_CONSECUTIVE_EMPTY_READS :: 128;
+
+reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
+ size := size;
+ size = max(size, MIN_READ_BUFFER_SIZE);
+ reader_reset(b, rd);
+ b.buf_allocator = allocator;
+ b.buf = make([]byte, size, allocator);
+}
+
+reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
+ reader_reset(b, rd);
+ b.buf_allocator = {};
+ b.buf = buf;
+}
+
+// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
+reader_destroy :: proc(b: ^Reader) {
+ delete(b.buf, b.buf_allocator);
+ b^ = {};
+}
+
+reader_size :: proc(b: ^Reader) -> int {
+ return len(b.buf);
+}
+
+reader_reset :: proc(b: ^Reader, r: io.Reader) {
+ b.rd = r;
+ b.r, b.w = 0, 0;
+ b.err = nil;
+ b.last_byte = -1;
+ b.last_rune_size = -1;
+}
+
+@(private)
+_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
+ if b.r > 0 {
+ copy(b.buf, b.buf[b.r:b.w]);
+ b.w -= b.r;
+ b.r = 0;
+ }
+
+ if b.w >= len(b.buf) {
+ return .Buffer_Full;
+ }
+
+ // read new data, and try a limited number of times
+ for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 {
+ n, err := io.read(b.rd, b.buf[b.w:]);
+ if n < 0 {
+ return .Negative_Read;
+ }
+ b.w += n;
+ if err != nil {
+ b.err = err;
+ return nil;
+ }
+ if n > 0 {
+ return nil;
+ }
+ }
+ b.err = .No_Progress;
+ return nil;
+}
+
+@(private)
+_reader_consume_err :: proc(b: ^Reader) -> io.Error {
+ err := b.err;
+ b.err = nil;
+ return err;
+}
+
+// reader_peek returns the next n bytes without advancing the reader
+// The bytes stop being valid on the next read call
+// If reader_peek returns fewer than n bytes, it also return an error
+// explaining why the read is short
+// The error will be .Buffer_Full if n is larger than the internal buffer size
+reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) {
+ n := n;
+
+ if n < 0 {
+ return nil, .Negative_Count;
+ }
+ b.last_byte = -1;
+ b.last_rune_size = -1;
+
+ for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
+ if fill_err := _reader_read_new_chunk(b); fill_err != nil {
+ return nil, fill_err;
+ }
+ }
+
+ if n > len(b.buf) {
+ return b.buf[b.r : b.w], .Buffer_Full;
+ }
+
+ if available := b.w - b.r; available < n {
+ n = available;
+ err = _reader_consume_err(b);
+ if err == nil {
+ err = .Buffer_Full;
+ }
+ }
+
+ return b.buf[b.r : b.r+n], err;
+}
+
+// reader_buffered returns the number of bytes that can be read from the current buffer
+reader_buffered :: proc(b: ^Reader) -> int {
+ return b.w - b.r;
+}
+
+// reader_discard skips the next n bytes, and returns the number of bytes that were discarded
+reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) {
+ if n < 0 {
+ return 0, .Negative_Count;
+ }
+ if n == 0 {
+ return;
+ }
+
+ remaining := n;
+ for {
+ skip := reader_buffered(b);
+ if skip == 0 {
+ if fill_err := _reader_read_new_chunk(b); fill_err != nil {
+ return 0, fill_err;
+ }
+ skip = reader_buffered(b);
+ }
+ skip = min(skip, remaining);
+ b.r += skip;
+ remaining -= skip;
+ if remaining == 0 {
+ return n, nil;
+ }
+ if b.err != nil {
+ return n - remaining, _reader_consume_err(b);
+ }
+ }
+
+ return;
+}
+
+// reader_read reads data into p
+// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p)
+reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+ n = len(p);
+ if n == 0 {
+ if reader_buffered(b) > 0 {
+ return 0, nil;
+ }
+ return 0, _reader_consume_err(b);
+ }
+ if b.r == b.w {
+ if b.err != nil {
+ return 0, _reader_consume_err(b);
+ }
+
+ if len(p) >= len(b.buf) {
+ n, b.err = io.read(b.rd, p);
+ if n < 0 {
+ return 0, .Negative_Read;
+ }
+
+ if n > 0 {
+ b.last_byte = int(p[n-1]);
+ b.last_rune_size = -1;
+ }
+ return n, _reader_consume_err(b);
+ }
+
+ b.r, b.w = 0, 0;
+ n, b.err = io.read(b.rd, b.buf);
+ if n < 0 {
+ return 0, .Negative_Read;
+ }
+ if n == 0 {
+ return 0, _reader_consume_err(b);
+ }
+ b.w += n;
+ }
+
+ n = copy(p, b.buf[b.r:b.w]);
+ b.r += n;
+ b.last_byte = int(b.buf[b.r-1]);
+ b.last_rune_size = -1;
+ return n, nil;
+}
+
+// reader_read_byte reads and returns a single byte
+// If no byte is available, it return an error
+reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) {
+ b.last_rune_size = -1;
+ for b.r == b.w {
+ if b.err != nil {
+ return 0, _reader_consume_err(b);
+ }
+ if err := _reader_read_new_chunk(b); err != nil {
+ return 0, err;
+ }
+ }
+ c := b.buf[b.r];
+ b.r += 1;
+ b.last_byte = int(c);
+ return c, nil;
+}
+
+// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread
+reader_unread_byte :: proc(b: ^Reader) -> io.Error {
+ if b.last_byte < 0 || b.r == 0 && b.w > 0 {
+ return .Invalid_Unread;
+ }
+ if b.r > 0 {
+ b.r -= 1;
+ } else {
+ // b.r == 0 && b.w == 0
+ b.w = 1;
+ }
+ b.buf[b.r] = byte(b.last_byte);
+ b.last_byte = -1;
+ b.last_rune_size = -1;
+ return nil;
+}
+
+// reader_read_rune reads a single UTF-8 encoded unicode character
+// and returns the rune and its size in bytes
+// If the encoded rune is invalid, it consumes one byte and returns utf8.RUNE_ERROR (U+FFFD) with a size of 1
+reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
+ for b.r+utf8.UTF_MAX > b.w &&
+ !utf8.full_rune(b.buf[b.r:b.w]) &&
+ b.err == nil &&
+ b.w-b.w < len(b.buf) {
+ if err = _reader_read_new_chunk(b); err != nil {
+ return;
+ }
+ }
+
+ b.last_rune_size = -1;
+ if b.r == b.w {
+ err = _reader_consume_err(b);
+ return;
+ }
+ r, size = rune(b.buf[b.r]), 1;
+ if r >= utf8.RUNE_SELF {
+ r, size = utf8.decode_rune(b.buf[b.r : b.w]);
+ }
+ b.r += size;
+ b.last_byte = int(b.buf[b.r-1]);
+ b.last_rune_size = size;
+ return;
+}
+
+// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread
+reader_unread_rune :: proc(b: ^Reader) -> io.Error {
+ if b.last_rune_size < 0 || b.r < b.last_rune_size {
+ return .Invalid_Unread;
+ }
+ b.r -= b.last_rune_size;
+ b.last_byte = -1;
+ b.last_rune_size = -1;
+ return nil;
+}
+
+reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
+ write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
+ n, err := io.write(w, b.buf[b.r:b.w]);
+ if n < 0 {
+ return 0, .Negative_Write;
+ }
+ b.r += n;
+ return i64(n), err;
+ }
+
+ n, err = write_buf(b, w);
+ if err != nil {
+ return;
+ }
+
+ m: i64;
+ if nr, cerr := io.to_writer_to(b.rd); cerr == nil {
+ m, err = io.write_to(nr, w);
+ n += m;
+ return n, err;
+ }
+
+ if nw, cerr := io.to_reader_from(w); cerr == nil {
+ m, err = io.read_from(nw, b.rd);
+ n += m;
+ return n, err;
+ }
+
+ if b.w-b.r < len(b.buf) {
+ if err = _reader_read_new_chunk(b); err != nil {
+ return;
+ }
+ }
+
+ for b.r < b.w {
+ m, err = write_buf(b, w);
+ n += m;
+ if err != nil {
+ return;
+ }
+ if err = _reader_read_new_chunk(b); err != nil {
+ return;
+ }
+ }
+
+ if b.err == .EOF {
+ b.err = nil;
+ }
+
+ err = _reader_consume_err(b);
+ return;
+}
+
+
+
+// reader_to_stream converts a Reader into an io.Stream
+reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
+ s.stream_data = b;
+ s.stream_vtable = _reader_vtable;
+ return;
+}
+
+
+
+@(private)
+_reader_vtable := &io.Stream_VTable{
+ impl_destroy = proc(s: io.Stream) -> io.Error {
+ b := (^Reader)(s.stream_data);
+ reader_destroy(b);
+ return nil;
+ },
+ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Reader)(s.stream_data);
+ return reader_read(b, p);
+ },
+ impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
+ b := (^Reader)(s.stream_data);
+ return reader_read_byte(b);
+ },
+ impl_unread_byte = proc(s: io.Stream) -> io.Error {
+ b := (^Reader)(s.stream_data);
+ return reader_unread_byte(b);
+ },
+ impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
+ b := (^Reader)(s.stream_data);
+ return reader_read_rune(b);
+ },
+ impl_unread_rune = proc(s: io.Stream) -> io.Error {
+ b := (^Reader)(s.stream_data);
+ return reader_unread_rune(b);
+ },
+ impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+ b := (^Reader)(s.stream_data);
+ return reader_write_to(b, w);
+ },
+};
+
+
+
+//
+// Utility procedures
+//
+
+
+// reader_read_slice reads until the first occurrence of delim from the reader
+// It returns a slice pointing at the bytes in the buffer
+// The bytes stop being valid at the next read
+// If reader_read_slice encounters an error before finding a delimiter
+// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim
+// Because the data returned from reader_read_slice will be overwritten on the
+// next IO operation, reader_read_bytes or reader_read_string is usually preferred
+//
+// reader_read_slice returns err != nil if and only if line does not end in delim
+//
+reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
+ s := 0;
+ for {
+ if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 {
+ i += s;
+ line = b.buf[b.r:][:i+1];
+ b.r += i + 1;
+ break;
+ }
+
+ if b.err != nil {
+ line = b.buf[b.r : b.w];
+ b.r = b.w;
+ err = _reader_consume_err(b);
+ break;
+ }
+
+ if reader_buffered(b) >= len(b.buf) {
+ b.r = b.w;
+ line = b.buf;
+ err = .Buffer_Full;
+ break;
+ }
+
+ s = b.w - b.r;
+
+ if err = _reader_read_new_chunk(b); err != nil {
+ break;
+ }
+ }
+
+ if i := len(line)-1; i >= 0 {
+ b.last_byte = int(line[i]);
+ b.last_rune_size = -1;
+ }
+
+ return;
+}
+
+// reader_read_bytes reads until the first occurrence of delim from the Reader
+// It returns an allocated slice containing the data up to and including the delimiter
+reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) {
+ full: [dynamic]byte;
+ full.allocator = allocator;
+
+ frag: []byte;
+ for {
+ e: io.Error;
+ frag, e = reader_read_slice(b, delim);
+ if e == nil {
+ break;
+ }
+ if e != .Buffer_Full {
+ err = e;
+ break;
+ }
+
+ append(&full, ..frag);
+ }
+ append(&full, ..frag);
+ return full[:], err;
+}
+
+// reader_read_string reads until the first occurrence of delim from the Reader
+// It returns an allocated string containing the data up to and including the delimiter
+reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) {
+ buf, err := reader_read_bytes(b, delim, allocator);
+ return string(buf), err;
+}
diff --git a/core/bufio/writer.odin b/core/bufio/writer.odin
new file mode 100644
index 000000000..97f24a224
--- /dev/null
+++ b/core/bufio/writer.odin
@@ -0,0 +1,255 @@
+package bufio
+
+import "core:io"
+import "core:mem"
+import "core:unicode/utf8"
+// import "core:bytes"
+
+// Writer is a buffered wrapper for an io.Writer
+Writer :: struct {
+ buf: []byte,
+ buf_allocator: mem.Allocator,
+
+ wr: io.Writer,
+ n: int,
+
+ err: io.Error,
+
+}
+
+writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
+ size := size;
+ size = max(size, MIN_READ_BUFFER_SIZE);
+ writer_reset(b, wr);
+ b.buf_allocator = allocator;
+ b.buf = make([]byte, size, allocator);
+}
+
+writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
+ writer_reset(b, wr);
+ b.buf_allocator = {};
+ b.buf = buf;
+}
+
+// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
+writer_destroy :: proc(b: ^Writer) {
+ delete(b.buf, b.buf_allocator);
+ b^ = {};
+}
+
+// writer_size returns the size of underlying buffer in bytes
+writer_size :: proc(b: ^Writer) -> int {
+ return len(b.buf);
+}
+
+writer_reset :: proc(b: ^Writer, w: io.Writer) {
+ b.wr = w;
+ b.n = 0;
+ b.err = nil;
+}
+
+
+// writer_flush writes any buffered data into the underlying io.Writer
+writer_flush :: proc(b: ^Writer) -> io.Error {
+ if b.err != nil {
+ return b.err;
+ }
+ if b.n == 0 {
+ return nil;
+ }
+
+ n, err := io.write(b.wr, b.buf[0:b.n]);
+ if n < b.n && err == nil {
+ err = .Short_Write;
+ }
+ if err != nil {
+ if n > 0 && n < b.n {
+ copy(b.buf[:b.n-n], b.buf[n : b.n]);
+ }
+ b.n -= n;
+ b.err = err;
+ return err;
+ }
+ b.n = 0;
+ return nil;
+}
+
+// writer_available returns how many bytes are unused in the buffer
+writer_available :: proc(b: ^Writer) -> int {
+ return len(b.buf) - b.n;
+}
+
+// writer_buffered returns the number of bytes that have been writted into the current buffer
+writer_buffered :: proc(b: ^Writer) -> int {
+ return b.n;
+}
+
+// writer_write writes the contents of p into the buffer
+// It returns the number of bytes written
+// If n < len(p), it will return an error explaining why the write is short
+writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
+ p := p;
+ for len(p) > writer_available(b) && b.err == nil {
+ m: int;
+ if writer_buffered(b) == 0 {
+ m, b.err = io.write(b.wr, p);
+ } else {
+ m = copy(b.buf[b.n:], p);
+ b.n += m;
+ writer_flush(b);
+ }
+ n += m;
+ p = p[m:];
+ }
+ if b.err != nil {
+ return n, b.err;
+ }
+ m := copy(b.buf[b.n:], p);
+ b.n += m;
+ m += n;
+ return m, nil;
+}
+
+// writer_write_byte writes a single byte
+writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error {
+ if b.err != nil {
+ return b.err;
+ }
+ if writer_available(b) <= 0 && writer_flush(b) != nil {
+ return b.err;
+ }
+ b.buf[b.n] = c;
+ b.n += 1;
+ return nil;
+}
+
+// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error
+writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) {
+ if r < utf8.RUNE_SELF {
+ err = writer_write_byte(b, byte(r));
+ size = 0 if err != nil else 1;
+ return;
+ }
+ if b.err != nil {
+ return 0, b.err;
+ }
+
+ buf: [4]u8;
+
+ n := writer_available(b);
+ if n < utf8.UTF_MAX {
+ writer_flush(b);
+ if b.err != nil {
+ return 0, b.err;
+ }
+ n = writer_available(b);
+ if n < utf8.UTF_MAX {
+ // this only happens if the buffer is very small
+ w: int;
+ buf, w = utf8.encode_rune(r);
+ return writer_write(b, buf[:w]);
+ }
+ }
+
+ buf, size = utf8.encode_rune(r);
+ copy(b.buf[b.n:], buf[:size]);
+ b.n += size;
+ return;
+}
+
+// writer_write writes a string into the buffer
+// It returns the number of bytes written
+// If n < len(p), it will return an error explaining why the write is short
+writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
+ return writer_write(b, transmute([]byte)s);
+}
+
+// writer_read_from is to support io.Reader_From types
+// If the underlying writer supports the io,read_from, and b has no buffered data yet,
+// this procedure calls the underlying read_from implementation without buffering
+writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
+ if b.err != nil {
+ return 0, b.err;
+ }
+ if writer_buffered(b) == 0 {
+ if w, cerr := io.to_reader_from(b.wr); cerr != nil {
+ n, err = io.read_from(w, r);
+ b.err = err;
+ return;
+ }
+ }
+
+ for {
+ if writer_available(b) == 0 {
+ if ferr := writer_flush(b); ferr != nil {
+ return n, ferr;
+ }
+ }
+ m: int;
+ nr := 0;
+ for nr < MAX_CONSECUTIVE_EMPTY_READS {
+ m, err = io.read(r, b.buf[b.n:]);
+ if m != 0 || err != nil {
+ break;
+ }
+ nr += 1;
+ }
+ if nr == MAX_CONSECUTIVE_EMPTY_READS {
+ return n, .No_Progress;
+ }
+ b.n += m;
+ n += i64(m);
+ if err != nil {
+ break;
+ }
+ }
+
+ if err == .EOF {
+ if writer_available(b) == 0 {
+ err = writer_flush(b);
+ } else {
+ err = nil;
+ }
+ }
+ return;
+}
+
+
+
+// writer_to_stream converts a Writer into an io.Stream
+writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
+ s.stream_data = b;
+ s.stream_vtable = _writer_vtable;
+ return;
+}
+
+
+
+@(private)
+_writer_vtable := &io.Stream_VTable{
+ impl_destroy = proc(s: io.Stream) -> io.Error {
+ b := (^Writer)(s.stream_data);
+ writer_destroy(b);
+ return nil;
+ },
+ impl_flush = proc(s: io.Stream) -> io.Error {
+ b := (^Writer)(s.stream_data);
+ return writer_flush(b);
+ },
+ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Writer)(s.stream_data);
+ return writer_write(b, p);
+ },
+ impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+ b := (^Writer)(s.stream_data);
+ return writer_write_byte(b, c);
+ },
+ impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
+ b := (^Writer)(s.stream_data);
+ return writer_write_rune(b, r);
+ },
+ impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
+ b := (^Writer)(s.stream_data);
+ return writer_read_from(b, r);
+ },
+};
diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin
new file mode 100644
index 000000000..61fde9605
--- /dev/null
+++ b/core/bytes/buffer.odin
@@ -0,0 +1,335 @@
+package bytes
+
+import "core:io"
+import "core:unicode/utf8"
+
+MIN_READ :: 512;
+
+@(private)
+SMALL_BUFFER_SIZE :: 64;
+
+Buffer :: struct {
+ buf: [dynamic]byte,
+ off: int,
+ last_read: Read_Op,
+}
+
+@(private)
+Read_Op :: enum i8 {
+ Read = -1,
+ Invalid = 0,
+ Read_Rune1 = 1,
+ Read_Rune2 = 2,
+ Read_Rune3 = 3,
+ Read_Rune4 = 4,
+}
+
+
+buffer_init :: proc(b: ^Buffer, buf: []byte) {
+ resize(&b.buf, len(buf));
+ copy(b.buf[:], buf);
+}
+
+buffer_init_string :: proc(b: ^Buffer, s: string) {
+ resize(&b.buf, len(s));
+ copy(b.buf[:], s);
+}
+
+
+buffer_destroy :: proc(b: ^Buffer) {
+ delete(b.buf);
+ buffer_reset(b);
+}
+
+buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
+ return b.buf[b.off:];
+}
+
+buffer_to_string :: proc(b: ^Buffer) -> string {
+ if b == nil {
+ return "<nil>";
+ }
+ return string(b.buf[b.off:]);
+}
+
+buffer_is_empty :: proc(b: ^Buffer) -> bool {
+ return len(b.buf) <= b.off;
+}
+
+buffer_length :: proc(b: ^Buffer) -> int {
+ return len(b.buf) - b.off;
+}
+
+buffer_capacity :: proc(b: ^Buffer) -> int {
+ return cap(b.buf);
+}
+
+buffer_reset :: proc(b: ^Buffer) {
+ clear(&b.buf);
+ b.off = 0;
+ b.last_read = .Invalid;
+}
+
+
+buffer_truncate :: proc(b: ^Buffer, n: int) {
+ if n == 0 {
+ buffer_reset(b);
+ return;
+ }
+ b.last_read = .Invalid;
+ if n < 0 || n > buffer_length(b) {
+ panic("bytes.truncate: truncation out of range");
+ }
+ resize(&b.buf, b.off+n);
+}
+
+@(private)
+_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
+ if l := len(b.buf); n <= cap(b.buf)-l {
+ resize(&b.buf, l+n);
+ return l, true;
+ }
+ return 0, false;
+}
+
+@(private)
+_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
+ m := buffer_length(b);
+ if m == 0 && b.off != 0 {
+ buffer_reset(b);
+ }
+ if i, ok := _buffer_try_grow(b, n); ok {
+ return i;
+ }
+ if b.buf == nil && n <= SMALL_BUFFER_SIZE {
+ b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
+ return 0;
+ }
+
+ c := cap(b.buf);
+ if n <= c/2 - m {
+ copy(b.buf[:], b.buf[b.off:]);
+ } else if c > max(int) - c - n {
+ panic("bytes.Buffer: too large");
+ } else {
+ resize(&b.buf, 2*c + n);
+ copy(b.buf[:], b.buf[b.off:]);
+ }
+ b.off = 0;
+ resize(&b.buf, m+n);
+ return m;
+}
+
+buffer_grow :: proc(b: ^Buffer, n: int) {
+ if n < 0 {
+ panic("bytes.buffer_grow: negative count");
+ }
+ m := _buffer_grow(b, n);
+ resize(&b.buf, m);
+}
+
+
+buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, len(p));
+ if !ok {
+ m = _buffer_grow(b, len(p));
+ }
+ return copy(b.buf[m:], p), nil;
+}
+
+buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, len(s));
+ if !ok {
+ m = _buffer_grow(b, len(s));
+ }
+ return copy(b.buf[m:], s), nil;
+}
+
+buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, 1);
+ if !ok {
+ m = _buffer_grow(b, 1);
+ }
+ b.buf[m] = c;
+ return nil;
+}
+
+buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
+ if r < utf8.RUNE_SELF {
+ buffer_write_byte(b, byte(r));
+ return 1, nil;
+ }
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
+ if !ok {
+ m = _buffer_grow(b, utf8.UTF_MAX);
+ }
+ res: [4]byte;
+ res, n = utf8.encode_rune(r);
+ copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
+ resize(&b.buf, m+n);
+ return;
+}
+
+buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
+ n := n;
+ b.last_read = .Invalid;
+ m := buffer_length(b);
+ if n > m {
+ n = m;
+ }
+ data := b.buf[b.off : b.off + n];
+ b.off += n;
+ if n > 0 {
+ b.last_read = .Read;
+ }
+ return data;
+}
+
+buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
+ b.last_read = .Invalid;
+ if buffer_is_empty(b) {
+ buffer_reset(b);
+ if len(p) == 0 {
+ return 0, nil;
+ }
+ return 0, .EOF;
+ }
+ n = copy(p, b.buf[b.off:]);
+ b.off += n;
+ if n > 0 {
+ b.last_read = .Read;
+ }
+ return;
+}
+
+buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
+ if buffer_is_empty(b) {
+ buffer_reset(b);
+ return 0, .EOF;
+ }
+ c := b.buf[b.off];
+ b.off += 1;
+ b.last_read = .Read;
+ return c, nil;
+}
+
+buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
+ if buffer_is_empty(b) {
+ buffer_reset(b);
+ return 0, 0, .EOF;
+ }
+ c := b.buf[b.off];
+ if c < utf8.RUNE_SELF {
+ b.off += 1;
+ b.last_read = .Read_Rune1;
+ return rune(c), 1, nil;
+ }
+ r, size = utf8.decode_rune(b.buf[b.off:]);
+ b.off += size;
+ b.last_read = Read_Op(i8(size));
+ return;
+}
+
+buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
+ if b.last_read == .Invalid {
+ return .Invalid_Unread;
+ }
+ b.last_read = .Invalid;
+ if b.off > 0 {
+ b.off -= 1;
+ }
+ return nil;
+}
+
+buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
+ if b.last_read <= .Invalid {
+ return .Invalid_Unread;
+ }
+ if b.off >= int(b.last_read) {
+ b.off -= int(i8(b.last_read));
+ }
+ b.last_read = .Invalid;
+ return nil;
+}
+
+
+buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
+ i := index_byte(b.buf[b.off:], delim);
+ end := b.off + i + 1;
+ if i < 0 {
+ end = len(b.buf);
+ err = .EOF;
+ }
+ line = b.buf[b.off:end];
+ b.off = end;
+ b.last_read = .Read;
+ return;
+}
+
+buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
+ slice: []byte;
+ slice, err = buffer_read_bytes(b, delim);
+ return string(slice), err;
+}
+
+
+
+buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
+ s.stream_data = b;
+ s.stream_vtable = _buffer_vtable;
+ return;
+}
+
+@(private)
+_buffer_vtable := &io.Stream_VTable{
+ impl_size = proc(s: io.Stream) -> i64 {
+ b := (^Buffer)(s.stream_data);
+ return i64(buffer_capacity(b));
+ },
+ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_read(b, p);
+ },
+ impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_read_byte(b);
+ },
+ impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_read_rune(b);
+ },
+ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_write(b, p);
+ },
+ impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ return buffer_write_byte(b, c);
+ },
+ impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_write_rune(b, r);
+ },
+ impl_unread_byte = proc(s: io.Stream) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ return buffer_unread_byte(b);
+ },
+ impl_unread_rune = proc(s: io.Stream) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ return buffer_unread_rune(b);
+ },
+ impl_destroy = proc(s: io.Stream) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ buffer_destroy(b);
+ return nil;
+ },
+
+ // TODO(bill): write_to and read_from
+ // impl_write_to = nil,
+ // impl_read_from = nil,
+};
+
diff --git a/core/bytes/reader.odin b/core/bytes/reader.odin
new file mode 100644
index 000000000..2032616d0
--- /dev/null
+++ b/core/bytes/reader.odin
@@ -0,0 +1,177 @@
+package bytes
+
+import "core:io"
+import "core:unicode/utf8"
+
+Reader :: struct {
+ s: []byte, // read-only buffer
+ i: i64, // current reading index
+ prev_rune: int, // previous reading index of rune or < 0
+}
+
+reader_init :: proc(r: ^Reader, s: []byte) {
+ r.s = s;
+ r.i = 0;
+ r.prev_rune = -1;
+}
+
+reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
+ s.stream_data = r;
+ s.stream_vtable = _reader_vtable;
+ return;
+}
+
+reader_length :: proc(r: ^Reader) -> int {
+ if r.i >= i64(len(r.s)) {
+ return 0;
+ }
+ return int(i64(len(r.s)) - r.i);
+}
+
+reader_size :: proc(r: ^Reader) -> i64 {
+ return i64(len(r.s));
+}
+
+reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+ if r.i >= i64(len(r.s)) {
+ return 0, .EOF;
+ }
+ r.prev_rune = -1;
+ n = copy(p, r.s[r.i:]);
+ r.i += i64(n);
+ return;
+}
+reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
+ if off < 0 {
+ return 0, .Invalid_Offset;
+ }
+ if off >= i64(len(r.s)) {
+ return 0, .EOF;
+ }
+ n = copy(p, r.s[off:]);
+ if n < len(p) {
+ err = .EOF;
+ }
+ return;
+}
+reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
+ r.prev_rune = -1;
+ if r.i >= i64(len(r.s)) {
+ return 0, .EOF;
+ }
+ b := r.s[r.i];
+ r.i += 1;
+ return b, nil;
+}
+reader_unread_byte :: proc(r: ^Reader) -> io.Error {
+ if r.i <= 0 {
+ return .Invalid_Unread;
+ }
+ r.prev_rune = -1;
+ r.i -= 1;
+ return nil;
+}
+reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
+ if r.i >= i64(len(r.s)) {
+ r.prev_rune = -1;
+ return 0, 0, .EOF;
+ }
+ r.prev_rune = int(r.i);
+ if c := r.s[r.i]; c < utf8.RUNE_SELF {
+ r.i += 1;
+ return rune(c), 1, nil;
+ }
+ ch, size = utf8.decode_rune(r.s[r.i:]);
+ r.i += i64(size);
+ return;
+}
+reader_unread_rune :: proc(r: ^Reader) -> io.Error {
+ if r.i <= 0 {
+ return .Invalid_Unread;
+ }
+ if r.prev_rune < 0 {
+ return .Invalid_Unread;
+ }
+ r.i = i64(r.prev_rune);
+ r.prev_rune = -1;
+ return nil;
+}
+reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+ r.prev_rune = -1;
+ abs: i64;
+ switch whence {
+ case .Start:
+ abs = offset;
+ case .Current:
+ abs = r.i + offset;
+ case .End:
+ abs = i64(len(r.s)) + offset;
+ case:
+ return 0, .Invalid_Whence;
+ }
+
+ if abs < 0 {
+ return 0, .Invalid_Offset;
+ }
+ r.i = abs;
+ return abs, nil;
+}
+reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
+ r.prev_rune = -1;
+ if r.i >= i64(len(r.s)) {
+ return 0, nil;
+ }
+ s := r.s[r.i:];
+ m: int;
+ m, err = io.write(w, s);
+ if m > len(s) {
+ panic("bytes.Reader.write_to: invalid io.write_string count");
+ }
+ r.i += i64(m);
+ n = i64(m);
+ if m != len(s) && err == nil {
+ err = .Short_Write;
+ }
+ return;
+}
+
+
+@(private)
+_reader_vtable := &io.Stream_VTable{
+ impl_size = proc(s: io.Stream) -> i64 {
+ r := (^Reader)(s.stream_data);
+ return reader_size(r);
+ },
+ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read(r, p);
+ },
+ impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read_at(r, p, off);
+ },
+ impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read_byte(r);
+ },
+ impl_unread_byte = proc(s: io.Stream) -> io.Error {
+ r := (^Reader)(s.stream_data);
+ return reader_unread_byte(r);
+ },
+ impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read_rune(r);
+ },
+ impl_unread_rune = proc(s: io.Stream) -> io.Error {
+ r := (^Reader)(s.stream_data);
+ return reader_unread_rune(r);
+ },
+ impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_seek(r, offset, whence);
+ },
+ impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_write_to(r, w);
+ },
+};
diff --git a/core/bytes/strings.odin b/core/bytes/strings.odin
new file mode 100644
index 000000000..61ae4631a
--- /dev/null
+++ b/core/bytes/strings.odin
@@ -0,0 +1,1032 @@
+package bytes
+
+import "core:mem"
+import "core:unicode"
+import "core:unicode/utf8"
+
+clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
+ c := make([]byte, len(s)+1, allocator, loc);
+ copy(c, s);
+ c[len(s)] = 0;
+ return c[:len(s)];
+}
+
+ptr_from_slice :: proc(str: []byte) -> ^byte {
+ d := transmute(mem.Raw_String)str;
+ return d.data;
+}
+
+// Compares two strings, returning a value representing which one comes first lexiographically.
+// -1 for `a`; 1 for `b`, or 0 if they are equal.
+compare :: proc(lhs, rhs: []byte) -> int {
+ return mem.compare(lhs, rhs);
+}
+
+contains_rune :: proc(s: []byte, r: rune) -> int {
+ for c, offset in string(s) {
+ if c == r {
+ return offset;
+ }
+ }
+ return -1;
+}
+
+contains :: proc(s, substr: []byte) -> bool {
+ return index(s, substr) >= 0;
+}
+
+contains_any :: proc(s, chars: []byte) -> bool {
+ return index_any(s, chars) >= 0;
+}
+
+
+rune_count :: proc(s: []byte) -> int {
+ return utf8.rune_count(s);
+}
+
+
+equal :: proc(a, b: []byte) -> bool {
+ return string(a) == string(b);
+}
+
+equal_fold :: proc(u, v: []byte) -> bool {
+ s, t := string(u), string(v);
+ loop: for s != "" && t != "" {
+ sr, tr: rune;
+ if s[0] < utf8.RUNE_SELF {
+ sr, s = rune(s[0]), s[1:];
+ } else {
+ r, size := utf8.decode_rune_in_string(s);
+ sr, s = r, s[size:];
+ }
+ if t[0] < utf8.RUNE_SELF {
+ tr, t = rune(t[0]), t[1:];
+ } else {
+ r, size := utf8.decode_rune_in_string(t);
+ tr, t = r, t[size:];
+ }
+
+ if tr == sr { // easy case
+ continue loop;
+ }
+
+ if tr < sr {
+ tr, sr = sr, tr;
+ }
+
+ if tr < utf8.RUNE_SELF {
+ switch sr {
+ case 'A'..'Z':
+ if tr == (sr+'a')-'A' {
+ continue loop;
+ }
+ }
+ return false;
+ }
+
+ // TODO(bill): Unicode folding
+
+ return false;
+ }
+
+ return s == t;
+}
+
+has_prefix :: proc(s, prefix: []byte) -> bool {
+ return len(s) >= len(prefix) && string(s[0:len(prefix)]) == string(prefix);
+}
+
+has_suffix :: proc(s, suffix: []byte) -> bool {
+ return len(s) >= len(suffix) && string(s[len(s)-len(suffix):]) == string(suffix);
+}
+
+
+join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte {
+ if len(a) == 0 {
+ return nil;
+ }
+
+ n := len(sep) * (len(a) - 1);
+ for s in a {
+ n += len(s);
+ }
+
+ b := make([]byte, n, allocator);
+ i := copy(b, a[0]);
+ for s in a[1:] {
+ i += copy(b[i:], sep);
+ i += copy(b[i:], s);
+ }
+ return b;
+}
+
+concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
+ if len(a) == 0 {
+ return nil;
+ }
+
+ n := 0;
+ for s in a {
+ n += len(s);
+ }
+ b := make([]byte, n, allocator);
+ i := 0;
+ for s in a {
+ i += copy(b[i:], s);
+ }
+ return b;
+}
+
+@private
+_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
+ s, n := s, n;
+
+ if n == 0 {
+ return nil;
+ }
+
+ if sep == nil {
+ l := utf8.rune_count(s);
+ if n < 0 || n > l {
+ n = l;
+ }
+
+ res := make([dynamic][]byte, n, allocator);
+ for i := 0; i < n-1; i += 1 {
+ _, w := utf8.decode_rune(s);
+ res[i] = s[:w];
+ s = s[w:];
+ }
+ if n > 0 {
+ res[n-1] = s;
+ }
+ return res[:];
+ }
+
+ if n < 0 {
+ n = count(s, sep) + 1;
+ }
+
+ res := make([dynamic][]byte, n, allocator);
+
+ n -= 1;
+
+ i := 0;
+ for ; i < n; i += 1 {
+ m := index(s, sep);
+ if m < 0 {
+ break;
+ }
+ res[i] = s[:m+sep_save];
+ s = s[m+len(sep):];
+ }
+ res[i] = s;
+
+ return res[:i+1];
+}
+
+split :: inline proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
+ return _split(s, sep, 0, -1, allocator);
+}
+
+split_n :: inline proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
+ return _split(s, sep, 0, n, allocator);
+}
+
+split_after :: inline proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
+ return _split(s, sep, len(sep), -1, allocator);
+}
+
+split_after_n :: inline proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
+ return _split(s, sep, len(sep), n, allocator);
+}
+
+
+
+
+index_byte :: proc(s: []byte, c: byte) -> int {
+ for i := 0; i < len(s); i += 1 {
+ if s[i] == c {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Returns -1 if c is not present
+last_index_byte :: proc(s: []byte, c: byte) -> int {
+ for i := len(s)-1; i >= 0; i -= 1 {
+ if s[i] == c {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+@private PRIME_RABIN_KARP :: 16777619;
+
+index :: proc(s, substr: []byte) -> int {
+ hash_str_rabin_karp :: proc(s: []byte) -> (hash: u32 = 0, pow: u32 = 1) {
+ for i := 0; i < len(s); i += 1 {
+ hash = hash*PRIME_RABIN_KARP + u32(s[i]);
+ }
+ sq := u32(PRIME_RABIN_KARP);
+ for i := len(s); i > 0; i >>= 1 {
+ if (i & 1) != 0 {
+ pow *= sq;
+ }
+ sq *= sq;
+ }
+ return;
+ }
+
+ n := len(substr);
+ switch {
+ case n == 0:
+ return 0;
+ case n == 1:
+ return index_byte(s, substr[0]);
+ case n == len(s):
+ if string(s) == string(substr) {
+ return 0;
+ }
+ return -1;
+ case n > len(s):
+ return -1;
+ }
+
+ hash, pow := hash_str_rabin_karp(substr);
+ h: u32;
+ for i := 0; i < n; i += 1 {
+ h = h*PRIME_RABIN_KARP + u32(s[i]);
+ }
+ if h == hash && string(s[:n]) == string(substr) {
+ return 0;
+ }
+ for i := n; i < len(s); /**/ {
+ h *= PRIME_RABIN_KARP;
+ h += u32(s[i]);
+ h -= pow * u32(s[i-n]);
+ i += 1;
+ if h == hash && string(s[i-n:i]) == string(substr) {
+ return i - n;
+ }
+ }
+ return -1;
+}
+
+last_index :: proc(s, substr: []byte) -> int {
+ hash_str_rabin_karp_reverse :: proc(s: []byte) -> (hash: u32 = 0, pow: u32 = 1) {
+ for i := len(s) - 1; i >= 0; i -= 1 {
+ hash = hash*PRIME_RABIN_KARP + u32(s[i]);
+ }
+ sq := u32(PRIME_RABIN_KARP);
+ for i := len(s); i > 0; i >>= 1 {
+ if (i & 1) != 0 {
+ pow *= sq;
+ }
+ sq *= sq;
+ }
+ return;
+ }
+
+ n := len(substr);
+ switch {
+ case n == 0:
+ return len(s);
+ case n == 1:
+ return last_index_byte(s, substr[0]);
+ case n == len(s):
+ return 0 if string(substr) == string(s) else -1;
+ case n > len(s):
+ return -1;
+ }
+
+ hash, pow := hash_str_rabin_karp_reverse(substr);
+ last := len(s) - n;
+ h: u32;
+ for i := len(s)-1; i >= last; i -= 1 {
+ h = h*PRIME_RABIN_KARP + u32(s[i]);
+ }
+ if h == hash && string(s[last:]) == string(substr) {
+ return last;
+ }
+
+ for i := last-1; i >= 0; i -= 1 {
+ h *= PRIME_RABIN_KARP;
+ h += u32(s[i]);
+ h -= pow * u32(s[i+n]);
+ if h == hash && string(s[i:i+n]) == string(substr) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+index_any :: proc(s, chars: []byte) -> int {
+ if chars == nil {
+ return -1;
+ }
+
+ // TODO(bill): Optimize
+ for r, i in s {
+ for c in chars {
+ if r == c {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+last_index_any :: proc(s, chars: []byte) -> int {
+ if chars == nil {
+ return -1;
+ }
+
+ for i := len(s); i > 0; {
+ r, w := utf8.decode_last_rune(s[:i]);
+ i -= w;
+ for c in string(chars) {
+ if r == c {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+count :: proc(s, substr: []byte) -> int {
+ if len(substr) == 0 { // special case
+ return rune_count(s) + 1;
+ }
+ if len(substr) == 1 {
+ c := substr[0];
+ switch len(s) {
+ case 0:
+ return 0;
+ case 1:
+ return int(s[0] == c);
+ }
+ n := 0;
+ for i := 0; i < len(s); i += 1 {
+ if s[i] == c {
+ n += 1;
+ }
+ }
+ return n;
+ }
+
+ // TODO(bill): Use a non-brute for approach
+ n := 0;
+ str := s;
+ for {
+ i := index(str, substr);
+ if i == -1 {
+ return n;
+ }
+ n += 1;
+ str = str[i+len(substr):];
+ }
+ return n;
+}
+
+
+repeat :: proc(s: []byte, count: int, allocator := context.allocator) -> []byte {
+ if count < 0 {
+ panic("bytes: negative repeat count");
+ } else if count > 0 && (len(s)*count)/count != len(s) {
+ panic("bytes: repeat count will cause an overflow");
+ }
+
+ b := make([]byte, len(s)*count, allocator);
+ i := copy(b, s);
+ for i < len(b) { // 2^N trick to reduce the need to copy
+ copy(b[i:], b[:i]);
+ i *= 2;
+ }
+ return b;
+}
+
+replace_all :: proc(s, old, new: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
+ return replace(s, old, new, -1, allocator);
+}
+
+// if n < 0, no limit on the number of replacements
+replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
+ if string(old) == string(new) || n == 0 {
+ was_allocation = false;
+ output = s;
+ return;
+ }
+ byte_count := n;
+ if m := count(s, old); m == 0 {
+ was_allocation = false;
+ output = s;
+ return;
+ } else if n < 0 || m < n {
+ byte_count = m;
+ }
+
+
+ t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator);
+ was_allocation = true;
+
+ w := 0;
+ start := 0;
+ for i := 0; i < byte_count; i += 1 {
+ j := start;
+ if len(old) == 0 {
+ if i > 0 {
+ _, width := utf8.decode_rune(s[start:]);
+ j += width;
+ }
+ } else {
+ j += index(s[start:], old);
+ }
+ w += copy(t[w:], s[start:j]);
+ w += copy(t[w:], new);
+ start = j + len(old);
+ }
+ w += copy(t[w:], s[start:]);
+ output = t[0:w];
+ return;
+}
+
+@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+
+
+is_ascii_space :: proc(r: rune) -> bool {
+ if r < utf8.RUNE_SELF {
+ return _ascii_space[u8(r)] != 0;
+ }
+ return false;
+}
+
+is_space :: proc(r: rune) -> bool {
+ if r < 0x2000 {
+ switch r {
+ case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xa0, 0x1680:
+ return true;
+ }
+ } else {
+ if r <= 0x200a {
+ return true;
+ }
+ switch r {
+ case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
+ return true;
+ }
+ }
+ return false;
+}
+
+is_null :: proc(r: rune) -> bool {
+ return r == 0x0000;
+}
+
+index_proc :: proc(s: []byte, p: proc(rune) -> bool, truth := true) -> int {
+ for r, i in string(s) {
+ if p(r) == truth {
+ return i;
+ }
+ }
+ return -1;
+}
+
+index_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
+ for r, i in string(s) {
+ if p(state, r) == truth {
+ return i;
+ }
+ }
+ return -1;
+}
+
+last_index_proc :: proc(s: []byte, p: proc(rune) -> bool, truth := true) -> int {
+ // TODO(bill): Probably use Rabin-Karp Search
+ for i := len(s); i > 0; {
+ r, size := utf8.decode_last_rune(s[:i]);
+ i -= size;
+ if p(r) == truth {
+ return i;
+ }
+ }
+ return -1;
+}
+
+last_index_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
+ // TODO(bill): Probably use Rabin-Karp Search
+ for i := len(s); i > 0; {
+ r, size := utf8.decode_last_rune(s[:i]);
+ i -= size;
+ if p(state, r) == truth {
+ return i;
+ }
+ }
+ return -1;
+}
+
+trim_left_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
+ i := index_proc(s, p, false);
+ if i == -1 {
+ return nil;
+ }
+ return s[i:];
+}
+
+
+index_rune :: proc(s: []byte, r: rune) -> int {
+ switch {
+ case 0 <= r && r < utf8.RUNE_SELF:
+ return index_byte(s, byte(r));
+
+ case r == utf8.RUNE_ERROR:
+ for c, i in string(s) {
+ if c == utf8.RUNE_ERROR {
+ return i;
+ }
+ }
+ return -1;
+
+ case !utf8.valid_rune(r):
+ return -1;
+ }
+
+ b, w := utf8.encode_rune(r);
+ return index(s, b[:w]);
+}
+
+
+trim_left_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr) -> []byte {
+ i := index_proc_with_state(s, p, state, false);
+ if i == -1 {
+ return nil;
+ }
+ return s[i:];
+}
+
+trim_right_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
+ i := last_index_proc(s, p, false);
+ if i >= 0 && s[i] >= utf8.RUNE_SELF {
+ _, w := utf8.decode_rune(s[i:]);
+ i += w;
+ } else {
+ i += 1;
+ }
+ return s[0:i];
+}
+
+trim_right_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr) -> []byte {
+ i := last_index_proc_with_state(s, p, state, false);
+ if i >= 0 && s[i] >= utf8.RUNE_SELF {
+ _, w := utf8.decode_rune(s[i:]);
+ i += w;
+ } else {
+ i += 1;
+ }
+ return s[0:i];
+}
+
+
+is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
+ if state == nil {
+ return false;
+ }
+ cutset := (^string)(state)^;
+ for c in cutset {
+ if r == c {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+trim_left :: proc(s: []byte, cutset: []byte) -> []byte {
+ if s == nil || cutset == nil {
+ return s;
+ }
+ state := cutset;
+ return trim_left_proc_with_state(s, is_in_cutset, &state);
+}
+
+trim_right :: proc(s: []byte, cutset: []byte) -> []byte {
+ if s == nil || cutset == nil {
+ return s;
+ }
+ state := cutset;
+ return trim_right_proc_with_state(s, is_in_cutset, &state);
+}
+
+trim :: proc(s: []byte, cutset: []byte) -> []byte {
+ return trim_right(trim_left(s, cutset), cutset);
+}
+
+trim_left_space :: proc(s: []byte) -> []byte {
+ return trim_left_proc(s, is_space);
+}
+
+trim_right_space :: proc(s: []byte) -> []byte {
+ return trim_right_proc(s, is_space);
+}
+
+trim_space :: proc(s: []byte) -> []byte {
+ return trim_right_space(trim_left_space(s));
+}
+
+
+trim_left_null :: proc(s: []byte) -> []byte {
+ return trim_left_proc(s, is_null);
+}
+
+trim_right_null :: proc(s: []byte) -> []byte {
+ return trim_right_proc(s, is_null);
+}
+
+trim_null :: proc(s: []byte) -> []byte {
+ return trim_right_null(trim_left_null(s));
+}
+
+trim_prefix :: proc(s, prefix: []byte) -> []byte {
+ if has_prefix(s, prefix) {
+ return s[len(prefix):];
+ }
+ return s;
+}
+
+trim_suffix :: proc(s, suffix: []byte) -> []byte {
+ if has_suffix(s, suffix) {
+ return s[:len(s)-len(suffix)];
+ }
+ return s;
+}
+
+split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator := context.allocator) -> [][]byte #no_bounds_check {
+ if s == nil || len(substrs) <= 0 {
+ return nil;
+ }
+
+ sublen := len(substrs[0]);
+
+ for substr in substrs[1:] {
+ sublen = min(sublen, len(substr));
+ }
+
+ shared := len(s) - sublen;
+
+ if shared <= 0 {
+ return nil;
+ }
+
+ // number, index, last
+ n, i, l := 0, 0, 0;
+
+ // count results
+ first_pass: for i <= shared {
+ for substr in substrs {
+ if string(s[i:i+sublen]) == string(substr) {
+ if !skip_empty || i - l > 0 {
+ n += 1;
+ }
+
+ i += sublen;
+ l = i;
+
+ continue first_pass;
+ }
+ }
+
+ _, skip := utf8.decode_rune(s[i:]);
+ i += skip;
+ }
+
+ if !skip_empty || len(s) - l > 0 {
+ n += 1;
+ }
+
+ if n < 1 {
+ // no results
+ return nil;
+ }
+
+ buf := make([][]byte, n, allocator);
+
+ n, i, l = 0, 0, 0;
+
+ // slice results
+ second_pass: for i <= shared {
+ for substr in substrs {
+ if string(s[i:i+sublen]) == string(substr) {
+ if !skip_empty || i - l > 0 {
+ buf[n] = s[l:i];
+ n += 1;
+ }
+
+ i += sublen;
+ l = i;
+
+ continue second_pass;
+ }
+ }
+
+ _, skip := utf8.decode_rune(s[i:]);
+ i += skip;
+ }
+
+ if !skip_empty || len(s) - l > 0 {
+ buf[n] = s[l:];
+ }
+
+ return buf;
+}
+
+/*
+// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
+// Adjacent invalid bytes are only replaced once
+scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
+ str := s;
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
+
+ has_error := false;
+ cursor := 0;
+ origin := str;
+
+ for len(str) > 0 {
+ r, w := utf8.decode_rune(str);
+
+ if r == utf8.RUNE_ERROR {
+ if !has_error {
+ has_error = true;
+ write(&b, origin[:cursor]);
+ }
+ } else if has_error {
+ has_error = false;
+ write(&b, replacement);
+
+ origin = origin[cursor:];
+ cursor = 0;
+ }
+
+ cursor += w;
+ str = str[w:];
+ }
+
+ return to_string(b);
+}
+*/
+
+
+reverse :: proc(s: []byte, allocator := context.allocator) -> []byte {
+ str := s;
+ n := len(str);
+ buf := make([]byte, n);
+ i := n;
+
+ for len(str) > 0 {
+ _, w := utf8.decode_rune(str);
+ i -= w;
+ copy(buf[i:], str[:w]);
+ str = str[w:];
+ }
+ return buf;
+}
+
+/*
+expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string {
+ if tab_size <= 0 {
+ panic("tab size must be positive");
+ }
+
+
+ if s == nil {
+ return nil;
+ }
+
+ b: Builder;
+ init_builder(&b, allocator);
+ writer := to_writer(&b);
+ str := s;
+ column: int;
+
+ for len(str) > 0 {
+ r, w := utf8.decode_rune_in_string(str);
+
+ if r == '\t' {
+ expand := tab_size - column%tab_size;
+
+ for i := 0; i < expand; i += 1 {
+ io.write_byte(writer, ' ');
+ }
+
+ column += expand;
+ } else {
+ if r == '\n' {
+ column = 0;
+ } else {
+ column += w;
+ }
+
+ io.write_rune(writer, r);
+ }
+
+ str = str[w:];
+ }
+
+ return to_string(b);
+}
+*/
+
+partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) {
+ i := index(str, sep);
+ if i == -1 {
+ head = str;
+ return;
+ }
+
+ head = str[:i];
+ match = str[i:i+len(sep)];
+ tail = str[i+len(sep):];
+ return;
+}
+
+/*
+center_justify :: centre_justify; // NOTE(bill): Because Americans exist
+
+// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length
+centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+ n := rune_count(str);
+ if n >= length || pad == nil {
+ return clone(str, allocator);
+ }
+
+ remains := length-1;
+ pad_len := rune_count(pad);
+
+ b: Builder;
+ init_builder(&b, allocator);
+ grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+ w := to_writer(&b);
+
+ write_pad_string(w, pad, pad_len, remains/2);
+ io.write_string(w, str);
+ write_pad_string(w, pad, pad_len, (remains+1)/2);
+
+ return to_string(b);
+}
+
+// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length
+left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+ n := rune_count(str);
+ if n >= length || pad == nil {
+ return clone(str, allocator);
+ }
+
+ remains := length-1;
+ pad_len := rune_count(pad);
+
+ b: Builder;
+ init_builder(&b, allocator);
+ grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+ w := to_writer(&b);
+
+ io.write_string(w, str);
+ write_pad_string(w, pad, pad_len, remains);
+
+ return to_string(b);
+}
+
+// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length
+right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+ n := rune_count(str);
+ if n >= length || pad == nil {
+ return clone(str, allocator);
+ }
+
+ remains := length-1;
+ pad_len := rune_count(pad);
+
+ b: Builder;
+ init_builder(&b, allocator);
+ grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+ w := to_writer(&b);
+
+ write_pad_string(w, pad, pad_len, remains);
+ io.write_string(w, str);
+
+ return to_string(b);
+}
+
+
+
+
+@private
+write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
+ repeats := remains / pad_len;
+
+ for i := 0; i < repeats; i += 1 {
+ io.write_string(w, pad);
+ }
+
+ n := remains % pad_len;
+ p := pad;
+
+ for i := 0; i < n; i += 1 {
+ r, width := utf8.decode_rune_in_string(p);
+ io.write_rune(w, r);
+ p = p[width:];
+ }
+}
+*/
+
+
+// fields splits the byte slice s around each instance of one or more consecutive white space character, defined by unicode.is_space
+// returning a slice of subslices of s or an empty slice if s only contains white space
+fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds_check {
+ n := 0;
+ was_space := 1;
+ set_bits := u8(0);
+
+ // check to see
+ for i in 0..<len(s) {
+ r := s[i];
+ set_bits |= r;
+ is_space := int(_ascii_space[r]);
+ n += was_space & ~is_space;
+ was_space = is_space;
+ }
+
+ if set_bits >= utf8.RUNE_SELF {
+ return fields_proc(s, unicode.is_space, allocator);
+ }
+
+ if n == 0 {
+ return nil;
+ }
+
+ a := make([][]byte, n, allocator);
+ na := 0;
+ field_start := 0;
+ i := 0;
+ for i < len(s) && _ascii_space[s[i]] != 0 {
+ i += 1;
+ }
+ field_start = i;
+ for i < len(s) {
+ if _ascii_space[s[i]] == 0 {
+ i += 1;
+ continue;
+ }
+ a[na] = s[field_start : i];
+ na += 1;
+ i += 1;
+ for i < len(s) && _ascii_space[s[i]] != 0 {
+ i += 1;
+ }
+ field_start = i;
+ }
+ if field_start < len(s) {
+ a[na] = s[field_start:];
+ }
+ return a;
+}
+
+
+// fields_proc splits the byte slice s at each run of unicode code points `ch` satisfying f(ch)
+// returns a slice of subslices of s
+// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned
+//
+// fields_proc makes no guarantee about the order in which it calls f(ch)
+// it assumes that `f` always returns the same value for a given ch
+fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.allocator) -> [][]byte #no_bounds_check {
+ subslices := make([dynamic][]byte, 0, 32, allocator);
+
+ start, end := -1, -1;
+ for r, offset in string(s) {
+ end = offset;
+ if f(r) {
+ if start >= 0 {
+ append(&subslices, s[start : end]);
+ // -1 could be used, but just speed it up through bitwise not
+ // gotta love 2's complement
+ start = ~start;
+ }
+ } else {
+ if start < 0 {
+ start = end;
+ }
+ }
+ }
+
+ if start >= 0 {
+ append(&subslices, s[start : end]);
+ }
+
+ return subslices[:];
+}
diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin
index 66742b835..141154f02 100644
--- a/core/dynlib/lib.odin
+++ b/core/dynlib/lib.odin
@@ -1,3 +1,3 @@
package dynlib
-Library :: opaque rawptr;
+Library :: #opaque rawptr;
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index 92b18c9e3..7bdc3abc3 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -14,7 +14,8 @@ Marshal_Error :: enum {
}
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
- b := strings.make_builder(allocator);
+ b: strings.Builder;
+ strings.init_builder(&b, allocator);
err := marshal_arg(&b, v);
@@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case b32: val = bool(b);
case b64: val = bool(b);
}
- write_string(b, val ? "true" : "false");
+ write_string_builder(b, val ? "true" : "false");
case Type_Info_Any:
return .Unsupported_Type;
@@ -208,14 +209,12 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
if i > 0 { write_string(b, ", "); }
data := uintptr(entries.data) + uintptr(i*entry_size);
- header := cast(^Map_Entry_Header)data;
-
- marshal_arg(b, any{rawptr(&header.key.key.val), info.key.id});
+ key := rawptr(data + entry_type.offsets[2]);
+ value := rawptr(data + entry_type.offsets[3]);
+ marshal_arg(b, any{key, info.key.id});
write_string(b, ": ");
-
- value := data + entry_type.offsets[2];
- marshal_arg(b, any{rawptr(value), info.value.id});
+ marshal_arg(b, any{value, info.value.id});
}
}
write_byte(b, '}');
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 8a0d475d8..84cf4f160 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -3,6 +3,7 @@ package fmt
import "core:math/bits"
import "core:mem"
import "core:os"
+import "core:io"
import "core:reflect"
import "core:runtime"
import "core:strconv"
@@ -29,7 +30,7 @@ Info :: struct {
reordered: bool,
good_arg_index: bool,
- buf: ^strings.Builder,
+ writer: io.Writer,
arg: any, // Temporary
record_level: int,
}
@@ -62,54 +63,26 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
}
-Flush_Data :: struct {
- handle: os.Handle,
- bytes_written: int,
-}
-
-fmt_file_builder :: proc(data: []byte, fd: ^Flush_Data) -> strings.Builder {
- b := strings.builder_from_slice(data);
- b.flush_data = rawptr(fd);
- b.flush_proc = proc(b: ^strings.Builder) -> (do_reset: bool) {
- fd := (^Flush_Data)(b.flush_data);
- res := strings.to_string(b^);
- n, _ := os.write_string(fd.handle, res);
- fd.bytes_written += max(n, 0);
- return true;
- };
-
- return b;
-}
-
fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
- data: [DEFAULT_BUFFER_SIZE]byte;
- flush_data := Flush_Data{handle=fd};
- buf := fmt_file_builder(data[:], &flush_data);
- _ = sbprint(buf=&buf, args=args, sep=sep);
- return flush_data.bytes_written;
+ w, _ := io.to_writer(os.stream_from_handle(fd));
+ return wprint(w=w, args=args, sep=sep);
}
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
- data: [DEFAULT_BUFFER_SIZE]byte;
- flush_data := Flush_Data{handle=fd};
- buf := fmt_file_builder(data[:], &flush_data);
- _ = sbprintln(buf=&buf, args=args, sep=sep);
- return flush_data.bytes_written;
+ w, _ := io.to_writer(os.stream_from_handle(fd));
+ return wprintln(w=w, args=args, sep=sep);
}
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
- data: [DEFAULT_BUFFER_SIZE]byte;
- flush_data := Flush_Data{handle=fd};
- buf := fmt_file_builder(data[:], &flush_data);
- _ = sbprintf(&buf, fmt, ..args);
- return flush_data.bytes_written;
+ w, _ := io.to_writer(os.stream_from_handle(fd));
+ return wprintf(w, fmt, ..args);
}
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> int {
- data: [DEFAULT_BUFFER_SIZE]byte;
- flush_data := Flush_Data{handle=fd};
- buf := fmt_file_builder(data[:], &flush_data);
- reflect.write_type(&buf, info);
- strings.flush_builder(&buf);
- return flush_data.bytes_written;
+ w, _ := io.to_writer(os.stream_from_handle(fd));
+ return wprint_type(w, info);
+}
+fprint_typeid :: proc(fd: os.Handle, id: typeid) -> int {
+ w, _ := io.to_writer(os.stream_from_handle(fd));
+ return wprint_typeid(w, id);
}
// print* procedures return the number of bytes written
@@ -130,17 +103,20 @@ eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fm
// aprint* procedures return a string that was allocated with the current context
// They must be freed accordingly
aprint :: proc(args: ..any, sep := " ") -> string {
- str := strings.make_builder();
+ str: strings.Builder;
+ strings.init_builder(&str);
sbprint(buf=&str, args=args, sep=sep);
return strings.to_string(str);
}
aprintln :: proc(args: ..any, sep := " ") -> string {
- str := strings.make_builder();
+ str: strings.Builder;
+ strings.init_builder(&str);
sbprintln(buf=&str, args=args, sep=sep);
return strings.to_string(str);
}
aprintf :: proc(fmt: string, args: ..any) -> string {
- str := strings.make_builder();
+ str: strings.Builder;
+ strings.init_builder(&str);
sbprintf(&str, fmt, ..args);
return strings.to_string(str);
}
@@ -148,17 +124,20 @@ aprintf :: proc(fmt: string, args: ..any) -> string {
// tprint* procedures return a string that was allocated with the current context's temporary allocator
tprint :: proc(args: ..any, sep := " ") -> string {
- str := strings.make_builder(context.temp_allocator);
+ str: strings.Builder;
+ strings.init_builder(&str, context.temp_allocator);
sbprint(buf=&str, args=args, sep=sep);
return strings.to_string(str);
}
tprintln :: proc(args: ..any, sep := " ") -> string {
- str := strings.make_builder(context.temp_allocator);
+ str: strings.Builder;
+ strings.init_builder(&str, context.temp_allocator);
sbprintln(buf=&str, args=args, sep=sep);
return strings.to_string(str);
}
tprintf :: proc(fmt: string, args: ..any) -> string {
- str := strings.make_builder(context.temp_allocator);
+ str: strings.Builder;
+ strings.init_builder(&str, context.temp_allocator);
sbprintf(&str, fmt, ..args);
return strings.to_string(str);
}
@@ -205,15 +184,31 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
+ wprint(w=strings.to_writer(buf), args=args, sep=sep);
+ return strings.to_string(buf^);
+}
+
+sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
+ wprintln(w=strings.to_writer(buf), args=args, sep=sep);
+ return strings.to_string(buf^);
+}
+
+sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
+ wprintf(w=strings.to_writer(buf), fmt=fmt, args=args);
+ return strings.to_string(buf^);
+}
+
+
+wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info;
- fi.buf = buf;
+ fi.writer = w;
// NOTE(bill): Old approach
// prev_string := false;
// for arg, i in args {
// is_string := arg != nil && reflect.is_string(type_info_of(arg.id));
// if i > 0 && !is_string && !prev_string {
- // strings.write_byte(buf, ' ');
+ // io.write_byte(writer, ' ');
// }
// fmt_value(&fi, args[i], 'v');
// prev_string = is_string;
@@ -221,49 +216,56 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
// NOTE(bill, 2020-06-19): I have found that the previous approach was not what people were expecting
// and were expecting `*print` to be the same `*println` except for the added newline
// so I am going to keep the same behaviour as `*println` for `*print`
+
+
+ size0 := io.size(auto_cast w);
+
for _, i in args {
if i > 0 {
- strings.write_string(buf, sep);
+ io.write_string(fi.writer, sep);
}
fmt_value(&fi, args[i], 'v');
}
- strings.flush_builder(buf);
- return strings.to_string(buf^);
+ io.flush(auto_cast w);
+ return int(io.size(auto_cast w) - size0);
}
-sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
+wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info;
- fi.buf = buf;
+ fi.writer = w;
+
+ size0 := io.size(auto_cast w);
for _, i in args {
if i > 0 {
- strings.write_string(buf, sep);
+ io.write_string(fi.writer, sep);
}
fmt_value(&fi, args[i], 'v');
}
- strings.write_byte(buf, '\n');
- strings.flush_builder(buf);
- return strings.to_string(buf^);
+ io.write_byte(fi.writer, '\n');
+ io.flush(auto_cast w);
+ return int(io.size(auto_cast w) - size0);
}
-sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
+wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi: Info;
arg_index: int = 0;
end := len(fmt);
was_prev_index := false;
+ size0 := io.size(auto_cast w);
loop: for i := 0; i < end; /**/ {
- fi = Info{buf = b, good_arg_index = true, reordered = fi.reordered};
+ fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered};
prev_i := i;
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
i += 1;
}
if i > prev_i {
- strings.write_string(b, fmt[prev_i:i]);
+ io.write_string(fi.writer, fmt[prev_i:i]);
}
if i >= end {
break loop;
@@ -278,13 +280,13 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
// Skip extra one
i += 1;
}
- strings.write_byte(b, char);
+ io.write_byte(fi.writer, char);
continue loop;
} else if char == '{' {
if i < end && fmt[i] == char {
// Skip extra one
i += 1;
- strings.write_byte(b, char);
+ io.write_byte(fi.writer, char);
continue loop;
}
}
@@ -315,7 +317,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
i += 1;
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index);
if !fi.width_set {
- strings.write_string(b, "%!(BAD WIDTH)");
+ io.write_string(w, "%!(BAD WIDTH)");
}
if fi.width < 0 {
@@ -346,7 +348,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
fi.prec_set = false;
}
if !fi.prec_set {
- strings.write_string(fi.buf, "%!(BAD PRECISION)");
+ io.write_string(fi.writer, "%!(BAD PRECISION)");
}
was_prev_index = false;
} else {
@@ -359,7 +361,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
}
if i >= end {
- strings.write_string(b, "%!(NO VERB)");
+ io.write_string(fi.writer, "%!(NO VERB)");
break loop;
}
@@ -368,11 +370,11 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
switch {
case verb == '%':
- strings.write_byte(b, '%');
+ io.write_byte(fi.writer, '%');
case !fi.good_arg_index:
- strings.write_string(b, "%!(BAD ARGUMENT NUMBER)");
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)");
case arg_index >= len(args):
- strings.write_string(b, "%!(MISSING ARGUMENT)");
+ io.write_string(fi.writer, "%!(MISSING ARGUMENT)");
case:
fmt_arg(&fi, args[arg_index], verb);
arg_index += 1;
@@ -388,14 +390,14 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
arg_index = new_arg_index;
i = new_i;
} else {
- strings.write_string(b, "%!(BAD ARGUMENT NUMBER ");
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ");
// Skip over the bad argument
start_index := i;
for i < end && fmt[i] != '}' && fmt[i] != ':' {
i += 1;
}
fmt_arg(&fi, fmt[start_index:i], 'v');
- strings.write_string(b, ")");
+ io.write_string(fi.writer, ")");
}
}
@@ -428,7 +430,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
i += 1;
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index);
if !fi.width_set {
- strings.write_string(b, "%!(BAD WIDTH)");
+ io.write_string(fi.writer, "%!(BAD WIDTH)");
}
if fi.width < 0 {
@@ -459,7 +461,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
fi.prec_set = false;
}
if !fi.prec_set {
- strings.write_string(fi.buf, "%!(BAD PRECISION)");
+ io.write_string(fi.writer, "%!(BAD PRECISION)");
}
was_prev_index = false;
} else {
@@ -473,7 +475,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
if i >= end {
- strings.write_string(b, "%!(NO VERB)");
+ io.write_string(fi.writer, "%!(NO VERB)");
break loop;
}
@@ -483,7 +485,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
}
if i >= end {
- strings.write_string(b, "%!(MISSING CLOSE BRACE)");
+ io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)");
break loop;
}
@@ -492,11 +494,11 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
switch {
case brace != '}':
- strings.write_string(b, "%!(MISSING CLOSE BRACE)");
+ io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)");
case !fi.good_arg_index:
- strings.write_string(b, "%!(BAD ARGUMENT NUMBER)");
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)");
case arg_index >= len(args):
- strings.write_string(b, "%!(MISSING ARGUMENT)");
+ io.write_string(fi.writer, "%!(MISSING ARGUMENT)");
case:
fmt_arg(&fi, args[arg_index], verb);
arg_index += 1;
@@ -505,27 +507,35 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string {
}
if !fi.reordered && arg_index < len(args) {
- strings.write_string(b, "%!(EXTRA ");
+ io.write_string(fi.writer, "%!(EXTRA ");
for arg, index in args[arg_index:] {
if index > 0 {
- strings.write_string(b, ", ");
+ io.write_string(fi.writer, ", ");
}
if arg == nil {
- strings.write_string(b, "<nil>");
+ io.write_string(fi.writer, "<nil>");
} else {
fmt_arg(&fi, args[index], 'v');
}
}
- strings.write_string(b, ")");
+ io.write_string(fi.writer, ")");
}
- strings.flush_builder(b);
- return strings.to_string(b^);
+ io.flush(auto_cast w);
+ return int(io.size(auto_cast w) - size0);
}
-
-
+wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> int {
+ n := reflect.write_type(w, info);
+ io.flush(auto_cast w);
+ return n;
+}
+wprint_typeid :: proc(w: io.Writer, id: typeid) -> int {
+ n := reflect.write_type(w, type_info_of(id));
+ io.flush(auto_cast w);
+ return n;
+}
@@ -599,23 +609,23 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
- strings.write_string(buf, "%!");
- strings.write_rune(buf, verb);
- strings.write_byte(buf, '(');
+ io.write_string(writer, "%!");
+ io.write_rune(writer, verb);
+ io.write_byte(writer, '(');
if arg.id != nil {
- reflect.write_typeid(buf, arg.id);
- strings.write_byte(buf, '=');
+ reflect.write_typeid(writer, arg.id);
+ io.write_byte(writer, '=');
fmt_value(fi, arg, 'v');
} else {
- strings.write_string(buf, "<nil>");
+ io.write_string(writer, "<nil>");
}
- strings.write_byte(buf, ')');
+ io.write_byte(writer, ')');
}
fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
switch verb {
case 't', 'v':
- strings.write_string(buf, b ? "true" : "false");
+ io.write_string(writer, b ? "true" : "false");
case:
fmt_bad_verb(fi, verb);
}
@@ -633,7 +643,7 @@ fmt_write_padding :: proc(fi: ^Info, width: int) {
}
for i := 0; i < width; i += 1 {
- strings.write_byte(fi.buf, pad_byte);
+ io.write_byte(fi.writer, pad_byte);
}
}
@@ -692,8 +702,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
case 16: c = 'x';
}
if c != 0 {
- strings.write_byte(fi.buf, '0');
- strings.write_byte(fi.buf, c);
+ io.write_byte(fi.writer, '0');
+ io.write_byte(fi.writer, c);
}
}
@@ -758,8 +768,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
case 16: c = 'x';
}
if c != 0 {
- strings.write_byte(fi.buf, '0');
- strings.write_byte(fi.buf, c);
+ io.write_byte(fi.writer, '0');
+ io.write_byte(fi.writer, c);
}
}
@@ -775,7 +785,9 @@ __DIGITS_UPPER := "0123456789ABCDEFX";
fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
switch verb {
case 'c', 'r', 'v':
- strings.write_rune(fi.buf, r);
+ io.write_rune(fi.writer, r);
+ case 'q':
+ strings.write_quoted_rune(fi.writer, r);
case:
fmt_int(fi, u64(r), false, 32, verb);
}
@@ -797,7 +809,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb);
} else {
- strings.write_string(fi.buf, "U+");
+ io.write_string(fi.writer, "U+");
_fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER);
}
@@ -822,7 +834,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb);
} else {
- strings.write_string(fi.buf, "U+");
+ io.write_string(fi.writer, "U+");
_fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER);
}
@@ -833,18 +845,18 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru
_pad :: proc(fi: ^Info, s: string) {
if !fi.width_set {
- strings.write_string(fi.buf, s);
+ io.write_string(fi.writer, s);
return;
}
width := fi.width - utf8.rune_count_in_string(s);
if fi.minus { // right pad
- strings.write_string(fi.buf, s);
+ io.write_string(fi.writer, s);
fmt_write_padding(fi, width);
} else { // left pad
fmt_write_padding(fi, width);
- strings.write_string(fi.buf, s);
+ io.write_string(fi.writer, s);
}
}
@@ -870,15 +882,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- strings.write_string(fi.buf, string(b));
+ io.write_string(fi.writer, string(b));
return;
}
if fi.plus || b[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(b) {
- strings.write_byte(fi.buf, b[0]);
+ io.write_byte(fi.writer, b[0]);
fmt_write_padding(fi, fi.width - len(b));
- strings.write_string(fi.buf, string(b[1:]));
+ io.write_string(fi.writer, string(b[1:]));
} else {
_pad(fi, string(b));
}
@@ -906,15 +918,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- strings.write_string(fi.buf, string(b));
+ io.write_string(fi.writer, string(b));
return;
}
if fi.plus || str[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(b) {
- strings.write_byte(fi.buf, b[0]);
+ io.write_byte(fi.writer, b[0]);
fmt_write_padding(fi, fi.width - len(b));
- strings.write_string(fi.buf, string(b[1:]));
+ io.write_string(fi.writer, string(b[1:]));
} else {
_pad(fi, string(b));
}
@@ -937,7 +949,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
case: panic("Unhandled float size");
}
- strings.write_string(fi.buf, "0h");
+ io.write_string(fi.writer, "0h");
_fmt_int(fi, u, 16, false, bit_size, __DIGITS_LOWER if verb == 'h' else __DIGITS_UPPER);
@@ -950,15 +962,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
switch verb {
case 's', 'v':
- strings.write_string(fi.buf, s);
+ io.write_string(fi.writer, s);
if fi.width_set && len(s) < fi.width {
for _ in 0..<fi.width - len(s) {
- strings.write_byte(fi.buf, ' ');
+ io.write_byte(fi.writer, ' ');
}
}
case 'q': // quoted string
- strings.write_quoted_string(fi.buf, s, '"');
+ strings.write_quoted_string(fi.writer, s, '"');
case 'x', 'X':
space := fi.space;
@@ -967,7 +979,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
for i in 0..<len(s) {
if i > 0 && space {
- strings.write_byte(fi.buf, ' ');
+ io.write_byte(fi.writer, ' ');
}
char_set := __DIGITS_UPPER;
if verb == 'x' {
@@ -989,7 +1001,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
switch verb {
case 'p', 'v':
if !fi.hash || verb == 'v' {
- strings.write_string(fi.buf, "0x");
+ io.write_string(fi.writer, "0x");
}
_fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
@@ -1050,7 +1062,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
if v.id == nil || v.data == nil {
- strings.write_string(fi.buf, "<nil>");
+ io.write_string(fi.writer, "<nil>");
return;
}
@@ -1067,7 +1079,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
if !ok {
str = "!%(BAD ENUM VALUE)";
}
- strings.write_string(fi.buf, str);
+ io.write_string(fi.writer, str);
}
}
}
@@ -1099,6 +1111,17 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T
return "", false;
}
+fmt_write_i64 :: proc(w: io.Writer, i: i64, base: int = 10) {
+ buf: [32]byte;
+ s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
+ io.write_string(w, s);
+}
+fmt_write_u64 :: proc(w: io.Writer, i: u64, base: int) {
+ buf: [32]byte;
+ s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil);
+ io.write_string(w, s);
+}
+
fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
@@ -1159,12 +1182,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
et := runtime.type_info_base(info.elem);
if name != "" {
- strings.write_string(fi.buf, name);
+ io.write_string(fi.writer, name);
} else {
- reflect.write_type(fi.buf, type_info);
+ reflect.write_type(fi.writer, type_info);
}
- strings.write_byte(fi.buf, '{');
- defer strings.write_byte(fi.buf, '}');
+ io.write_byte(fi.writer, '{');
+ defer io.write_byte(fi.writer, '}');
e, is_enum := et.variant.(runtime.Type_Info_Enum);
commas := 0;
@@ -1174,21 +1197,21 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
}
if commas > 0 {
- strings.write_string(fi.buf, ", ");
+ io.write_string(fi.writer, ", ");
}
if is_enum {
for ev, evi in e.values {
v := u64(ev);
if v == u64(i) {
- strings.write_string(fi.buf, e.names[evi]);
+ io.write_string(fi.writer, e.names[evi]);
commas += 1;
continue loop;
}
}
}
v := i64(i) + info.lower;
- strings.write_i64(fi.buf, v, 10);
+ fmt_write_i64(fi.writer, v, 10);
commas += 1;
}
}
@@ -1210,19 +1233,19 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") {
}
if bit_field_name != "" {
- strings.write_string(fi.buf, bit_field_name);
- strings.write_byte(fi.buf, '{');
+ io.write_string(fi.writer, bit_field_name);
+ io.write_byte(fi.writer, '{');
} else {
- strings.write_string(fi.buf, "bit_field{");
+ io.write_string(fi.writer, "bit_field{");
}
for name, i in info.names {
if i > 0 {
- strings.write_string(fi.buf, ", ");
+ io.write_string(fi.writer, ", ");
}
bits := u64(info.bits[i]);
offset := u64(info.offsets[i]);
- strings.write_string(fi.buf, name);
- strings.write_string(fi.buf, " = ");
+ io.write_string(fi.writer, name);
+ io.write_string(fi.writer, " = ");
n := 8*u64(size_of(u64));
sa := n - bits;
@@ -1230,10 +1253,10 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") {
u <<= sa;
u >>= sa;
- strings.write_u64(fi.buf, u, 10);
+ fmt_write_u64(fi.writer, u, 10);
}
- strings.write_byte(fi.buf, '}');
+ io.write_byte(fi.writer, '}');
}
}
@@ -1256,16 +1279,16 @@ fmt_opaque :: proc(fi: ^Info, v: any) {
type_info := type_info_of(v.id);
if is_nil(v.data, type_info.size) {
- strings.write_string(fi.buf, "nil");
+ io.write_string(fi.writer, "nil");
return;
}
if ot, ok := rt.type_info_base(type_info).variant.(rt.Type_Info_Opaque); ok {
elem := rt.type_info_base(ot.elem);
if elem == nil { return; }
- reflect.write_type(fi.buf, type_info);
- strings.write_byte(fi.buf, '{');
- defer strings.write_byte(fi.buf, '}');
+ reflect.write_type(fi.writer, type_info);
+ io.write_byte(fi.writer, '{');
+ defer io.write_byte(fi.writer, '}');
#partial switch in elem.variant {
case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float:
@@ -1274,9 +1297,9 @@ fmt_opaque :: proc(fi: ^Info, v: any) {
// Okay
}
} else {
- reflect.write_type(fi.buf, type_info);
- strings.write_byte(fi.buf, '{');
- strings.write_byte(fi.buf, '}');
+ reflect.write_type(fi.writer, type_info);
+ io.write_byte(fi.writer, '{');
+ io.write_byte(fi.writer, '}');
}
}
@@ -1287,14 +1310,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
n -= 1;
}
for in 0..<n {
- strings.write_byte(fi.buf, '0');
+ io.write_byte(fi.writer, '0');
}
- strings.write_i64(fi.buf, i, 10);
+ fmt_write_i64(fi.writer, i, 10);
}
if v.data == nil || v.id == nil {
- strings.write_string(fi.buf, "<nil>");
+ io.write_string(fi.writer, "<nil>");
return;
}
@@ -1316,6 +1339,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Named:
// Built-in Custom Formatters for core library types
switch a in v {
+ case runtime.Source_Code_Location:
+ io.write_string(fi.writer, a.file_path);
+ io.write_byte(fi.writer, '(');
+ io.write_int(fi.writer, a.line);
+ io.write_byte(fi.writer, ':');
+ io.write_int(fi.writer, a.column);
+ io.write_byte(fi.writer, ')');
+ return;
+
case time.Duration:
ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) {
v := v;
@@ -1367,7 +1399,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
w -= 1;
switch {
case u == 0:
- strings.write_string(fi.buf, "0s");
+ io.write_string(fi.writer, "0s");
return;
case u < u64(time.Microsecond):
prec = 0;
@@ -1406,7 +1438,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
w -= 1;
buf[w] = '-';
}
- strings.write_string(fi.buf, string(buf[w:]));
+ io.write_string(fi.writer, string(buf[w:]));
return;
case time.Time:
@@ -1415,20 +1447,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
h, min, s := time.clock(t);
ns := i64(t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9;
write_padded_number(fi, i64(y), 4);
- strings.write_byte(fi.buf, '-');
+ io.write_byte(fi.writer, '-');
write_padded_number(fi, i64(mon), 2);
- strings.write_byte(fi.buf, '-');
+ io.write_byte(fi.writer, '-');
write_padded_number(fi, i64(d), 2);
- strings.write_byte(fi.buf, ' ');
+ io.write_byte(fi.writer, ' ');
write_padded_number(fi, i64(h), 2);
- strings.write_byte(fi.buf, ':');
+ io.write_byte(fi.writer, ':');
write_padded_number(fi, i64(min), 2);
- strings.write_byte(fi.buf, ':');
+ io.write_byte(fi.writer, ':');
write_padded_number(fi, i64(s), 2);
- strings.write_byte(fi.buf, '.');
+ io.write_byte(fi.writer, '.');
write_padded_number(fi, i64(ns), 9);
- strings.write_string(fi.buf, " +0000 UTC");
+ io.write_string(fi.writer, " +0000 UTC");
return;
}
@@ -1439,15 +1471,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return;
}
if b.is_raw_union {
- strings.write_string(fi.buf, info.name);
- strings.write_string(fi.buf, "{}");
+ io.write_string(fi.writer, info.name);
+ io.write_string(fi.writer, "{}");
return;
};
is_soa := b.soa_kind != .None;
- strings.write_string(fi.buf, info.name);
- strings.write_byte(fi.buf, '[' if is_soa else '{');
+ io.write_string(fi.writer, info.name);
+ io.write_byte(fi.writer, '[' if is_soa else '{');
hash := fi.hash; defer fi.hash = hash;
indent := fi.indent; defer fi.indent -= 1;
@@ -1456,13 +1488,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fi.indent += 1;
if hash {
- strings.write_byte(fi.buf, '\n');
+ io.write_byte(fi.writer, '\n');
}
defer {
if hash {
- for in 0..<indent { strings.write_byte(fi.buf, '\t'); }
+ for in 0..<indent { io.write_byte(fi.writer, '\t'); }
}
- strings.write_byte(fi.buf, ']' if is_soa else '}');
+ io.write_byte(fi.writer, ']' if is_soa else '}');
}
if is_soa {
@@ -1475,37 +1507,37 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
for index in 0..<uintptr(b.soa_len) {
- if !hash && index > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && index > 0 { io.write_string(fi.writer, ", "); }
field_count := -1;
- if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", "); }
- strings.write_string(fi.buf, base_type_name);
- strings.write_byte(fi.buf, '{');
- defer strings.write_byte(fi.buf, '}');
+ io.write_string(fi.writer, base_type_name);
+ io.write_byte(fi.writer, '{');
+ defer io.write_byte(fi.writer, '}');
for name, i in b.names {
field_count += 1;
- if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", "); }
if hash {
- for in 0..<fi.indent { strings.write_byte(fi.buf, '\t'); }
+ for in 0..<fi.indent { io.write_byte(fi.writer, '\t'); }
}
- strings.write_string(fi.buf, name);
- strings.write_string(fi.buf, " = ");
+ io.write_string(fi.writer, name);
+ io.write_string(fi.writer, " = ");
t := b.types[i].variant.(runtime.Type_Info_Array).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
- strings.write_string(fi.buf, "any{}");
+ io.write_string(fi.writer, "any{}");
} else {
data := rawptr(uintptr(v.data) + b.offsets[i] + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
}
- if hash { strings.write_string(fi.buf, ",\n"); }
+ if hash { io.write_string(fi.writer, ",\n"); }
}
}
} else {
@@ -1513,22 +1545,22 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
for name, i in b.names {
field_count += 1;
- if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", "); }
if hash {
- for in 0..<fi.indent { strings.write_byte(fi.buf, '\t'); }
+ for in 0..<fi.indent { io.write_byte(fi.writer, '\t'); }
}
- strings.write_string(fi.buf, name);
- strings.write_string(fi.buf, " = ");
+ io.write_string(fi.writer, name);
+ io.write_string(fi.writer, " = ");
if t := b.types[i]; reflect.is_any(t) {
- strings.write_string(fi.buf, "any{}");
+ io.write_string(fi.writer, "any{}");
} else {
data := rawptr(uintptr(v.data) + b.offsets[i]);
fmt_arg(fi, any{data, t.id}, 'v');
}
- if hash { strings.write_string(fi.buf, ",\n"); }
+ if hash { io.write_string(fi.writer, ",\n"); }
}
}
@@ -1552,7 +1584,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Pointer:
if v.id == typeid_of(^runtime.Type_Info) {
- reflect.write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
+ reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^);
} else {
ptr := (^rawptr)(v.data)^;
if verb != 'p' && info.elem != nil {
@@ -1566,13 +1598,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Map:
if ptr == nil {
- strings.write_string(fi.buf, "<nil>");
+ io.write_string(fi.writer, "<nil>");
return;
}
if fi.record_level < 1 {
fi.record_level += 1;
defer fi.record_level -= 1;
- strings.write_byte(fi.buf, '&');
+ io.write_byte(fi.writer, '&');
fmt_value(fi, a, verb);
return;
}
@@ -1580,13 +1612,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Struct,
runtime.Type_Info_Union:
if ptr == nil {
- strings.write_string(fi.buf, "<nil>");
+ io.write_string(fi.writer, "<nil>");
return;
}
if fi.record_level < 1 {
fi.record_level += 1;
defer fi.record_level -= 1;
- strings.write_byte(fi.buf, '&');
+ io.write_byte(fi.writer, '&');
fmt_value(fi, a, verb);
return;
}
@@ -1597,44 +1629,51 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
case runtime.Type_Info_Array:
- strings.write_byte(fi.buf, '[');
- defer strings.write_byte(fi.buf, ']');
- for i in 0..<info.count {
- if i > 0 { strings.write_string(fi.buf, ", "); }
+ if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
+ s := strings.string_from_ptr((^byte)(v.data), info.count);
+ fmt_string(fi, s, verb);
+ } else {
+ io.write_byte(fi.writer, '[');
+ defer io.write_byte(fi.writer, ']');
+ for i in 0..<info.count {
+ if i > 0 { io.write_string(fi.writer, ", "); }
- data := uintptr(v.data) + uintptr(i*info.elem_size);
- fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
+ data := uintptr(v.data) + uintptr(i*info.elem_size);
+ fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
+ }
}
case runtime.Type_Info_Enumerated_Array:
- strings.write_byte(fi.buf, '[');
- defer strings.write_byte(fi.buf, ']');
+ io.write_byte(fi.writer, '[');
+ defer io.write_byte(fi.writer, ']');
for i in 0..<info.count {
- if i > 0 { strings.write_string(fi.buf, ", "); }
+ if i > 0 { io.write_string(fi.writer, ", "); }
idx, ok := stored_enum_value_to_string(info.index, info.min_value, i);
if ok {
- strings.write_byte(fi.buf, '.');
- strings.write_string(fi.buf, idx);
+ io.write_byte(fi.writer, '.');
+ io.write_string(fi.writer, idx);
} else {
- strings.write_i64(fi.buf, i64(info.min_value)+i64(i));
+ fmt_write_i64(fi.writer, i64(info.min_value)+i64(i));
}
- strings.write_string(fi.buf, " = ");
+ io.write_string(fi.writer, " = ");
data := uintptr(v.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
}
case runtime.Type_Info_Dynamic_Array:
- if verb == 'p' {
- slice := cast(^mem.Raw_Dynamic_Array)v.data;
- fmt_pointer(fi, slice.data, 'p');
+ array := cast(^mem.Raw_Dynamic_Array)v.data;
+ if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
+ s := strings.string_from_ptr((^byte)(array.data), array.len);
+ fmt_string(fi, s, verb);
+ } else if verb == 'p' {
+ fmt_pointer(fi, array.data, 'p');
} else {
- strings.write_byte(fi.buf, '[');
- defer strings.write_byte(fi.buf, ']');
- array := cast(^mem.Raw_Dynamic_Array)v.data;
+ io.write_byte(fi.writer, '[');
+ defer io.write_byte(fi.writer, ']');
for i in 0..<array.len {
- if i > 0 { strings.write_string(fi.buf, ", "); }
+ if i > 0 { io.write_string(fi.writer, ", "); }
data := uintptr(array.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
@@ -1643,12 +1682,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Simd_Vector:
if info.is_x86_mmx {
- strings.write_string(fi.buf, "intrinsics.x86_mmx<>");
+ io.write_string(fi.writer, "intrinsics.x86_mmx<>");
}
- strings.write_byte(fi.buf, '<');
- defer strings.write_byte(fi.buf, '>');
+ io.write_byte(fi.writer, '<');
+ defer io.write_byte(fi.writer, '>');
for i in 0..<info.count {
- if i > 0 { strings.write_string(fi.buf, ", "); }
+ if i > 0 { io.write_string(fi.writer, ", "); }
data := uintptr(v.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
@@ -1656,15 +1695,17 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Slice:
- if verb == 'p' {
- slice := cast(^mem.Raw_Slice)v.data;
+ slice := cast(^mem.Raw_Slice)v.data;
+ if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
+ s := strings.string_from_ptr((^byte)(slice.data), slice.len);
+ fmt_string(fi, s, verb);
+ } else if verb == 'p' {
fmt_pointer(fi, slice.data, 'p');
} else {
- strings.write_byte(fi.buf, '[');
- defer strings.write_byte(fi.buf, ']');
- slice := cast(^mem.Raw_Slice)v.data;
+ io.write_byte(fi.writer, '[');
+ defer io.write_byte(fi.writer, ']');
for i in 0..<slice.len {
- if i > 0 { strings.write_string(fi.buf, ", "); }
+ if i > 0 { io.write_string(fi.writer, ", "); }
data := uintptr(slice.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
@@ -1676,8 +1717,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return;
}
- strings.write_string(fi.buf, "map[");
- defer strings.write_byte(fi.buf, ']');
+ io.write_string(fi.writer, "map[");
+ defer io.write_byte(fi.writer, ']');
m := (^mem.Raw_Map)(v.data);
if m != nil {
@@ -1691,42 +1732,37 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
entry_size := ed.elem_size;
for i in 0..<entries.len {
- if i > 0 { strings.write_string(fi.buf, ", "); }
+ if i > 0 { io.write_string(fi.writer, ", "); }
data := uintptr(entries.data) + uintptr(i*entry_size);
- header := cast(^runtime.Map_Entry_Header)data;
- if reflect.is_string(info.key) {
- strings.write_string(fi.buf, header.key.key.str);
- } else {
- fi := Info{buf = fi.buf};
- fmt_arg(&fi, any{rawptr(&header.key.key.val), info.key.id}, 'v');
- }
+ key := data + entry_type.offsets[2];
+ fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v');
- strings.write_string(fi.buf, "=");
+ io.write_string(fi.writer, "=");
- value := data + entry_type.offsets[2];
+ value := data + entry_type.offsets[3];
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v');
}
}
case runtime.Type_Info_Struct:
if info.is_raw_union {
- strings.write_string(fi.buf, "(raw_union)");
+ io.write_string(fi.writer, "(raw_union)");
return;
}
is_soa := info.soa_kind != .None;
- strings.write_byte(fi.buf, '[' if is_soa else '{');
- defer strings.write_byte(fi.buf, ']' if is_soa else '}');
+ io.write_byte(fi.writer, '[' if is_soa else '{');
+ defer io.write_byte(fi.writer, ']' if is_soa else '}');
fi.indent += 1; defer fi.indent -= 1;
hash := fi.hash; defer fi.hash = hash;
fi.hash = false;
- if hash { strings.write_byte(fi.buf, '\n'); }
+ if hash { io.write_byte(fi.writer, '\n'); }
if is_soa {
fi.indent += 1;
@@ -1755,33 +1791,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
for index in 0..<n {
- if !hash && index > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && index > 0 { io.write_string(fi.writer, ", "); }
field_count := -1;
- if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", "); }
- strings.write_string(fi.buf, base_type_name);
- strings.write_byte(fi.buf, '{');
- defer strings.write_byte(fi.buf, '}');
+ io.write_string(fi.writer, base_type_name);
+ io.write_byte(fi.writer, '{');
+ defer io.write_byte(fi.writer, '}');
for i in 0..<actual_field_count {
name := info.names[i];
field_count += 1;
- if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", "); }
if hash {
- for in 0..<fi.indent { strings.write_byte(fi.buf, '\t'); }
+ for in 0..<fi.indent { io.write_byte(fi.writer, '\t'); }
}
- strings.write_string(fi.buf, name);
- strings.write_string(fi.buf, " = ");
+ io.write_string(fi.writer, name);
+ io.write_string(fi.writer, " = ");
if info.soa_kind == .Fixed {
t := info.types[i].variant.(runtime.Type_Info_Array).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
- strings.write_string(fi.buf, "any{}");
+ io.write_string(fi.writer, "any{}");
} else {
data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
@@ -1790,7 +1826,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
t := info.types[i].variant.(runtime.Type_Info_Pointer).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
- strings.write_string(fi.buf, "any{}");
+ io.write_string(fi.writer, "any{}");
} else {
field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^;
data := rawptr(uintptr(field_ptr) + index*t_size);
@@ -1798,7 +1834,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
}
- if hash { strings.write_string(fi.buf, ",\n"); }
+ if hash { io.write_string(fi.writer, ",\n"); }
}
}
} else {
@@ -1806,23 +1842,23 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
for name, i in info.names {
field_count += 1;
- if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", "); }
if hash {
- for in 0..<fi.indent { strings.write_byte(fi.buf, '\t'); }
+ for in 0..<fi.indent { io.write_byte(fi.writer, '\t'); }
}
- strings.write_string(fi.buf, name);
- strings.write_string(fi.buf, " = ");
+ io.write_string(fi.writer, name);
+ io.write_string(fi.writer, " = ");
if t := info.types[i]; reflect.is_any(t) {
- strings.write_string(fi.buf, "any{}");
+ io.write_string(fi.writer, "any{}");
} else {
data := rawptr(uintptr(v.data) + info.offsets[i]);
fmt_arg(fi, any{data, t.id}, 'v');
}
if hash {
- strings.write_string(fi.buf, ",\n");
+ io.write_string(fi.writer, ",\n");
}
}
}
@@ -1830,14 +1866,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Union:
if type_info.size == 0 {
- strings.write_string(fi.buf, "nil");
+ io.write_string(fi.writer, "nil");
return;
}
if reflect.type_info_union_is_pure_maybe(info) {
if v.data == nil {
- strings.write_string(fi.buf, "nil");
+ io.write_string(fi.writer, "nil");
} else {
id := info.variants[0].id;
fmt_arg(fi, any{v.data, id}, verb);
@@ -1863,12 +1899,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
assert(tag >= 0);
if v.data == nil {
- strings.write_string(fi.buf, "nil");
+ io.write_string(fi.writer, "nil");
} else if info.no_nil {
id := info.variants[tag].id;
fmt_arg(fi, any{v.data, id}, verb);
} else if tag == 0 {
- strings.write_string(fi.buf, "nil");
+ io.write_string(fi.writer, "nil");
} else {
id := info.variants[tag-1].id;
fmt_arg(fi, any{v.data, id}, verb);
@@ -1880,16 +1916,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Procedure:
ptr := (^rawptr)(v.data)^;
if ptr == nil {
- strings.write_string(fi.buf, "nil");
+ io.write_string(fi.writer, "nil");
} else {
- reflect.write_typeid(fi.buf, v.id);
- strings.write_string(fi.buf, " @ ");
+ reflect.write_typeid(fi.writer, v.id);
+ io.write_string(fi.writer, " @ ");
fmt_pointer(fi, ptr, 'p');
}
case runtime.Type_Info_Type_Id:
id := (^typeid)(v.data)^;
- reflect.write_typeid(fi.buf, id);
+ reflect.write_typeid(fi.writer, id);
case runtime.Type_Info_Bit_Field:
fmt_bit_field(fi, v);
@@ -1912,18 +1948,18 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if verb == 'p' {
fmt_pointer(fi, ptr, 'p');
} else if ptr == nil {
- strings.write_string(fi.buf, "[]");
+ io.write_string(fi.writer, "[]");
} else {
len_ptr := uintptr(v.data) + uintptr(info.base_integer.size);
len_any := any{rawptr(len_ptr), info.base_integer.id};
len, _ := reflect.as_int(len_any);
slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice);
- strings.write_byte(fi.buf, '[');
- defer strings.write_byte(fi.buf, ']');
+ io.write_byte(fi.writer, '[');
+ defer io.write_byte(fi.writer, ']');
for i in 0..<len {
- if i > 0 { strings.write_string(fi.buf, ", "); }
+ if i > 0 { io.write_string(fi.writer, ", "); }
data := uintptr(ptr) + uintptr(i*slice_type.elem_size);
fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb);
@@ -1950,10 +1986,10 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
r, i := real(c), imag(c);
fmt_float(fi, r, bits/2, verb);
if !fi.plus && i >= 0 {
- strings.write_rune(fi.buf, '+');
+ io.write_rune(fi.writer, '+');
}
fmt_float(fi, i, bits/2, verb);
- strings.write_rune(fi.buf, 'i');
+ io.write_rune(fi.writer, 'i');
case:
fmt_bad_verb(fi, verb);
@@ -1969,22 +2005,22 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
fmt_float(fi, r, bits/4, verb);
if !fi.plus && i >= 0 {
- strings.write_rune(fi.buf, '+');
+ io.write_rune(fi.writer, '+');
}
fmt_float(fi, i, bits/4, verb);
- strings.write_rune(fi.buf, 'i');
+ io.write_rune(fi.writer, 'i');
if !fi.plus && j >= 0 {
- strings.write_rune(fi.buf, '+');
+ io.write_rune(fi.writer, '+');
}
fmt_float(fi, j, bits/4, verb);
- strings.write_rune(fi.buf, 'j');
+ io.write_rune(fi.writer, 'j');
if !fi.plus && k >= 0 {
- strings.write_rune(fi.buf, '+');
+ io.write_rune(fi.writer, '+');
}
fmt_float(fi, k, bits/4, verb);
- strings.write_rune(fi.buf, 'k');
+ io.write_rune(fi.writer, 'k');
case:
fmt_bad_verb(fi, verb);
@@ -1994,7 +2030,7 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
if arg == nil {
- strings.write_string(fi.buf, "<nil>");
+ io.write_string(fi.writer, "<nil>");
return;
}
fi.arg = arg;
@@ -2004,7 +2040,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
switch a in arg {
case ^runtime.Type_Info: ti = a;
}
- reflect.write_type(fi.buf, ti);
+ reflect.write_type(fi.writer, ti);
return;
}
@@ -2022,12 +2058,12 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
custom_types: switch a in arg {
case runtime.Source_Code_Location:
if fi.hash && verb == 'v' {
- strings.write_string(fi.buf, a.file_path);
- strings.write_byte(fi.buf, '(');
- strings.write_i64(fi.buf, i64(a.line), 10);
- strings.write_byte(fi.buf, ':');
- strings.write_i64(fi.buf, i64(a.column), 10);
- strings.write_byte(fi.buf, ')');
+ io.write_string(fi.writer, a.file_path);
+ io.write_byte(fi.writer, '(');
+ fmt_write_i64(fi.writer, i64(a.line), 10);
+ io.write_byte(fi.writer, ':');
+ fmt_write_i64(fi.writer, i64(a.column), 10);
+ io.write_byte(fi.writer, ')');
return;
}
}
@@ -2074,7 +2110,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case string: fmt_string(fi, a, verb);
case cstring: fmt_cstring(fi, a, verb);
- case typeid: reflect.write_typeid(fi.buf, a);
+ case typeid: reflect.write_typeid(fi.writer, a);
case i16le: fmt_int(fi, u64(a), true, 16, verb);
case u16le: fmt_int(fi, u64(a), false, 16, verb);
diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin
index 52d930fb8..f817116f0 100644
--- a/core/intrinsics/intrinsics.odin
+++ b/core/intrinsics/intrinsics.odin
@@ -1,4 +1,5 @@
// This is purely for documentation
+//+ignore
package intrinsics
// Types
@@ -114,7 +115,7 @@ type_is_ordered_numeric :: proc($T: typeid) -> bool ---
type_is_indexable :: proc($T: typeid) -> bool ---
type_is_sliceable :: proc($T: typeid) -> bool ---
type_is_comparable :: proc($T: typeid) -> bool ---
-type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
+type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
type_is_dereferenceable :: proc($T: typeid) -> bool ---
type_is_valid_map_key :: proc($T: typeid) -> bool ---
@@ -152,3 +153,6 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
+
+type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) ---
+type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) ---
diff --git a/core/io/conv.odin b/core/io/conv.odin
new file mode 100644
index 000000000..f164c09e7
--- /dev/null
+++ b/core/io/conv.odin
@@ -0,0 +1,200 @@
+package io
+
+Conversion_Error :: enum {
+ None,
+ Missing_Procedure,
+ Fallback_Possible,
+}
+
+to_reader :: proc(s: Stream) -> (r: Reader, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_writer :: proc(s: Stream) -> (w: Writer, err: Conversion_Error) {
+ w.stream = s;
+ if s.stream_vtable == nil || s.impl_write == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+
+to_closer :: proc(s: Stream) -> (c: Closer, err: Conversion_Error) {
+ c.stream = s;
+ if s.stream_vtable == nil || s.impl_close == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_flusher :: proc(s: Stream) -> (f: Flusher, err: Conversion_Error) {
+ f.stream = s;
+ if s.stream_vtable == nil || s.impl_flush == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_seeker :: proc(s: Stream) -> (seeker: Seeker, err: Conversion_Error) {
+ seeker.stream = s;
+ if s.stream_vtable == nil || s.impl_seek == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+
+to_read_writer :: proc(s: Stream) -> (r: Read_Writer, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_read_closer :: proc(s: Stream) -> (r: Read_Closer, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, err: Conversion_Error) {
+ w.stream = s;
+ if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, err: Conversion_Error) {
+ w.stream = s;
+ if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+
+to_reader_at :: proc(s: Stream) -> (r: Reader_At, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read_at == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_writer_at :: proc(s: Stream) -> (w: Writer_At, err: Conversion_Error) {
+ w.stream = s;
+ if s.stream_vtable == nil || s.impl_write_at == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_reader_from :: proc(s: Stream) -> (r: Reader_From, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read_from == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) {
+ w.stream = s;
+ if s.stream_vtable == nil || s.impl_write_to == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_write_closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) {
+ w.stream = s;
+ if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) {
+ w.stream = s;
+ if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
+ err = .Missing_Procedure;
+ }
+ return;
+}
+
+
+to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) {
+ b.stream = s;
+ if s.stream_vtable == nil || s.impl_read_byte == nil {
+ err = .Missing_Procedure;
+ if s.stream_vtable != nil && s.impl_read != nil {
+ err = .Fallback_Possible;
+ }
+ }
+ return;
+}
+to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, err: Conversion_Error) {
+ b.stream = s;
+ if s.stream_vtable != nil {
+ if s.impl_unread_byte == nil {
+ err = .Missing_Procedure;
+ return;
+ }
+ if s.impl_read_byte != nil {
+ err = .None;
+ } else if s.impl_read != nil {
+ err = .Fallback_Possible;
+ } else {
+ err = .Missing_Procedure;
+ }
+ }
+ return;
+}
+to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, err: Conversion_Error) {
+ b.stream = s;
+ if s.stream_vtable == nil || s.impl_write_byte == nil {
+ err = .Missing_Procedure;
+ if s.stream_vtable != nil && s.impl_write != nil {
+ err = .Fallback_Possible;
+ }
+ }
+ return;
+}
+
+to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable == nil || s.impl_read_rune == nil {
+ err = .Missing_Procedure;
+ if s.stream_vtable != nil && s.impl_read != nil {
+ err = .Fallback_Possible;
+ }
+ }
+ return;
+
+}
+to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, err: Conversion_Error) {
+ r.stream = s;
+ if s.stream_vtable != nil {
+ if s.impl_unread_rune == nil {
+ err = .Missing_Procedure;
+ return;
+ }
+ if s.impl_read_rune != nil {
+ err = .None;
+ } else if s.impl_read != nil {
+ err = .Fallback_Possible;
+ } else {
+ err = .Missing_Procedure;
+ }
+ } else {
+ err = .Missing_Procedure;
+ }
+ return;
+}
diff --git a/core/io/io.odin b/core/io/io.odin
new file mode 100644
index 000000000..e33b33064
--- /dev/null
+++ b/core/io/io.odin
@@ -0,0 +1,504 @@
+package io
+
+import "intrinsics"
+import "core:runtime"
+import "core:unicode/utf8"
+
+Seek_From :: enum {
+ Start = 0, // seek relative to the origin of the file
+ Current = 1, // seek relative to the current offset
+ End = 2, // seek relative to the end
+}
+
+Error :: enum i32 {
+ // No Error
+ None = 0,
+
+ // EOF is the error returned by `read` when no more input is available
+ EOF,
+
+ // Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
+ Unexpected_EOF,
+
+ // Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
+ Short_Write,
+
+ // Short_Buffer means that a read required a longer buffer than was provided
+ Short_Buffer,
+
+ // No_Progress is returned by some implementations of `io.Reader` when many calls
+ // to `read` have failed to return any data or error.
+ // This is usually a signed of a broken `io.Reader` implementation
+ No_Progress,
+
+ Invalid_Whence,
+ Invalid_Offset,
+ Invalid_Unread,
+
+ Negative_Read,
+ Negative_Write,
+ Negative_Count,
+ Buffer_Full,
+
+ // Empty is returned when a procedure has not been implemented for an io.Stream
+ Empty = -1,
+}
+
+Close_Proc :: proc(using s: Stream) -> Error;
+Flush_Proc :: proc(using s: Stream) -> Error;
+Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error);
+Size_Proc :: proc(using s: Stream) -> i64;
+Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
+Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
+Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error);
+Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error);
+Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error);
+Unread_Byte_Proc :: proc(using s: Stream) -> Error;
+Unread_Rune_Proc :: proc(using s: Stream) -> Error;
+Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
+Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
+Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error);
+Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error;
+Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error);
+Destroy_Proc :: proc(using s: Stream) -> Error;
+
+
+Stream :: struct {
+ using stream_vtable: ^Stream_VTable,
+ stream_data: rawptr,
+}
+Stream_VTable :: struct {
+ impl_close: Close_Proc,
+ impl_flush: Flush_Proc,
+
+ impl_seek: Seek_Proc,
+ impl_size: Size_Proc,
+
+ impl_read: Read_Proc,
+ impl_read_at: Read_At_Proc,
+ impl_read_byte: Read_Byte_Proc,
+ impl_read_rune: Read_Rune_Proc,
+ impl_write_to: Write_To_Proc,
+
+ impl_write: Write_Proc,
+ impl_write_at: Write_At_Proc,
+ impl_write_byte: Write_Byte_Proc,
+ impl_write_rune: Write_Rune_Proc,
+ impl_read_from: Read_From_Proc,
+
+ impl_unread_byte: Unread_Byte_Proc,
+ impl_unread_rune: Unread_Rune_Proc,
+
+ impl_destroy: Destroy_Proc,
+}
+
+
+Reader :: struct {using stream: Stream};
+Writer :: struct {using stream: Stream};
+Closer :: struct {using stream: Stream};
+Flusher :: struct {using stream: Stream};
+Seeker :: struct {using stream: Stream};
+
+Read_Writer :: struct {using stream: Stream};
+Read_Closer :: struct {using stream: Stream};
+Read_Write_Closer :: struct {using stream: Stream};
+Read_Write_Seeker :: struct {using stream: Stream};
+
+Write_Closer :: struct {using stream: Stream};
+Write_Seeker :: struct {using stream: Stream};
+Write_Flusher :: struct {using stream: Stream};
+Write_Flush_Closer :: struct {using stream: Stream};
+
+Reader_At :: struct {using stream: Stream};
+Writer_At :: struct {using stream: Stream};
+Reader_From :: struct {using stream: Stream};
+Writer_To :: struct {using stream: Stream};
+
+Byte_Reader :: struct {using stream: Stream};
+Byte_Scanner :: struct {using stream: Stream};
+Byte_Writer :: struct {using stream: Stream};
+
+Rune_Reader :: struct {using stream: Stream};
+Rune_Scanner :: struct {using stream: Stream};
+
+
+destroy :: proc(s: Stream) -> Error {
+ close_err := close({s});
+ if s.stream_vtable != nil && s.impl_destroy != nil {
+ return s->impl_destroy();
+ }
+ if close_err != .None {
+ return close_err;
+ }
+ return .Empty;
+}
+
+read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) {
+ if s.stream_vtable != nil && s.impl_read != nil {
+ return s->impl_read(p);
+ }
+ return 0, .Empty;
+}
+
+write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) {
+ if s.stream_vtable != nil && s.impl_write != nil {
+ return s->impl_write(p);
+ }
+ return 0, .Empty;
+}
+
+seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+ if s.stream_vtable != nil && s.impl_seek != nil {
+ return s->impl_seek(offset, whence);
+ }
+ return 0, .Empty;
+}
+
+close :: proc(s: Closer) -> Error {
+ if s.stream_vtable != nil && s.impl_close != nil {
+ return s->impl_close();
+ }
+ // Instead of .Empty, .None is fine in this case
+ return .None;
+}
+
+flush :: proc(s: Flusher) -> Error {
+ if s.stream_vtable != nil && s.impl_flush != nil {
+ return s->impl_flush();
+ }
+ // Instead of .Empty, .None is fine in this case
+ return .None;
+}
+
+size :: proc(s: Stream) -> i64 {
+ if s.stream_vtable == nil {
+ return 0;
+ }
+ if s.impl_size != nil {
+ return s->impl_size();
+ }
+ if s.impl_seek == nil {
+ return 0;
+ }
+
+ curr, end: i64;
+ err: Error;
+ if curr, err = s->impl_seek(0, .Current); err != nil {
+ return 0;
+ }
+
+ if end, err = s->impl_seek(0, .End); err != nil {
+ return 0;
+ }
+
+ if _, err = s->impl_seek(curr, .Start); err != nil {
+ return 0;
+ }
+
+ return end;
+}
+
+
+
+
+read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) {
+ if r.stream_vtable == nil {
+ return 0, .Empty;
+ }
+ if r.impl_read_at != nil {
+ return r->impl_read_at(p, offset);
+ }
+ if r.impl_seek == nil || r.impl_read == nil {
+ return 0, .Empty;
+ }
+
+ current_offset: i64;
+ current_offset, err = r->impl_seek(offset, .Current);
+ if err != nil {
+ return 0, err;
+ }
+
+ n, err = r->impl_read(p);
+ if err != nil {
+ return;
+ }
+ _, err = r->impl_seek(current_offset, .Start);
+ return;
+
+}
+
+write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) {
+ if w.stream_vtable == nil {
+ return 0, .Empty;
+ }
+ if w.impl_write_at != nil {
+ return w->impl_write_at(p, offset);
+ }
+ if w.impl_seek == nil || w.impl_write == nil {
+ return 0, .Empty;
+ }
+
+ current_offset: i64;
+ current_offset, err = w->impl_seek(offset, .Current);
+ if err != nil {
+ return 0, err;
+ }
+ defer w->impl_seek(current_offset, .Start);
+
+ return w->impl_write(p);
+}
+
+write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
+ if r.stream_vtable == nil || w.stream_vtable == nil {
+ return 0, .Empty;
+ }
+ if r.impl_write_to != nil {
+ return r->impl_write_to(w);
+ }
+ return 0, .Empty;
+}
+read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
+ if r.stream_vtable == nil || w.stream_vtable == nil {
+ return 0, .Empty;
+ }
+ if r.impl_read_from != nil {
+ return w->impl_read_from(r);
+ }
+ return 0, .Empty;
+}
+
+
+read_byte :: proc(r: Byte_Reader) -> (byte, Error) {
+ if r.stream_vtable == nil {
+ return 0, .Empty;
+ }
+ if r.impl_read_byte != nil {
+ return r->impl_read_byte();
+ }
+ if r.impl_read == nil {
+ return 0, .Empty;
+ }
+
+ b: [1]byte;
+ _, err := r->impl_read(b[:]);
+ return b[0], err;
+}
+
+write_byte :: proc{
+ write_byte_to_byte_writer,
+ write_byte_to_writer,
+};
+
+write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte) -> Error {
+ return _write_byte(auto_cast w, c);
+}
+
+write_byte_to_writer :: proc(w: Writer, c: byte) -> Error {
+ return _write_byte(auto_cast w, c);
+}
+
+@(private)
+_write_byte :: proc(w: Byte_Writer, c: byte) -> Error {
+ if w.stream_vtable == nil {
+ return .Empty;
+ }
+ if w.impl_write_byte != nil {
+ return w->impl_write_byte(c);
+ }
+ if w.impl_write == nil {
+ return .Empty;
+ }
+
+ b := [1]byte{c};
+ _, err := w->impl_write(b[:]);
+ return err;
+}
+
+read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) {
+ if br.stream_vtable == nil {
+ return 0, 0, .Empty;
+ }
+ if br.impl_read_rune != nil {
+ return br->impl_read_rune();
+ }
+ if br.impl_read == nil {
+ return 0, 0, .Empty;
+ }
+
+ b: [utf8.UTF_MAX]byte;
+ _, err = br->impl_read(b[:1]);
+
+ s0 := b[0];
+ ch = rune(s0);
+ size = 1;
+ if err != nil {
+ return;
+ }
+ if ch < utf8.RUNE_SELF {
+ return;
+ }
+ x := utf8.accept_sizes[s0];
+ if x >= 0xf0 {
+ mask := rune(x) << 31 >> 31;
+ ch = ch &~ mask | utf8.RUNE_ERROR&mask;
+ return;
+ }
+ sz := int(x&7);
+ n: int;
+ n, err = br->impl_read(b[1:sz]);
+ if err != nil || n+1 < sz {
+ ch = utf8.RUNE_ERROR;
+ return;
+ }
+
+ ch, size = utf8.decode_rune(b[:sz]);
+ return;
+}
+
+unread_byte :: proc(s: Byte_Scanner) -> Error {
+ if s.stream_vtable != nil && s.impl_unread_byte != nil {
+ return s->impl_unread_byte();
+ }
+ return .Empty;
+}
+unread_rune :: proc(s: Rune_Scanner) -> Error {
+ if s.stream_vtable != nil && s.impl_unread_rune != nil {
+ return s->impl_unread_rune();
+ }
+ return .Empty;
+}
+
+
+write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) {
+ return write(s, transmute([]byte)str);
+}
+
+write_rune :: proc(s: Writer, r: rune) -> (size: int, err: Error) {
+ if s.stream_vtable != nil && s.impl_write_rune != nil {
+ return s->impl_write_rune(r);
+ }
+
+ if r < utf8.RUNE_SELF {
+ err = write_byte(s, byte(r));
+ if err == nil {
+ size = 1;
+ }
+ return;
+ }
+ buf, w := utf8.encode_rune(r);
+ return write(s, buf[:w]);
+}
+
+
+
+read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
+ return read_at_least(r, buf, len(buf));
+}
+
+
+read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
+ if len(buf) < min {
+ return 0, .Short_Buffer;
+ }
+ for n < min && err == nil {
+ nn: int;
+ nn, err = read(r, buf[n:]);
+ n += n;
+ }
+
+ if n >= min {
+ err = nil;
+ } else if n > 0 && err == .EOF {
+ err = .Unexpected_EOF;
+ }
+ return;
+}
+
+// copy copies from src to dst till either EOF is reached on src or an error occurs
+// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
+copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
+ return _copy_buffer(dst, src, nil);
+}
+
+// copy_buffer is the same as copy except that it stages through the provided buffer (if one is required)
+// rather than allocating a temporary one on the stack through `intrinsics.alloca`
+// If buf is `nil`, it is allocate through `intrinsics.alloca`; otherwise if it has zero length, it will panic
+copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
+ if buf != nil && len(buf) == 0 {
+ panic("empty buffer in io.copy_buffer");
+ }
+ return _copy_buffer(dst, src, buf);
+}
+
+
+
+// copy_n copies n bytes (or till an error) from src to dst.
+// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
+// On return, written == n IFF err == nil
+copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
+ nsrc := inline_limited_reader(&Limited_Reader{}, src, n);
+ written, err = copy(dst, nsrc);
+ if written == n {
+ return n, nil;
+ }
+ if written < n && err == nil {
+ // src stopped early and must have been an EOF
+ err = .EOF;
+ }
+ return;
+}
+
+
+@(private)
+_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
+ if dst.stream_vtable == nil || src.stream_vtable == nil {
+ return 0, .Empty;
+ }
+ if src.impl_write_to != nil {
+ return src->impl_write_to(dst);
+ }
+ if src.impl_read_from != nil {
+ return dst->impl_read_from(src);
+ }
+ buf := buf;
+ if buf == nil {
+ DEFAULT_SIZE :: 4 * 1024;
+ size := DEFAULT_SIZE;
+ if src.stream_vtable == _limited_reader_vtable {
+ l := (^Limited_Reader)(src.stream_data);
+ if i64(size) > l.n {
+ if l.n < 1 {
+ size = 1;
+ } else {
+ size = int(l.n);
+ }
+ }
+ }
+ // NOTE(bill): alloca is fine here
+ buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size};
+ }
+ for {
+ nr, er := read(src, buf);
+ if nr > 0 {
+ nw, ew := write(dst, buf[0:nr]);
+ if nw > 0 {
+ written += i64(nw);
+ }
+ if ew != nil {
+ err = ew;
+ break;
+ }
+ if nr != nw {
+ err = .Short_Write;
+ break;
+ }
+ }
+ if er != nil {
+ if er != .EOF {
+ err = er;
+ }
+ break;
+ }
+ }
+ return;
+}
diff --git a/core/io/multi.odin b/core/io/multi.odin
new file mode 100644
index 000000000..990cda61d
--- /dev/null
+++ b/core/io/multi.odin
@@ -0,0 +1,113 @@
+package io
+
+import "core:runtime"
+
+@(private)
+Multi_Reader :: struct {
+ readers: [dynamic]Reader,
+}
+
+@(private)
+_multi_reader_vtable := &Stream_VTable{
+ impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+ mr := (^Multi_Reader)(s.stream_data);
+ for len(mr.readers) > 0 {
+ r := mr.readers[0];
+ n, err = read(r, p);
+ if err == .EOF {
+ ordered_remove(&mr.readers, 0);
+ }
+ if n > 0 || err != .EOF {
+ if err == .EOF && len(mr.readers) > 0 {
+ // Don't return EOF yet, more readers remain
+ err = nil;
+ }
+ return;
+ }
+ }
+ return 0, .EOF;
+ },
+ impl_destroy = proc(s: Stream) -> Error {
+ mr := (^Multi_Reader)(s.stream_data);
+ context.allocator = mr.readers.allocator;
+ delete(mr.readers);
+ free(mr);
+ return .None;
+ },
+};
+
+multi_reader :: proc(readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
+ context.allocator = allocator;
+ mr := new(Multi_Reader);
+ all_readers := make([dynamic]Reader, 0, len(readers));
+
+ for w in readers {
+ if w.stream_vtable == _multi_reader_vtable {
+ other := (^Multi_Reader)(w.stream_data);
+ append(&all_readers, ..other.readers[:]);
+ } else {
+ append(&all_readers, w);
+ }
+ }
+
+ mr.readers = all_readers;
+
+ r.stream_vtable = _multi_reader_vtable;
+ r.stream_data = mr;
+ return;
+}
+
+
+@(private)
+Multi_Writer :: struct {
+ writers: []Writer,
+ allocator: runtime.Allocator,
+}
+
+@(private)
+_multi_writer_vtable := &Stream_VTable{
+ impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+ mw := (^Multi_Writer)(s.stream_data);
+ for w in mw.writers {
+ n, err = write(w, p);
+ if err != nil {
+ return;
+ }
+ if n != len(p) {
+ err = .Short_Write;
+ return;
+ }
+ }
+
+ return len(p), nil;
+ },
+ impl_destroy = proc(s: Stream) -> Error {
+ mw := (^Multi_Writer)(s.stream_data);
+ context.allocator = mw.allocator;
+ delete(mw.writers);
+ free(mw);
+ return .None;
+ },
+};
+
+multi_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
+ context.allocator = allocator;
+ mw := new(Multi_Writer);
+ mw.allocator = allocator;
+ all_writers := make([dynamic]Writer, 0, len(writers));
+
+ for w in writers {
+ if w.stream_vtable == _multi_writer_vtable {
+ other := (^Multi_Writer)(w.stream_data);
+ append(&all_writers, ..other.writers);
+ } else {
+ append(&all_writers, w);
+ }
+ }
+
+ mw.writers = all_writers[:];
+
+ out.stream_vtable = _multi_writer_vtable;
+ out.stream_data = mw;
+ return;
+}
diff --git a/core/io/util.odin b/core/io/util.odin
new file mode 100644
index 000000000..2036aa570
--- /dev/null
+++ b/core/io/util.odin
@@ -0,0 +1,192 @@
+package io
+
+import "core:runtime"
+import "core:strconv"
+
+write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
+ buf: [32]byte;
+ s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil);
+ return write_string(w, s);
+}
+write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) {
+ buf: [32]byte;
+ s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
+ return write_string(w, s);
+}
+
+write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) {
+ return write_u64(w, u64(i), base);
+}
+write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) {
+ return write_i64(w, i64(i), base);
+}
+
+@(private)
+Tee_Reader :: struct {
+ r: Reader,
+ w: Writer,
+ allocator: runtime.Allocator,
+}
+
+@(private)
+_tee_reader_vtable := &Stream_VTable{
+ impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+ t := (^Tee_Reader)(s.stream_data);
+ n, err = read(t.r, p);
+ if n > 0 {
+ if wn, werr := write(t.w, p[:n]); werr != nil {
+ return wn, werr;
+ }
+ }
+ return;
+ },
+ impl_destroy = proc(s: Stream) -> Error {
+ t := (^Tee_Reader)(s.stream_data);
+ allocator := t.allocator;
+ free(t, allocator);
+ return .None;
+ },
+};
+
+// tee_reader returns a Reader that writes to 'w' what it reads from 'r'
+// All reads from 'r' performed through it are matched with a corresponding write to 'w'
+// There is no internal buffering done
+// The write must complete before th read completes
+// Any error encountered whilst writing is reported as a 'read' error
+// tee_reader must call io.destroy when done with
+tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) {
+ t := new(Tee_Reader, allocator);
+ t.r, t.w = r, w;
+ t.allocator = allocator;
+
+ out.stream_data = t;
+ out.stream_vtable = _tee_reader_vtable;
+ return;
+}
+
+
+// A Limited_Reader reads from r but limits the amount of data returned to just n bytes.
+// Each call to read updates n to reflect the new amount remaining.
+// read returns EOF when n <= 0 or when the underlying r returns EOF.
+Limited_Reader :: struct {
+ r: Reader, // underlying reader
+ n: i64, // max_bytes
+}
+
+@(private)
+_limited_reader_vtable := &Stream_VTable{
+ impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+ l := (^Limited_Reader)(s.stream_data);
+ if l.n <= 0 {
+ return 0, .EOF;
+ }
+ p := p;
+ if i64(len(p)) > l.n {
+ p = p[0:l.n];
+ }
+ n, err = read(l.r, p);
+ l.n -= i64(n);
+ return;
+ },
+};
+
+new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader {
+ l := new(Limited_Reader);
+ l.r = r;
+ l.n = n;
+ return l;
+}
+
+limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
+ r.stream_vtable = _limited_reader_vtable;
+ r.stream_data = l;
+ return;
+}
+
+@(private="package")
+inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
+ l.r = r;
+ l.n = n;
+ return limited_reader_to_reader(l);
+}
+
+// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At
+Section_Reader :: struct {
+ r: Reader_At,
+ base: i64,
+ off: i64,
+ limit: i64,
+}
+
+init_section_reader :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
+ s.r = r;
+ s.off = off;
+ s.limit = off + n;
+ return;
+}
+section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
+ out.stream_data = s;
+ out.stream_vtable = _section_reader_vtable;
+ return;
+}
+
+@(private)
+_section_reader_vtable := &Stream_VTable{
+ impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
+ s := (^Section_Reader)(stream.stream_data);
+ if s.off >= s.limit {
+ return 0, .EOF;
+ }
+ p := p;
+ if max := s.limit - s.off; i64(len(p)) > max {
+ p = p[0:max];
+ }
+ n, err = read_at(s.r, p, s.off);
+ s.off += i64(n);
+ return;
+ },
+ impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) {
+ s := (^Section_Reader)(stream.stream_data);
+ p, off := p, off;
+
+ if off < 0 || off >= s.limit - s.base {
+ return 0, .EOF;
+ }
+ off += s.base;
+ if max := s.limit - off; i64(len(p)) > max {
+ p = p[0:max];
+ n, err = read_at(s.r, p, off);
+ if err == nil {
+ err = .EOF;
+ }
+ return;
+ }
+ return read_at(s.r, p, off);
+ },
+ impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+ s := (^Section_Reader)(stream.stream_data);
+
+ offset := offset;
+ switch whence {
+ case:
+ return 0, .Invalid_Whence;
+ case .Start:
+ offset += s.base;
+ case .Current:
+ offset += s.off;
+ case .End:
+ offset += s.limit;
+ }
+ if offset < s.base {
+ return 0, .Invalid_Offset;
+ }
+ s.off = offset;
+ n = offset - s.base;
+ return;
+ },
+ impl_size = proc(stream: Stream) -> i64 {
+ s := (^Section_Reader)(stream.stream_data);
+ return s.limit - s.base;
+ },
+};
+
diff --git a/core/math/math.odin b/core/math/math.odin
index fb463c08d..ee93a4d8c 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -593,6 +593,30 @@ is_inf :: proc{is_inf_f32, is_inf_f64};
+inf_f32 :: proc(sign: int) -> f32 {
+ return f32(inf_f64(sign));
+}
+inf_f64 :: proc(sign: int) -> f64 {
+ v: u64;
+ if sign >= 0 {
+ v = 0x7ff00000_00000000;
+ } else {
+ v = 0xfff00000_00000000;
+ }
+ return transmute(f64)v;
+}
+
+
+nan_f32 :: proc() -> f32 {
+ return f32(nan_f64());
+}
+nan_f64 :: proc() -> f64 {
+ v: u64 = 0x7ff80000_00000001;
+ return transmute(f64)v;
+}
+
+
+
is_power_of_two :: proc(x: int) -> bool {
return x > 0 && (x & (x-1)) == 0;
}
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index 3b0df5fda..990e7b47e 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -146,6 +146,7 @@ Paren_Expr :: struct {
Selector_Expr :: struct {
using node: Expr,
expr: ^Expr,
+ op: tokenizer.Token,
field: ^Ident,
}
@@ -154,6 +155,13 @@ Implicit_Selector_Expr :: struct {
field: ^Ident,
}
+Selector_Call_Expr :: struct {
+ using node: Expr,
+ expr: ^Expr,
+ call: ^Call_Expr,
+ modified_call: bool,
+}
+
Index_Expr :: struct {
using node: Expr,
expr: ^Expr,
@@ -206,9 +214,9 @@ Ternary_Expr :: struct {
Ternary_If_Expr :: struct {
using node: Expr,
- x: ^Expr,
+ x: ^Expr,
op1: tokenizer.Token,
- cond: ^Expr,
+ cond: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
@@ -217,7 +225,7 @@ Ternary_When_Expr :: struct {
using node: Expr,
x: ^Expr,
op1: tokenizer.Token,
- cond: ^Expr,
+ cond: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
@@ -561,7 +569,6 @@ Distinct_Type :: struct {
Opaque_Type :: struct {
using node: Expr,
- tok: tokenizer.Token_Kind,
type: ^Expr,
}
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 70bdc9e26..583a4cc03 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -190,6 +190,50 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0)
return;
}
+peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
+ prev_parser := p^;
+ defer p^ = prev_parser;
+
+ p.tok.err = nil;
+ for i := 0; i <= lookahead; i += 1 {
+ advance_token(p);
+ }
+ tok = p.curr_tok;
+ return;
+}
+skip_possible_newline :: proc(p: ^Parser) -> bool {
+ if .Insert_Semicolon not_in p.tok.flags {
+ return false;
+ }
+
+ prev := p.curr_tok;
+ if tokenizer.is_newline(prev) {
+ advance_token(p);
+ return true;
+ }
+ return false;
+}
+
+skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool {
+ if .Insert_Semicolon not_in p.tok.flags {
+ return false;
+ }
+
+ curr_pos := p.curr_tok.pos;
+ if tokenizer.is_newline(p.curr_tok) {
+ next := peek_token(p);
+ if curr_pos.line+1 >= next.pos.line {
+ #partial switch next.kind {
+ case .Open_Brace, .Else, .Where:
+ advance_token(p);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
next_token0 :: proc(p: ^Parser) -> bool {
p.curr_tok = tokenizer.scan(&p.tok);
@@ -280,7 +324,7 @@ expect_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> tokenizer.Token
prev := p.curr_tok;
if prev.kind != kind {
e := tokenizer.to_string(kind);
- g := tokenizer.to_string(prev.kind);
+ g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected '%s', got '%s'", e, g);
}
advance_token(p);
@@ -291,7 +335,7 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
prev := p.curr_tok;
if prev.kind != kind {
e := tokenizer.to_string(kind);
- g := tokenizer.to_string(prev.kind);
+ g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
}
advance_token(p);
@@ -300,8 +344,10 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
expect_operator :: proc(p: ^Parser) -> tokenizer.Token {
prev := p.curr_tok;
- if !tokenizer.is_operator(prev.kind) {
- g := tokenizer.to_string(prev.kind);
+ if prev.kind == .If || prev.kind == .When {
+ // okay
+ } else if !tokenizer.is_operator(prev.kind) {
+ g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected an operator, got '%s'", g);
}
advance_token(p);
@@ -398,7 +444,16 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
}
if node != nil {
- if prev.pos.line != p.curr_tok.pos.line {
+ if .Insert_Semicolon in p.tok.flags {
+ #partial switch p.curr_tok.kind {
+ case .Close_Brace, .Close_Paren, .Else, .EOF:
+ return true;
+ }
+
+ if is_semicolon_optional_for_node(p, node) {
+ return true;
+ }
+ } else if prev.pos.line != p.curr_tok.pos.line {
if is_semicolon_optional_for_node(p, node) {
return true;
}
@@ -418,7 +473,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
}
}
- error(p, prev.pos, "expected ';', got %s", tokenizer.to_string(p.curr_tok.kind));
+ error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
return false;
}
@@ -491,6 +546,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_block_stmt(p, true);
+ skip_possible_newline_for_literal(p);
}
if allow_token(p, .Else) {
@@ -566,6 +622,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_block_stmt(p, false);
+ skip_possible_newline_for_literal(p);
}
if allow_token(p, .Else) {
@@ -627,6 +684,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_body(p);
+ skip_possible_newline_for_literal(p);
}
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
@@ -661,6 +719,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_body(p);
+ skip_possible_newline_for_literal(p);
}
@@ -838,6 +897,8 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
attribute.elems = elems[:];
attribute.close = close.pos;
+ skip_possible_newline(p);
+
decl := parse_stmt(p);
switch d in &decl.derived {
case ast.Value_Decl:
@@ -1026,10 +1087,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_block_stmt(p, false);
+ skip_possible_newline_for_literal(p);
}
if bad_stmt {
- return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
+ return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
}
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
@@ -1204,7 +1266,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
tok := advance_token(p);
- error(p, tok.pos, "expected a statement, got %s", tokenizer.to_string(tok.kind));
+ error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok));
s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
return s;
}
@@ -1957,13 +2019,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
bl.tok = tok;
return bl;
-
- case .Size_Of, .Align_Of, .Offset_Of:
- tok := advance_token(p);
- expr := ast.new(ast.Implicit, tok.pos, end_pos(tok));
- expr.tok = tok;
- return parse_call_expr(p, expr);
-
case .Open_Brace:
if !lhs {
return parse_literal_value(p, nil);
@@ -1992,15 +2047,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Opaque:
tok := advance_token(p);
+ warn(p, tok.pos, "opaque is deprecated in favour of #opaque");
type := parse_type(p);
ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
- ot.tok = tok.kind;
ot.type = type;
return ot;
+
case .Hash:
tok := expect_token(p, .Hash);
name := expect_token(p, .Ident);
switch name.text {
+ case "opaque":
+ type := parse_type(p);
+ ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
+ ot.type = type;
+ return ot;
+
case "type":
type := parse_type(p);
hp := ast.new(ast.Helper_Type, tok.pos, type.end);
@@ -2156,7 +2218,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
where_token: tokenizer.Token;
where_clauses: []^ast.Expr;
- if (p.curr_tok.kind == .Where) {
+
+ skip_possible_newline_for_literal(p);
+
+ if p.curr_tok.kind == .Where {
where_token = expect_token(p, .Where);
prev_level := p.expr_level;
p.expr_level = -1;
@@ -2225,25 +2290,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
ti.specialization = nil;
return ti;
- case .Type_Of:
- tok := advance_token(p);
- i := ast.new(ast.Implicit, tok.pos, end_pos(tok));
- i.tok = tok;
- type: ^ast.Expr = parse_call_expr(p, i);
- for p.curr_tok.kind == .Period {
- period := advance_token(p);
-
- field := parse_ident(p);
- sel := ast.new(ast.Selector_Expr, period.pos, field.end);
- sel.expr = type;
- sel.field = field;
-
- type = sel;
- }
-
- return type;
-
-
case .Pointer:
tok := expect_token(p, .Pointer);
elem := parse_type(p);
@@ -2351,12 +2397,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
where_token: tokenizer.Token;
where_clauses: []^ast.Expr;
- if (p.curr_tok.kind == .Where) {
+
+ skip_possible_newline_for_literal(p);
+
+ if p.curr_tok.kind == .Where {
where_token = expect_token(p, .Where);
- prev_level := p.expr_level;
+ where_prev_level := p.expr_level;
p.expr_level = -1;
where_clauses = parse_rhs_expr_list(p);
- p.expr_level = prev_level;
+ p.expr_level = where_prev_level;
}
expect_token(p, .Open_Brace);
@@ -2414,12 +2463,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
where_token: tokenizer.Token;
where_clauses: []^ast.Expr;
- if (p.curr_tok.kind == .Where) {
+
+ skip_possible_newline_for_literal(p);
+
+ if p.curr_tok.kind == .Where {
where_token = expect_token(p, .Where);
- prev_level := p.expr_level;
+ where_prev_level := p.expr_level;
p.expr_level = -1;
where_clauses = parse_rhs_expr_list(p);
- p.expr_level = prev_level;
+ p.expr_level = where_prev_level;
}
variants: [dynamic]^ast.Expr;
@@ -2628,7 +2680,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
return lit;
}
-parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
+parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
args: [dynamic]^ast.Expr;
ellipsis: tokenizer.Token;
@@ -2686,6 +2738,14 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
ce.ellipsis = ellipsis;
ce.close = close.pos;
+ o := ast.unparen_expr(operand);
+ if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
+ sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end);
+ sce.expr = o;
+ sce.call = ce;
+ return sce;
+ }
+
return ce;
}
@@ -2739,7 +2799,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
case .Colon:
interval = advance_token(p);
is_slice_op = true;
- if (p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF) {
+ if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
indicies[1] = parse_expr(p, false);
}
}
@@ -2776,6 +2836,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
sel.expr = operand;
+ sel.op = tok;
sel.field = field;
operand = sel;
@@ -2811,6 +2872,24 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
}
+ case .Arrow_Right:
+ tok := expect_token(p, .Arrow_Right);
+ #partial switch p.curr_tok.kind {
+ case .Ident:
+ field := parse_ident(p);
+
+ sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
+ sel.expr = operand;
+ sel.op = tok;
+ sel.field = field;
+
+ operand = sel;
+ case:
+ error(p, p.curr_tok.pos, "expected a selector");
+ advance_token(p);
+ operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
+ }
+
case .Pointer:
op := expect_token(p, .Pointer);
deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op));
diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin
index 997ca7ac1..997b4967d 100644
--- a/core/odin/tokenizer/token.odin
+++ b/core/odin/tokenizer/token.odin
@@ -133,7 +133,6 @@ Token_Kind :: enum u32 {
Defer,
Return,
Proc,
- Macro,
Struct,
Union,
Enum,
@@ -150,11 +149,6 @@ Token_Kind :: enum u32 {
Inline,
No_Inline,
Context,
- Size_Of,
- Align_Of,
- Offset_Of,
- Type_Of,
- Const,
B_Keyword_End,
COUNT,
@@ -268,7 +262,6 @@ tokens := [Token_Kind.COUNT]string {
"defer",
"return",
"proc",
- "macro",
"struct",
"union",
"enum",
@@ -285,16 +278,24 @@ tokens := [Token_Kind.COUNT]string {
"inline",
"no_inline",
"context",
- "size_of",
- "align_of",
- "offset_of",
- "type_of",
- "const",
"",
};
custom_keyword_tokens: []string;
+
+is_newline :: proc(tok: Token) -> bool {
+ return tok.kind == .Semicolon && tok.text == "\n";
+}
+
+
+token_to_string :: proc(tok: Token) -> string {
+ if is_newline(tok) {
+ return "newline";
+ }
+ return to_string(tok.kind);
+}
+
to_string :: proc(kind: Token_Kind) -> string {
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
return tokens[kind];
diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin
index 132b63572..3df65e49b 100644
--- a/core/odin/tokenizer/tokenizer.odin
+++ b/core/odin/tokenizer/tokenizer.odin
@@ -1,22 +1,31 @@
package odin_tokenizer
import "core:fmt"
+import "core:unicode"
import "core:unicode/utf8"
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
+Flag :: enum {
+ Insert_Semicolon,
+}
+Flags :: distinct bit_set[Flag; u32];
+
Tokenizer :: struct {
// Immutable data
path: string,
src: []byte,
err: Error_Handler,
+ flags: Flags,
+
// Tokenizing state
ch: rune,
offset: int,
read_offset: int,
line_offset: int,
line_count: int,
+ insert_semicolon: bool,
// Mutable data
error_count: int,
@@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
}
skip_whitespace :: proc(t: ^Tokenizer) {
- for t.ch == ' ' ||
- t.ch == '\t' ||
- t.ch == '\n' ||
- t.ch == '\r' {
- advance_rune(t);
+ for {
+ switch t.ch {
+ case ' ', '\t', '\r':
+ advance_rune(t);
+ case '\n':
+ if t.insert_semicolon {
+ return;
+ }
+ advance_rune(t);
+ case:
+ return;
+ }
}
}
@@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool {
return true;
}
}
- // TODO(bill): Add unicode lookup tables
- return false;
+ return unicode.is_letter(r);
}
is_digit :: proc(r: rune) -> bool {
- // TODO(bill): Add unicode lookup tables
- return '0' <= r && r <= '9';
+ if '0' <= r && r <= '9' {
+ return true;
+ }
+ return unicode.is_digit(r);
}
@@ -491,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token {
lit: string;
pos := offset_to_pos(t, offset);
+ insert_semicolon := false;
+
switch ch := t.ch; true {
case is_letter(ch):
lit = scan_identifier(t);
@@ -509,24 +528,39 @@ scan :: proc(t: ^Tokenizer) -> Token {
break check_keyword;
}
}
- if kind == .Ident && lit == "notin" {
- kind = .Not_In;
+
+ #partial switch kind {
+ case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
+ insert_semicolon = true;
}
}
case '0' <= ch && ch <= '9':
+ insert_semicolon = true;
kind, lit = scan_number(t, false);
case:
advance_rune(t);
switch ch {
case -1:
kind = .EOF;
+ if t.insert_semicolon {
+ t.insert_semicolon = false;
+ kind = .Semicolon;
+ lit = "\n";
+ }
+ case '\n':
+ t.insert_semicolon = false;
+ kind = .Semicolon;
+ lit = "\n";
case '"':
+ insert_semicolon = true;
kind = .String;
lit = scan_string(t);
case '\'':
+ insert_semicolon = true;
kind = .Rune;
lit = scan_rune(t);
case '`':
+ insert_semicolon = true;
kind = .String;
lit = scan_raw_string(t);
case '=':
@@ -540,10 +574,13 @@ scan :: proc(t: ^Tokenizer) -> Token {
case '#':
kind = .Hash;
if t.ch == '!' {
+ insert_semicolon = t.insert_semicolon;
kind = .Comment;
lit = scan_comment(t);
}
- case '?': kind = .Question;
+ case '?':
+ insert_semicolon = true;
+ kind = .Question;
case '@': kind = .At;
case '$': kind = .Dollar;
case '^': kind = .Pointer;
@@ -562,6 +599,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
case '*': kind = switch2(t, .Mul, .Mul_Eq);
case '/':
if t.ch == '/' || t.ch == '*' {
+ insert_semicolon = t.insert_semicolon;
kind = .Comment;
lit = scan_comment(t);
} else {
@@ -604,11 +642,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
case ',': kind = .Comma;
case ';': kind = .Semicolon;
case '(': kind = .Open_Paren;
- case ')': kind = .Close_Paren;
+ case ')':
+ insert_semicolon = true;
+ kind = .Close_Paren;
case '[': kind = .Open_Bracket;
- case ']': kind = .Close_Bracket;
+ case ']':
+ insert_semicolon = true;
+ kind = .Close_Bracket;
case '{': kind = .Open_Brace;
- case '}': kind = .Close_Brace;
+ case '}':
+ insert_semicolon = true;
+ kind = .Close_Brace;
case '\\': kind = .Back_Slash;
@@ -616,10 +660,15 @@ scan :: proc(t: ^Tokenizer) -> Token {
if ch != utf8.RUNE_BOM {
error(t, t.offset, "illegal character '%r': %d", ch, ch);
}
+ insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
kind = .Invalid;
}
}
+ if .Insert_Semicolon in t.flags {
+ t.insert_semicolon = insert_semicolon;
+ }
+
if lit == "" {
lit = string(t.src[offset : t.offset]);
}
diff --git a/core/os/stream.odin b/core/os/stream.odin
new file mode 100644
index 000000000..db310e6ec
--- /dev/null
+++ b/core/os/stream.odin
@@ -0,0 +1,69 @@
+package os
+
+import "core:io"
+
+stream_from_handle :: proc(fd: Handle) -> io.Stream {
+ s: io.Stream;
+ s.stream_data = rawptr(uintptr(fd));
+ s.stream_vtable = _file_stream_vtable;
+ return s;
+}
+
+
+@(private)
+_file_stream_vtable := &io.Stream_VTable{
+ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ fd := Handle(uintptr(s.stream_data));
+ os_err: Errno;
+ n, os_err = read(fd, p);
+ return;
+ },
+ impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
+ when ODIN_OS == "windows" {
+ fd := Handle(uintptr(s.stream_data));
+ os_err: Errno;
+ n, os_err = read_at(fd, p, offset);
+ }
+ return;
+ },
+ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ fd := Handle(uintptr(s.stream_data));
+ os_err: Errno;
+ n, os_err = write(fd, p);
+ return;
+ },
+ impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
+ when ODIN_OS == "windows" {
+ fd := Handle(uintptr(s.stream_data));
+ os_err: Errno;
+ n, os_err = write_at(fd, p, offset);
+ _ = os_err;
+ }
+ return;
+ },
+ impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+ fd := Handle(uintptr(s.stream_data));
+ n, os_err := seek(fd, offset, int(whence));
+ _ = os_err;
+ return n, nil;
+ },
+ impl_size = proc(s: io.Stream) -> i64 {
+ fd := Handle(uintptr(s.stream_data));
+ sz, _ := file_size(fd);
+ return sz;
+ },
+ impl_flush = proc(s: io.Stream) -> io.Error {
+ when ODIN_OS == "windows" {
+ fd := Handle(uintptr(s.stream_data));
+ flush(fd);
+ } else {
+ // TOOD(bill): other operating systems
+ }
+ return nil;
+ },
+ impl_close = proc(s: io.Stream) -> io.Error {
+ fd := Handle(uintptr(s.stream_data));
+ close(fd);
+ return nil;
+ },
+};
diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin
index fc93bdfa2..b538e1640 100644
--- a/core/path/filepath/path_windows.odin
+++ b/core/path/filepath/path_windows.odin
@@ -8,7 +8,8 @@ SEPARATOR :: '\\';
SEPARATOR_STRING :: `\`;
LIST_SEPARATOR :: ';';
-reserved_names := []string{
+@(private)
+reserved_names := [?]string{
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
diff --git a/core/path/match.odin b/core/path/match.odin
index 555c1b05c..e77bf79c3 100644
--- a/core/path/match.odin
+++ b/core/path/match.odin
@@ -28,8 +28,6 @@ Match_Error :: enum {
// match requires that the pattern matches the entirety of the name, not just a substring
// The only possible error returned is .Syntax_Error
//
-// NOTE(bill): This is effectively the shell pattern matching system found
-//
match :: proc(pattern, name: string) -> (matched: bool, err: Match_Error) {
pattern, name := pattern, name;
pattern_loop: for len(pattern) > 0 {
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index 947a10771..ed50d658b 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -1206,3 +1206,82 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
return;
}
+
+/*
+not_equal :: proc(a, b: any) -> bool {
+ return !equal(a, b);
+}
+equal :: proc(a, b: any) -> bool {
+ if a == nil && b == nil {
+ return true;
+ }
+
+ if a.id != b.id {
+ return false;
+ }
+
+ if a.data == b.data {
+ return true;
+ }
+
+ t := type_info_of(a.id);
+ if .Comparable not_in t.flags {
+ return false;
+ }
+
+ if t.size == 0 {
+ return true;
+ }
+
+ if .Simple_Compare in t.flags {
+ return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0;
+ }
+
+ t = runtime.type_info_core(t);
+
+ #partial switch v in t.variant {
+ case Type_Info_String:
+ if v.is_cstring {
+ x := string((^cstring)(a.data)^);
+ y := string((^cstring)(b.data)^);
+ return x == y;
+ } else {
+ x := (^string)(a.data)^;
+ y := (^string)(b.data)^;
+ return x == y;
+ }
+
+ case Type_Info_Array:
+ for i in 0..<v.count {
+ x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
+ y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
+ if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
+ return false;
+ }
+ }
+ case Type_Info_Enumerated_Array:
+ for i in 0..<v.count {
+ x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
+ y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
+ if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
+ return false;
+ }
+ }
+ case Type_Info_Struct:
+ if v.equal != nil {
+ return v.equal(a.data, b.data);
+ } else {
+ for offset, i in v.offsets {
+ x := rawptr(uintptr(a.data) + offset);
+ y := rawptr(uintptr(b.data) + offset);
+ id := v.types[i].id;
+ if !equal(any{x, id}, any{y, id}) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+*/
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index 91de8ee0b..a775669aa 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -1,5 +1,6 @@
package reflect
+import "core:io"
import "core:strings"
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
@@ -218,6 +219,14 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
return false;
}
+is_byte :: proc(info: ^Type_Info) -> bool {
+ if info == nil { return false; }
+ #partial switch i in type_info_base(info).variant {
+ case Type_Info_Integer: return info.size == 1;
+ }
+ return false;
+}
+
is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
@@ -352,258 +361,278 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
-write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
+write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
write_type(buf, type_info_of(id));
}
+write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
+ write_type(writer, type_info_of(id));
+}
-write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) {
+write_typeid :: proc{
+ write_typeid_builder,
+ write_typeid_writer,
+};
+
+write_type :: proc{
+ write_type_builder,
+ write_type_writer,
+};
+
+write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) -> int {
+ return write_type_writer(strings.to_writer(buf), ti);
+}
+write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
using strings;
if ti == nil {
- write_string(buf, "nil");
- return;
+ return write_string(w, "nil");
}
+ _n1 :: proc(err: io.Error) -> int { return 1 if err == nil else 0; };
+ _n2 :: proc(n: int, _: io.Error) -> int { return n; };
+ _n :: proc{_n1, _n2};
+
switch info in ti.variant {
case Type_Info_Named:
- write_string(buf, info.name);
+ return write_string(w, info.name);
case Type_Info_Integer:
switch ti.id {
- case int: write_string(buf, "int");
- case uint: write_string(buf, "uint");
- case uintptr: write_string(buf, "uintptr");
+ case int: return write_string(w, "int");
+ case uint: return write_string(w, "uint");
+ case uintptr: return write_string(w, "uintptr");
case:
- write_byte(buf, 'i' if info.signed else 'u');
- write_i64(buf, i64(8*ti.size), 10);
+ n += _n(io.write_byte(w, 'i' if info.signed else 'u'));
+ n += _n(io.write_i64(w, i64(8*ti.size), 10));
switch info.endianness {
case .Platform: // Okay
- case .Little: write_string(buf, "le");
- case .Big: write_string(buf, "be");
+ case .Little: n += write_string(w, "le");
+ case .Big: n += write_string(w, "be");
}
}
case Type_Info_Rune:
- write_string(buf, "rune");
+ n += _n(io.write_string(w, "rune"));
case Type_Info_Float:
- write_byte(buf, 'f');
- write_i64(buf, i64(8*ti.size), 10);
+ n += _n(io.write_byte(w, 'f'));
+ n += _n(io.write_i64(w, i64(8*ti.size), 10));
switch info.endianness {
case .Platform: // Okay
- case .Little: write_string(buf, "le");
- case .Big: write_string(buf, "be");
+ case .Little: n += write_string(w, "le");
+ case .Big: n += write_string(w, "be");
}
case Type_Info_Complex:
- write_string(buf, "complex");
- write_i64(buf, i64(8*ti.size), 10);
+ n += _n(io.write_string(w, "complex"));
+ n += _n(io.write_i64(w, i64(8*ti.size), 10));
case Type_Info_Quaternion:
- write_string(buf, "quaternion");
- write_i64(buf, i64(8*ti.size), 10);
+ n += _n(io.write_string(w, "quaternion"));
+ n += _n(io.write_i64(w, i64(8*ti.size), 10));
case Type_Info_String:
if info.is_cstring {
- write_string(buf, "cstring");
+ n += write_string(w, "cstring");
} else {
- write_string(buf, "string");
+ n += write_string(w, "string");
}
case Type_Info_Boolean:
switch ti.id {
- case bool: write_string(buf, "bool");
+ case bool: n += write_string(w, "bool");
case:
- write_byte(buf, 'b');
- write_i64(buf, i64(8*ti.size), 10);
+ n += _n(io.write_byte(w, 'b'));
+ n += _n(io.write_i64(w, i64(8*ti.size), 10));
}
case Type_Info_Any:
- write_string(buf, "any");
+ n += write_string(w, "any");
case Type_Info_Type_Id:
- write_string(buf, "typeid");
+ n += write_string(w, "typeid");
case Type_Info_Pointer:
if info.elem == nil {
- write_string(buf, "rawptr");
+ write_string(w, "rawptr");
} else {
- write_string(buf, "^");
- write_type(buf, info.elem);
+ write_string(w, "^");
+ write_type(w, info.elem);
}
case Type_Info_Procedure:
- write_string(buf, "proc");
+ n += write_string(w, "proc");
if info.params == nil {
- write_string(buf, "()");
+ n += write_string(w, "()");
} else {
t := info.params.variant.(Type_Info_Tuple);
- write_string(buf, "(");
+ n += write_string(w, "(");
for t, i in t.types {
if i > 0 {
- write_string(buf, ", ");
+ n += write_string(w, ", ");
}
- write_type(buf, t);
+ n += write_type(w, t);
}
- write_string(buf, ")");
+ n += write_string(w, ")");
}
if info.results != nil {
- write_string(buf, " -> ");
- write_type(buf, info.results);
+ n += write_string(w, " -> ");
+ n += write_type(w, info.results);
}
case Type_Info_Tuple:
count := len(info.names);
- if count != 1 { write_string(buf, "("); }
+ if count != 1 { n += write_string(w, "("); }
for name, i in info.names {
- if i > 0 { write_string(buf, ", "); }
+ if i > 0 { n += write_string(w, ", "); }
t := info.types[i];
if len(name) > 0 {
- write_string(buf, name);
- write_string(buf, ": ");
+ n += write_string(w, name);
+ n += write_string(w, ": ");
}
- write_type(buf, t);
+ n += write_type(w, t);
}
- if count != 1 { write_string(buf, ")"); }
+ if count != 1 { n += write_string(w, ")"); }
case Type_Info_Array:
- write_string(buf, "[");
- write_i64(buf, i64(info.count), 10);
- write_string(buf, "]");
- write_type(buf, info.elem);
+ n += _n(io.write_string(w, "["));
+ n += _n(io.write_i64(w, i64(info.count), 10));
+ n += _n(io.write_string(w, "]"));
+ n += write_type(w, info.elem);
case Type_Info_Enumerated_Array:
- write_string(buf, "[");
- write_type(buf, info.index);
- write_string(buf, "]");
- write_type(buf, info.elem);
+ n += write_string(w, "[");
+ n += write_type(w, info.index);
+ n += write_string(w, "]");
+ n += write_type(w, info.elem);
case Type_Info_Dynamic_Array:
- write_string(buf, "[dynamic]");
- write_type(buf, info.elem);
+ n += _n(io.write_string(w, "[dynamic]"));
+ n += write_type(w, info.elem);
case Type_Info_Slice:
- write_string(buf, "[]");
- write_type(buf, info.elem);
+ n += _n(io.write_string(w, "[]"));
+ n += write_type(w, info.elem);
case Type_Info_Map:
- write_string(buf, "map[");
- write_type(buf, info.key);
- write_byte(buf, ']');
- write_type(buf, info.value);
+ n += _n(io.write_string(w, "map["));
+ n += write_type(w, info.key);
+ n += _n(io.write_byte(w, ']'));
+ n += write_type(w, info.value);
case Type_Info_Struct:
switch info.soa_kind {
case .None: // Ignore
case .Fixed:
- write_string(buf, "#soa[");
- write_i64(buf, i64(info.soa_len));
- write_byte(buf, ']');
- write_type(buf, info.soa_base_type);
+ n += _n(io.write_string(w, "#soa["));
+ n += _n(io.write_i64(w, i64(info.soa_len)));
+ n += _n(io.write_byte(w, ']'));
+ n += write_type(w, info.soa_base_type);
return;
case .Slice:
- write_string(buf, "#soa[]");
- write_type(buf, info.soa_base_type);
+ n += _n(io.write_string(w, "#soa[]"));
+ n += write_type(w, info.soa_base_type);
return;
case .Dynamic:
- write_string(buf, "#soa[dynamic]");
- write_type(buf, info.soa_base_type);
+ n += _n(io.write_string(w, "#soa[dynamic]"));
+ n += write_type(w, info.soa_base_type);
return;
}
- write_string(buf, "struct ");
- if info.is_packed { write_string(buf, "#packed "); }
- if info.is_raw_union { write_string(buf, "#raw_union "); }
+ n += write_string(w, "struct ");
+ if info.is_packed { n += write_string(w, "#packed "); }
+ if info.is_raw_union { n += write_string(w, "#raw_union "); }
if info.custom_align {
- write_string(buf, "#align ");
- write_i64(buf, i64(ti.align), 10);
- write_byte(buf, ' ');
+ n += _n(io.write_string(w, "#align "));
+ n += _n(io.write_i64(w, i64(ti.align), 10));
+ n += _n(io.write_byte(w, ' '));
}
- write_byte(buf, '{');
+ n += _n(io.write_byte(w, '{'));
for name, i in info.names {
- if i > 0 { write_string(buf, ", "); }
- write_string(buf, name);
- write_string(buf, ": ");
- write_type(buf, info.types[i]);
+ if i > 0 { n += write_string(w, ", "); }
+ n += _n(io.write_string(w, name));
+ n += _n(io.write_string(w, ": "));
+ n += write_type(w, info.types[i]);
}
- write_byte(buf, '}');
+ n += _n(io.write_byte(w, '}'));
case Type_Info_Union:
- write_string(buf, "union ");
+ n += write_string(w, "union ");
if info.custom_align {
- write_string(buf, "#align ");
- write_i64(buf, i64(ti.align), 10);
- write_byte(buf, ' ');
+ n += write_string(w, "#align ");
+ n += _n(io.write_i64(w, i64(ti.align), 10));
+ n += _n(io.write_byte(w, ' '));
}
- write_byte(buf, '{');
+ n += _n(io.write_byte(w, '{'));
for variant, i in info.variants {
- if i > 0 { write_string(buf, ", "); }
- write_type(buf, variant);
+ if i > 0 { n += write_string(w, ", "); }
+ n += write_type(w, variant);
}
- write_byte(buf, '}');
+ n += _n(io.write_byte(w, '}'));
case Type_Info_Enum:
- write_string(buf, "enum ");
- write_type(buf, info.base);
- write_string(buf, " {");
+ n += write_string(w, "enum ");
+ n += write_type(w, info.base);
+ n += write_string(w, " {");
for name, i in info.names {
- if i > 0 { write_string(buf, ", "); }
- write_string(buf, name);
+ if i > 0 { n += write_string(w, ", "); }
+ n += write_string(w, name);
}
- write_byte(buf, '}');
+ n += _n(io.write_byte(w, '}'));
case Type_Info_Bit_Field:
- write_string(buf, "bit_field ");
+ n += write_string(w, "bit_field ");
if ti.align != 1 {
- write_string(buf, "#align ");
- write_i64(buf, i64(ti.align), 10);
- write_byte(buf, ' ');
+ n += write_string(w, "#align ");
+ n += _n(io.write_i64(w, i64(ti.align), 10));
+ n += _n(io.write_byte(w, ' '));
}
- write_string(buf, " {");
+ n += write_string(w, " {");
for name, i in info.names {
- if i > 0 { write_string(buf, ", "); }
- write_string(buf, name);
- write_string(buf, ": ");
- write_i64(buf, i64(info.bits[i]), 10);
+ if i > 0 { n += write_string(w, ", "); }
+ n += write_string(w, name);
+ n += write_string(w, ": ");
+ n += _n(io.write_i64(w, i64(info.bits[i]), 10));
}
- write_byte(buf, '}');
+ n += _n(io.write_byte(w, '}'));
case Type_Info_Bit_Set:
- write_string(buf, "bit_set[");
+ n += write_string(w, "bit_set[");
switch {
case is_enum(info.elem):
- write_type(buf, info.elem);
+ n += write_type(w, info.elem);
case is_rune(info.elem):
- write_encoded_rune(buf, rune(info.lower));
- write_string(buf, "..");
- write_encoded_rune(buf, rune(info.upper));
+ n += write_encoded_rune(w, rune(info.lower));
+ n += write_string(w, "..");
+ n += write_encoded_rune(w, rune(info.upper));
case:
- write_i64(buf, info.lower, 10);
- write_string(buf, "..");
- write_i64(buf, info.upper, 10);
+ n += _n(io.write_i64(w, info.lower, 10));
+ n += write_string(w, "..");
+ n += _n(io.write_i64(w, info.upper, 10));
}
if info.underlying != nil {
- write_string(buf, "; ");
- write_type(buf, info.underlying);
+ n += write_string(w, "; ");
+ n += write_type(w, info.underlying);
}
- write_byte(buf, ']');
+ n += _n(io.write_byte(w, ']'));
case Type_Info_Opaque:
- write_string(buf, "opaque ");
- write_type(buf, info.elem);
+ n += write_string(w, "#opaque ");
+ n += write_type(w, info.elem);
case Type_Info_Simd_Vector:
if info.is_x86_mmx {
- write_string(buf, "intrinsics.x86_mmx");
+ n += write_string(w, "intrinsics.x86_mmx");
} else {
- write_string(buf, "#simd[");
- write_i64(buf, i64(info.count));
- write_byte(buf, ']');
- write_type(buf, info.elem);
+ n += write_string(w, "#simd[");
+ n += _n(io.write_i64(w, i64(info.count)));
+ n += _n(io.write_byte(w, ']'));
+ n += write_type(w, info.elem);
}
case Type_Info_Relative_Pointer:
- write_string(buf, "#relative(");
- write_type(buf, info.base_integer);
- write_string(buf, ") ");
- write_type(buf, info.pointer);
+ n += write_string(w, "#relative(");
+ n += write_type(w, info.base_integer);
+ n += write_string(w, ") ");
+ n += write_type(w, info.pointer);
case Type_Info_Relative_Slice:
- write_string(buf, "#relative(");
- write_type(buf, info.base_integer);
- write_string(buf, ") ");
- write_type(buf, info.slice);
-
+ n += write_string(w, "#relative(");
+ n += write_type(w, info.base_integer);
+ n += write_string(w, ") ");
+ n += write_type(w, info.slice);
}
+
+ return;
}
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index 7556a3645..2b8871f04 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -1,11 +1,7 @@
// This is the runtime code required by the compiler
// IMPORTANT NOTE(bill): Do not change the order of any of this data
// The compiler relies upon this _exact_ order
-package runtime
-
-import "intrinsics"
-_ :: intrinsics;
-
+//
// Naming Conventions:
// In general, Ada_Case for types and snake_case for values
//
@@ -16,12 +12,13 @@ _ :: intrinsics;
// Procedures: snake_case
// Local Variables: snake_case
// Constant Variables: SCREAMING_SNAKE_CASE
-
-
+//
// IMPORTANT NOTE(bill): `type_info_of` cannot be used within a
// #shared_global_scope due to the internals of the compiler.
// This could change at a later date if the all these data structures are
// implemented within the compiler rather than in this "preload" file
+//
+package runtime
// NOTE(bill): This must match the compiler's
Calling_Convention :: enum u8 {
@@ -45,6 +42,11 @@ Platform_Endianness :: enum u8 {
Big = 2,
}
+// Procedure type to test whether two values of the same type are equal
+Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool;
+// Procedure type to hash a value, default seed value is 0
+Hasher_Proc :: distinct proc "contextless" (data: rawptr, seed: uintptr = 0) -> uintptr;
+
Type_Info_Struct_Soa_Kind :: enum u8 {
None = 0,
Fixed = 1,
@@ -53,7 +55,12 @@ Type_Info_Struct_Soa_Kind :: enum u8 {
}
// Variant Types
-Type_Info_Named :: struct {name: string, base: ^Type_Info};
+Type_Info_Named :: struct {
+ name: string,
+ base: ^Type_Info,
+ pkg: string,
+ loc: Source_Code_Location,
+};
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness};
Type_Info_Rune :: struct {};
Type_Info_Float :: struct {endianness: Platform_Endianness};
@@ -87,10 +94,11 @@ Type_Info_Enumerated_Array :: struct {
};
Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int};
Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int};
-Type_Info_Tuple :: struct { // Only really used for procedures
+Type_Info_Tuple :: struct { // Only used for procedures parameters and results
types: []^Type_Info,
names: []string,
};
+
Type_Info_Struct :: struct {
types: []^Type_Info,
names: []string,
@@ -100,6 +108,9 @@ Type_Info_Struct :: struct {
is_packed: bool,
is_raw_union: bool,
custom_align: bool,
+
+ equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
+
// These are only set iff this structure is an SOA structure
soa_kind: Type_Info_Struct_Soa_Kind,
soa_base_type: ^Type_Info,
@@ -122,6 +133,8 @@ Type_Info_Map :: struct {
key: ^Type_Info,
value: ^Type_Info,
generated_struct: ^Type_Info,
+ key_equal: Equal_Proc,
+ key_hasher: Hasher_Proc,
};
Type_Info_Bit_Field :: struct {
names: []string,
@@ -152,9 +165,16 @@ Type_Info_Relative_Slice :: struct {
base_integer: ^Type_Info,
};
+Type_Info_Flag :: enum u8 {
+ Comparable = 0,
+ Simple_Compare = 1,
+};
+Type_Info_Flags :: distinct bit_set[Type_Info_Flag; u32];
+
Type_Info :: struct {
size: int,
align: int,
+ flags: Type_Info_Flags,
id: typeid,
variant: union {
@@ -237,15 +257,11 @@ args__: []cstring;
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
-@builtin
-Maybe :: union(T: typeid) #maybe {T};
-
Source_Code_Location :: struct {
file_path: string,
line, column: int,
procedure: string,
- hash: u64,
}
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
@@ -320,6 +336,9 @@ Context :: struct {
user_data: any,
user_ptr: rawptr,
user_index: int,
+
+ // Internal use only
+ _internal: rawptr,
}
@@ -345,47 +364,6 @@ Raw_Map :: struct {
entries: Raw_Dynamic_Array,
}
-INITIAL_MAP_CAP :: 16;
-
-Map_Key :: struct {
- hash: u64,
- /* NOTE(bill)
- size_of(Map_Key) == 16 Bytes on 32-bit systems
- size_of(Map_Key) == 24 Bytes on 64-bit systems
-
- This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems
- however, this is probably not a huge problem in terms of memory usage
- */
- key: struct #raw_union {
- str: string,
- val: u64,
- },
-}
-
-Map_Find_Result :: struct {
- hash_index: int,
- entry_prev: int,
- entry_index: int,
-}
-
-Map_Entry_Header :: struct {
- key: Map_Key,
- next: int,
-/*
- value: Value_Type,
-*/
-}
-
-Map_Header :: struct {
- m: ^Raw_Map,
- is_key_string: bool,
-
- entry_size: int,
- entry_align: int,
-
- value_offset: uintptr,
- value_size: int,
-}
/////////////////////////////
// Init Startup Procedures //
@@ -521,13 +499,6 @@ __init_context :: proc "contextless" (c: ^Context) {
c.logger.data = nil;
}
-@thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
-
-@builtin
-init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
- default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator);
-}
-
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) {
print_caller_location(loc);
@@ -540,1224 +511,3 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
print_byte('\n');
debug_trap();
}
-
-
-
-
-@builtin
-copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
- n := max(0, min(len(dst), len(src)));
- if n > 0 {
- mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
- }
- return n;
-}
-@builtin
-copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
- n := max(0, min(len(dst), len(src)));
- if n > 0 {
- mem_copy(raw_data(dst), raw_data(src), n);
- }
- return n;
-}
-@builtin
-copy :: proc{copy_slice, copy_from_string};
-
-
-
-@builtin
-unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
- bounds_check_error_loc(loc, index, len(array));
- n := len(array)-1;
- if index != n {
- array[index] = array[n];
- }
- pop(array);
-}
-
-@builtin
-ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
- bounds_check_error_loc(loc, index, len(array));
- if index+1 < len(array) {
- copy(array[index:], array[index+1:]);
- }
- pop(array);
-}
-
-@builtin
-remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
- slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
- n := max(hi-lo, 0);
- if n > 0 {
- if hi != len(array) {
- copy(array[lo:], array[hi:]);
- }
- (^Raw_Dynamic_Array)(array).len -= n;
- }
-}
-
-
-@builtin
-pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
- assert(len(array) > 0, "", loc);
- res = array[len(array)-1];
- (^Raw_Dynamic_Array)(array).len -= 1;
- return res;
-}
-
-
-@builtin
-pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
- if len(array) == 0 {
- return;
- }
- res, ok = array[len(array)-1], true;
- (^Raw_Dynamic_Array)(array).len -= 1;
- return;
-}
-
-@builtin
-pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
- assert(len(array) > 0, "", loc);
- res = array[0];
- if len(array) > 1 {
- copy(array[0:], array[1:]);
- }
- (^Raw_Dynamic_Array)(array).len -= 1;
- return res;
-}
-
-@builtin
-pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
- if len(array) == 0 {
- return;
- }
- res, ok = array[0], true;
- if len(array) > 1 {
- copy(array[0:], array[1:]);
- }
- (^Raw_Dynamic_Array)(array).len -= 1;
- return;
-}
-
-
-@builtin
-clear :: proc{clear_dynamic_array, clear_map};
-
-@builtin
-reserve :: proc{reserve_dynamic_array, reserve_map};
-
-@builtin
-resize :: proc{resize_dynamic_array};
-
-
-@builtin
-free :: proc{mem_free};
-
-@builtin
-free_all :: proc{mem_free_all};
-
-
-
-@builtin
-delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
- mem_free(raw_data(str), allocator, loc);
-}
-@builtin
-delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
- mem_free((^byte)(str), allocator, loc);
-}
-@builtin
-delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
- mem_free(raw_data(array), array.allocator, loc);
-}
-@builtin
-delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
- mem_free(raw_data(array), allocator, loc);
-}
-@builtin
-delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
- raw := transmute(Raw_Map)m;
- delete_slice(raw.hashes);
- mem_free(raw.entries.data, raw.entries.allocator, loc);
-}
-
-
-@builtin
-delete :: proc{
- delete_string,
- delete_cstring,
- delete_dynamic_array,
- delete_slice,
- delete_map,
-};
-
-
-@builtin
-new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
- ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
- if ptr != nil { ptr^ = T{}; }
- return ptr;
-}
-
-@builtin
-new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
- ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
- if ptr != nil { ptr^ = data; }
- return ptr;
-}
-
-make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
- make_slice_error_loc(loc, len);
- data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
- if data == nil && size_of(E) != 0 {
- return nil;
- }
- // mem_zero(data, size_of(E)*len);
- s := Raw_Slice{data, len};
- return transmute(T)s;
-}
-
-@builtin
-make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
- return make_aligned(T, len, align_of(E), allocator, loc);
-}
-
-@builtin
-make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
- return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
-}
-
-@builtin
-make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
- return make_dynamic_array_len_cap(T, len, len, allocator, loc);
-}
-
-@builtin
-make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
- make_dynamic_array_error_loc(loc, len, cap);
- data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
- s := Raw_Dynamic_Array{data, len, cap, allocator};
- if data == nil && size_of(E) != 0 {
- s.len, s.cap = 0, 0;
- }
- // mem_zero(data, size_of(E)*cap);
- return transmute(T)s;
-}
-
-@builtin
-make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
- make_map_expr_error_loc(loc, cap);
- context.allocator = allocator;
-
- m: T;
- reserve_map(&m, cap);
- return m;
-}
-
-@builtin
-make :: proc{
- make_slice,
- make_dynamic_array,
- make_dynamic_array_len,
- make_dynamic_array_len_cap,
- make_map,
-};
-
-
-
-@builtin
-clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
- if m == nil {
- return;
- }
- raw_map := (^Raw_Map)(m);
- entries := (^Raw_Dynamic_Array)(&raw_map.entries);
- entries.len = 0;
- for _, i in raw_map.hashes {
- raw_map.hashes[i] = -1;
- }
-}
-
-@builtin
-reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
- if m != nil {
- __dynamic_map_reserve(__get_map_header(m), capacity);
- }
-}
-
-@builtin
-delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
- if m != nil {
- __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key));
- }
-}
-
-
-
-@builtin
-append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
- if array == nil {
- return;
- }
-
- arg_len := 1;
-
- if cap(array) < len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- a := (^Raw_Dynamic_Array)(array);
- if size_of(E) != 0 {
- data := (^E)(a.data);
- assert(data != nil);
- val := arg;
- mem_copy(ptr_offset(data, a.len), &val, size_of(E));
- }
- a.len += arg_len;
- }
-}
-@builtin
-append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
- if array == nil {
- return;
- }
-
- arg_len := len(args);
- if arg_len <= 0 {
- return;
- }
-
-
- if cap(array) < len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- a := (^Raw_Dynamic_Array)(array);
- if size_of(E) != 0 {
- data := (^E)(a.data);
- assert(data != nil);
- mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
- }
- a.len += arg_len;
- }
-}
-@builtin
-append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
- args := transmute([]E)arg;
- append_elems(array=array, args=args, loc=loc);
-}
-
-@builtin
-reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
-
- old_cap := cap(array);
- if capacity <= old_cap {
- return true;
- }
-
- if array.allocator.procedure == nil {
- array.allocator = context.allocator;
- }
- assert(array.allocator.procedure != nil);
-
-
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
-
- field_count := uintptr(len(si.offsets) - 3);
-
- if field_count == 0 {
- return true;
- }
-
- cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
- assert(cap_ptr^ == old_cap);
-
-
- old_size := 0;
- new_size := 0;
-
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- old_size = align_forward_int(old_size, type.align);
- new_size = align_forward_int(new_size, type.align);
-
- old_size += type.size * old_cap;
- new_size += type.size * capacity;
- }
-
- old_size = align_forward_int(old_size, max_align);
- new_size = align_forward_int(new_size, max_align);
-
- old_data := (^rawptr)(array)^;
-
- new_data := array.allocator.procedure(
- array.allocator.data, .Alloc, new_size, max_align,
- nil, old_size, 0, loc,
- );
- if new_data == nil {
- return false;
- }
-
-
- cap_ptr^ = capacity;
-
- old_offset := 0;
- new_offset := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- old_offset = align_forward_int(old_offset, type.align);
- new_offset = align_forward_int(new_offset, type.align);
-
- new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
- old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
-
- mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
-
- (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
-
- old_offset += type.size * old_cap;
- new_offset += type.size * capacity;
- }
-
- array.allocator.procedure(
- array.allocator.data, .Free, 0, max_align,
- old_data, old_size, 0, loc,
- );
-
- return true;
-}
-
-@builtin
-append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
- if array == nil {
- return;
- }
-
- arg_len := 1;
-
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(len(si.offsets) - 3);
-
- if field_count == 0 {
- return;
- }
-
- data := (^rawptr)(array)^;
-
- len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
-
-
- soa_offset := 0;
- item_offset := 0;
-
- arg_copy := arg;
- arg_ptr := &arg_copy;
-
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- soa_offset = align_forward_int(soa_offset, type.align);
- item_offset = align_forward_int(item_offset, type.align);
-
- dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
- src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
- mem_copy(dst, src, type.size);
-
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
-
- len_ptr^ += arg_len;
- }
-}
-
-@builtin
-append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
- if array == nil {
- return;
- }
-
- arg_len := len(args);
- if arg_len == 0 {
- return;
- }
-
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(len(si.offsets) - 3);
-
- if field_count == 0 {
- return;
- }
-
- data := (^rawptr)(array)^;
-
- len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
-
-
- soa_offset := 0;
- item_offset := 0;
-
- args_ptr := &args[0];
-
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- soa_offset = align_forward_int(soa_offset, type.align);
- item_offset = align_forward_int(item_offset, type.align);
-
- dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
- src := uintptr(args_ptr) + uintptr(item_offset);
- for j in 0..<arg_len {
- d := rawptr(dst + uintptr(j*type.size));
- s := rawptr(src + uintptr(j*size_of(E)));
- mem_copy(d, s, type.size);
- }
-
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
-
- len_ptr^ += arg_len;
- }
-}
-
-@builtin
-append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
- for arg in args {
- append(array = array, args = transmute([]E)(arg), loc = loc);
- }
-}
-
-
-@builtin append :: proc{append_elem, append_elems, append_elem_string};
-@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
-
-@builtin
-append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
- if array == nil {
- return;
- }
- resize(array, len(array)+1);
-}
-
-
-@builtin
-insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
- if array == nil {
- return;
- }
- n := len(array);
- m :: 1;
- resize(array, n+m, loc);
- if n+m <= len(array) {
- when size_of(E) != 0 {
- copy(array[index+m:], array[index:]);
- array[index] = arg;
- }
- ok = true;
- }
- return;
-}
-
-@builtin
-insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
- if array == nil {
- return;
- }
- if len(args) == 0 {
- ok = true;
- return;
- }
-
- n := len(array);
- m := len(args);
- resize(array, n+m, loc);
- if n+m <= len(array) {
- when size_of(E) != 0 {
- copy(array[index+m:], array[index:]);
- copy(array[index:], args);
- }
- ok = true;
- }
- return;
-}
-
-@builtin
-insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
- if array == nil {
- return;
- }
- if len(args) == 0 {
- ok = true;
- return;
- }
-
- n := len(array);
- m := len(args);
- resize(array, n+m, loc);
- if n+m <= len(array) {
- copy(array[index+m:], array[index:]);
- copy(array[index:], args);
- ok = true;
- }
- return;
-}
-
-@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string};
-
-
-
-
-@builtin
-clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
- if array != nil {
- (^Raw_Dynamic_Array)(array).len = 0;
- }
-}
-
-@builtin
-reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
- a := (^Raw_Dynamic_Array)(array);
-
- if capacity <= a.cap {
- return true;
- }
-
- if a.allocator.procedure == nil {
- a.allocator = context.allocator;
- }
- assert(a.allocator.procedure != nil);
-
- old_size := a.cap * size_of(E);
- new_size := capacity * size_of(E);
- allocator := a.allocator;
-
- new_data := allocator.procedure(
- allocator.data, .Resize, new_size, align_of(E),
- a.data, old_size, 0, loc,
- );
- if new_data == nil {
- return false;
- }
-
- a.data = new_data;
- a.cap = capacity;
- return true;
-}
-
-@builtin
-resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
- a := (^Raw_Dynamic_Array)(array);
-
- if length <= a.cap {
- a.len = max(length, 0);
- return true;
- }
-
- if a.allocator.procedure == nil {
- a.allocator = context.allocator;
- }
- assert(a.allocator.procedure != nil);
-
- old_size := a.cap * size_of(E);
- new_size := length * size_of(E);
- allocator := a.allocator;
-
- new_data := allocator.procedure(
- allocator.data, .Resize, new_size, align_of(E),
- a.data, old_size, 0, loc,
- );
- if new_data == nil {
- return false;
- }
-
- a.data = new_data;
- a.len = length;
- a.cap = length;
- return true;
-}
-
-
-
-@builtin
-incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
- s^ |= {elem};
- return s^;
-}
-@builtin
-incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
- for elem in elems {
- s^ |= {elem};
- }
- return s^;
-}
-@builtin
-incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
- s^ |= other;
- return s^;
-}
-@builtin
-excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
- s^ &~= {elem};
- return s^;
-}
-@builtin
-excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
- for elem in elems {
- s^ &~= {elem};
- }
- return s^;
-}
-@builtin
-excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
- s^ &~= other;
- return s^;
-}
-
-@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
-@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
-
-
-@builtin
-card :: proc(s: $S/bit_set[$E; $U]) -> int {
- when size_of(S) == 1 {
- foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- }
- return int(count_ones(transmute(u8)s));
- } else when size_of(S) == 2 {
- foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
- return int(count_ones(transmute(u16)s));
- } else when size_of(S) == 4 {
- foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
- return int(count_ones(transmute(u32)s));
- } else when size_of(S) == 8 {
- foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
- return int(count_ones(transmute(u64)s));
- } else when size_of(S) == 16 {
- foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
- return int(count_ones(transmute(u128)s));
- } else {
- #panic("Unhandled card bit_set size");
- }
-}
-
-
-
-@builtin
-raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
- return (^E)(a);
-}
-@builtin
-raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
- ptr := (transmute(Raw_Slice)s).data;
- return (^E)(ptr);
-}
-@builtin
-raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
- ptr := (transmute(Raw_Dynamic_Array)s).data;
- return (^E)(ptr);
-}
-@builtin
-raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
- return (transmute(Raw_String)s).data;
-}
-
-@builtin
-raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data};
-
-
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-assert :: proc(condition: bool, message := "", loc := #caller_location) {
- if !condition {
- proc(message: string, loc: Source_Code_Location) {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- p("runtime assertion", message, loc);
- }(message, loc);
- }
-}
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-panic :: proc(message: string, loc := #caller_location) -> ! {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- p("panic", message, loc);
-}
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-unimplemented :: proc(message := "", loc := #caller_location) -> ! {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- p("not yet implemented", message, loc);
-}
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-unreachable :: proc(message := "", loc := #caller_location) -> ! {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- if message != "" {
- p("internal error", message, loc);
- } else {
- p("internal error", "entered unreachable code", loc);
- }
-}
-
-
-// Dynamic Array
-
-
-__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
- array := (^Raw_Dynamic_Array)(array_);
- array.allocator = context.allocator;
- assert(array.allocator.procedure != nil);
-
- if cap > 0 {
- __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc);
- array.len = len;
- }
-}
-
-__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
- array := (^Raw_Dynamic_Array)(array_);
-
- // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
- // assuming that appending/reserving will set the allocator, if it is not already set.
- if array.allocator.procedure == nil {
- array.allocator = context.allocator;
- }
- assert(array.allocator.procedure != nil);
-
- if cap <= array.cap {
- return true;
- }
-
- old_size := array.cap * elem_size;
- new_size := cap * elem_size;
- allocator := array.allocator;
-
- new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
- if new_data != nil || elem_size == 0 {
- array.data = new_data;
- array.cap = cap;
- return true;
- }
- return false;
-}
-
-__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
- array := (^Raw_Dynamic_Array)(array_);
-
- ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
- if ok {
- array.len = len;
- }
- return ok;
-}
-
-
-__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
- items: rawptr, item_count: int, loc := #caller_location) -> int {
- array := (^Raw_Dynamic_Array)(array_);
-
- if items == nil {
- return 0;
- }
- if item_count <= 0 {
- return 0;
- }
-
-
- ok := true;
- if array.cap <= array.len+item_count {
- cap := 2 * array.cap + max(8, item_count);
- ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
- }
- // TODO(bill): Better error handling for failed reservation
- if !ok {
- return array.len;
- }
-
- assert(array.data != nil);
- data := uintptr(array.data) + uintptr(elem_size*array.len);
-
- mem_copy(rawptr(data), items, elem_size * item_count);
- array.len += item_count;
- return array.len;
-}
-
-__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
- array := (^Raw_Dynamic_Array)(array_);
-
- ok := true;
- if array.cap <= array.len+1 {
- cap := 2 * array.cap + max(8, 1);
- ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
- }
- // TODO(bill): Better error handling for failed reservation
- if !ok {
- return array.len;
- }
-
- assert(array.data != nil);
- data := uintptr(array.data) + uintptr(elem_size*array.len);
- mem_zero(rawptr(data), elem_size);
- array.len += 1;
- return array.len;
-}
-
-
-
-
-// Map
-
-__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
- header := Map_Header{m = (^Raw_Map)(m)};
- Entry :: struct {
- key: Map_Key,
- next: int,
- value: V,
- };
-
- header.is_key_string = intrinsics.type_is_string(K);
- header.entry_size = int(size_of(Entry));
- header.entry_align = int(align_of(Entry));
- header.value_offset = uintptr(offset_of(Entry, value));
- header.value_size = int(size_of(V));
- return header;
-}
-
-__get_map_key :: proc "contextless" (k: $K) -> Map_Key {
- key := k;
- map_key: Map_Key;
-
- T :: intrinsics.type_core_type(K);
-
- when intrinsics.type_is_integer(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
-
- sz :: 8*size_of(T);
- when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); }
- else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); }
- else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); }
- else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); }
- else { #panic("Unhandled integer size"); }
- } else when intrinsics.type_is_rune(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
- map_key.key.val = u64((^rune)(&key)^);
- } else when intrinsics.type_is_pointer(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
- map_key.key.val = u64(uintptr((^rawptr)(&key)^));
- } else when intrinsics.type_is_float(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
-
- sz :: 8*size_of(T);
- when sz == 32 { map_key.key.val = u64((^u32)(&key)^); }
- else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); }
- else { #panic("Unhandled float size"); }
- } else when intrinsics.type_is_string(T) {
- #assert(T == string);
- str := (^string)(&key)^;
- map_key.hash = default_hash_string(str);
- map_key.key.str = str;
- } else {
- #panic("Unhandled map key type");
- }
-
- return map_key;
-}
-
-_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 {
- h: u64 = seed;
- for b in data {
- h = (h ~ u64(b)) * 0x100000001b3;
- }
- return h;
-}
-
-
-default_hash :: inline proc "contextless" (data: []byte) -> u64 {
- return _fnv64a(data);
-}
-default_hash_string :: inline proc "contextless" (s: string) -> u64 {
- return default_hash(transmute([]byte)(s));
-}
-default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 {
- s := Raw_Slice{data, size};
- return default_hash(transmute([]byte)(s));
-}
-
-
-source_code_location_hash :: proc(s: Source_Code_Location) -> u64 {
- hash := _fnv64a(transmute([]byte)s.file_path);
- hash = hash ~ (u64(s.line) * 0x100000001b3);
- hash = hash ~ (u64(s.column) * 0x100000001b3);
- return hash;
-}
-
-
-
-__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
- array := (^Raw_Slice)(array_);
-
- if new_count < array.len {
- return true;
- }
-
- assert(allocator.procedure != nil);
-
- old_size := array.len*size_of(T);
- new_size := new_count*size_of(T);
-
- new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
- if new_data == nil {
- return false;
- }
- array.data = new_data;
- array.len = new_count;
- return true;
-}
-
-__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
- __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc);
-
- old_len := len(m.hashes);
- __slice_resize(&m.hashes, cap, m.entries.allocator, loc);
- for i in old_len..<len(m.hashes) {
- m.hashes[i] = -1;
- }
-
-}
-__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
- new_header: Map_Header = header;
- nm := Raw_Map{};
- nm.entries.allocator = m.entries.allocator;
- new_header.m = &nm;
-
- c := context;
- if m.entries.allocator.procedure != nil {
- c.allocator = m.entries.allocator;
- }
- context = c;
-
- __dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
- __slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
- for i in 0 ..< new_count {
- nm.hashes[i] = -1;
- }
-
- for i in 0 ..< m.entries.len {
- if len(nm.hashes) == 0 {
- __dynamic_map_grow(new_header, loc);
- }
-
- entry_header := __dynamic_map_get_entry(header, i);
- data := uintptr(entry_header);
-
- fr := __dynamic_map_find(new_header, entry_header.key);
- j := __dynamic_map_add_entry(new_header, entry_header.key, loc);
- if fr.entry_prev < 0 {
- nm.hashes[fr.hash_index] = j;
- } else {
- e := __dynamic_map_get_entry(new_header, fr.entry_prev);
- e.next = j;
- }
-
- e := __dynamic_map_get_entry(new_header, j);
- e.next = fr.entry_index;
- ndata := uintptr(e);
- mem_copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size);
-
- if __dynamic_map_full(new_header) {
- __dynamic_map_grow(new_header, loc);
- }
- }
- delete(m.hashes, m.entries.allocator, loc);
- free(m.entries.data, m.entries.allocator, loc);
- header.m^ = nm;
-}
-
-__dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> rawptr {
- index := __dynamic_map_find(h, key).entry_index;
- if index >= 0 {
- data := uintptr(__dynamic_map_get_entry(h, index));
- return rawptr(data + h.value_offset);
- }
- return nil;
-}
-
-__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check {
- index: int;
- assert(value != nil);
-
- if len(h.m.hashes) == 0 {
- __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc);
- __dynamic_map_grow(h, loc);
- }
-
- fr := __dynamic_map_find(h, key);
- if fr.entry_index >= 0 {
- index = fr.entry_index;
- } else {
- index = __dynamic_map_add_entry(h, key, loc);
- if fr.entry_prev >= 0 {
- entry := __dynamic_map_get_entry(h, fr.entry_prev);
- entry.next = index;
- } else {
- h.m.hashes[fr.hash_index] = index;
- }
- }
- {
- e := __dynamic_map_get_entry(h, index);
- e.key = key;
- val := (^byte)(uintptr(e) + h.value_offset);
- mem_copy(val, value, h.value_size);
- }
-
- if __dynamic_map_full(h) {
- __dynamic_map_grow(h, loc);
- }
-}
-
-
-__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
- // TODO(bill): Determine an efficient growing rate
- new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP);
- __dynamic_map_rehash(h, new_count, loc);
-}
-
-__dynamic_map_full :: inline proc(using h: Map_Header) -> bool {
- return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
-}
-
-
-__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool {
- if a.hash == b.hash {
- if h.is_key_string {
- return a.key.str == b.key.str;
- } else {
- return a.key.val == b.key.val;
- }
- return true;
- }
- return false;
-}
-
-__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check {
- fr := Map_Find_Result{-1, -1, -1};
- if n := u64(len(m.hashes)); n > 0 {
- fr.hash_index = int(key.hash % n);
- fr.entry_index = m.hashes[fr.hash_index];
- for fr.entry_index >= 0 {
- entry := __dynamic_map_get_entry(h, fr.entry_index);
- if __dynamic_map_hash_equal(h, entry.key, key) {
- return fr;
- }
- fr.entry_prev = fr.entry_index;
- fr.entry_index = entry.next;
- }
- }
- return fr;
-}
-
-__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int {
- prev := m.entries.len;
- c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc);
- if c != prev {
- end := __dynamic_map_get_entry(h, c-1);
- end.key = key;
- end.next = -1;
- }
- return prev;
-}
-
-__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) {
- fr := __dynamic_map_find(h, key);
- if fr.entry_index >= 0 {
- __dynamic_map_erase(h, fr);
- }
-}
-
-__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
- assert(0 <= index && index < m.entries.len);
- return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size));
-}
-
-__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
- if fr.entry_prev < 0 {
- m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
- } else {
- prev := __dynamic_map_get_entry(h, fr.entry_prev);
- curr := __dynamic_map_get_entry(h, fr.entry_index);
- prev.next = curr.next;
- }
- if (fr.entry_index == m.entries.len-1) {
- // NOTE(bill): No need to do anything else, just pop
- } else {
- old := __dynamic_map_get_entry(h, fr.entry_index);
- end := __dynamic_map_get_entry(h, m.entries.len-1);
- mem_copy(old, end, entry_size);
-
- if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 {
- last_entry := __dynamic_map_get_entry(h, last.entry_prev);
- last_entry.next = fr.entry_index;
- } else {
- m.hashes[last.hash_index] = fr.entry_index;
- }
- }
-
- // TODO(bill): Is this correct behaviour?
- m.entries.len -= 1;
-}
diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin
new file mode 100644
index 000000000..8a1be60d9
--- /dev/null
+++ b/core/runtime/core_builtin.odin
@@ -0,0 +1,838 @@
+package runtime
+
+@builtin
+Maybe :: union(T: typeid) #maybe {T};
+
+@thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
+
+@builtin
+init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
+ default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator);
+}
+
+
+@builtin
+copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
+ n := max(0, min(len(dst), len(src)));
+ if n > 0 {
+ mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
+ }
+ return n;
+}
+@builtin
+copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
+ n := max(0, min(len(dst), len(src)));
+ if n > 0 {
+ mem_copy(raw_data(dst), raw_data(src), n);
+ }
+ return n;
+}
+@builtin
+copy :: proc{copy_slice, copy_from_string};
+
+
+
+@builtin
+unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
+ bounds_check_error_loc(loc, index, len(array));
+ n := len(array)-1;
+ if index != n {
+ array[index] = array[n];
+ }
+ pop(array);
+}
+
+@builtin
+ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
+ bounds_check_error_loc(loc, index, len(array));
+ if index+1 < len(array) {
+ copy(array[index:], array[index+1:]);
+ }
+ pop(array);
+}
+
+@builtin
+remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
+ slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
+ n := max(hi-lo, 0);
+ if n > 0 {
+ if hi != len(array) {
+ copy(array[lo:], array[hi:]);
+ }
+ (^Raw_Dynamic_Array)(array).len -= n;
+ }
+}
+
+
+@builtin
+pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
+ assert(len(array) > 0, "", loc);
+ res = array[len(array)-1];
+ (^Raw_Dynamic_Array)(array).len -= 1;
+ return res;
+}
+
+
+@builtin
+pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
+ if len(array) == 0 {
+ return;
+ }
+ res, ok = array[len(array)-1], true;
+ (^Raw_Dynamic_Array)(array).len -= 1;
+ return;
+}
+
+@builtin
+pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
+ assert(len(array) > 0, "", loc);
+ res = array[0];
+ if len(array) > 1 {
+ copy(array[0:], array[1:]);
+ }
+ (^Raw_Dynamic_Array)(array).len -= 1;
+ return res;
+}
+
+@builtin
+pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
+ if len(array) == 0 {
+ return;
+ }
+ res, ok = array[0], true;
+ if len(array) > 1 {
+ copy(array[0:], array[1:]);
+ }
+ (^Raw_Dynamic_Array)(array).len -= 1;
+ return;
+}
+
+
+@builtin
+clear :: proc{clear_dynamic_array, clear_map};
+
+@builtin
+reserve :: proc{reserve_dynamic_array, reserve_map};
+
+@builtin
+resize :: proc{resize_dynamic_array};
+
+
+@builtin
+free :: proc{mem_free};
+
+@builtin
+free_all :: proc{mem_free_all};
+
+
+
+@builtin
+delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
+ mem_free(raw_data(str), allocator, loc);
+}
+@builtin
+delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
+ mem_free((^byte)(str), allocator, loc);
+}
+@builtin
+delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
+ mem_free(raw_data(array), array.allocator, loc);
+}
+@builtin
+delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
+ mem_free(raw_data(array), allocator, loc);
+}
+@builtin
+delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
+ raw := transmute(Raw_Map)m;
+ delete_slice(raw.hashes);
+ mem_free(raw.entries.data, raw.entries.allocator, loc);
+}
+
+
+@builtin
+delete :: proc{
+ delete_string,
+ delete_cstring,
+ delete_dynamic_array,
+ delete_slice,
+ delete_map,
+};
+
+
+// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
+// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
+@builtin
+new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
+ ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
+ if ptr != nil { ptr^ = T{}; }
+ return ptr;
+}
+
+@builtin
+new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
+ ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
+ if ptr != nil { ptr^ = data; }
+ return ptr;
+}
+
+make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
+ make_slice_error_loc(loc, len);
+ data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
+ if data == nil && size_of(E) != 0 {
+ return nil;
+ }
+ // mem_zero(data, size_of(E)*len);
+ s := Raw_Slice{data, len};
+ return transmute(T)s;
+}
+
+@builtin
+make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
+ return make_aligned(T, len, align_of(E), allocator, loc);
+}
+
+@builtin
+make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
+ return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
+}
+
+@builtin
+make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
+ return make_dynamic_array_len_cap(T, len, len, allocator, loc);
+}
+
+@builtin
+make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
+ make_dynamic_array_error_loc(loc, len, cap);
+ data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
+ s := Raw_Dynamic_Array{data, len, cap, allocator};
+ if data == nil && size_of(E) != 0 {
+ s.len, s.cap = 0, 0;
+ }
+ // mem_zero(data, size_of(E)*cap);
+ return transmute(T)s;
+}
+
+@builtin
+make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
+ make_map_expr_error_loc(loc, cap);
+ context.allocator = allocator;
+
+ m: T;
+ reserve_map(&m, cap);
+ return m;
+}
+
+// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only)
+// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the
+// type of its argument, not a pointer to it.
+// Make uses the specified allocator, default is context.allocator, default is context.allocator
+@builtin
+make :: proc{
+ make_slice,
+ make_dynamic_array,
+ make_dynamic_array_len,
+ make_dynamic_array_len_cap,
+ make_map,
+};
+
+
+
+@builtin
+clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
+ if m == nil {
+ return;
+ }
+ raw_map := (^Raw_Map)(m);
+ entries := (^Raw_Dynamic_Array)(&raw_map.entries);
+ entries.len = 0;
+ for _, i in raw_map.hashes {
+ raw_map.hashes[i] = -1;
+ }
+}
+
+@builtin
+reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
+ if m != nil {
+ __dynamic_map_reserve(__get_map_header(m), capacity);
+ }
+}
+
+// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
+// If m is nil, or there is no such element, this procedure is a no-op
+@builtin
+delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
+ if m != nil {
+ key := key;
+ __dynamic_map_delete_key(__get_map_header(m), __get_map_hash(&key));
+ }
+}
+
+
+
+@builtin
+append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
+ if array == nil {
+ return;
+ }
+
+ arg_len := 1;
+
+ if cap(array) < len(array)+arg_len {
+ cap := 2 * cap(array) + max(8, arg_len);
+ _ = reserve(array, cap, loc);
+ }
+ arg_len = min(cap(array)-len(array), arg_len);
+ if arg_len > 0 {
+ a := (^Raw_Dynamic_Array)(array);
+ if size_of(E) != 0 {
+ data := (^E)(a.data);
+ assert(data != nil);
+ val := arg;
+ mem_copy(ptr_offset(data, a.len), &val, size_of(E));
+ }
+ a.len += arg_len;
+ }
+}
+
+@builtin
+append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
+ if array == nil {
+ return;
+ }
+
+ arg_len := len(args);
+ if arg_len <= 0 {
+ return;
+ }
+
+
+ if cap(array) < len(array)+arg_len {
+ cap := 2 * cap(array) + max(8, arg_len);
+ _ = reserve(array, cap, loc);
+ }
+ arg_len = min(cap(array)-len(array), arg_len);
+ if arg_len > 0 {
+ a := (^Raw_Dynamic_Array)(array);
+ if size_of(E) != 0 {
+ data := (^E)(a.data);
+ assert(data != nil);
+ mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
+ }
+ a.len += arg_len;
+ }
+}
+
+// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
+@builtin
+append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
+ args := transmute([]E)arg;
+ append_elems(array=array, args=args, loc=loc);
+}
+
+@builtin
+reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+ if array == nil {
+ return false;
+ }
+
+ old_cap := cap(array);
+ if capacity <= old_cap {
+ return true;
+ }
+
+ if array.allocator.procedure == nil {
+ array.allocator = context.allocator;
+ }
+ assert(array.allocator.procedure != nil);
+
+
+ ti := type_info_of(typeid_of(T));
+ ti = type_info_base(ti);
+ si := &ti.variant.(Type_Info_Struct);
+
+ field_count := uintptr(len(si.offsets) - 3);
+
+ if field_count == 0 {
+ return true;
+ }
+
+ cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
+ assert(cap_ptr^ == old_cap);
+
+
+ old_size := 0;
+ new_size := 0;
+
+ max_align := 0;
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+ max_align = max(max_align, type.align);
+
+ old_size = align_forward_int(old_size, type.align);
+ new_size = align_forward_int(new_size, type.align);
+
+ old_size += type.size * old_cap;
+ new_size += type.size * capacity;
+ }
+
+ old_size = align_forward_int(old_size, max_align);
+ new_size = align_forward_int(new_size, max_align);
+
+ old_data := (^rawptr)(array)^;
+
+ new_data := array.allocator.procedure(
+ array.allocator.data, .Alloc, new_size, max_align,
+ nil, old_size, 0, loc,
+ );
+ if new_data == nil {
+ return false;
+ }
+
+
+ cap_ptr^ = capacity;
+
+ old_offset := 0;
+ new_offset := 0;
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+ max_align = max(max_align, type.align);
+
+ old_offset = align_forward_int(old_offset, type.align);
+ new_offset = align_forward_int(new_offset, type.align);
+
+ new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
+ old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
+
+ mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
+
+ (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
+
+ old_offset += type.size * old_cap;
+ new_offset += type.size * capacity;
+ }
+
+ array.allocator.procedure(
+ array.allocator.data, .Free, 0, max_align,
+ old_data, old_size, 0, loc,
+ );
+
+ return true;
+}
+
+@builtin
+append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
+ if array == nil {
+ return;
+ }
+
+ arg_len := 1;
+
+ if cap(array) <= len(array)+arg_len {
+ cap := 2 * cap(array) + max(8, arg_len);
+ _ = reserve_soa(array, cap, loc);
+ }
+ arg_len = min(cap(array)-len(array), arg_len);
+ if arg_len > 0 {
+ ti := type_info_of(typeid_of(T));
+ ti = type_info_base(ti);
+ si := &ti.variant.(Type_Info_Struct);
+ field_count := uintptr(len(si.offsets) - 3);
+
+ if field_count == 0 {
+ return;
+ }
+
+ data := (^rawptr)(array)^;
+
+ len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
+
+
+ soa_offset := 0;
+ item_offset := 0;
+
+ arg_copy := arg;
+ arg_ptr := &arg_copy;
+
+ max_align := 0;
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+ max_align = max(max_align, type.align);
+
+ soa_offset = align_forward_int(soa_offset, type.align);
+ item_offset = align_forward_int(item_offset, type.align);
+
+ dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
+ src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
+ mem_copy(dst, src, type.size);
+
+ soa_offset += type.size * cap(array);
+ item_offset += type.size;
+ }
+
+ len_ptr^ += arg_len;
+ }
+}
+
+@builtin
+append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
+ if array == nil {
+ return;
+ }
+
+ arg_len := len(args);
+ if arg_len == 0 {
+ return;
+ }
+
+ if cap(array) <= len(array)+arg_len {
+ cap := 2 * cap(array) + max(8, arg_len);
+ _ = reserve_soa(array, cap, loc);
+ }
+ arg_len = min(cap(array)-len(array), arg_len);
+ if arg_len > 0 {
+ ti := type_info_of(typeid_of(T));
+ ti = type_info_base(ti);
+ si := &ti.variant.(Type_Info_Struct);
+ field_count := uintptr(len(si.offsets) - 3);
+
+ if field_count == 0 {
+ return;
+ }
+
+ data := (^rawptr)(array)^;
+
+ len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
+
+
+ soa_offset := 0;
+ item_offset := 0;
+
+ args_ptr := &args[0];
+
+ max_align := 0;
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+ max_align = max(max_align, type.align);
+
+ soa_offset = align_forward_int(soa_offset, type.align);
+ item_offset = align_forward_int(item_offset, type.align);
+
+ dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
+ src := uintptr(args_ptr) + uintptr(item_offset);
+ for j in 0..<arg_len {
+ d := rawptr(dst + uintptr(j*type.size));
+ s := rawptr(src + uintptr(j*size_of(E)));
+ mem_copy(d, s, type.size);
+ }
+
+ soa_offset += type.size * cap(array);
+ item_offset += type.size;
+ }
+
+ len_ptr^ += arg_len;
+ }
+}
+
+// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
+@builtin
+append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
+ for arg in args {
+ append(array = array, args = transmute([]E)(arg), loc = loc);
+ }
+}
+
+// The append built-in procedure appends elements to the end of a dynamic array
+@builtin append :: proc{append_elem, append_elems, append_elem_string};
+
+// The append_soa built-in procedure appends elements to the end of an #soa dynamic array
+@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
+
+@builtin
+append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
+ if array == nil {
+ return;
+ }
+ resize(array, len(array)+1);
+}
+
+
+@builtin
+insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+ if array == nil {
+ return;
+ }
+ n := len(array);
+ m :: 1;
+ resize(array, n+m, loc);
+ if n+m <= len(array) {
+ when size_of(E) != 0 {
+ copy(array[index+m:], array[index:]);
+ array[index] = arg;
+ }
+ ok = true;
+ }
+ return;
+}
+
+@builtin
+insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+ if array == nil {
+ return;
+ }
+ if len(args) == 0 {
+ ok = true;
+ return;
+ }
+
+ n := len(array);
+ m := len(args);
+ resize(array, n+m, loc);
+ if n+m <= len(array) {
+ when size_of(E) != 0 {
+ copy(array[index+m:], array[index:]);
+ copy(array[index:], args);
+ }
+ ok = true;
+ }
+ return;
+}
+
+@builtin
+insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+ if array == nil {
+ return;
+ }
+ if len(args) == 0 {
+ ok = true;
+ return;
+ }
+
+ n := len(array);
+ m := len(args);
+ resize(array, n+m, loc);
+ if n+m <= len(array) {
+ copy(array[index+m:], array[index:]);
+ copy(array[index:], args);
+ ok = true;
+ }
+ return;
+}
+
+@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string};
+
+
+
+
+@builtin
+clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
+ if array != nil {
+ (^Raw_Dynamic_Array)(array).len = 0;
+ }
+}
+
+@builtin
+reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+ if array == nil {
+ return false;
+ }
+ a := (^Raw_Dynamic_Array)(array);
+
+ if capacity <= a.cap {
+ return true;
+ }
+
+ if a.allocator.procedure == nil {
+ a.allocator = context.allocator;
+ }
+ assert(a.allocator.procedure != nil);
+
+ old_size := a.cap * size_of(E);
+ new_size := capacity * size_of(E);
+ allocator := a.allocator;
+
+ new_data := allocator.procedure(
+ allocator.data, .Resize, new_size, align_of(E),
+ a.data, old_size, 0, loc,
+ );
+ if new_data == nil {
+ return false;
+ }
+
+ a.data = new_data;
+ a.cap = capacity;
+ return true;
+}
+
+@builtin
+resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
+ if array == nil {
+ return false;
+ }
+ a := (^Raw_Dynamic_Array)(array);
+
+ if length <= a.cap {
+ a.len = max(length, 0);
+ return true;
+ }
+
+ if a.allocator.procedure == nil {
+ a.allocator = context.allocator;
+ }
+ assert(a.allocator.procedure != nil);
+
+ old_size := a.cap * size_of(E);
+ new_size := length * size_of(E);
+ allocator := a.allocator;
+
+ new_data := allocator.procedure(
+ allocator.data, .Resize, new_size, align_of(E),
+ a.data, old_size, 0, loc,
+ );
+ if new_data == nil {
+ return false;
+ }
+
+ a.data = new_data;
+ a.len = length;
+ a.cap = length;
+ return true;
+}
+
+
+
+@builtin
+incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
+ s^ |= {elem};
+ return s^;
+}
+@builtin
+incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
+ for elem in elems {
+ s^ |= {elem};
+ }
+ return s^;
+}
+@builtin
+incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
+ s^ |= other;
+ return s^;
+}
+@builtin
+excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
+ s^ &~= {elem};
+ return s^;
+}
+@builtin
+excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
+ for elem in elems {
+ s^ &~= {elem};
+ }
+ return s^;
+}
+@builtin
+excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
+ s^ &~= other;
+ return s^;
+}
+
+@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
+@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
+
+
+@builtin
+card :: proc(s: $S/bit_set[$E; $U]) -> int {
+ when size_of(S) == 1 {
+ foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- }
+ return int(count_ones(transmute(u8)s));
+ } else when size_of(S) == 2 {
+ foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
+ return int(count_ones(transmute(u16)s));
+ } else when size_of(S) == 4 {
+ foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
+ return int(count_ones(transmute(u32)s));
+ } else when size_of(S) == 8 {
+ foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
+ return int(count_ones(transmute(u64)s));
+ } else when size_of(S) == 16 {
+ foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
+ return int(count_ones(transmute(u128)s));
+ } else {
+ #panic("Unhandled card bit_set size");
+ }
+}
+
+
+
+@builtin
+raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
+ return (^E)(a);
+}
+@builtin
+raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
+ ptr := (transmute(Raw_Slice)s).data;
+ return (^E)(ptr);
+}
+@builtin
+raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
+ ptr := (transmute(Raw_Dynamic_Array)s).data;
+ return (^E)(ptr);
+}
+@builtin
+raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
+ return (transmute(Raw_String)s).data;
+}
+
+@builtin
+raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data};
+
+
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+assert :: proc(condition: bool, message := "", loc := #caller_location) {
+ if !condition {
+ proc(message: string, loc: Source_Code_Location) {
+ p := context.assertion_failure_proc;
+ if p == nil {
+ p = default_assertion_failure_proc;
+ }
+ p("runtime assertion", message, loc);
+ }(message, loc);
+ }
+}
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+panic :: proc(message: string, loc := #caller_location) -> ! {
+ p := context.assertion_failure_proc;
+ if p == nil {
+ p = default_assertion_failure_proc;
+ }
+ p("panic", message, loc);
+}
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+unimplemented :: proc(message := "", loc := #caller_location) -> ! {
+ p := context.assertion_failure_proc;
+ if p == nil {
+ p = default_assertion_failure_proc;
+ }
+ p("not yet implemented", message, loc);
+}
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+unreachable :: proc(message := "", loc := #caller_location) -> ! {
+ p := context.assertion_failure_proc;
+ if p == nil {
+ p = default_assertion_failure_proc;
+ }
+ if message != "" {
+ p("internal error", message, loc);
+ } else {
+ p("internal error", "entered unreachable code", loc);
+ }
+}
diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin
new file mode 100644
index 000000000..55289bbe4
--- /dev/null
+++ b/core/runtime/dynamic_array_internal.odin
@@ -0,0 +1,100 @@
+package runtime
+
+__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
+ array := (^Raw_Dynamic_Array)(array_);
+ array.allocator = context.allocator;
+ assert(array.allocator.procedure != nil);
+
+ if cap > 0 {
+ __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc);
+ array.len = len;
+ }
+}
+
+__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
+ array := (^Raw_Dynamic_Array)(array_);
+
+ // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
+ // assuming that appending/reserving will set the allocator, if it is not already set.
+ if array.allocator.procedure == nil {
+ array.allocator = context.allocator;
+ }
+ assert(array.allocator.procedure != nil);
+
+ if cap <= array.cap {
+ return true;
+ }
+
+ old_size := array.cap * elem_size;
+ new_size := cap * elem_size;
+ allocator := array.allocator;
+
+ new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
+ if new_data != nil || elem_size == 0 {
+ array.data = new_data;
+ array.cap = cap;
+ return true;
+ }
+ return false;
+}
+
+__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
+ array := (^Raw_Dynamic_Array)(array_);
+
+ ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
+ if ok {
+ array.len = len;
+ }
+ return ok;
+}
+
+
+__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
+ items: rawptr, item_count: int, loc := #caller_location) -> int {
+ array := (^Raw_Dynamic_Array)(array_);
+
+ if items == nil {
+ return 0;
+ }
+ if item_count <= 0 {
+ return 0;
+ }
+
+
+ ok := true;
+ if array.cap <= array.len+item_count {
+ cap := 2 * array.cap + max(8, item_count);
+ ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
+ }
+ // TODO(bill): Better error handling for failed reservation
+ if !ok {
+ return array.len;
+ }
+
+ assert(array.data != nil);
+ data := uintptr(array.data) + uintptr(elem_size*array.len);
+
+ mem_copy(rawptr(data), items, elem_size * item_count);
+ array.len += item_count;
+ return array.len;
+}
+
+__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
+ array := (^Raw_Dynamic_Array)(array_);
+
+ ok := true;
+ if array.cap <= array.len+1 {
+ cap := 2 * array.cap + max(8, 1);
+ ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
+ }
+ // TODO(bill): Better error handling for failed reservation
+ if !ok {
+ return array.len;
+ }
+
+ assert(array.data != nil);
+ data := uintptr(array.data) + uintptr(elem_size*array.len);
+ mem_zero(rawptr(data), elem_size);
+ array.len += 1;
+ return array.len;
+}
diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin
new file mode 100644
index 000000000..e880a043f
--- /dev/null
+++ b/core/runtime/dynamic_map_internal.odin
@@ -0,0 +1,394 @@
+package runtime
+
+import "intrinsics"
+_ :: intrinsics;
+
+INITIAL_MAP_CAP :: 16;
+
+// Temporary data structure for comparing hashes and keys
+Map_Hash :: struct {
+ hash: uintptr,
+ key_ptr: rawptr, // address of Map_Entry_Header.key
+}
+
+__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) {
+ hasher := intrinsics.type_hasher_proc(K);
+ map_hash.key_ptr = k;
+ map_hash.hash = hasher(k, 0);
+ return;
+}
+
+__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) {
+ hash.hash = entry.hash;
+ hash.key_ptr = rawptr(uintptr(entry) + h.key_offset);
+ return;
+}
+
+
+
+Map_Find_Result :: struct {
+ hash_index: int,
+ entry_prev: int,
+ entry_index: int,
+}
+
+Map_Entry_Header :: struct {
+ hash: uintptr,
+ next: int,
+/*
+ key: Key_Value,
+ value: Value_Type,
+*/
+}
+
+Map_Header :: struct {
+ m: ^Raw_Map,
+ equal: Equal_Proc,
+
+ entry_size: int,
+ entry_align: int,
+
+ key_offset: uintptr,
+ key_size: int,
+
+ value_offset: uintptr,
+ value_size: int,
+}
+
+INITIAL_HASH_SEED :: 0xcbf29ce484222325;
+
+_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 {
+ h: u64 = seed;
+ for b in data {
+ h = (h ~ u64(b)) * 0x100000001b3;
+ }
+ return h;
+}
+
+default_hash :: inline proc "contextless" (data: []byte) -> uintptr {
+ return uintptr(_fnv64a(data));
+}
+default_hash_string :: inline proc "contextless" (s: string) -> uintptr {
+ return default_hash(transmute([]byte)(s));
+}
+default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintptr {
+ s := Raw_Slice{data, size};
+ return default_hash(transmute([]byte)(s));
+}
+
+@(private)
+_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr where N <= 16 {
+ h := u64(seed) + 0xcbf29ce484222325;
+ p := uintptr(data);
+ inline for _ in 0..<N {
+ b := u64((^byte)(p)^);
+ h = (h ~ b) * 0x100000001b3;
+ p += 1;
+ }
+ return uintptr(h);
+}
+
+default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr {
+ h := u64(seed) + 0xcbf29ce484222325;
+ p := uintptr(data);
+ for _ in 0..<N {
+ b := u64((^byte)(p)^);
+ h = (h ~ b) * 0x100000001b3;
+ p += 1;
+ }
+ return uintptr(h);
+}
+
+// NOTE(bill): There are loads of predefined ones to improve optimizations for small types
+
+default_hasher1 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 1); }
+default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); }
+default_hasher3 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 3); }
+default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); }
+default_hasher5 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 5); }
+default_hasher6 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 6); }
+default_hasher7 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 7); }
+default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); }
+default_hasher9 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 9); }
+default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 10); }
+default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 11); }
+default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 12); }
+default_hasher13 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 13); }
+default_hasher14 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 14); }
+default_hasher15 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 15); }
+default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 16); }
+
+default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
+ h := u64(seed) + 0xcbf29ce484222325;
+ str := (^[]byte)(data)^;
+ for b in str {
+ h = (h ~ u64(b)) * 0x100000001b3;
+ }
+ return uintptr(h);
+}
+default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
+ h := u64(seed) + 0xcbf29ce484222325;
+ ptr := (^uintptr)(data)^;
+ for (^byte)(ptr)^ != 0 {
+ b := (^byte)(ptr)^;
+ h = (h ~ u64(b)) * 0x100000001b3;
+ ptr += 1;
+ }
+ return uintptr(h);
+}
+
+
+
+source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr {
+ hash := _fnv64a(transmute([]byte)s.file_path);
+ hash = hash ~ (u64(s.line) * 0x100000001b3);
+ hash = hash ~ (u64(s.column) * 0x100000001b3);
+ return uintptr(hash);
+}
+
+
+
+__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
+ header := Map_Header{m = (^Raw_Map)(m)};
+ Entry :: struct {
+ hash: uintptr,
+ next: int,
+ key: K,
+ value: V,
+ };
+
+ header.equal = intrinsics.type_equal_proc(K);
+
+ header.entry_size = int(size_of(Entry));
+ header.entry_align = int(align_of(Entry));
+
+ header.key_offset = uintptr(offset_of(Entry, key));
+ header.key_size = int(size_of(K));
+
+ header.value_offset = uintptr(offset_of(Entry, value));
+ header.value_size = int(size_of(V));
+
+ return header;
+}
+
+__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
+ array := (^Raw_Slice)(array_);
+
+ if new_count < array.len {
+ return true;
+ }
+
+ assert(allocator.procedure != nil);
+
+ old_size := array.len*size_of(T);
+ new_size := new_count*size_of(T);
+
+ new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
+ if new_data == nil {
+ return false;
+ }
+ array.data = new_data;
+ array.len = new_count;
+ return true;
+}
+
+__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
+ __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc);
+
+ old_len := len(m.hashes);
+ __slice_resize(&m.hashes, cap, m.entries.allocator, loc);
+ for i in old_len..<len(m.hashes) {
+ m.hashes[i] = -1;
+ }
+
+}
+__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
+ new_header: Map_Header = header;
+ nm := Raw_Map{};
+ nm.entries.allocator = m.entries.allocator;
+ new_header.m = &nm;
+
+ c := context;
+ if m.entries.allocator.procedure != nil {
+ c.allocator = m.entries.allocator;
+ }
+ context = c;
+
+ new_count := new_count;
+ new_count = max(new_count, 2*m.entries.len);
+
+ __dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
+ __slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
+ for i in 0 ..< new_count {
+ nm.hashes[i] = -1;
+ }
+
+ for i in 0 ..< m.entries.len {
+ if len(nm.hashes) == 0 {
+ __dynamic_map_grow(new_header, loc);
+ }
+
+ entry_header := __dynamic_map_get_entry(header, i);
+ entry_hash := __get_map_hash_from_entry(header, entry_header);
+
+ fr := __dynamic_map_find(new_header, entry_hash);
+ j := __dynamic_map_add_entry(new_header, entry_hash, loc);
+ if fr.entry_prev < 0 {
+ nm.hashes[fr.hash_index] = j;
+ } else {
+ e := __dynamic_map_get_entry(new_header, fr.entry_prev);
+ e.next = j;
+ }
+
+ e := __dynamic_map_get_entry(new_header, j);
+ __dynamic_map_copy_entry(header, e, entry_header);
+ e.next = fr.entry_index;
+
+ if __dynamic_map_full(new_header) {
+ __dynamic_map_grow(new_header, loc);
+ }
+ }
+
+ delete(m.hashes, m.entries.allocator, loc);
+ free(m.entries.data, m.entries.allocator, loc);
+ header.m^ = nm;
+}
+
+__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
+ index := __dynamic_map_find(h, hash).entry_index;
+ if index >= 0 {
+ data := uintptr(__dynamic_map_get_entry(h, index));
+ return rawptr(data + h.value_offset);
+ }
+ return nil;
+}
+
+__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check {
+ index: int;
+ assert(value != nil);
+
+ if len(h.m.hashes) == 0 {
+ __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc);
+ __dynamic_map_grow(h, loc);
+ }
+
+ fr := __dynamic_map_find(h, hash);
+ if fr.entry_index >= 0 {
+ index = fr.entry_index;
+ } else {
+ index = __dynamic_map_add_entry(h, hash, loc);
+ if fr.entry_prev >= 0 {
+ entry := __dynamic_map_get_entry(h, fr.entry_prev);
+ entry.next = index;
+ } else {
+ h.m.hashes[fr.hash_index] = index;
+ }
+ }
+ {
+ e := __dynamic_map_get_entry(h, index);
+ e.hash = hash.hash;
+
+ key := rawptr(uintptr(e) + h.key_offset);
+ mem_copy(key, hash.key_ptr, h.key_size);
+
+ val := rawptr(uintptr(e) + h.value_offset);
+ mem_copy(val, value, h.value_size);
+ }
+
+ if __dynamic_map_full(h) {
+ __dynamic_map_grow(h, loc);
+ }
+}
+
+
+__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
+ // TODO(bill): Determine an efficient growing rate
+ new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP);
+ __dynamic_map_rehash(h, new_count, loc);
+}
+
+__dynamic_map_full :: inline proc "contextless" (using h: Map_Header) -> bool {
+ return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
+}
+
+
+__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool {
+ if a.hash == b.hash {
+ return h.equal(a.key_ptr, b.key_ptr);
+ }
+ return false;
+}
+
+__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check {
+ fr := Map_Find_Result{-1, -1, -1};
+ if n := uintptr(len(m.hashes)); n > 0 {
+ fr.hash_index = int(hash.hash % n);
+ fr.entry_index = m.hashes[fr.hash_index];
+ for fr.entry_index >= 0 {
+ entry := __dynamic_map_get_entry(h, fr.entry_index);
+ entry_hash := __get_map_hash_from_entry(h, entry);
+ if __dynamic_map_hash_equal(h, entry_hash, hash) {
+ return fr;
+ }
+ fr.entry_prev = fr.entry_index;
+ fr.entry_index = entry.next;
+ }
+ }
+ return fr;
+}
+
+__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int {
+ prev := m.entries.len;
+ c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc);
+ if c != prev {
+ end := __dynamic_map_get_entry(h, c-1);
+ end.hash = hash.hash;
+ mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size);
+ end.next = -1;
+ }
+ return prev;
+}
+
+__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) {
+ fr := __dynamic_map_find(h, hash);
+ if fr.entry_index >= 0 {
+ __dynamic_map_erase(h, fr);
+ }
+}
+
+__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
+ assert(0 <= index && index < m.entries.len);
+ return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size));
+}
+
+__dynamic_map_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) {
+ mem_copy(new, old, h.entry_size);
+}
+
+__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
+ if fr.entry_prev < 0 {
+ m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
+ } else {
+ prev := __dynamic_map_get_entry(h, fr.entry_prev);
+ curr := __dynamic_map_get_entry(h, fr.entry_index);
+ prev.next = curr.next;
+ }
+ if (fr.entry_index == m.entries.len-1) {
+ // NOTE(bill): No need to do anything else, just pop
+ } else {
+ old := __dynamic_map_get_entry(h, fr.entry_index);
+ end := __dynamic_map_get_entry(h, m.entries.len-1);
+ __dynamic_map_copy_entry(h, old, end);
+
+ old_hash := __get_map_hash_from_entry(h, old);
+
+ if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 {
+ last_entry := __dynamic_map_get_entry(h, last.entry_prev);
+ last_entry.next = fr.entry_index;
+ } else {
+ m.hashes[last.hash_index] = fr.entry_index;
+ }
+ }
+
+ m.entries.len -= 1;
+}
diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin
index b1bc0b646..38d6641ab 100644
--- a/core/runtime/error_checks.odin
+++ b/core/runtime/error_checks.odin
@@ -23,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
}
handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
context = default_context();
- print_caller_location(Source_Code_Location{file, line, column, "", 0});
+ print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Index ");
print_i64(i64(index));
print_string(" is out of bounds range 0:");
@@ -36,7 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! {
context = default_context();
- print_caller_location(Source_Code_Location{file, line, column, "", 0});
+ print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid slice indices: ");
print_i64(i64(lo));
print_string(":");
@@ -67,7 +67,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int,
}
handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
context = default_context();
- print_caller_location(Source_Code_Location{file, line, column, "", 0});
+ print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid dynamic array values: ");
print_i64(i64(low));
print_string(":");
@@ -87,7 +87,7 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
}
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) {
context = default_context();
- print_caller_location(Source_Code_Location{file, line, column, "", 0});
+ print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid type assertion from ");
print_typeid(from);
print_string(" to ");
@@ -98,6 +98,59 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
handle_error(file, line, column, from, to);
}
+type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid, from_data: rawptr) {
+ if ok {
+ return;
+ }
+
+ variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
+ if id == nil || data == nil {
+ return id;
+ }
+ ti := type_info_base(type_info_of(id));
+ #partial switch v in ti.variant {
+ case Type_Info_Any:
+ return (^any)(data).id;
+ case Type_Info_Union:
+ tag_ptr := uintptr(data) + v.tag_offset;
+ idx := 0;
+ switch v.tag_type.size {
+ case 1: idx = int((^u8)(tag_ptr)^) - 1;
+ case 2: idx = int((^u16)(tag_ptr)^) - 1;
+ case 4: idx = int((^u32)(tag_ptr)^) - 1;
+ case 8: idx = int((^u64)(tag_ptr)^) - 1;
+ case 16: idx = int((^u128)(tag_ptr)^) - 1;
+ }
+ if idx < 0 {
+ return nil;
+ } else if idx < len(v.variants) {
+ return v.variants[idx].id;
+ }
+ }
+ return id;
+ }
+
+ handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid, from_data: rawptr) {
+ context = default_context();
+
+ actual := variant_type(from, from_data);
+
+ print_caller_location(Source_Code_Location{file, line, column, ""});
+ print_string(" Invalid type assertion from ");
+ print_typeid(from);
+ print_string(" to ");
+ print_typeid(to);
+ if actual != from {
+ print_string(", actual type: ");
+ print_typeid(actual);
+ }
+ print_byte('\n');
+ type_assertion_trap();
+ }
+ handle_error(file, line, column, from, to, from_data);
+}
+
+
make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
if 0 <= len {
return;
diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin
index 8662d045f..27cd3e767 100644
--- a/core/runtime/internal.odin
+++ b/core/runtime/internal.odin
@@ -93,18 +93,18 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
when ODIN_USE_LLVM_API {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
- llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+ llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
- llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+ llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
} else {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
- llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+ llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
- llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+ llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
}
}
}
@@ -121,18 +121,18 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
when ODIN_USE_LLVM_API {
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
- llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+ llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
- llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+ llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
} else {
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
- llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+ llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
- llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+ llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
}
}
}
@@ -180,9 +180,16 @@ mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int =
}
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
}
-
-
+memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
+ return memory_compare(a, b, n) == 0;
+}
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
+ switch {
+ case a == b: return 0;
+ case a == nil: return -1;
+ case b == nil: return +1;
+ }
+
x := uintptr(a);
y := uintptr(b);
n := uintptr(n);
@@ -389,45 +396,45 @@ string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
}
-@(default_calling_convention = "c")
+@(default_calling_convention = "none")
foreign {
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
}
abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
foreign {
- @(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 ---
+ @(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 ---
}
return _abs(x);
}
abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
foreign {
- @(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 ---
+ @(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 ---
}
return _abs(x);
}
min_f32 :: proc(a, b: f32) -> f32 {
foreign {
- @(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 ---
+ @(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 ---
}
return _min(a, b);
}
min_f64 :: proc(a, b: f64) -> f64 {
foreign {
- @(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 ---
+ @(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 ---
}
return _min(a, b);
}
max_f32 :: proc(a, b: f32) -> f32 {
foreign {
- @(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 ---
+ @(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 ---
}
return _max(a, b);
}
max_f64 :: proc(a, b: f64) -> f64 {
foreign {
- @(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 ---
+ @(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 ---
}
return _max(a, b);
}
diff --git a/core/runtime/internal_linux.odin b/core/runtime/internal_linux.odin
new file mode 100644
index 000000000..241ed0fdf
--- /dev/null
+++ b/core/runtime/internal_linux.odin
@@ -0,0 +1,135 @@
+package runtime
+
+@(link_name="__umodti3")
+umodti3 :: proc "c" (a, b: u128) -> u128 {
+ r: u128 = ---;
+ _ = udivmod128(a, b, &r);
+ return r;
+}
+
+
+@(link_name="__udivmodti4")
+udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
+ return udivmod128(a, b, rem);
+}
+
+@(link_name="__udivti3")
+udivti3 :: proc "c" (a, b: u128) -> u128 {
+ return udivmodti4(a, b, nil);
+}
+
+
+@(link_name="__modti3")
+modti3 :: proc "c" (a, b: i128) -> i128 {
+ s_a := a >> (128 - 1);
+ s_b := b >> (128 - 1);
+ an := (a ~ s_a) - s_a;
+ bn := (b ~ s_b) - s_b;
+
+ r: u128 = ---;
+ _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
+ return (transmute(i128)r ~ s_a) - s_a;
+}
+
+
+@(link_name="__divmodti4")
+divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
+ u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
+ return transmute(i128)u;
+}
+
+@(link_name="__divti3")
+divti3 :: proc "c" (a, b: i128) -> i128 {
+ u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
+ return transmute(i128)u;
+}
+
+
+@(link_name="__fixdfti")
+fixdfti :: proc(a: u64) -> i128 {
+ significandBits :: 52;
+ typeWidth :: (size_of(u64)*8);
+ exponentBits :: (typeWidth - significandBits - 1);
+ maxExponent :: ((1 << exponentBits) - 1);
+ exponentBias :: (maxExponent >> 1);
+
+ implicitBit :: (u64(1) << significandBits);
+ significandMask :: (implicitBit - 1);
+ signBit :: (u64(1) << (significandBits + exponentBits));
+ absMask :: (signBit - 1);
+ exponentMask :: (absMask ~ significandMask);
+
+ // Break a into sign, exponent, significand
+ aRep := a;
+ aAbs := aRep & absMask;
+ sign := i128(-1 if aRep & signBit != 0 else 1);
+ exponent := u64((aAbs >> significandBits) - exponentBias);
+ significand := u64((aAbs & significandMask) | implicitBit);
+
+ // If exponent is negative, the result is zero.
+ if exponent < 0 {
+ return 0;
+ }
+
+ // If the value is too large for the integer type, saturate.
+ if exponent >= size_of(i128) * 8 {
+ return max(i128) if sign == 1 else min(i128);
+ }
+
+ // If 0 <= exponent < significandBits, right shift to get the result.
+ // Otherwise, shift left.
+ if exponent < significandBits {
+ return sign * i128(significand >> (significandBits - exponent));
+ } else {
+ return sign * (i128(significand) << (exponent - significandBits));
+ }
+
+}
+
+@(default_calling_convention = "none")
+foreign {
+ @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
+}
+
+
+@(link_name="__floattidf")
+floattidf :: proc(a: i128) -> f64 {
+ DBL_MANT_DIG :: 53;
+ if a == 0 {
+ return 0.0;
+ }
+ a := a;
+ N :: size_of(i128) * 8;
+ s := a >> (N-1);
+ a = (a ~ s) - s;
+ sd: = N - _clz_i128(a); // number of significant digits
+ e := u32(sd - 1); // exponent
+ if sd > DBL_MANT_DIG {
+ switch sd {
+ case DBL_MANT_DIG + 1:
+ a <<= 1;
+ case DBL_MANT_DIG + 2:
+ // okay
+ case:
+ a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
+ i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
+ };
+
+ a |= i128((a & 4) != 0);
+ a += 1;
+ a >>= 2;
+
+ if a & (1 << DBL_MANT_DIG) != 0 {
+ a >>= 1;
+ e += 1;
+ }
+ } else {
+ a <<= u128(DBL_MANT_DIG - sd);
+ }
+ fb: [2]u32;
+ fb[1] = (u32(s) & 0x80000000) | // sign
+ ((e + 1023) << 20) | // exponent
+ ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
+ fb[1] = u32(a); // mantissa-low
+ return transmute(f64)fb;
+}
diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_windows.odin
index 241ed0fdf..10d2e2249 100644
--- a/core/runtime/internal_windows.odin
+++ b/core/runtime/internal_windows.odin
@@ -2,134 +2,134 @@ package runtime
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: u128) -> u128 {
- r: u128 = ---;
- _ = udivmod128(a, b, &r);
- return r;
+ r: u128 = ---;
+ _ = udivmod128(a, b, &r);
+ return r;
}
@(link_name="__udivmodti4")
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
- return udivmod128(a, b, rem);
+ return udivmod128(a, b, rem);
}
@(link_name="__udivti3")
udivti3 :: proc "c" (a, b: u128) -> u128 {
- return udivmodti4(a, b, nil);
+ return udivmodti4(a, b, nil);
}
@(link_name="__modti3")
modti3 :: proc "c" (a, b: i128) -> i128 {
- s_a := a >> (128 - 1);
- s_b := b >> (128 - 1);
- an := (a ~ s_a) - s_a;
- bn := (b ~ s_b) - s_b;
-
- r: u128 = ---;
- _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
- return (transmute(i128)r ~ s_a) - s_a;
+ s_a := a >> (128 - 1);
+ s_b := b >> (128 - 1);
+ an := (a ~ s_a) - s_a;
+ bn := (b ~ s_b) - s_b;
+
+ r: u128 = ---;
+ _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
+ return (transmute(i128)r ~ s_a) - s_a;
}
@(link_name="__divmodti4")
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
- u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
- return transmute(i128)u;
+ u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
+ return transmute(i128)u;
}
@(link_name="__divti3")
divti3 :: proc "c" (a, b: i128) -> i128 {
- u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
- return transmute(i128)u;
+ u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
+ return transmute(i128)u;
}
@(link_name="__fixdfti")
fixdfti :: proc(a: u64) -> i128 {
- significandBits :: 52;
- typeWidth :: (size_of(u64)*8);
- exponentBits :: (typeWidth - significandBits - 1);
- maxExponent :: ((1 << exponentBits) - 1);
- exponentBias :: (maxExponent >> 1);
-
- implicitBit :: (u64(1) << significandBits);
- significandMask :: (implicitBit - 1);
- signBit :: (u64(1) << (significandBits + exponentBits));
- absMask :: (signBit - 1);
- exponentMask :: (absMask ~ significandMask);
-
- // Break a into sign, exponent, significand
- aRep := a;
- aAbs := aRep & absMask;
- sign := i128(-1 if aRep & signBit != 0 else 1);
- exponent := u64((aAbs >> significandBits) - exponentBias);
- significand := u64((aAbs & significandMask) | implicitBit);
-
- // If exponent is negative, the result is zero.
- if exponent < 0 {
- return 0;
- }
-
- // If the value is too large for the integer type, saturate.
- if exponent >= size_of(i128) * 8 {
- return max(i128) if sign == 1 else min(i128);
- }
-
- // If 0 <= exponent < significandBits, right shift to get the result.
- // Otherwise, shift left.
- if exponent < significandBits {
- return sign * i128(significand >> (significandBits - exponent));
- } else {
- return sign * (i128(significand) << (exponent - significandBits));
- }
+ significandBits :: 52;
+ typeWidth :: (size_of(u64)*8);
+ exponentBits :: (typeWidth - significandBits - 1);
+ maxExponent :: ((1 << exponentBits) - 1);
+ exponentBias :: (maxExponent >> 1);
+
+ implicitBit :: (u64(1) << significandBits);
+ significandMask :: (implicitBit - 1);
+ signBit :: (u64(1) << (significandBits + exponentBits));
+ absMask :: (signBit - 1);
+ exponentMask :: (absMask ~ significandMask);
+
+ // Break a into sign, exponent, significand
+ aRep := a;
+ aAbs := aRep & absMask;
+ sign := i128(-1 if aRep & signBit != 0 else 1);
+ exponent := u64((aAbs >> significandBits) - exponentBias);
+ significand := u64((aAbs & significandMask) | implicitBit);
+
+ // If exponent is negative, the result is zero.
+ if exponent < 0 {
+ return 0;
+ }
+
+ // If the value is too large for the integer type, saturate.
+ if exponent >= size_of(i128) * 8 {
+ return max(i128) if sign == 1 else min(i128);
+ }
+
+ // If 0 <= exponent < significandBits, right shift to get the result.
+ // Otherwise, shift left.
+ if exponent < significandBits {
+ return sign * i128(significand >> (significandBits - exponent));
+ } else {
+ return sign * (i128(significand) << (exponent - significandBits));
+ }
}
@(default_calling_convention = "none")
foreign {
- @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
+ @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
}
@(link_name="__floattidf")
floattidf :: proc(a: i128) -> f64 {
- DBL_MANT_DIG :: 53;
- if a == 0 {
- return 0.0;
- }
- a := a;
- N :: size_of(i128) * 8;
- s := a >> (N-1);
- a = (a ~ s) - s;
- sd: = N - _clz_i128(a); // number of significant digits
- e := u32(sd - 1); // exponent
- if sd > DBL_MANT_DIG {
- switch sd {
- case DBL_MANT_DIG + 1:
- a <<= 1;
- case DBL_MANT_DIG + 2:
- // okay
- case:
- a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
- i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
- };
-
- a |= i128((a & 4) != 0);
- a += 1;
- a >>= 2;
-
- if a & (1 << DBL_MANT_DIG) != 0 {
- a >>= 1;
- e += 1;
- }
- } else {
- a <<= u128(DBL_MANT_DIG - sd);
- }
- fb: [2]u32;
- fb[1] = (u32(s) & 0x80000000) | // sign
- ((e + 1023) << 20) | // exponent
- ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
- fb[1] = u32(a); // mantissa-low
- return transmute(f64)fb;
+ DBL_MANT_DIG :: 53;
+ if a == 0 {
+ return 0.0;
+ }
+ a := a;
+ N :: size_of(i128) * 8;
+ s := a >> (N-1);
+ a = (a ~ s) - s;
+ sd: = N - _clz_i128(a); // number of significant digits
+ e := u32(sd - 1); // exponent
+ if sd > DBL_MANT_DIG {
+ switch sd {
+ case DBL_MANT_DIG + 1:
+ a <<= 1;
+ case DBL_MANT_DIG + 2:
+ // okay
+ case:
+ a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
+ i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
+ };
+
+ a |= i128((a & 4) != 0);
+ a += 1;
+ a >>= 2;
+
+ if a & (1 << DBL_MANT_DIG) != 0 {
+ a >>= 1;
+ e += 1;
+ }
+ } else {
+ a <<= u128(DBL_MANT_DIG - sd);
+ }
+ fb: [2]u32;
+ fb[1] = (u32(s) & 0x80000000) | // sign
+ ((e + 1023) << 20) | // exponent
+ ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
+ fb[1] = u32(a); // mantissa-low
+ return transmute(f64)fb;
}
diff --git a/core/runtime/print.odin b/core/runtime/print.odin
index 49b0404a0..88e8c9d9e 100644
--- a/core/runtime/print.odin
+++ b/core/runtime/print.odin
@@ -350,7 +350,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_byte(']');
case Type_Info_Opaque:
- print_string("opaque ");
+ print_string("#opaque ");
print_type(info.elem);
case Type_Info_Simd_Vector:
diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin
index 8593d96f9..511b1866d 100644
--- a/core/runtime/procs_windows_amd64.odin
+++ b/core/runtime/procs_windows_amd64.odin
@@ -2,15 +2,14 @@ package runtime
foreign import kernel32 "system:Kernel32.lib"
-windows_trap_array_bounds :: proc "contextless" () -> ! {
- DWORD :: u32;
- ULONG_PTR :: uint;
+@(private)
+foreign kernel32 {
+ RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
+}
+windows_trap_array_bounds :: proc "contextless" () -> ! {
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C;
- foreign kernel32 {
- RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
- }
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil);
}
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index 764eb6334..d6172e27c 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -216,7 +216,7 @@ split_last :: proc(array: $T/[]$E) -> (rest: T, last: E) {
first :: proc(array: $T/[]$E) -> E {
return array[0];
}
-last :: proc(array: $T/[]$E) -> ^E {
+last :: proc(array: $T/[]$E) -> E {
return array[len(array)-1];
}
@@ -252,3 +252,44 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
as_ptr :: proc(array: $T/[]$E) -> ^E {
return raw_data(array);
}
+
+
+mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> []V {
+ r := make([]V, len(s), allocator);
+ for v, i in s {
+ r[i] = f(v);
+ }
+ return r;
+}
+
+reduce :: proc(s: $S/[]$U, initializer: $V, f: proc(V, U) -> V) -> V {
+ r := initializer;
+ for v in s {
+ r = f(r, v);
+ }
+ return r;
+}
+
+filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -> S {
+ r := make([dynamic]S, 0, 0, allocator);
+ for v in s {
+ if f(v) {
+ append(&r, v);
+ }
+ }
+ return r[:];
+}
+
+
+
+dot_product :: proc(a, b: $S/[]$T) -> T
+ where intrinsics.type_is_numeric(T) {
+ if len(a) != len(b) {
+ panic("slice.dot_product: slices of unequal length");
+ }
+ r: T;
+ #no_bounds_check for _, i in a {
+ r += a[i] * b[i];
+ }
+ return r;
+}
diff --git a/core/slice/sort.odin b/core/slice/sort.odin
index 1e346b1f6..1e7051de5 100644
--- a/core/slice/sort.odin
+++ b/core/slice/sort.odin
@@ -54,17 +54,17 @@ reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
// TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough?
sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
- context.user_ptr = rawptr(key);
+ context._internal = rawptr(key);
sort_by(data, proc(i, j: E) -> bool {
- k := (proc(E) -> K)(context.user_ptr);
+ k := (proc(E) -> K)(context._internal);
return k(i) < k(j);
});
}
reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
- context.user_ptr = rawptr(key);
+ context._internal = rawptr(key);
sort_by(data, proc(i, j: E) -> bool {
- k := (proc(E) -> K)(context.user_ptr);
+ k := (proc(E) -> K)(context._internal);
return k(j) < k(i);
});
}
diff --git a/core/strings/builder.odin b/core/strings/builder.odin
index 2683ea9c9..83e07b772 100644
--- a/core/strings/builder.odin
+++ b/core/strings/builder.odin
@@ -3,15 +3,12 @@ package strings
import "core:mem"
import "core:unicode/utf8"
import "core:strconv"
+import "core:io"
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool);
Builder :: struct {
buf: [dynamic]byte,
-
- // The custom flush procedure allows for the ability to flush the buffer, i.e. write to file
- flush_proc: Builder_Flush_Proc,
- flush_data: rawptr,
}
make_builder_none :: proc(allocator := context.allocator) -> Builder {
@@ -32,6 +29,61 @@ make_builder :: proc{
make_builder_len_cap,
};
+init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
+ b.buf = make([dynamic]byte, allocator);
+}
+
+init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
+ b.buf = make([dynamic]byte, len, allocator);
+}
+
+init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
+ b.buf = make([dynamic]byte, len, cap, allocator);
+}
+
+init_builder :: proc{
+ init_builder_none,
+ init_builder_len,
+ init_builder_len_cap,
+};
+
+@(private)
+_builder_stream_vtable := &io.Stream_VTable{
+ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Builder)(s.stream_data);
+ n = write_bytes(b, p);
+ if len(b.buf) == cap(b.buf) {
+ err = .EOF;
+ }
+ return;
+ },
+ impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+ b := (^Builder)(s.stream_data);
+ _ = write_byte(b, c);
+ if len(b.buf) == cap(b.buf) {
+ return .EOF;
+ }
+ return nil;
+ },
+ impl_size = proc(s: io.Stream) -> i64 {
+ b := (^Builder)(s.stream_data);
+ return i64(len(b.buf));
+ },
+ impl_destroy = proc(s: io.Stream) -> io.Error {
+ b := (^Builder)(s.stream_data);
+ delete(b.buf);
+ return .None;
+ },
+};
+
+to_stream :: proc(b: ^Builder) -> io.Stream {
+ return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b};
+}
+to_writer :: proc(b: ^Builder) -> io.Writer {
+ w, _ := io.to_writer(to_stream(b));
+ return w;
+}
+
@@ -48,24 +100,6 @@ reset_builder :: proc(b: ^Builder) {
clear(&b.buf);
}
-flush_builder :: proc(b: ^Builder) -> (was_reset: bool) {
- if b.flush_proc != nil {
- was_reset = b.flush_proc(b);
- if was_reset {
- reset_builder(b);
-
- }
- }
- return;
-}
-
-flush_builder_check_space :: proc(b: ^Builder, required: int) -> (was_reset: bool) {
- if n := max(cap(b.buf) - len(b.buf), 0); n < required {
- was_reset = flush_builder(b);
- }
- return;
-}
-
builder_from_slice :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing;
@@ -94,7 +128,6 @@ builder_space :: proc(b: Builder) -> int {
}
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
- flush_builder_check_space(b, 1);
if builder_space(b^) > 0 {
append(&b.buf, x);
n += 1;
@@ -105,7 +138,6 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
x := x;
for len(x) != 0 {
- flush_builder_check_space(b, len(x));
space := builder_space(b^);
if space == 0 {
break; // No need to append
@@ -121,20 +153,56 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
return;
}
-write_rune :: proc(b: ^Builder, r: rune) -> int {
- if r < utf8.RUNE_SELF {
- return write_byte(b, byte(r));
+write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
+ return io.write_rune(to_writer(b), r);
+}
+
+
+write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
+ return write_quoted_rune(to_writer(b), r);
+}
+
+@(private)
+_write_byte :: proc(w: io.Writer, c: byte) -> int {
+ err := io.write_byte(w, c);
+ return 1 if err == nil else 0;
+}
+
+
+write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
+ quote := byte('\'');
+ n += _write_byte(w, quote);
+ buf, width := utf8.encode_rune(r);
+ if width == 1 && r == utf8.RUNE_ERROR {
+ n += _write_byte(w, '\\');
+ n += _write_byte(w, 'x');
+ n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]);
+ n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]);
+ } else {
+ n += write_escaped_rune(w, r, quote);
}
+ n += _write_byte(w, quote);
+ return;
+}
- s, n := utf8.encode_rune(r);
- write_bytes(b, s[:n]);
- return n;
+
+write_string :: proc{
+ write_string_builder,
+ write_string_writer,
+};
+
+write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
+ return write_string_writer(to_writer(b), s);
}
-write_string :: proc(b: ^Builder, s: string) -> (n: int) {
- return write_bytes(b, transmute([]byte)s);
+write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
+ n, _ = io.write(w, transmute([]byte)s);
+ return;
}
+
+
+
pop_byte :: proc(b: ^Builder) -> (r: byte) {
if len(b.buf) == 0 {
return 0;
@@ -156,8 +224,17 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
@(private, static)
DIGITS_LOWER := "0123456789abcdefx";
-write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
- n += write_byte(b, quote);
+write_quoted_string :: proc{
+ write_quoted_string_builder,
+ write_quoted_string_writer,
+};
+
+write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
+ return write_quoted_string_writer(to_writer(b), str, quote);
+}
+
+write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) {
+ n += _write_byte(w, quote);
for width, s := 0, str; len(s) > 0; s = s[width:] {
r := rune(s[0]);
width = 1;
@@ -165,57 +242,75 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n:
r, width = utf8.decode_rune_in_string(s);
}
if width == 1 && r == utf8.RUNE_ERROR {
- n += write_byte(b, '\\');
- n += write_byte(b, 'x');
- n += write_byte(b, DIGITS_LOWER[s[0]>>4]);
- n += write_byte(b, DIGITS_LOWER[s[0]&0xf]);
+ n += _write_byte(w, '\\');
+ n += _write_byte(w, 'x');
+ n += _write_byte(w, DIGITS_LOWER[s[0]>>4]);
+ n += _write_byte(w, DIGITS_LOWER[s[0]&0xf]);
continue;
}
- n += write_escaped_rune(b, r, quote);
+ n += write_escaped_rune(w, r, quote);
}
- n += write_byte(b, quote);
+ n += _write_byte(w, quote);
return;
}
+write_encoded_rune :: proc{
+ write_encoded_rune_builder,
+ write_encoded_rune_writer,
+};
-write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
+write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
+ return write_encoded_rune_writer(to_writer(b), r, write_quote);
+
+}
+write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) {
if write_quote {
- n += write_byte(b, '\'');
+ n += _write_byte(w, '\'');
}
switch r {
- case '\a': n += write_string(b, `\a"`);
- case '\b': n += write_string(b, `\b"`);
- case '\e': n += write_string(b, `\e"`);
- case '\f': n += write_string(b, `\f"`);
- case '\n': n += write_string(b, `\n"`);
- case '\r': n += write_string(b, `\r"`);
- case '\t': n += write_string(b, `\t"`);
- case '\v': n += write_string(b, `\v"`);
+ case '\a': n += write_string(w, `\a"`);
+ case '\b': n += write_string(w, `\b"`);
+ case '\e': n += write_string(w, `\e"`);
+ case '\f': n += write_string(w, `\f"`);
+ case '\n': n += write_string(w, `\n"`);
+ case '\r': n += write_string(w, `\r"`);
+ case '\t': n += write_string(w, `\t"`);
+ case '\v': n += write_string(w, `\v"`);
case:
if r < 32 {
- n += write_string(b, `\x`);
+ n += write_string(w, `\x`);
buf: [2]byte;
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil);
switch len(s) {
- case 0: n += write_string(b, "00");
- case 1: n += write_byte(b, '0');
- case 2: n += write_string(b, s);
+ case 0: n += write_string(w, "00");
+ case 1: n += _write_byte(w, '0');
+ case 2: n += write_string(w, s);
}
} else {
- n += write_rune(b, r);
+ rn, _ := io.write_rune(w, r);
+ n += rn;
}
}
if write_quote {
- n += write_byte(b, '\'');
+ n += _write_byte(w, '\'');
}
return;
}
-write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
+write_escaped_rune :: proc{
+ write_escaped_rune_builder,
+ write_escaped_rune_writer,
+};
+
+write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
+ return write_escaped_rune_writer(to_writer(b), r, quote, html_safe);
+}
+
+write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) {
is_printable :: proc(r: rune) -> bool {
if r <= 0xff {
switch r {
@@ -233,54 +328,54 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
if html_safe {
switch r {
case '<', '>', '&':
- n += write_byte(b, '\\');
- n += write_byte(b, 'u');
+ n += _write_byte(w, '\\');
+ n += _write_byte(w, 'u');
for s := 12; s >= 0; s -= 4 {
- n += write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
+ n += _write_byte(w, DIGITS_LOWER[r>>uint(s) & 0xf]);
}
return;
}
}
if r == rune(quote) || r == '\\' {
- n += write_byte(b, '\\');
- n += write_byte(b, byte(r));
+ n += _write_byte(w, '\\');
+ n += _write_byte(w, byte(r));
return;
} else if is_printable(r) {
- n += write_encoded_rune(b, r, false);
+ n += write_encoded_rune(w, r, false);
return;
}
switch r {
- case '\a': n += write_string(b, `\a`);
- case '\b': n += write_string(b, `\b`);
- case '\e': n += write_string(b, `\e`);
- case '\f': n += write_string(b, `\f`);
- case '\n': n += write_string(b, `\n`);
- case '\r': n += write_string(b, `\r`);
- case '\t': n += write_string(b, `\t`);
- case '\v': n += write_string(b, `\v`);
+ case '\a': n += write_string(w, `\a`);
+ case '\b': n += write_string(w, `\b`);
+ case '\e': n += write_string(w, `\e`);
+ case '\f': n += write_string(w, `\f`);
+ case '\n': n += write_string(w, `\n`);
+ case '\r': n += write_string(w, `\r`);
+ case '\t': n += write_string(w, `\t`);
+ case '\v': n += write_string(w, `\v`);
case:
switch c := r; {
case c < ' ':
- n += write_byte(b, '\\');
- n += write_byte(b, 'x');
- n += write_byte(b, DIGITS_LOWER[byte(c)>>4]);
- n += write_byte(b, DIGITS_LOWER[byte(c)&0xf]);
+ n += _write_byte(w, '\\');
+ n += _write_byte(w, 'x');
+ n += _write_byte(w, DIGITS_LOWER[byte(c)>>4]);
+ n += _write_byte(w, DIGITS_LOWER[byte(c)&0xf]);
case c > utf8.MAX_RUNE:
c = 0xfffd;
fallthrough;
case c < 0x10000:
- n += write_byte(b, '\\');
- n += write_byte(b, 'u');
+ n += _write_byte(w, '\\');
+ n += _write_byte(w, 'u');
for s := 12; s >= 0; s -= 4 {
- n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
+ n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
case:
- n += write_byte(b, '\\');
- n += write_byte(b, 'U');
+ n += _write_byte(w, '\\');
+ n += _write_byte(w, 'U');
for s := 28; s >= 0; s -= 4 {
- n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
+ n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
}
}
diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin
new file mode 100644
index 000000000..c03bed86a
--- /dev/null
+++ b/core/strings/conversion.odin
@@ -0,0 +1,269 @@
+package strings
+
+import "core:io"
+import "core:unicode"
+import "core:unicode/utf8"
+
+to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
+ if len(s) == 0 {
+ return "";
+ }
+
+ b: Builder;
+ init_builder(&b, 0, 0, allocator);
+
+ s := s;
+ for c, i in s {
+ if c != utf8.RUNE_ERROR {
+ continue;
+ }
+
+ _, w := utf8.decode_rune_in_string(s[i:]);
+ if w == 1 {
+ grow_builder(&b, len(s) + len(replacement));
+ write_string(&b, s[:i]);
+ s = s[i:];
+ break;
+ }
+ }
+
+ if builder_cap(b) == 0 {
+ return clone(s, allocator);
+ }
+
+ invalid := false;
+
+ for i := 0; i < len(s); /**/ {
+ c := s[i];
+ if c < utf8.RUNE_SELF {
+ i += 1;
+ invalid = false;
+ write_byte(&b, c);
+ continue;
+ }
+
+ _, w := utf8.decode_rune_in_string(s[i:]);
+ if w == 1 {
+ i += 1;
+ if !invalid {
+ invalid = true;
+ write_string(&b, replacement);
+ }
+ continue;
+ }
+ invalid = false;
+ write_string(&b, s[i:][:w]);
+ i += w;
+ }
+ return to_string(b);
+}
+
+to_lower :: proc(s: string, allocator := context.allocator) -> string {
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
+ for r in s {
+ write_rune_builder(&b, unicode.to_lower(r));
+ }
+ return to_string(b);
+}
+to_upper :: proc(s: string, allocator := context.allocator) -> string {
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
+ for r in s {
+ write_rune_builder(&b, unicode.to_upper(r));
+ }
+ return to_string(b);
+}
+
+
+
+
+is_delimiter :: proc(c: rune) -> bool {
+ return c == '-' || c == '_' || is_space(c);
+}
+
+is_separator :: proc(r: rune) -> bool {
+ if r <= 0x7f {
+ switch r {
+ case '0'..'9': return false;
+ case 'a'..'z': return false;
+ case 'A'..'Z': return false;
+ case '_': return false;
+ }
+ return true;
+ }
+
+ // TODO(bill): unicode categories
+ // if unicode.is_letter(r) || unicode.is_digit(r) {
+ // return false;
+ // }
+
+ return unicode.is_space(r);
+}
+
+
+string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
+ prev, curr: rune;
+ for next in s {
+ if curr == 0 {
+ prev = curr;
+ curr = next;
+ continue;
+ }
+
+ callback(w, prev, curr, next);
+
+ prev = curr;
+ curr = next;
+ }
+
+ if len(s) > 0 {
+ callback(w, prev, curr, 0);
+ }
+}
+
+
+to_lower_camel_case :: to_camel_case;
+to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
+ s := s;
+ s = trim_space(s);
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
+ w := to_writer(&b);
+
+ string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
+ if !is_delimiter(curr) {
+ if is_delimiter(prev) {
+ io.write_rune(w, unicode.to_upper(curr));
+ } else if unicode.is_lower(prev) {
+ io.write_rune(w, curr);
+ } else {
+ io.write_rune(w, unicode.to_lower(curr));
+ }
+ }
+ });
+
+ return to_string(b);
+}
+
+to_upper_camel_case :: to_pascal_case;
+to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
+ s := s;
+ s = trim_space(s);
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
+ w := to_writer(&b);
+
+ string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
+ if !is_delimiter(curr) {
+ if is_delimiter(prev) || prev == 0 {
+ io.write_rune(w, unicode.to_upper(curr));
+ } else if unicode.is_lower(prev) {
+ io.write_rune(w, curr);
+ } else {
+ io.write_rune(w, unicode.to_lower(curr));
+ }
+ }
+ });
+
+ return to_string(b);
+}
+
+to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
+ s := s;
+ s = trim_space(s);
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
+ w := to_writer(&b);
+
+ adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
+
+ prev, curr: rune;
+
+ for next in s {
+ if is_delimiter(curr) {
+ if !is_delimiter(prev) {
+ io.write_rune(w, delimiter);
+ }
+ } else if unicode.is_upper(curr) {
+ if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
+ io.write_rune(w, delimiter);
+ }
+ io.write_rune(w, adjust_case(curr));
+ } else if curr != 0 {
+ io.write_rune(w, adjust_case(curr));
+ }
+
+ prev = curr;
+ curr = next;
+ }
+
+ if len(s) > 0 {
+ if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
+ io.write_rune(w, delimiter);
+ }
+ io.write_rune(w, adjust_case(curr));
+ }
+
+ return to_string(b);
+}
+
+
+to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
+ return to_delimiter_case(s, '_', false, allocator);
+}
+
+to_screaming_snake_case :: to_upper_snake_case;
+to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
+ return to_delimiter_case(s, '_', true, allocator);
+}
+
+to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
+ return to_delimiter_case(s, '-', false, allocator);
+}
+
+to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
+ return to_delimiter_case(s, '-', true, allocator);
+}
+
+to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
+ delimiter :: '_';
+
+ s := s;
+ s = trim_space(s);
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
+ w := to_writer(&b);
+
+ prev, curr: rune;
+
+ for next in s {
+ if is_delimiter(curr) {
+ if !is_delimiter(prev) {
+ io.write_rune(w, delimiter);
+ }
+ } else if unicode.is_upper(curr) {
+ if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
+ io.write_rune(w, delimiter);
+ }
+ io.write_rune(w, unicode.to_upper(curr));
+ } else if curr != 0 {
+ io.write_rune(w, unicode.to_lower(curr));
+ }
+
+ prev = curr;
+ curr = next;
+ }
+
+ if len(s) > 0 {
+ if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
+ io.write_rune(w, delimiter);
+ io.write_rune(w, unicode.to_upper(curr));
+ } else {
+ io.write_rune(w, unicode.to_lower(curr));
+ }
+ }
+
+ return to_string(b);
+}
+
diff --git a/core/strings/reader.odin b/core/strings/reader.odin
new file mode 100644
index 000000000..468c2f836
--- /dev/null
+++ b/core/strings/reader.odin
@@ -0,0 +1,177 @@
+package strings
+
+import "core:io"
+import "core:unicode/utf8"
+
+Reader :: struct {
+ s: string, // read-only buffer
+ i: i64, // current reading index
+ prev_rune: int, // previous reading index of rune or < 0
+}
+
+reader_init :: proc(r: ^Reader, s: string) {
+ r.s = s;
+ r.i = 0;
+ r.prev_rune = -1;
+}
+
+reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
+ s.stream_data = r;
+ s.stream_vtable = _reader_vtable;
+ return;
+}
+
+reader_length :: proc(r: ^Reader) -> int {
+ if r.i >= i64(len(r.s)) {
+ return 0;
+ }
+ return int(i64(len(r.s)) - r.i);
+}
+
+reader_size :: proc(r: ^Reader) -> i64 {
+ return i64(len(r.s));
+}
+
+reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+ if r.i >= i64(len(r.s)) {
+ return 0, .EOF;
+ }
+ r.prev_rune = -1;
+ n = copy(p, r.s[r.i:]);
+ r.i += i64(n);
+ return;
+}
+reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
+ if off < 0 {
+ return 0, .Invalid_Offset;
+ }
+ if off >= i64(len(r.s)) {
+ return 0, .EOF;
+ }
+ n = copy(p, r.s[off:]);
+ if n < len(p) {
+ err = .EOF;
+ }
+ return;
+}
+reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
+ r.prev_rune = -1;
+ if r.i >= i64(len(r.s)) {
+ return 0, .EOF;
+ }
+ b := r.s[r.i];
+ r.i += 1;
+ return b, nil;
+}
+reader_unread_byte :: proc(r: ^Reader) -> io.Error {
+ if r.i <= 0 {
+ return .Invalid_Unread;
+ }
+ r.prev_rune = -1;
+ r.i -= 1;
+ return nil;
+}
+reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
+ if r.i >= i64(len(r.s)) {
+ r.prev_rune = -1;
+ return 0, 0, .EOF;
+ }
+ r.prev_rune = int(r.i);
+ if c := r.s[r.i]; c < utf8.RUNE_SELF {
+ r.i += 1;
+ return rune(c), 1, nil;
+ }
+ ch, size = utf8.decode_rune_in_string(r.s[r.i:]);
+ r.i += i64(size);
+ return;
+}
+reader_unread_rune :: proc(r: ^Reader) -> io.Error {
+ if r.i <= 0 {
+ return .Invalid_Unread;
+ }
+ if r.prev_rune < 0 {
+ return .Invalid_Unread;
+ }
+ r.i = i64(r.prev_rune);
+ r.prev_rune = -1;
+ return nil;
+}
+reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+ r.prev_rune = -1;
+ abs: i64;
+ switch whence {
+ case .Start:
+ abs = offset;
+ case .Current:
+ abs = r.i + offset;
+ case .End:
+ abs = i64(len(r.s)) + offset;
+ case:
+ return 0, .Invalid_Whence;
+ }
+
+ if abs < 0 {
+ return 0, .Invalid_Offset;
+ }
+ r.i = abs;
+ return abs, nil;
+}
+reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
+ r.prev_rune = -1;
+ if r.i >= i64(len(r.s)) {
+ return 0, nil;
+ }
+ s := r.s[r.i:];
+ m: int;
+ m, err = io.write_string(w, s);
+ if m > len(s) {
+ panic("bytes.Reader.write_to: invalid io.write_string count");
+ }
+ r.i += i64(m);
+ n = i64(m);
+ if m != len(s) && err == nil {
+ err = .Short_Write;
+ }
+ return;
+}
+
+
+@(private)
+_reader_vtable := &io.Stream_VTable{
+ impl_size = proc(s: io.Stream) -> i64 {
+ r := (^Reader)(s.stream_data);
+ return reader_size(r);
+ },
+ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read(r, p);
+ },
+ impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read_at(r, p, off);
+ },
+ impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read_byte(r);
+ },
+ impl_unread_byte = proc(s: io.Stream) -> io.Error {
+ r := (^Reader)(s.stream_data);
+ return reader_unread_byte(r);
+ },
+ impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_read_rune(r);
+ },
+ impl_unread_rune = proc(s: io.Stream) -> io.Error {
+ r := (^Reader)(s.stream_data);
+ return reader_unread_rune(r);
+ },
+ impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_seek(r, offset, whence);
+ },
+ impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+ r := (^Reader)(s.stream_data);
+ return reader_write_to(r, w);
+ },
+};
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index c706f5940..09c39050f 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -1,5 +1,6 @@
package strings
+import "core:io"
import "core:mem"
import "core:unicode"
import "core:unicode/utf8"
@@ -225,7 +226,7 @@ index_byte :: proc(s: string, c: byte) -> int {
return -1;
}
-// Returns i1 if c is not present
+// Returns -1 if c is not present
last_index_byte :: proc(s: string, c: byte) -> int {
for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c {
@@ -467,10 +468,12 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
return;
}
+@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+
+
is_ascii_space :: proc(r: rune) -> bool {
- switch r {
- case '\t', '\n', '\v', '\f', '\r', ' ':
- return true;
+ if r < utf8.RUNE_SELF {
+ return _ascii_space[u8(r)] != 0;
}
return false;
}
@@ -757,7 +760,8 @@ split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator
// Adjacent invalid bytes are only replaced once
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
str := s;
- b := make_builder(0, len(str), allocator);
+ b: Builder;
+ init_builder(&b, 0, len(s), allocator);
has_error := false;
cursor := 0;
@@ -787,207 +791,6 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
}
-to_lower :: proc(s: string, allocator := context.allocator) -> string {
- b := make_builder(0, len(s), allocator);
- for r in s {
- write_rune(&b, unicode.to_lower(r));
- }
- return to_string(b);
-}
-to_upper :: proc(s: string, allocator := context.allocator) -> string {
- b := make_builder(0, len(s), allocator);
- for r in s {
- write_rune(&b, unicode.to_upper(r));
- }
- return to_string(b);
-}
-
-
-
-
-is_delimiter :: proc(c: rune) -> bool {
- return c == '-' || c == '_' || is_space(c);
-}
-
-is_separator :: proc(r: rune) -> bool {
- if r <= 0x7f {
- switch r {
- case '0'..'9': return false;
- case 'a'..'z': return false;
- case 'A'..'Z': return false;
- case '_': return false;
- }
- return true;
- }
-
- // TODO(bill): unicode categories
- // if unicode.is_letter(r) || unicode.is_digit(r) {
- // return false;
- // }
-
- return unicode.is_space(r);
-}
-
-
-string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, prev, curr, next: rune)) {
- prev, curr: rune;
- for next in s {
- if curr == 0 {
- prev = curr;
- curr = next;
- continue;
- }
-
- callback(b, prev, curr, next);
-
- prev = curr;
- curr = next;
- }
-
- if len(s) > 0 {
- callback(b, prev, curr, 0);
- }
-}
-
-
-to_lower_camel_case :: to_camel_case;
-to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
- s := s;
- s = trim_space(s);
- b := make_builder(0, len(s), allocator);
-
- string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
- if !is_delimiter(curr) {
- if is_delimiter(prev) {
- write_rune(b, unicode.to_upper(curr));
- } else if unicode.is_lower(prev) {
- write_rune(b, curr);
- } else {
- write_rune(b, unicode.to_lower(curr));
- }
- }
- });
-
- return to_string(b);
-}
-
-to_upper_camel_case :: to_pascal_case;
-to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
- s := s;
- s = trim_space(s);
- b := make_builder(0, len(s), allocator);
-
- string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
- if !is_delimiter(curr) {
- if is_delimiter(prev) || prev == 0 {
- write_rune(b, unicode.to_upper(curr));
- } else if unicode.is_lower(prev) {
- write_rune(b, curr);
- } else {
- write_rune(b, unicode.to_lower(curr));
- }
- }
- });
-
- return to_string(b);
-}
-
-to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
- s := s;
- s = trim_space(s);
- b := make_builder(0, len(s), allocator);
-
- adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
-
- prev, curr: rune;
-
- for next in s {
- if is_delimiter(curr) {
- if !is_delimiter(prev) {
- write_rune(&b, delimiter);
- }
- } else if unicode.is_upper(curr) {
- if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
- write_rune(&b, delimiter);
- }
- write_rune(&b, adjust_case(curr));
- } else if curr != 0 {
- write_rune(&b, adjust_case(curr));
- }
-
- prev = curr;
- curr = next;
- }
-
- if len(s) > 0 {
- if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
- write_rune(&b, delimiter);
- }
- write_rune(&b, adjust_case(curr));
- }
-
- return to_string(b);
-}
-
-
-to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
- return to_delimiter_case(s, '_', false, allocator);
-}
-
-to_screaming_snake_case :: to_upper_snake_case;
-to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
- return to_delimiter_case(s, '_', true, allocator);
-}
-
-to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
- return to_delimiter_case(s, '-', false, allocator);
-}
-
-to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
- return to_delimiter_case(s, '-', true, allocator);
-}
-
-to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
- delimiter :: '_';
-
- s := s;
- s = trim_space(s);
- b := make_builder(0, len(s), allocator);
-
- prev, curr: rune;
-
- for next in s {
- if is_delimiter(curr) {
- if !is_delimiter(prev) {
- write_rune(&b, delimiter);
- }
- } else if unicode.is_upper(curr) {
- if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
- write_rune(&b, delimiter);
- }
- write_rune(&b, unicode.to_upper(curr));
- } else if curr != 0 {
- write_rune(&b, unicode.to_lower(curr));
- }
-
- prev = curr;
- curr = next;
- }
-
- if len(s) > 0 {
- if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
- write_rune(&b, delimiter);
- write_rune(&b, unicode.to_upper(curr));
- } else {
- write_rune(&b, unicode.to_lower(curr));
- }
- }
-
- return to_string(b);
-}
-
-
-
reverse :: proc(s: string, allocator := context.allocator) -> string {
str := s;
n := len(str);
@@ -1013,7 +816,9 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
return "";
}
- b := make_builder(allocator);
+ b: Builder;
+ init_builder(&b, allocator);
+ writer := to_writer(&b);
str := s;
column: int;
@@ -1024,7 +829,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
expand := tab_size - column%tab_size;
for i := 0; i < expand; i += 1 {
- write_byte(&b, ' ');
+ io.write_byte(writer, ' ');
}
column += expand;
@@ -1035,7 +840,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
column += w;
}
- write_rune(&b, r);
+ io.write_rune(writer, r);
}
str = str[w:];
@@ -1070,12 +875,15 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
remains := length-1;
pad_len := rune_count(pad);
- b := make_builder(allocator);
+ b: Builder;
+ init_builder(&b, allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
- write_pad_string(&b, pad, pad_len, remains/2);
- write_string(&b, str);
- write_pad_string(&b, pad, pad_len, (remains+1)/2);
+ w := to_writer(&b);
+
+ write_pad_string(w, pad, pad_len, remains/2);
+ io.write_string(w, str);
+ write_pad_string(w, pad, pad_len, (remains+1)/2);
return to_string(b);
}
@@ -1090,11 +898,14 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
remains := length-1;
pad_len := rune_count(pad);
- b := make_builder(allocator);
+ b: Builder;
+ init_builder(&b, allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
- write_string(&b, str);
- write_pad_string(&b, pad, pad_len, remains);
+ w := to_writer(&b);
+
+ io.write_string(w, str);
+ write_pad_string(w, pad, pad_len, remains);
return to_string(b);
}
@@ -1109,86 +920,121 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
remains := length-1;
pad_len := rune_count(pad);
- b := make_builder(allocator);
+ b: Builder;
+ init_builder(&b, allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
- write_pad_string(&b, pad, pad_len, remains);
- write_string(&b, str);
+ w := to_writer(&b);
+
+ write_pad_string(w, pad, pad_len, remains);
+ io.write_string(w, str);
return to_string(b);
}
-to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
- if len(s) == 0 {
- return "";
+
+@private
+write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
+ repeats := remains / pad_len;
+
+ for i := 0; i < repeats; i += 1 {
+ io.write_string(w, pad);
}
- b := make_builder_len_cap(0, 0, allocator);
+ n := remains % pad_len;
+ p := pad;
- s := s;
- for c, i in s {
- if c != utf8.RUNE_ERROR {
- continue;
- }
+ for i := 0; i < n; i += 1 {
+ r, width := utf8.decode_rune_in_string(p);
+ io.write_rune(w, r);
+ p = p[width:];
+ }
+}
- _, w := utf8.decode_rune_in_string(s[i:]);
- if w == 1 {
- grow_builder(&b, len(s) + len(replacement));
- write_string(&b, s[:i]);
- s = s[i:];
- break;
- }
+
+// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space
+// returning a slice of substrings of s or an empty slice if s only contains white space
+fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check {
+ n := 0;
+ was_space := 1;
+ set_bits := u8(0);
+
+ // check to see
+ for i in 0..<len(s) {
+ r := s[i];
+ set_bits |= r;
+ is_space := int(_ascii_space[r]);
+ n += was_space & ~is_space;
+ was_space = is_space;
}
- if builder_cap(b) == 0 {
- return clone(s, allocator);
+ if set_bits >= utf8.RUNE_SELF {
+ return fields_proc(s, unicode.is_space, allocator);
}
- invalid := false;
+ if n == 0 {
+ return nil;
+ }
- for i := 0; i < len(s); /**/ {
- c := s[i];
- if c < utf8.RUNE_SELF {
+ a := make([]string, n, allocator);
+ na := 0;
+ field_start := 0;
+ i := 0;
+ for i < len(s) && _ascii_space[s[i]] != 0 {
+ i += 1;
+ }
+ field_start = i;
+ for i < len(s) {
+ if _ascii_space[s[i]] == 0 {
i += 1;
- invalid = false;
- write_byte(&b, c);
continue;
}
-
- _, w := utf8.decode_rune_in_string(s[i:]);
- if w == 1 {
+ a[na] = s[field_start : i];
+ na += 1;
+ i += 1;
+ for i < len(s) && _ascii_space[s[i]] != 0 {
i += 1;
- if !invalid {
- invalid = true;
- write_string(&b, replacement);
- }
- continue;
}
- invalid = false;
- write_string(&b, s[i:][:w]);
- i += w;
+ field_start = i;
}
- return to_string(b);
+ if field_start < len(s) {
+ a[na] = s[field_start:];
+ }
+ return a;
}
+// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch)
+// returns a slice of substrings of s
+// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned
+//
+// fields_proc makes no guarantee about the order in which it calls f(ch)
+// it assumes that `f` always returns the same value for a given ch
+fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check {
+ substrings := make([dynamic]string, 0, 32, allocator);
-@private
-write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
- repeats := remains / pad_len;
-
- for i := 0; i < repeats; i += 1 {
- write_string(b, pad);
+ start, end := -1, -1;
+ for r, offset in s {
+ end = offset;
+ if f(r) {
+ if start >= 0 {
+ append(&substrings, s[start : end]);
+ // -1 could be used, but just speed it up through bitwise not
+ // gotta love 2's complement
+ start = ~start;
+ }
+ } else {
+ if start < 0 {
+ start = end;
+ }
+ }
}
- n := remains % pad_len;
- p := pad;
-
- for i := 0; i < n; i += 1 {
- r, w := utf8.decode_rune_in_string(p);
- write_rune(b, r);
- p = p[w:];
+ if start >= 0 {
+ append(&substrings, s[start : end]);
}
+
+ return substrings[:];
}
diff --git a/core/sync/channel.odin b/core/sync/channel.odin
index 4259384ce..7eddbfaf0 100644
--- a/core/sync/channel.odin
+++ b/core/sync/channel.odin
@@ -2,60 +2,89 @@ package sync
import "core:mem"
import "core:time"
-import "core:intrinsics"
+import "intrinsics"
import "core:math/rand"
_, _ :: time, rand;
+Channel_Direction :: enum i8 {
+ Both = 0,
+ Send = +1,
+ Recv = -1,
+}
-Channel :: struct(T: typeid) {
+Channel :: struct(T: typeid, Direction := Channel_Direction.Both) {
using _internal: ^Raw_Channel,
}
-channel_init :: proc(ch: ^$C/Channel($T), cap := 0, allocator := context.allocator) {
+channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
-channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T)) {
+channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
-channel_destroy :: proc(ch: $C/Channel($T)) {
+channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
+ context.allocator = allocator;
+ ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
+ return;
+}
+channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
+ context.allocator = allocator;
+ ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
+ return;
+}
+
+channel_destroy :: proc(ch: $C/Channel($T, $D)) {
raw_channel_destroy(ch._internal);
}
+channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
+ res._internal = ch._internal;
+ return;
+}
+
+channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
+ res._internal = ch._internal;
+ return;
+}
-channel_len :: proc(ch: $C/Channel($T)) -> int {
- return ch._internal.len;
+
+channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
+ return ch._internal.len if ch._internal != nil else 0;
}
-channel_cap :: proc(ch: $C/Channel($T)) -> int {
- return ch._internal.cap;
+channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
+ return ch._internal.cap if ch._internal != nil else 0;
}
-channel_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) {
+channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
msg := msg;
_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc);
}
-channel_try_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) -> bool {
+channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
msg := msg;
return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc);
}
-channel_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T) {
+channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
c := ch._internal;
+ if c == nil {
+ panic(message="cannot recv message; channel is nil", loc=loc);
+ }
mutex_lock(&c.mutex);
raw_channel_recv_impl(c, &msg, loc);
mutex_unlock(&c.mutex);
return;
}
-channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T, ok: bool) {
+channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
- if mutex_try_lock(&c.mutex) {
+ if c != nil && mutex_try_lock(&c.mutex) {
if c.len > 0 {
raw_channel_recv_impl(c, &msg, loc);
ok = true;
@@ -64,7 +93,7 @@ channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T
}
return;
}
-channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_location) -> (ok: bool) {
+channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
res: T;
res, ok = channel_try_recv(ch, loc);
if ok && msg != nil {
@@ -74,32 +103,32 @@ channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_locatio
}
-channel_is_nil :: proc(ch: $C/Channel($T)) -> bool {
+channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
return ch._internal == nil;
}
-channel_is_open :: proc(ch: $C/Channel($T)) -> bool {
+channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
c := ch._internal;
return c != nil && !c.closed;
}
-channel_eq :: proc(a, b: $C/Channel($T)) -> bool {
+channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal == b._internal;
}
-channel_ne :: proc(a, b: $C/Channel($T)) -> bool {
+channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal != b._internal;
}
-channel_can_send :: proc(ch: $C/Channel($T)) -> (ok: bool) {
+channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
return raw_channel_can_send(ch._internal);
}
-channel_can_recv :: proc(ch: $C/Channel($T)) -> (ok: bool) {
+channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
return raw_channel_can_recv(ch._internal);
}
-channel_peek :: proc(ch: $C/Channel($T)) -> int {
+channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
c := ch._internal;
if c == nil {
return -1;
@@ -111,12 +140,12 @@ channel_peek :: proc(ch: $C/Channel($T)) -> int {
}
-channel_close :: proc(ch: $C/Channel($T), loc := #caller_location) {
+channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
raw_channel_close(ch._internal, loc);
}
-channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
+channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
if c == nil {
return;
@@ -127,12 +156,12 @@ channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
}
return;
}
-channel_drain :: proc(ch: $C/Channel($T)) {
+channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
raw_channel_drain(ch._internal);
}
-channel_move :: proc(dst, src: $C/Channel($T)) {
+channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
for msg in channel_iterator(src) {
channel_send(dst, msg);
}
@@ -258,18 +287,19 @@ raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc :=
for c.len >= c.cap {
condition_wait_for(&c.cond);
}
- } else if c.len > 0 {
+ } else if c.len > 0 { // TODO(bill): determine correct behaviour
if !block {
return false;
}
condition_wait_for(&c.cond);
+ } else if c.len == 0 && !block {
+ return false;
}
send(c, msg);
condition_signal(&c.cond);
raw_channel_wait_queue_signal(c.recvq);
-
return true;
}
@@ -509,7 +539,7 @@ select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
return;
}
-select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
+select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
@@ -535,7 +565,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
- raw_channel_wait_queue_wait_on(&state);
+ raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
@@ -560,7 +590,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
return;
}
-select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
+select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
@@ -589,7 +619,7 @@ select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
- raw_channel_wait_queue_wait_on(&state);
+ raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
@@ -781,16 +811,15 @@ select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_che
return;
}
-select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
+select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
- index = 0;
+ index = -1;
return;
case 1:
- if c := channels[0]; channel_can_recv(c) {
+ ok: bool;
+ if msg, ok = channel_try_recv(channels[0]); ok {
index = 0;
- msg = channel_recv(c);
- return;
}
return;
}
@@ -820,16 +849,14 @@ select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int)
return;
}
-select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
+select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
+ index = -1;
switch len(channels) {
case 0:
- index = 0;
return;
case 1:
- if c := channels[0]; channel_can_send(c) {
+ if channel_try_send(channels[0], msg) {
index = 0;
- channel_send(c, msg);
- return;
}
return;
}
diff --git a/core/sys/cpu/cpu.odin b/core/sys/cpu/cpu.odin
new file mode 100644
index 000000000..b6f770aed
--- /dev/null
+++ b/core/sys/cpu/cpu.odin
@@ -0,0 +1,35 @@
+package sys_cpu
+
+#assert(ODIN_USE_LLVM_API);
+
+Cache_Line_Pad :: struct {_: [_cache_line_size]byte};
+
+initialized: bool;
+
+x86: struct {
+ _: Cache_Line_Pad,
+ has_aes: bool, // AES hardware implementation (AES NI)
+ has_adx: bool, // Multi-precision add-carry instruction extensions
+ has_avx: bool, // Advanced vector extension
+ has_avx2: bool, // Advanced vector extension 2
+ has_bmi1: bool, // Bit manipulation instruction set 1
+ has_bmi2: bool, // Bit manipulation instruction set 2
+ has_erms: bool, // Enhanced REP for MOVSB and STOSB
+ has_fma: bool, // Fused-multiply-add instructions
+ has_os_xsave: bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
+ has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM
+ has_popcnt: bool, // Hamming weight instruction POPCNT.
+ has_rdrand: bool, // RDRAND instruction (on-chip random number generator)
+ has_rdseed: bool, // RDSEED instruction (on-chip random number generator)
+ has_sse2: bool, // Streaming SIMD extension 2 (always available on amd64)
+ has_sse3: bool, // Streaming SIMD extension 3
+ has_ssse3: bool, // Supplemental streaming SIMD extension 3
+ has_sse41: bool, // Streaming SIMD extension 4 and 4.1
+ has_sse42: bool, // Streaming SIMD extension 4 and 4.2
+ _: Cache_Line_Pad,
+};
+
+
+init :: proc() {
+ _init();
+}
diff --git a/core/sys/cpu/cpu_x86.odin b/core/sys/cpu/cpu_x86.odin
new file mode 100644
index 000000000..8f3560a87
--- /dev/null
+++ b/core/sys/cpu/cpu_x86.odin
@@ -0,0 +1,67 @@
+//+build 386, amd64
+package sys_cpu
+
+_cache_line_size :: 64;
+
+cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) {
+ return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} {
+ "cpuid",
+ "={ax},={bx},={cx},={dx},{ax},{cx}",
+ }(ax, cx));
+}
+
+xgetbv :: proc() -> (eax, edx: u32) {
+ return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} {
+ "xgetbv",
+ "={ax},={dx},{cx}",
+ }(0));
+}
+
+_init :: proc() {
+ is_set :: proc(hwc: u32, value: u32) -> bool {
+ return hwc&value != 0;
+ }
+
+ initialized = true;
+
+ max_id, _, _, _ := cpuid(0, 0);
+
+ if max_id < 1 {
+ return;
+ }
+
+ _, _, ecx1, edx1 := cpuid(1, 0);
+
+ x86.has_sse2 = is_set(26, edx1);
+
+ x86.has_sse3 = is_set(0, ecx1);
+ x86.has_pclmulqdq = is_set(1, ecx1);
+ x86.has_ssse3 = is_set(9, ecx1);
+ x86.has_fma = is_set(12, ecx1);
+ x86.has_sse41 = is_set(19, ecx1);
+ x86.has_sse42 = is_set(20, ecx1);
+ x86.has_popcnt = is_set(23, ecx1);
+ x86.has_aes = is_set(25, ecx1);
+ x86.has_os_xsave = is_set(27, ecx1);
+ x86.has_rdrand = is_set(30, ecx1);
+
+ os_supports_avx := false;
+ if x86.has_os_xsave {
+ eax, _ := xgetbv();
+ os_supports_avx = is_set(1, eax) && is_set(2, eax);
+ }
+
+ x86.has_avx = is_set(28, ecx1) && os_supports_avx;
+
+ if max_id < 7 {
+ return;
+ }
+
+ _, ebx7, _, _ := cpuid(7, 0);
+ x86.has_bmi1 = is_set(3, ebx7);
+ x86.has_avx2 = is_set(5, ebx7) && os_supports_avx;
+ x86.has_bmi2 = is_set(8, ebx7);
+ x86.has_erms = is_set(9, ebx7);
+ x86.has_rdseed = is_set(18, ebx7);
+ x86.has_adx = is_set(19, ebx7);
+}
diff --git a/core/sys/unix/pthread_darwin.odin b/core/sys/unix/pthread_darwin.odin
index 7f7f59189..8460c4852 100644
--- a/core/sys/unix/pthread_darwin.odin
+++ b/core/sys/unix/pthread_darwin.odin
@@ -14,44 +14,44 @@ PTHREAD_ONCE_SIZE :: 8;
PTHREAD_RWLOCK_SIZE :: 192;
PTHREAD_RWLOCKATTR_SIZE :: 16;
-pthread_t :: opaque u64;
+pthread_t :: #opaque u64;
-pthread_attr_t :: opaque struct #align 16 {
+pthread_attr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_ATTR_SIZE] c.char,
};
-pthread_cond_t :: opaque struct #align 16 {
+pthread_cond_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_COND_SIZE] c.char,
};
-pthread_condattr_t :: opaque struct #align 16 {
+pthread_condattr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_CONDATTR_SIZE] c.char,
};
-pthread_mutex_t :: opaque struct #align 16 {
+pthread_mutex_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_MUTEX_SIZE] c.char,
};
-pthread_mutexattr_t :: opaque struct #align 16 {
+pthread_mutexattr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_MUTEXATTR_SIZE] c.char,
};
-pthread_once_t :: opaque struct #align 16 {
+pthread_once_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_ONCE_SIZE] c.char,
};
-pthread_rwlock_t :: opaque struct #align 16 {
+pthread_rwlock_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_RWLOCK_SIZE] c.char,
};
-pthread_rwlockattr_t :: opaque struct #align 16 {
+pthread_rwlockattr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_RWLOCKATTR_SIZE] c.char,
};
diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin
index 77b922686..cbd7e99f1 100644
--- a/core/sys/unix/pthread_freebsd.odin
+++ b/core/sys/unix/pthread_freebsd.odin
@@ -26,32 +26,32 @@ when size_of(int) == 8 {
PTHREAD_BARRIER_T_SIZE :: 20;
}
-pthread_cond_t :: opaque struct #align 16 {
+pthread_cond_t :: #opaque struct #align 16 {
_: [PTHREAD_COND_T_SIZE] c.char,
};
-pthread_mutex_t :: opaque struct #align 16 {
+pthread_mutex_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEX_T_SIZE] c.char,
};
-pthread_rwlock_t :: opaque struct #align 16 {
+pthread_rwlock_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
};
-pthread_barrier_t :: opaque struct #align 16 {
+pthread_barrier_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIER_T_SIZE] c.char,
};
-pthread_attr_t :: opaque struct #align 16 {
+pthread_attr_t :: #opaque struct #align 16 {
_: [PTHREAD_ATTR_T_SIZE] c.char,
};
-pthread_condattr_t :: opaque struct #align 16 {
+pthread_condattr_t :: #opaque struct #align 16 {
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
};
-pthread_mutexattr_t :: opaque struct #align 16 {
+pthread_mutexattr_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
};
-pthread_rwlockattr_t :: opaque struct #align 16 {
+pthread_rwlockattr_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
};
-pthread_barrierattr_t :: opaque struct #align 16 {
+pthread_barrierattr_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
};
diff --git a/core/sys/unix/pthread_linux.odin b/core/sys/unix/pthread_linux.odin
index 5edd1ef5a..76a0719fb 100644
--- a/core/sys/unix/pthread_linux.odin
+++ b/core/sys/unix/pthread_linux.odin
@@ -33,32 +33,32 @@ when size_of(int) == 8 {
PTHREAD_BARRIER_T_SIZE :: 20;
}
-pthread_cond_t :: opaque struct #align 16 {
+pthread_cond_t :: #opaque struct #align 16 {
_: [PTHREAD_COND_T_SIZE] c.char,
};
-pthread_mutex_t :: opaque struct #align 16 {
+pthread_mutex_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEX_T_SIZE] c.char,
};
-pthread_rwlock_t :: opaque struct #align 16 {
+pthread_rwlock_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
};
-pthread_barrier_t :: opaque struct #align 16 {
+pthread_barrier_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIER_T_SIZE] c.char,
};
-pthread_attr_t :: opaque struct #align 16 {
+pthread_attr_t :: #opaque struct #align 16 {
_: [PTHREAD_ATTR_T_SIZE] c.char,
};
-pthread_condattr_t :: opaque struct #align 16 {
+pthread_condattr_t :: #opaque struct #align 16 {
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
};
-pthread_mutexattr_t :: opaque struct #align 16 {
+pthread_mutexattr_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
};
-pthread_rwlockattr_t :: opaque struct #align 16 {
+pthread_rwlockattr_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
};
-pthread_barrierattr_t :: opaque struct #align 16 {
+pthread_barrierattr_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
};
diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin
index d5cefad94..ba3898730 100644
--- a/core/sys/win32/kernel32.odin
+++ b/core/sys/win32/kernel32.odin
@@ -8,12 +8,12 @@ foreign kernel32 {
@(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring,
process_attributes, thread_attributes: ^Security_Attributes,
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
- current_direcotry: cstring, startup_info: ^Startup_Info,
+ current_directory: cstring, startup_info: ^Startup_Info,
process_information: ^Process_Information) -> Bool ---;
@(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring,
process_attributes, thread_attributes: ^Security_Attributes,
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
- current_direcotry: Wstring, startup_info: ^Startup_Info,
+ current_directory: Wstring, startup_info: ^Startup_Info,
process_information: ^Process_Information) -> Bool ---;
@(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---;
@(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---;
diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin
new file mode 100644
index 000000000..bbd2090b6
--- /dev/null
+++ b/core/text/scanner/scanner.odin
@@ -0,0 +1,585 @@
+package text_scanner
+
+import "core:fmt"
+import "core:strings"
+import "core:unicode"
+import "core:unicode/utf8"
+
+Position :: struct {
+ filename: string, // filename, if present
+ offset: int, // byte offset, starting @ 0
+ line: int, // line number, starting @ 1
+ column: int, // column number, starting @ 1 (character count per line)
+}
+
+position_is_valid :: proc(pos: Position) -> bool {
+ return pos.line > 0;
+}
+
+position_to_string :: proc(pos: Position, allocator := context.temp_allocator) -> string {
+ s := pos.filename;
+ if s == "" {
+ s = "<input>";
+ }
+
+ context.allocator = allocator;
+ if position_is_valid(pos) {
+ return fmt.aprintf("%s(%d:%d)", s, pos.line, pos.column);
+ } else {
+ return strings.clone(s);
+ }
+}
+
+EOF :: -1;
+Ident :: -2;
+Int :: -3;
+Float :: -4;
+Char :: -5;
+String :: -6;
+Raw_String :: -7;
+Comment :: -8;
+
+Scan_Flag :: enum u32 {
+ Scan_Idents,
+ Scan_Ints,
+ Scan_C_Int_Prefixes,
+ Scan_Floats,
+ Scan_Chars,
+ Scan_Strings,
+ Scan_Raw_Strings,
+ Scan_Comments,
+ Skip_Comments,
+}
+Scan_Flags :: bit_set[Scan_Flag; u32];
+
+Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
+C_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
+
+Odin_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' ';
+C_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<'\v' | 1<<'\f' | 1<<' ';
+
+
+Scanner :: struct {
+ src: string,
+
+ src_pos: int,
+ src_end: int,
+
+ tok_pos: int,
+ tok_end: int,
+
+ ch: rune,
+
+ line: int,
+ column: int,
+ prev_line_len: int,
+ prev_char_len: int,
+
+ error: proc(s: ^Scanner, msg: string),
+ error_count: int,
+
+ flags: Scan_Flags,
+ whitespace: u64,
+
+ is_ident_rune: proc(ch: rune, i: int) -> bool,
+
+ pos: Position,
+}
+
+init :: proc(s: ^Scanner, src: string, filename := "") -> ^Scanner {
+ s^ = {};
+
+ s.src = src;
+ s.pos.filename = filename;
+
+ s.tok_pos = -1;
+
+ s.ch = -2; // no char read yet, not an EOF
+
+ s.line = 1;
+
+ s.flags = Odin_Like_Tokens;
+ s.whitespace = Odin_Whitespace;
+
+ return s;
+}
+
+
+@(private)
+advance :: proc(s: ^Scanner) -> rune {
+ if s.src_pos >= len(s.src) {
+ s.prev_char_len = 0;
+ return EOF;
+ }
+ ch, width := rune(s.src[s.src_pos]), 1;
+
+ if ch >= utf8.RUNE_SELF {
+ ch, width = utf8.decode_rune_in_string(s.src[s.src_pos:]);
+ if ch == utf8.RUNE_ERROR && width == 1 {
+ s.src_pos += width;
+ s.prev_char_len = width;
+ s.column += 1;
+ error(s, "invalid UTF-8 encoding");
+ return ch;
+ }
+ }
+
+ s.src_pos += width;
+ s.prev_char_len = width;
+ s.column += 1;
+
+ switch ch {
+ case 0:
+ error(s, "invalid character NUL");
+ case '\n':
+ s.line += 1;
+ s.prev_line_len = s.column;
+ s.column = 0;
+ }
+
+ return ch;
+}
+
+next :: proc(s: ^Scanner) -> rune {
+ s.tok_pos = -1;
+ s.pos.line = 0;
+ ch := peek(s);
+ if ch != EOF {
+ s.ch = advance(s);
+ }
+ return ch;
+}
+
+peek :: proc(s: ^Scanner) -> rune {
+ if s.ch == -2 {
+ s.ch = advance(s);
+ if s.ch == '\ufeff' { // Ignore BOM
+ s.ch = advance(s);
+ }
+ }
+ return s.ch;
+}
+
+
+error :: proc(s: ^Scanner, msg: string) {
+ s.error_count += 1;
+ if s.error != nil {
+ s.error(s, msg);
+ return;
+ }
+ p := s.pos;
+ if !position_is_valid(p) {
+ p = position(s);
+ }
+
+ s := p.filename;
+ if s == "" {
+ s = "<input>";
+ }
+
+ if position_is_valid(p) {
+ fmt.eprintf("%s(%d:%d): %s\n", s, p.line, p.column, msg);
+ } else {
+ fmt.eprintf("%s: %s\n", s, msg);
+ }
+}
+
+errorf :: proc(s: ^Scanner, format: string, args: ..any) {
+ error(s, fmt.tprintf(format, ..args));
+}
+
+@(private)
+is_ident_rune :: proc(s: ^Scanner, ch: rune, i: int) -> bool {
+ if s.is_ident_rune != nil {
+ return s.is_ident_rune(ch, i);
+ }
+ return ch == '_' || unicode.is_letter(ch) || unicode.is_digit(ch) && i > 0;
+}
+
+@(private)
+scan_identifier :: proc(s: ^Scanner) -> rune {
+ ch := advance(s);
+ for i := 1; is_ident_rune(s, ch, i); i += 1 {
+ ch = advance(s);
+ }
+ return ch;
+}
+
+@(private) lower :: proc(ch: rune) -> rune { return ('a' - 'A') | ch; }
+@(private) is_decimal :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9'; }
+@(private) is_hex :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f'; }
+
+
+
+@(private)
+scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) {
+ lit_name :: proc(prefix: rune) -> string {
+ switch prefix {
+ case 'b': return "binary literal";
+ case 'o': return "octal literal";
+ case 'z': return "dozenal literal";
+ case 'x': return "hexadecimal literal";
+ }
+ return "decimal literal";
+ }
+
+ digits :: proc(s: ^Scanner, ch0: rune, base: int, invalid: ^rune) -> (ch: rune, digsep: int) {
+ ch = ch0;
+ if base <= 10 {
+ max := rune('0' + base);
+ for is_decimal(ch) || ch == '_' {
+ ds := 1;
+ if ch == '_' {
+ ds = 2;
+ } else if ch >= max && invalid^ == 0 {
+ invalid^ = ch;
+ }
+ digsep |= ds;
+ ch = advance(s);
+ }
+ } else {
+ for is_hex(ch) || ch == '_' {
+ ds := 1;
+ if ch == '_' {
+ ds = 2;
+ }
+ digsep |= ds;
+ ch = advance(s);
+ }
+ }
+ return;
+ }
+
+ ch, seen_dot := ch, seen_dot;
+
+ base := 10;
+ prefix := rune(0);
+ digsep := 0;
+ invalid := rune(0);
+
+ tok: rune;
+ ds: int;
+
+ if !seen_dot {
+ tok = Int;
+ if ch == '0' {
+ ch = advance(s);
+
+ p := lower(ch);
+ if .Scan_C_Int_Prefixes in s.flags {
+ switch p {
+ case 'b':
+ ch = advance(s);
+ base, prefix = 2, 'b';
+ case 'x':
+ ch = advance(s);
+ base, prefix = 16, 'x';
+ case:
+ base, prefix = 8, 'o';
+ digsep = 1; // Leading zero
+ }
+ } else {
+ switch p {
+ case 'b':
+ ch = advance(s);
+ base, prefix = 2, 'b';
+ case 'o':
+ ch = advance(s);
+ base, prefix = 8, 'o';
+ case 'd':
+ ch = advance(s);
+ base, prefix = 10, 'd';
+ case 'z':
+ ch = advance(s);
+ base, prefix = 12, 'z';
+ case 'h':
+ tok = Float;
+ fallthrough;
+ case 'x':
+ ch = advance(s);
+ base, prefix = 16, 'x';
+ case:
+ digsep = 1; // Leading zero
+ }
+ }
+ }
+
+ ch, ds = digits(s, ch, base, &invalid);
+ digsep |= ds;
+ if ch == '.' && .Scan_Floats in s.flags {
+ ch = advance(s);
+ seen_dot = true;
+ }
+ }
+
+ if seen_dot {
+ tok = Float;
+ if prefix != 0 && prefix != 'x' {
+ errorf(s, "invalid radix point in %s", lit_name(prefix));
+ }
+ ch, ds = digits(s, ch, base, &invalid);
+ digsep |= ds;
+ }
+
+ if digsep&1 == 0 {
+ errorf(s, "%s has no digits", lit_name(prefix));
+ }
+
+ if e := lower(ch); (e == 'e' || e == 'p') && .Scan_Floats in s.flags {
+ switch {
+ case e == 'e' && prefix != 0:
+ errorf(s, "%q exponent requires decimal mantissa", ch);
+ case e == 'p' && prefix != 'x':
+ errorf(s, "%q exponent requires hexadecimal mantissa", ch);
+ }
+ ch = advance(s);
+ tok = Float;
+ if ch == '+' || ch == '-' {
+ ch = advance(s);
+ }
+ ch, ds = digits(s, ch, 10, nil);
+ digsep |= ds;
+ if ds&1 == 0 {
+ error(s, "exponent has no digits");
+ }
+ } else if prefix == 'x' && tok == Float {
+ error(s, "hexadecimal mantissa requires a 'p' exponent");
+ }
+
+ if tok == Int && invalid != 0 {
+ errorf(s, "invalid digit %q in %s", invalid, lit_name(prefix));
+ }
+
+ if digsep&2 != 0 {
+ s.tok_end = s.src_pos - s.prev_char_len;
+ }
+ return tok, ch;
+}
+
+@(private)
+scan_string :: proc(s: ^Scanner, quote: rune) -> (n: int) {
+ digit_val :: proc(ch: rune) -> int {
+ switch v := lower(ch); v {
+ case '0'..'9': return int(v - '0');
+ case 'a'..'z': return int(v - 'a');
+ }
+ return 16;
+ }
+
+ scan_digits :: proc(s: ^Scanner, ch: rune, base, n: int) -> rune {
+ ch, n := ch, n;
+ for n > 0 && digit_val(ch) < base {
+ ch = advance(s);
+ n -= 1;
+ }
+ if n > 0 {
+ error(s, "invalid char escape");
+ }
+ return ch;
+ }
+
+ ch := advance(s);
+ for ch != quote {
+ if ch == '\n' || ch < 0 {
+ error(s, "literal no terminated");
+ return;
+ }
+ if ch == '\\' {
+ ch = advance(s);
+ switch ch {
+ case quote, 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\':
+ ch = advance(s);
+ case '0'..'7': ch = scan_digits(s, advance(s), 8, 3);
+ case 'x': ch = scan_digits(s, advance(s), 16, 2);
+ case 'u': ch = scan_digits(s, advance(s), 16, 4);
+ case 'U': ch = scan_digits(s, advance(s), 16, 8);
+ case:
+ error(s, "invalid char escape");
+ }
+ } else {
+ ch = advance(s);
+ }
+ n += 1;
+ }
+ return;
+}
+
+@(private)
+scan_raw_string :: proc(s: ^Scanner) {
+ ch := advance(s);
+ for ch != '`' {
+ if ch < 0 {
+ error(s, "literal not terminated");
+ return;
+ }
+ ch = advance(s);
+ }
+}
+
+@(private)
+scan_char :: proc(s: ^Scanner) {
+ if scan_string(s, '\'') != 1 {
+ error(s, "invalid char literal");
+ }
+}
+
+@(private)
+scan_comment :: proc(s: ^Scanner, ch: rune) -> rune {
+ ch := ch;
+ if ch == '/' { // line comment
+ ch = advance(s);
+ for ch != '\n' && ch >= 0 {
+ ch = advance(s);
+ }
+ return ch;
+ }
+
+ // block /**/ comment
+ ch = advance(s);
+ for {
+ if ch < 0 {
+ error(s, "comment not terminated");
+ break;
+ }
+ ch0 := ch;
+ ch = advance(s);
+ if ch0 == '*' && ch == '/' {
+ return advance(s);
+ }
+ }
+ return ch;
+}
+
+scan :: proc(s: ^Scanner) -> (tok: rune) {
+ ch := peek(s);
+ if ch == EOF {
+ return ch;
+ }
+
+ // reset position
+ s.tok_pos = -1;
+ s.pos.line = 0;
+
+ redo: for {
+ for s.whitespace & (1<<uint(ch)) != 0 {
+ ch = advance(s);
+ }
+
+ s.tok_pos = s.src_pos - s.prev_char_len;
+ s.pos.offset = s.tok_pos;
+
+ if s.column > 0 {
+ s.pos.line = s.line;
+ s.pos.column = s.column;
+ } else {
+ // previous character was newline
+ s.pos.line = s.line - 1;
+ s.pos.column = s.prev_line_len;
+ }
+
+ tok = ch;
+ if is_ident_rune(s, ch, 0) {
+ if .Scan_Idents in s.flags {
+ tok = Ident;
+ ch = scan_identifier(s);
+ } else {
+ ch = advance(s);
+ }
+
+ } else if is_decimal(ch) {
+ if s.flags >= {.Scan_Ints, .Scan_Floats} {
+ tok, ch = scan_number(s, ch, false);
+ } else {
+ ch = advance(s);
+ }
+ } else {
+ switch ch {
+ case EOF:
+ break;
+ case '"':
+ if .Scan_Strings in s.flags {
+ scan_string(s, '"');
+ tok = String;
+ }
+ ch = advance(s);
+ case '\'':
+ if .Scan_Chars in s.flags {
+ scan_string(s, '\'');
+ tok = Char;
+ }
+ ch = advance(s);
+ case '`':
+ if .Scan_Raw_Strings in s.flags {
+ scan_raw_string(s);
+ tok = Raw_String;
+ }
+ ch = advance(s);
+ case '.':
+ ch = advance(s);
+ if is_decimal(ch) && .Scan_Floats in s.flags {
+ tok, ch = scan_number(s, ch, true);
+ }
+ case '/':
+ ch = advance(s);
+ if (ch == '/' || ch == '*') && .Scan_Comments in s.flags {
+ if .Skip_Comments in s.flags {
+ s.tok_pos = -1;
+ ch = scan_comment(s, ch);
+ continue redo;
+ }
+ ch = scan_comment(s, ch);
+ tok = Comment;
+ }
+ case:
+ ch = advance(s);
+ }
+ }
+
+ break redo;
+ }
+
+ s.tok_end = s.src_pos - s.prev_char_len;
+
+ s.ch = ch;
+ return tok;
+}
+
+position :: proc(s: ^Scanner) -> Position {
+ pos: Position;
+ pos.filename = s.pos.filename;
+ pos.offset = s.src_pos - s.prev_char_len;
+ switch {
+ case s.column > 0:
+ pos.line = s.line;
+ pos.column = s.column;
+ case s.prev_line_len > 0:
+ pos.line = s.line-1;
+ pos.column = s.prev_line_len;
+ case:
+ pos.line = 1;
+ pos.column = 1;
+ }
+ return pos;
+}
+
+token_text :: proc(s: ^Scanner) -> string {
+ if s.tok_pos < 0 {
+ return "";
+ }
+ return string(s.src[s.tok_pos:s.tok_end]);
+}
+
+token_string :: proc(tok: rune, allocator := context.temp_allocator) -> string {
+ context.allocator = allocator;
+ switch tok {
+ case EOF: return strings.clone("EOF");
+ case Ident: return strings.clone("Ident");
+ case Int: return strings.clone("Int");
+ case Float: return strings.clone("Float");
+ case Char: return strings.clone("Char");
+ case String: return strings.clone("String");
+ case Raw_String: return strings.clone("Raw_String");
+ case Comment: return strings.clone("Comment");
+ }
+ return fmt.aprintf("%q", tok);
+}
diff --git a/core/thread/thread.odin b/core/thread/thread.odin
index 85e0cc3fe..51fb116e3 100644
--- a/core/thread/thread.odin
+++ b/core/thread/thread.odin
@@ -2,17 +2,26 @@ package thread
import "core:runtime"
import "core:sync"
-import "core:intrinsics"
+import "core:mem"
+import "intrinsics"
+
+_ :: intrinsics;
Thread_Proc :: #type proc(^Thread);
+MAX_USER_ARGUMENTS :: 8;
+
Thread :: struct {
- using specific: Thread_Os_Specific,
- procedure: Thread_Proc,
- data: rawptr,
- user_index: int,
+ using specific: Thread_Os_Specific,
+ procedure: Thread_Proc,
+ data: rawptr,
+ user_index: int,
+ user_args: [MAX_USER_ARGUMENTS]rawptr,
init_context: Maybe(runtime.Context),
+
+
+ creation_allocator: mem.Allocator,
}
#assert(size_of(Thread{}.user_index) == size_of(uintptr));
@@ -34,17 +43,108 @@ run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority :=
run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(rawptr))t.data;
- data := rawptr(uintptr(t.user_index));
+ assert(t.user_index >= 1);
+ data := t.user_args[0];
fn(data);
destroy(t);
}
t := create(thread_proc, priority);
t.data = rawptr(fn);
- t.user_index = int(uintptr(data));
+ t.user_index = 1;
+ t.user_args = data;
t.init_context = init_context;
start(t);
}
+run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+ where size_of(T) <= size_of(rawptr) {
+ thread_proc :: proc(t: ^Thread) {
+ fn := cast(proc(T))t.data;
+ assert(t.user_index >= 1);
+ data := (^T)(&t.user_args[0])^;
+ fn(data);
+ destroy(t);
+ }
+ t := create(thread_proc, priority);
+ t.data = rawptr(fn);
+ t.user_index = 1;
+ data := data;
+ mem.copy(&t.user_args[0], &data, size_of(data));
+ t.init_context = init_context;
+ start(t);
+}
+
+run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+ where size_of(T1) <= size_of(rawptr),
+ size_of(T2) <= size_of(rawptr) {
+ thread_proc :: proc(t: ^Thread) {
+ fn := cast(proc(T1, T2))t.data;
+ assert(t.user_index >= 2);
+ arg1 := (^T1)(&t.user_args[0])^;
+ arg2 := (^T2)(&t.user_args[1])^;
+ fn(arg1, arg2);
+ destroy(t);
+ }
+ t := create(thread_proc, priority);
+ t.data = rawptr(fn);
+ t.user_index = 2;
+ arg1, arg2 := arg1, arg2;
+ mem.copy(&t.user_args[0], &arg1, size_of(arg1));
+ mem.copy(&t.user_args[1], &arg2, size_of(arg2));
+ t.init_context = init_context;
+ start(t);
+}
+
+run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+ where size_of(T1) <= size_of(rawptr),
+ size_of(T2) <= size_of(rawptr),
+ size_of(T3) <= size_of(rawptr) {
+ thread_proc :: proc(t: ^Thread) {
+ fn := cast(proc(T1, T2, T3))t.data;
+ assert(t.user_index >= 3);
+ arg1 := (^T1)(&t.user_args[0])^;
+ arg2 := (^T2)(&t.user_args[1])^;
+ arg3 := (^T3)(&t.user_args[2])^;
+ fn(arg1, arg2, arg3);
+ destroy(t);
+ }
+ t := create(thread_proc, priority);
+ t.data = rawptr(fn);
+ t.user_index = 3;
+ arg1, arg2, arg3 := arg1, arg2, arg3;
+ mem.copy(&t.user_args[0], &arg1, size_of(arg1));
+ mem.copy(&t.user_args[1], &arg2, size_of(arg2));
+ mem.copy(&t.user_args[2], &arg3, size_of(arg3));
+ t.init_context = init_context;
+ start(t);
+}
+run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+ where size_of(T1) <= size_of(rawptr),
+ size_of(T2) <= size_of(rawptr),
+ size_of(T3) <= size_of(rawptr) {
+ thread_proc :: proc(t: ^Thread) {
+ fn := cast(proc(T1, T2, T3, T4))t.data;
+ assert(t.user_index >= 4);
+ arg1 := (^T1)(&t.user_args[0])^;
+ arg2 := (^T2)(&t.user_args[1])^;
+ arg3 := (^T3)(&t.user_args[2])^;
+ arg4 := (^T4)(&t.user_args[3])^;
+ fn(arg1, arg2, arg3, arg4);
+ destroy(t);
+ }
+ t := create(thread_proc, priority);
+ t.data = rawptr(fn);
+ t.user_index = 4;
+ arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4;
+ mem.copy(&t.user_args[0], &arg1, size_of(arg1));
+ mem.copy(&t.user_args[1], &arg2, size_of(arg2));
+ mem.copy(&t.user_args[2], &arg3, size_of(arg3));
+ mem.copy(&t.user_args[3], &arg4, size_of(arg4));
+ t.init_context = init_context;
+ start(t);
+}
+
+
create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
t := create(fn, priority);
diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin
index 027ffe026..d87291c0e 100644
--- a/core/thread/thread_unix.odin
+++ b/core/thread/thread_unix.odin
@@ -85,6 +85,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
if thread == nil {
return nil;
}
+ thread.creation_allocator = context.allocator;
// Set thread priority.
policy: i32;
@@ -106,7 +107,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
sync.mutex_init(&thread.start_mutex);
sync.condition_init(&thread.start_gate, &thread.start_mutex);
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
- free(thread);
+ free(thread, thread.creation_allocator);
return nil;
}
thread.procedure = procedure;
@@ -172,7 +173,7 @@ join_multiple :: proc(threads: ..^Thread) {
destroy :: proc(t: ^Thread) {
join(t);
t.unix_thread = {};
- free(t);
+ free(t, t.creation_allocator);
}
diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin
index f94632b35..27a14c7f6 100644
--- a/core/thread/thread_windows.odin
+++ b/core/thread/thread_windows.odin
@@ -49,10 +49,14 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
thread := new(Thread);
+ if thread == nil {
+ return nil;
+ }
+ thread.creation_allocator = context.allocator;
win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
if win32_thread == nil {
- free(thread);
+ free(thread, thread.creation_allocator);
return nil;
}
thread.procedure = procedure;
@@ -111,7 +115,7 @@ join_multiple :: proc(threads: ..^Thread) {
destroy :: proc(thread: ^Thread) {
join(thread);
- free(thread);
+ free(thread, thread.creation_allocator);
}
terminate :: proc(using thread : ^Thread, exit_code: u32) {
diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin
index 4bf5cf4ea..b498e4272 100644
--- a/core/unicode/letter.odin
+++ b/core/unicode/letter.odin
@@ -60,7 +60,7 @@ to_title :: proc(r: rune) -> rune {
is_lower :: proc(r: rune) -> bool {
if r <= MAX_ASCII {
- return u8(r)-'a' < 26;
+ return u32(r)-'a' < 26;
}
c := i32(r);
p := binary_search(c, to_upper_ranges[:], len(to_upper_ranges)/3, 3);
@@ -76,7 +76,7 @@ is_lower :: proc(r: rune) -> bool {
is_upper :: proc(r: rune) -> bool {
if r <= MAX_ASCII {
- return u8(r)-'A' < 26;
+ return u32(r)-'A' < 26;
}
c := i32(r);
p := binary_search(c, to_lower_ranges[:], len(to_lower_ranges)/3, 3);
@@ -90,9 +90,10 @@ is_upper :: proc(r: rune) -> bool {
return false;
}
-is_alpha :: proc(r: rune) -> bool {
- if r <= MAX_ASCII {
- return (u8(r)|32)-'a' < 26;
+is_alpha :: is_letter;
+is_letter :: proc(r: rune) -> bool {
+ if u32(r) <= MAX_LATIN1 {
+ return char_properties[u8(r)]&pLmask != 0;
}
if is_upper(r) || is_lower(r) {
return true;
@@ -150,1000 +151,45 @@ is_combining :: proc(r: rune) -> bool {
}
-@(static)
-alpha_ranges := [?]i32{
- 0x00d8, 0x00f6,
- 0x00f8, 0x01f5,
- 0x0250, 0x02a8,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03d6,
- 0x03e2, 0x03f3,
- 0x0490, 0x04c4,
- 0x0561, 0x0587,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0621, 0x063a,
- 0x0640, 0x064a,
- 0x0671, 0x06b7,
- 0x06ba, 0x06be,
- 0x06c0, 0x06ce,
- 0x06d0, 0x06d3,
- 0x0905, 0x0939,
- 0x0958, 0x0961,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b6, 0x09b9,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e1,
- 0x09f0, 0x09f1,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a59, 0x0a5c,
- 0x0a85, 0x0a8b,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b36, 0x0b39,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c60, 0x0c61,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0ce0, 0x0ce1,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d60, 0x0d61,
- 0x0e01, 0x0e30,
- 0x0e32, 0x0e33,
- 0x0e40, 0x0e46,
- 0x0e5a, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e87, 0x0e88,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eae,
- 0x0eb2, 0x0eb3,
- 0x0ec0, 0x0ec4,
- 0x0edc, 0x0edd,
- 0x0f18, 0x0f19,
- 0x0f40, 0x0f47,
- 0x0f49, 0x0f69,
- 0x10d0, 0x10f6,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1e00, 0x1e9b,
- 0x1f50, 0x1f57,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x210a, 0x2113,
- 0x2115, 0x211d,
- 0x2120, 0x2122,
- 0x212a, 0x2131,
- 0x2133, 0x2138,
- 0x3041, 0x3094,
- 0x30a1, 0x30fa,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3192, 0x319f,
- 0x3260, 0x327b,
- 0x328a, 0x32b0,
- 0x32d0, 0x32fe,
- 0x3300, 0x3357,
- 0x3371, 0x3376,
- 0x337b, 0x3394,
- 0x3399, 0x339e,
- 0x33a9, 0x33ad,
- 0x33b0, 0x33c1,
- 0x33c3, 0x33c5,
- 0x33c7, 0x33d7,
- 0x33d9, 0x33dd,
- 0x4e00, 0x9fff,
- 0xac00, 0xd7a3,
- 0xf900, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1f, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdf9,
- 0xfe70, 0xfe72,
- 0xfe76, 0xfefc,
- 0xff66, 0xff6f,
- 0xff71, 0xff9d,
- 0xffa0, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
-};
-
-@(static)
-alpha_singlets := [?]i32{
- 0x00aa,
- 0x00b5,
- 0x00ba,
- 0x03da,
- 0x03dc,
- 0x03de,
- 0x03e0,
- 0x06d5,
- 0x09b2,
- 0x0a5e,
- 0x0a8d,
- 0x0ae0,
- 0x0b9c,
- 0x0cde,
- 0x0e4f,
- 0x0e84,
- 0x0e8a,
- 0x0e8d,
- 0x0ea5,
- 0x0ea7,
- 0x0eb0,
- 0x0ebd,
- 0x1fbe,
- 0x207f,
- 0x20a8,
- 0x2102,
- 0x2107,
- 0x2124,
- 0x2126,
- 0x2128,
- 0xfb3e,
- 0xfe74,
-};
-
-@(static)
-space_ranges := [?]i32{
- 0x0009, 0x000d, // tab and newline
- 0x0020, 0x0020, // space
- 0x0085, 0x0085, // next line
- 0x00a0, 0x00a0,
- 0x1680, 0x1680, // Ogham space mark
- 0x2000, 0x200b, // en dash .. zero-width space
- 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace)
- 0x2028, 0x2029, 0x3000, 0x3000,
- 0x202f, 0x202f, // narrow no-break space
- 0x205f, 0x205f, // medium mathematical space
- 0x3000, 0x3000, // ideographic space
- 0xfeff, 0xfeff,
-};
-@(static)
-unicode_spaces := [?]i32{
- 0x0009, // tab
- 0x000a, // LF
- 0x000d, // CR
- 0x0020, // space
- 0x0085, // next line
- 0x00a0, // unknown
- 0x1680, // Ogham space mark
- 0x2000, // en dash .. zero-width space
- 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace)
- 0x2028, 0x2029, 0x3000, 0x3000,
- 0x202f, // narrow no-break space
- 0x205f, // medium mathematical space
- 0x3000, // ideographic space
- 0xfeff, // unknown
-};
+is_graphic :: proc(r: rune) -> bool {
+ if u32(r) <= MAX_LATIN1 {
+ return char_properties[u8(r)]&pg != 0;
+ }
+ return false;
+}
-@(static)
-to_upper_ranges := [?]i32{
- 0x0061, 0x007a, 468, // a-z A-Z
- 0x00e0, 0x00f6, 468,
- 0x00f8, 0x00fe, 468,
- 0x0256, 0x0257, 295,
- 0x0258, 0x0259, 298,
- 0x028a, 0x028b, 283,
- 0x03ad, 0x03af, 463,
- 0x03b1, 0x03c1, 468,
- 0x03c3, 0x03cb, 468,
- 0x03cd, 0x03ce, 437,
- 0x0430, 0x044f, 468,
- 0x0451, 0x045c, 420,
- 0x045e, 0x045f, 420,
- 0x0561, 0x0586, 452,
- 0x1f00, 0x1f07, 508,
- 0x1f10, 0x1f15, 508,
- 0x1f20, 0x1f27, 508,
- 0x1f30, 0x1f37, 508,
- 0x1f40, 0x1f45, 508,
- 0x1f60, 0x1f67, 508,
- 0x1f70, 0x1f71, 574,
- 0x1f72, 0x1f75, 586,
- 0x1f76, 0x1f77, 600,
- 0x1f78, 0x1f79, 628,
- 0x1f7a, 0x1f7b, 612,
- 0x1f7c, 0x1f7d, 626,
- 0x1f80, 0x1f87, 508,
- 0x1f90, 0x1f97, 508,
- 0x1fa0, 0x1fa7, 508,
- 0x1fb0, 0x1fb1, 508,
- 0x1fd0, 0x1fd1, 508,
- 0x1fe0, 0x1fe1, 508,
- 0x2170, 0x217f, 484,
- 0x24d0, 0x24e9, 474,
- 0xff41, 0xff5a, 468,
-};
+is_print :: proc(r: rune) -> bool {
+ if u32(r) <= MAX_LATIN1 {
+ return char_properties[u8(r)]&pp != 0;
+ }
+ return false;
+}
-@(static)
-to_upper_singlets := [?]i32{
- 0x00ff, 621,
- 0x0101, 499,
- 0x0103, 499,
- 0x0105, 499,
- 0x0107, 499,
- 0x0109, 499,
- 0x010b, 499,
- 0x010d, 499,
- 0x010f, 499,
- 0x0111, 499,
- 0x0113, 499,
- 0x0115, 499,
- 0x0117, 499,
- 0x0119, 499,
- 0x011b, 499,
- 0x011d, 499,
- 0x011f, 499,
- 0x0121, 499,
- 0x0123, 499,
- 0x0125, 499,
- 0x0127, 499,
- 0x0129, 499,
- 0x012b, 499,
- 0x012d, 499,
- 0x012f, 499,
- 0x0131, 268, // I
- 0x0133, 499,
- 0x0135, 499,
- 0x0137, 499,
- 0x013a, 499,
- 0x013c, 499,
- 0x013e, 499,
- 0x0140, 499,
- 0x0142, 499,
- 0x0144, 499,
- 0x0146, 499,
- 0x0148, 499,
- 0x014b, 499,
- 0x014d, 499,
- 0x014f, 499,
- 0x0151, 499,
- 0x0153, 499,
- 0x0155, 499,
- 0x0157, 499,
- 0x0159, 499,
- 0x015b, 499,
- 0x015d, 499,
- 0x015f, 499,
- 0x0161, 499,
- 0x0163, 499,
- 0x0165, 499,
- 0x0167, 499,
- 0x0169, 499,
- 0x016b, 499,
- 0x016d, 499,
- 0x016f, 499,
- 0x0171, 499,
- 0x0173, 499,
- 0x0175, 499,
- 0x0177, 499,
- 0x017a, 499,
- 0x017c, 499,
- 0x017e, 499,
- 0x017f, 200, // S
- 0x0183, 499,
- 0x0185, 499,
- 0x0188, 499,
- 0x018c, 499,
- 0x0192, 499,
- 0x0199, 499,
- 0x01a1, 499,
- 0x01a3, 499,
- 0x01a5, 499,
- 0x01a8, 499,
- 0x01ad, 499,
- 0x01b0, 499,
- 0x01b4, 499,
- 0x01b6, 499,
- 0x01b9, 499,
- 0x01bd, 499,
- 0x01c5, 499,
- 0x01c6, 498,
- 0x01c8, 499,
- 0x01c9, 498,
- 0x01cb, 499,
- 0x01cc, 498,
- 0x01ce, 499,
- 0x01d0, 499,
- 0x01d2, 499,
- 0x01d4, 499,
- 0x01d6, 499,
- 0x01d8, 499,
- 0x01da, 499,
- 0x01dc, 499,
- 0x01df, 499,
- 0x01e1, 499,
- 0x01e3, 499,
- 0x01e5, 499,
- 0x01e7, 499,
- 0x01e9, 499,
- 0x01eb, 499,
- 0x01ed, 499,
- 0x01ef, 499,
- 0x01f2, 499,
- 0x01f3, 498,
- 0x01f5, 499,
- 0x01fb, 499,
- 0x01fd, 499,
- 0x01ff, 499,
- 0x0201, 499,
- 0x0203, 499,
- 0x0205, 499,
- 0x0207, 499,
- 0x0209, 499,
- 0x020b, 499,
- 0x020d, 499,
- 0x020f, 499,
- 0x0211, 499,
- 0x0213, 499,
- 0x0215, 499,
- 0x0217, 499,
- 0x0253, 290,
- 0x0254, 294,
- 0x025b, 297,
- 0x0260, 295,
- 0x0263, 293,
- 0x0268, 291,
- 0x0269, 289,
- 0x026f, 289,
- 0x0272, 287,
- 0x0283, 282,
- 0x0288, 282,
- 0x0292, 281,
- 0x03ac, 462,
- 0x03cc, 436,
- 0x03d0, 438,
- 0x03d1, 443,
- 0x03d5, 453,
- 0x03d6, 446,
- 0x03e3, 499,
- 0x03e5, 499,
- 0x03e7, 499,
- 0x03e9, 499,
- 0x03eb, 499,
- 0x03ed, 499,
- 0x03ef, 499,
- 0x03f0, 414,
- 0x03f1, 420,
- 0x0461, 499,
- 0x0463, 499,
- 0x0465, 499,
- 0x0467, 499,
- 0x0469, 499,
- 0x046b, 499,
- 0x046d, 499,
- 0x046f, 499,
- 0x0471, 499,
- 0x0473, 499,
- 0x0475, 499,
- 0x0477, 499,
- 0x0479, 499,
- 0x047b, 499,
- 0x047d, 499,
- 0x047f, 499,
- 0x0481, 499,
- 0x0491, 499,
- 0x0493, 499,
- 0x0495, 499,
- 0x0497, 499,
- 0x0499, 499,
- 0x049b, 499,
- 0x049d, 499,
- 0x049f, 499,
- 0x04a1, 499,
- 0x04a3, 499,
- 0x04a5, 499,
- 0x04a7, 499,
- 0x04a9, 499,
- 0x04ab, 499,
- 0x04ad, 499,
- 0x04af, 499,
- 0x04b1, 499,
- 0x04b3, 499,
- 0x04b5, 499,
- 0x04b7, 499,
- 0x04b9, 499,
- 0x04bb, 499,
- 0x04bd, 499,
- 0x04bf, 499,
- 0x04c2, 499,
- 0x04c4, 499,
- 0x04c8, 499,
- 0x04cc, 499,
- 0x04d1, 499,
- 0x04d3, 499,
- 0x04d5, 499,
- 0x04d7, 499,
- 0x04d9, 499,
- 0x04db, 499,
- 0x04dd, 499,
- 0x04df, 499,
- 0x04e1, 499,
- 0x04e3, 499,
- 0x04e5, 499,
- 0x04e7, 499,
- 0x04e9, 499,
- 0x04eb, 499,
- 0x04ef, 499,
- 0x04f1, 499,
- 0x04f3, 499,
- 0x04f5, 499,
- 0x04f9, 499,
- 0x1e01, 499,
- 0x1e03, 499,
- 0x1e05, 499,
- 0x1e07, 499,
- 0x1e09, 499,
- 0x1e0b, 499,
- 0x1e0d, 499,
- 0x1e0f, 499,
- 0x1e11, 499,
- 0x1e13, 499,
- 0x1e15, 499,
- 0x1e17, 499,
- 0x1e19, 499,
- 0x1e1b, 499,
- 0x1e1d, 499,
- 0x1e1f, 499,
- 0x1e21, 499,
- 0x1e23, 499,
- 0x1e25, 499,
- 0x1e27, 499,
- 0x1e29, 499,
- 0x1e2b, 499,
- 0x1e2d, 499,
- 0x1e2f, 499,
- 0x1e31, 499,
- 0x1e33, 499,
- 0x1e35, 499,
- 0x1e37, 499,
- 0x1e39, 499,
- 0x1e3b, 499,
- 0x1e3d, 499,
- 0x1e3f, 499,
- 0x1e41, 499,
- 0x1e43, 499,
- 0x1e45, 499,
- 0x1e47, 499,
- 0x1e49, 499,
- 0x1e4b, 499,
- 0x1e4d, 499,
- 0x1e4f, 499,
- 0x1e51, 499,
- 0x1e53, 499,
- 0x1e55, 499,
- 0x1e57, 499,
- 0x1e59, 499,
- 0x1e5b, 499,
- 0x1e5d, 499,
- 0x1e5f, 499,
- 0x1e61, 499,
- 0x1e63, 499,
- 0x1e65, 499,
- 0x1e67, 499,
- 0x1e69, 499,
- 0x1e6b, 499,
- 0x1e6d, 499,
- 0x1e6f, 499,
- 0x1e71, 499,
- 0x1e73, 499,
- 0x1e75, 499,
- 0x1e77, 499,
- 0x1e79, 499,
- 0x1e7b, 499,
- 0x1e7d, 499,
- 0x1e7f, 499,
- 0x1e81, 499,
- 0x1e83, 499,
- 0x1e85, 499,
- 0x1e87, 499,
- 0x1e89, 499,
- 0x1e8b, 499,
- 0x1e8d, 499,
- 0x1e8f, 499,
- 0x1e91, 499,
- 0x1e93, 499,
- 0x1e95, 499,
- 0x1ea1, 499,
- 0x1ea3, 499,
- 0x1ea5, 499,
- 0x1ea7, 499,
- 0x1ea9, 499,
- 0x1eab, 499,
- 0x1ead, 499,
- 0x1eaf, 499,
- 0x1eb1, 499,
- 0x1eb3, 499,
- 0x1eb5, 499,
- 0x1eb7, 499,
- 0x1eb9, 499,
- 0x1ebb, 499,
- 0x1ebd, 499,
- 0x1ebf, 499,
- 0x1ec1, 499,
- 0x1ec3, 499,
- 0x1ec5, 499,
- 0x1ec7, 499,
- 0x1ec9, 499,
- 0x1ecb, 499,
- 0x1ecd, 499,
- 0x1ecf, 499,
- 0x1ed1, 499,
- 0x1ed3, 499,
- 0x1ed5, 499,
- 0x1ed7, 499,
- 0x1ed9, 499,
- 0x1edb, 499,
- 0x1edd, 499,
- 0x1edf, 499,
- 0x1ee1, 499,
- 0x1ee3, 499,
- 0x1ee5, 499,
- 0x1ee7, 499,
- 0x1ee9, 499,
- 0x1eeb, 499,
- 0x1eed, 499,
- 0x1eef, 499,
- 0x1ef1, 499,
- 0x1ef3, 499,
- 0x1ef5, 499,
- 0x1ef7, 499,
- 0x1ef9, 499,
- 0x1f51, 508,
- 0x1f53, 508,
- 0x1f55, 508,
- 0x1f57, 508,
- 0x1fb3, 509,
- 0x1fc3, 509,
- 0x1fe5, 507,
- 0x1ff3, 509,
-};
+is_control :: proc(r: rune) -> bool {
+ if u32(r) <= MAX_LATIN1 {
+ return char_properties[u8(r)]&pC != 0;
+ }
+ return false;
+}
-@(static)
-to_lower_ranges := [?]i32{
- 0x0041, 0x005a, 532, // A-Z a-z
- 0x00c0, 0x00d6, 532, // - -
- 0x00d8, 0x00de, 532, // - -
- 0x0189, 0x018a, 705, // - -
- 0x018e, 0x018f, 702, // - -
- 0x01b1, 0x01b2, 717, // - -
- 0x0388, 0x038a, 537, // - -
- 0x038e, 0x038f, 563, // - -
- 0x0391, 0x03a1, 532, // - -
- 0x03a3, 0x03ab, 532, // - -
- 0x0401, 0x040c, 580, // - -
- 0x040e, 0x040f, 580, // - -
- 0x0410, 0x042f, 532, // - -
- 0x0531, 0x0556, 548, // - -
- 0x10a0, 0x10c5, 548, // - -
- 0x1f08, 0x1f0f, 492, // - -
- 0x1f18, 0x1f1d, 492, // - -
- 0x1f28, 0x1f2f, 492, // - -
- 0x1f38, 0x1f3f, 492, // - -
- 0x1f48, 0x1f4d, 492, // - -
- 0x1f68, 0x1f6f, 492, // - -
- 0x1f88, 0x1f8f, 492, // - -
- 0x1f98, 0x1f9f, 492, // - -
- 0x1fa8, 0x1faf, 492, // - -
- 0x1fb8, 0x1fb9, 492, // - -
- 0x1fba, 0x1fbb, 426, // - -
- 0x1fc8, 0x1fcb, 414, // - -
- 0x1fd8, 0x1fd9, 492, // - -
- 0x1fda, 0x1fdb, 400, // - -
- 0x1fe8, 0x1fe9, 492, // - -
- 0x1fea, 0x1feb, 388, // - -
- 0x1ff8, 0x1ff9, 372, // - -
- 0x1ffa, 0x1ffb, 374, // - -
- 0x2160, 0x216f, 516, // - -
- 0x24b6, 0x24cf, 526, // - -
- 0xff21, 0xff3a, 532, // - -
-};
+is_number :: proc(r: rune) -> bool {
+ if u32(r) <= MAX_LATIN1 {
+ return char_properties[u8(r)]&pN != 0;
+ }
+ return false;
+}
-@(static)
-to_lower_singlets := [?]i32{
- 0x0100, 501,
- 0x0102, 501,
- 0x0104, 501,
- 0x0106, 501,
- 0x0108, 501,
- 0x010a, 501,
- 0x010c, 501,
- 0x010e, 501,
- 0x0110, 501,
- 0x0112, 501,
- 0x0114, 501,
- 0x0116, 501,
- 0x0118, 501,
- 0x011a, 501,
- 0x011c, 501,
- 0x011e, 501,
- 0x0120, 501,
- 0x0122, 501,
- 0x0124, 501,
- 0x0126, 501,
- 0x0128, 501,
- 0x012a, 501,
- 0x012c, 501,
- 0x012e, 501,
- 0x0130, 301, // i
- 0x0132, 501,
- 0x0134, 501,
- 0x0136, 501,
- 0x0139, 501,
- 0x013b, 501,
- 0x013d, 501,
- 0x013f, 501,
- 0x0141, 501,
- 0x0143, 501,
- 0x0145, 501,
- 0x0147, 501,
- 0x014a, 501,
- 0x014c, 501,
- 0x014e, 501,
- 0x0150, 501,
- 0x0152, 501,
- 0x0154, 501,
- 0x0156, 501,
- 0x0158, 501,
- 0x015a, 501,
- 0x015c, 501,
- 0x015e, 501,
- 0x0160, 501,
- 0x0162, 501,
- 0x0164, 501,
- 0x0166, 501,
- 0x0168, 501,
- 0x016a, 501,
- 0x016c, 501,
- 0x016e, 501,
- 0x0170, 501,
- 0x0172, 501,
- 0x0174, 501,
- 0x0176, 501,
- 0x0178, 379,
- 0x0179, 501,
- 0x017b, 501,
- 0x017d, 501,
- 0x0181, 710,
- 0x0182, 501,
- 0x0184, 501,
- 0x0186, 706,
- 0x0187, 501,
- 0x018b, 501,
- 0x0190, 703,
- 0x0191, 501,
- 0x0193, 705,
- 0x0194, 707,
- 0x0196, 711,
- 0x0197, 709,
- 0x0198, 501,
- 0x019c, 711,
- 0x019d, 713,
- 0x01a0, 501,
- 0x01a2, 501,
- 0x01a4, 501,
- 0x01a7, 501,
- 0x01a9, 718,
- 0x01ac, 501,
- 0x01ae, 718,
- 0x01af, 501,
- 0x01b3, 501,
- 0x01b5, 501,
- 0x01b7, 719,
- 0x01b8, 501,
- 0x01bc, 501,
- 0x01c4, 502,
- 0x01c5, 501,
- 0x01c7, 502,
- 0x01c8, 501,
- 0x01ca, 502,
- 0x01cb, 501,
- 0x01cd, 501,
- 0x01cf, 501,
- 0x01d1, 501,
- 0x01d3, 501,
- 0x01d5, 501,
- 0x01d7, 501,
- 0x01d9, 501,
- 0x01db, 501,
- 0x01de, 501,
- 0x01e0, 501,
- 0x01e2, 501,
- 0x01e4, 501,
- 0x01e6, 501,
- 0x01e8, 501,
- 0x01ea, 501,
- 0x01ec, 501,
- 0x01ee, 501,
- 0x01f1, 502,
- 0x01f2, 501,
- 0x01f4, 501,
- 0x01fa, 501,
- 0x01fc, 501,
- 0x01fe, 501,
- 0x0200, 501,
- 0x0202, 501,
- 0x0204, 501,
- 0x0206, 501,
- 0x0208, 501,
- 0x020a, 501,
- 0x020c, 501,
- 0x020e, 501,
- 0x0210, 501,
- 0x0212, 501,
- 0x0214, 501,
- 0x0216, 501,
- 0x0386, 538,
- 0x038c, 564,
- 0x03e2, 501,
- 0x03e4, 501,
- 0x03e6, 501,
- 0x03e8, 501,
- 0x03ea, 501,
- 0x03ec, 501,
- 0x03ee, 501,
- 0x0460, 501,
- 0x0462, 501,
- 0x0464, 501,
- 0x0466, 501,
- 0x0468, 501,
- 0x046a, 501,
- 0x046c, 501,
- 0x046e, 501,
- 0x0470, 501,
- 0x0472, 501,
- 0x0474, 501,
- 0x0476, 501,
- 0x0478, 501,
- 0x047a, 501,
- 0x047c, 501,
- 0x047e, 501,
- 0x0480, 501,
- 0x0490, 501,
- 0x0492, 501,
- 0x0494, 501,
- 0x0496, 501,
- 0x0498, 501,
- 0x049a, 501,
- 0x049c, 501,
- 0x049e, 501,
- 0x04a0, 501,
- 0x04a2, 501,
- 0x04a4, 501,
- 0x04a6, 501,
- 0x04a8, 501,
- 0x04aa, 501,
- 0x04ac, 501,
- 0x04ae, 501,
- 0x04b0, 501,
- 0x04b2, 501,
- 0x04b4, 501,
- 0x04b6, 501,
- 0x04b8, 501,
- 0x04ba, 501,
- 0x04bc, 501,
- 0x04be, 501,
- 0x04c1, 501,
- 0x04c3, 501,
- 0x04c7, 501,
- 0x04cb, 501,
- 0x04d0, 501,
- 0x04d2, 501,
- 0x04d4, 501,
- 0x04d6, 501,
- 0x04d8, 501,
- 0x04da, 501,
- 0x04dc, 501,
- 0x04de, 501,
- 0x04e0, 501,
- 0x04e2, 501,
- 0x04e4, 501,
- 0x04e6, 501,
- 0x04e8, 501,
- 0x04ea, 501,
- 0x04ee, 501,
- 0x04f0, 501,
- 0x04f2, 501,
- 0x04f4, 501,
- 0x04f8, 501,
- 0x1e00, 501,
- 0x1e02, 501,
- 0x1e04, 501,
- 0x1e06, 501,
- 0x1e08, 501,
- 0x1e0a, 501,
- 0x1e0c, 501,
- 0x1e0e, 501,
- 0x1e10, 501,
- 0x1e12, 501,
- 0x1e14, 501,
- 0x1e16, 501,
- 0x1e18, 501,
- 0x1e1a, 501,
- 0x1e1c, 501,
- 0x1e1e, 501,
- 0x1e20, 501,
- 0x1e22, 501,
- 0x1e24, 501,
- 0x1e26, 501,
- 0x1e28, 501,
- 0x1e2a, 501,
- 0x1e2c, 501,
- 0x1e2e, 501,
- 0x1e30, 501,
- 0x1e32, 501,
- 0x1e34, 501,
- 0x1e36, 501,
- 0x1e38, 501,
- 0x1e3a, 501,
- 0x1e3c, 501,
- 0x1e3e, 501,
- 0x1e40, 501,
- 0x1e42, 501,
- 0x1e44, 501,
- 0x1e46, 501,
- 0x1e48, 501,
- 0x1e4a, 501,
- 0x1e4c, 501,
- 0x1e4e, 501,
- 0x1e50, 501,
- 0x1e52, 501,
- 0x1e54, 501,
- 0x1e56, 501,
- 0x1e58, 501,
- 0x1e5a, 501,
- 0x1e5c, 501,
- 0x1e5e, 501,
- 0x1e60, 501,
- 0x1e62, 501,
- 0x1e64, 501,
- 0x1e66, 501,
- 0x1e68, 501,
- 0x1e6a, 501,
- 0x1e6c, 501,
- 0x1e6e, 501,
- 0x1e70, 501,
- 0x1e72, 501,
- 0x1e74, 501,
- 0x1e76, 501,
- 0x1e78, 501,
- 0x1e7a, 501,
- 0x1e7c, 501,
- 0x1e7e, 501,
- 0x1e80, 501,
- 0x1e82, 501,
- 0x1e84, 501,
- 0x1e86, 501,
- 0x1e88, 501,
- 0x1e8a, 501,
- 0x1e8c, 501,
- 0x1e8e, 501,
- 0x1e90, 501,
- 0x1e92, 501,
- 0x1e94, 501,
- 0x1ea0, 501,
- 0x1ea2, 501,
- 0x1ea4, 501,
- 0x1ea6, 501,
- 0x1ea8, 501,
- 0x1eaa, 501,
- 0x1eac, 501,
- 0x1eae, 501,
- 0x1eb0, 501,
- 0x1eb2, 501,
- 0x1eb4, 501,
- 0x1eb6, 501,
- 0x1eb8, 501,
- 0x1eba, 501,
- 0x1ebc, 501,
- 0x1ebe, 501,
- 0x1ec0, 501,
- 0x1ec2, 501,
- 0x1ec4, 501,
- 0x1ec6, 501,
- 0x1ec8, 501,
- 0x1eca, 501,
- 0x1ecc, 501,
- 0x1ece, 501,
- 0x1ed0, 501,
- 0x1ed2, 501,
- 0x1ed4, 501,
- 0x1ed6, 501,
- 0x1ed8, 501,
- 0x1eda, 501,
- 0x1edc, 501,
- 0x1ede, 501,
- 0x1ee0, 501,
- 0x1ee2, 501,
- 0x1ee4, 501,
- 0x1ee6, 501,
- 0x1ee8, 501,
- 0x1eea, 501,
- 0x1eec, 501,
- 0x1eee, 501,
- 0x1ef0, 501,
- 0x1ef2, 501,
- 0x1ef4, 501,
- 0x1ef6, 501,
- 0x1ef8, 501,
- 0x1f59, 492,
- 0x1f5b, 492,
- 0x1f5d, 492,
- 0x1f5f, 492,
- 0x1fbc, 491,
- 0x1fcc, 491,
- 0x1fec, 493,
- 0x1ffc, 491,
-};
+is_punct :: proc(r: rune) -> bool {
+ if u32(r) <= MAX_LATIN1 {
+ return char_properties[u8(r)]&pP != 0;
+ }
+ return false;
+}
-@(static)
-to_title_singlets := [?]i32{
- 0x01c4, 501,
- 0x01c6, 499,
- 0x01c7, 501,
- 0x01c9, 499,
- 0x01ca, 501,
- 0x01cc, 499,
- 0x01f1, 501,
- 0x01f3, 499,
-};
+is_symbol :: proc(r: rune) -> bool {
+ if u32(r) <= MAX_LATIN1 {
+ return char_properties[u8(r)]&pS != 0;
+ }
+ return false;
+}
diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin
new file mode 100644
index 000000000..bb858fd04
--- /dev/null
+++ b/core/unicode/tables.odin
@@ -0,0 +1,1272 @@
+package unicode
+
+@(private) pC :: 1<<0; // a control character.
+@(private) pP :: 1<<1; // a punctuation character.
+@(private) pN :: 1<<2; // a numeral.
+@(private) pS :: 1<<3; // a symbolic character.
+@(private) pZ :: 1<<4; // a spacing character.
+@(private) pLu :: 1<<5; // an upper-case letter.
+@(private) pLl :: 1<<6; // a lower-case letter.
+@(private) pp :: 1<<7; // a printable character according to Go's definition.
+@(private) pg :: pp | pZ; // a graphical character according to the Unicode definition.
+@(private) pLo :: pLl | pLu; // a letter that is neither upper nor lower case.
+@(private) pLmask :: pLo;
+
+@(static)
+char_properties := [MAX_LATIN1+1]u8{
+ 0x00 = pC, // '\x00'
+ 0x01 = pC, // '\x01'
+ 0x02 = pC, // '\x02'
+ 0x03 = pC, // '\x03'
+ 0x04 = pC, // '\x04'
+ 0x05 = pC, // '\x05'
+ 0x06 = pC, // '\x06'
+ 0x07 = pC, // '\a'
+ 0x08 = pC, // '\b'
+ 0x09 = pC, // '\t'
+ 0x0A = pC, // '\n'
+ 0x0B = pC, // '\v'
+ 0x0C = pC, // '\f'
+ 0x0D = pC, // '\r'
+ 0x0E = pC, // '\x0e'
+ 0x0F = pC, // '\x0f'
+ 0x10 = pC, // '\x10'
+ 0x11 = pC, // '\x11'
+ 0x12 = pC, // '\x12'
+ 0x13 = pC, // '\x13'
+ 0x14 = pC, // '\x14'
+ 0x15 = pC, // '\x15'
+ 0x16 = pC, // '\x16'
+ 0x17 = pC, // '\x17'
+ 0x18 = pC, // '\x18'
+ 0x19 = pC, // '\x19'
+ 0x1A = pC, // '\x1a'
+ 0x1B = pC, // '\x1b'
+ 0x1C = pC, // '\x1c'
+ 0x1D = pC, // '\x1d'
+ 0x1E = pC, // '\x1e'
+ 0x1F = pC, // '\x1f'
+ 0x20 = pZ | pp, // ' '
+ 0x21 = pP | pp, // '!'
+ 0x22 = pP | pp, // '"'
+ 0x23 = pP | pp, // '#'
+ 0x24 = pS | pp, // '$'
+ 0x25 = pP | pp, // '%'
+ 0x26 = pP | pp, // '&'
+ 0x27 = pP | pp, // '\''
+ 0x28 = pP | pp, // '('
+ 0x29 = pP | pp, // ')'
+ 0x2A = pP | pp, // '*'
+ 0x2B = pS | pp, // '+'
+ 0x2C = pP | pp, // ','
+ 0x2D = pP | pp, // '-'
+ 0x2E = pP | pp, // '.'
+ 0x2F = pP | pp, // '/'
+ 0x30 = pN | pp, // '0'
+ 0x31 = pN | pp, // '1'
+ 0x32 = pN | pp, // '2'
+ 0x33 = pN | pp, // '3'
+ 0x34 = pN | pp, // '4'
+ 0x35 = pN | pp, // '5'
+ 0x36 = pN | pp, // '6'
+ 0x37 = pN | pp, // '7'
+ 0x38 = pN | pp, // '8'
+ 0x39 = pN | pp, // '9'
+ 0x3A = pP | pp, // ':'
+ 0x3B = pP | pp, // ';'
+ 0x3C = pS | pp, // '<'
+ 0x3D = pS | pp, // '='
+ 0x3E = pS | pp, // '>'
+ 0x3F = pP | pp, // '?'
+ 0x40 = pP | pp, // '@'
+ 0x41 = pLu | pp, // 'A'
+ 0x42 = pLu | pp, // 'B'
+ 0x43 = pLu | pp, // 'C'
+ 0x44 = pLu | pp, // 'D'
+ 0x45 = pLu | pp, // 'E'
+ 0x46 = pLu | pp, // 'F'
+ 0x47 = pLu | pp, // 'G'
+ 0x48 = pLu | pp, // 'H'
+ 0x49 = pLu | pp, // 'I'
+ 0x4A = pLu | pp, // 'J'
+ 0x4B = pLu | pp, // 'K'
+ 0x4C = pLu | pp, // 'L'
+ 0x4D = pLu | pp, // 'M'
+ 0x4E = pLu | pp, // 'N'
+ 0x4F = pLu | pp, // 'O'
+ 0x50 = pLu | pp, // 'P'
+ 0x51 = pLu | pp, // 'Q'
+ 0x52 = pLu | pp, // 'R'
+ 0x53 = pLu | pp, // 'S'
+ 0x54 = pLu | pp, // 'T'
+ 0x55 = pLu | pp, // 'U'
+ 0x56 = pLu | pp, // 'V'
+ 0x57 = pLu | pp, // 'W'
+ 0x58 = pLu | pp, // 'X'
+ 0x59 = pLu | pp, // 'Y'
+ 0x5A = pLu | pp, // 'Z'
+ 0x5B = pP | pp, // '['
+ 0x5C = pP | pp, // '\\'
+ 0x5D = pP | pp, // ']'
+ 0x5E = pS | pp, // '^'
+ 0x5F = pP | pp, // '_'
+ 0x60 = pS | pp, // '`'
+ 0x61 = pLl | pp, // 'a'
+ 0x62 = pLl | pp, // 'b'
+ 0x63 = pLl | pp, // 'c'
+ 0x64 = pLl | pp, // 'd'
+ 0x65 = pLl | pp, // 'e'
+ 0x66 = pLl | pp, // 'f'
+ 0x67 = pLl | pp, // 'g'
+ 0x68 = pLl | pp, // 'h'
+ 0x69 = pLl | pp, // 'i'
+ 0x6A = pLl | pp, // 'j'
+ 0x6B = pLl | pp, // 'k'
+ 0x6C = pLl | pp, // 'l'
+ 0x6D = pLl | pp, // 'm'
+ 0x6E = pLl | pp, // 'n'
+ 0x6F = pLl | pp, // 'o'
+ 0x70 = pLl | pp, // 'p'
+ 0x71 = pLl | pp, // 'q'
+ 0x72 = pLl | pp, // 'r'
+ 0x73 = pLl | pp, // 's'
+ 0x74 = pLl | pp, // 't'
+ 0x75 = pLl | pp, // 'u'
+ 0x76 = pLl | pp, // 'v'
+ 0x77 = pLl | pp, // 'w'
+ 0x78 = pLl | pp, // 'x'
+ 0x79 = pLl | pp, // 'y'
+ 0x7A = pLl | pp, // 'z'
+ 0x7B = pP | pp, // '{'
+ 0x7C = pS | pp, // '|'
+ 0x7D = pP | pp, // '}'
+ 0x7E = pS | pp, // '~'
+ 0x7F = pC, // '\u007f'
+ 0x80 = pC, // '\u0080'
+ 0x81 = pC, // '\u0081'
+ 0x82 = pC, // '\u0082'
+ 0x83 = pC, // '\u0083'
+ 0x84 = pC, // '\u0084'
+ 0x85 = pC, // '\u0085'
+ 0x86 = pC, // '\u0086'
+ 0x87 = pC, // '\u0087'
+ 0x88 = pC, // '\u0088'
+ 0x89 = pC, // '\u0089'
+ 0x8A = pC, // '\u008a'
+ 0x8B = pC, // '\u008b'
+ 0x8C = pC, // '\u008c'
+ 0x8D = pC, // '\u008d'
+ 0x8E = pC, // '\u008e'
+ 0x8F = pC, // '\u008f'
+ 0x90 = pC, // '\u0090'
+ 0x91 = pC, // '\u0091'
+ 0x92 = pC, // '\u0092'
+ 0x93 = pC, // '\u0093'
+ 0x94 = pC, // '\u0094'
+ 0x95 = pC, // '\u0095'
+ 0x96 = pC, // '\u0096'
+ 0x97 = pC, // '\u0097'
+ 0x98 = pC, // '\u0098'
+ 0x99 = pC, // '\u0099'
+ 0x9A = pC, // '\u009a'
+ 0x9B = pC, // '\u009b'
+ 0x9C = pC, // '\u009c'
+ 0x9D = pC, // '\u009d'
+ 0x9E = pC, // '\u009e'
+ 0x9F = pC, // '\u009f'
+ 0xA0 = pZ, // '\u00a0'
+ 0xA1 = pP | pp, // '¡'
+ 0xA2 = pS | pp, // '¢'
+ 0xA3 = pS | pp, // '£'
+ 0xA4 = pS | pp, // '¤'
+ 0xA5 = pS | pp, // '¥'
+ 0xA6 = pS | pp, // '¦'
+ 0xA7 = pP | pp, // '§'
+ 0xA8 = pS | pp, // '¨'
+ 0xA9 = pS | pp, // '©'
+ 0xAA = pLo | pp, // 'ª'
+ 0xAB = pP | pp, // '«'
+ 0xAC = pS | pp, // '¬'
+ 0xAD = 0, // '\u00ad'
+ 0xAE = pS | pp, // '®'
+ 0xAF = pS | pp, // '¯'
+ 0xB0 = pS | pp, // '°'
+ 0xB1 = pS | pp, // '±'
+ 0xB2 = pN | pp, // '²'
+ 0xB3 = pN | pp, // '³'
+ 0xB4 = pS | pp, // '´'
+ 0xB5 = pLl | pp, // 'µ'
+ 0xB6 = pP | pp, // '¶'
+ 0xB7 = pP | pp, // '·'
+ 0xB8 = pS | pp, // '¸'
+ 0xB9 = pN | pp, // '¹'
+ 0xBA = pLo | pp, // 'º'
+ 0xBB = pP | pp, // '»'
+ 0xBC = pN | pp, // '¼'
+ 0xBD = pN | pp, // '½'
+ 0xBE = pN | pp, // '¾'
+ 0xBF = pP | pp, // '¿'
+ 0xC0 = pLu | pp, // 'À'
+ 0xC1 = pLu | pp, // 'Á'
+ 0xC2 = pLu | pp, // 'Â'
+ 0xC3 = pLu | pp, // 'Ã'
+ 0xC4 = pLu | pp, // 'Ä'
+ 0xC5 = pLu | pp, // 'Å'
+ 0xC6 = pLu | pp, // 'Æ'
+ 0xC7 = pLu | pp, // 'Ç'
+ 0xC8 = pLu | pp, // 'È'
+ 0xC9 = pLu | pp, // 'É'
+ 0xCA = pLu | pp, // 'Ê'
+ 0xCB = pLu | pp, // 'Ë'
+ 0xCC = pLu | pp, // 'Ì'
+ 0xCD = pLu | pp, // 'Í'
+ 0xCE = pLu | pp, // 'Î'
+ 0xCF = pLu | pp, // 'Ï'
+ 0xD0 = pLu | pp, // 'Ð'
+ 0xD1 = pLu | pp, // 'Ñ'
+ 0xD2 = pLu | pp, // 'Ò'
+ 0xD3 = pLu | pp, // 'Ó'
+ 0xD4 = pLu | pp, // 'Ô'
+ 0xD5 = pLu | pp, // 'Õ'
+ 0xD6 = pLu | pp, // 'Ö'
+ 0xD7 = pS | pp, // '×'
+ 0xD8 = pLu | pp, // 'Ø'
+ 0xD9 = pLu | pp, // 'Ù'
+ 0xDA = pLu | pp, // 'Ú'
+ 0xDB = pLu | pp, // 'Û'
+ 0xDC = pLu | pp, // 'Ü'
+ 0xDD = pLu | pp, // 'Ý'
+ 0xDE = pLu | pp, // 'Þ'
+ 0xDF = pLl | pp, // 'ß'
+ 0xE0 = pLl | pp, // 'à'
+ 0xE1 = pLl | pp, // 'á'
+ 0xE2 = pLl | pp, // 'â'
+ 0xE3 = pLl | pp, // 'ã'
+ 0xE4 = pLl | pp, // 'ä'
+ 0xE5 = pLl | pp, // 'å'
+ 0xE6 = pLl | pp, // 'æ'
+ 0xE7 = pLl | pp, // 'ç'
+ 0xE8 = pLl | pp, // 'è'
+ 0xE9 = pLl | pp, // 'é'
+ 0xEA = pLl | pp, // 'ê'
+ 0xEB = pLl | pp, // 'ë'
+ 0xEC = pLl | pp, // 'ì'
+ 0xED = pLl | pp, // 'í'
+ 0xEE = pLl | pp, // 'î'
+ 0xEF = pLl | pp, // 'ï'
+ 0xF0 = pLl | pp, // 'ð'
+ 0xF1 = pLl | pp, // 'ñ'
+ 0xF2 = pLl | pp, // 'ò'
+ 0xF3 = pLl | pp, // 'ó'
+ 0xF4 = pLl | pp, // 'ô'
+ 0xF5 = pLl | pp, // 'õ'
+ 0xF6 = pLl | pp, // 'ö'
+ 0xF7 = pS | pp, // '÷'
+ 0xF8 = pLl | pp, // 'ø'
+ 0xF9 = pLl | pp, // 'ù'
+ 0xFA = pLl | pp, // 'ú'
+ 0xFB = pLl | pp, // 'û'
+ 0xFC = pLl | pp, // 'ü'
+ 0xFD = pLl | pp, // 'ý'
+ 0xFE = pLl | pp, // 'þ'
+ 0xFF = pLl | pp, // 'ÿ'
+};
+
+
+@(static)
+alpha_ranges := [?]i32{
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x01f5,
+ 0x0250, 0x02a8,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03d6,
+ 0x03e2, 0x03f3,
+ 0x0490, 0x04c4,
+ 0x0561, 0x0587,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f2,
+ 0x0621, 0x063a,
+ 0x0640, 0x064a,
+ 0x0671, 0x06b7,
+ 0x06ba, 0x06be,
+ 0x06c0, 0x06ce,
+ 0x06d0, 0x06d3,
+ 0x0905, 0x0939,
+ 0x0958, 0x0961,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b6, 0x09b9,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e1,
+ 0x09f0, 0x09f1,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a59, 0x0a5c,
+ 0x0a85, 0x0a8b,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b36, 0x0b39,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c60, 0x0c61,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0ce0, 0x0ce1,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d60, 0x0d61,
+ 0x0e01, 0x0e30,
+ 0x0e32, 0x0e33,
+ 0x0e40, 0x0e46,
+ 0x0e5a, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e87, 0x0e88,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eae,
+ 0x0eb2, 0x0eb3,
+ 0x0ec0, 0x0ec4,
+ 0x0edc, 0x0edd,
+ 0x0f18, 0x0f19,
+ 0x0f40, 0x0f47,
+ 0x0f49, 0x0f69,
+ 0x10d0, 0x10f6,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1e00, 0x1e9b,
+ 0x1f50, 0x1f57,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x210a, 0x2113,
+ 0x2115, 0x211d,
+ 0x2120, 0x2122,
+ 0x212a, 0x2131,
+ 0x2133, 0x2138,
+ 0x3041, 0x3094,
+ 0x30a1, 0x30fa,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3192, 0x319f,
+ 0x3260, 0x327b,
+ 0x328a, 0x32b0,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3357,
+ 0x3371, 0x3376,
+ 0x337b, 0x3394,
+ 0x3399, 0x339e,
+ 0x33a9, 0x33ad,
+ 0x33b0, 0x33c1,
+ 0x33c3, 0x33c5,
+ 0x33c7, 0x33d7,
+ 0x33d9, 0x33dd,
+ 0x4e00, 0x9fff,
+ 0xac00, 0xd7a3,
+ 0xf900, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1f, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdf9,
+ 0xfe70, 0xfe72,
+ 0xfe76, 0xfefc,
+ 0xff66, 0xff6f,
+ 0xff71, 0xff9d,
+ 0xffa0, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+};
+
+@(static)
+alpha_singlets := [?]i32{
+ 0x00aa,
+ 0x00b5,
+ 0x00ba,
+ 0x03da,
+ 0x03dc,
+ 0x03de,
+ 0x03e0,
+ 0x06d5,
+ 0x09b2,
+ 0x0a5e,
+ 0x0a8d,
+ 0x0ae0,
+ 0x0b9c,
+ 0x0cde,
+ 0x0e4f,
+ 0x0e84,
+ 0x0e8a,
+ 0x0e8d,
+ 0x0ea5,
+ 0x0ea7,
+ 0x0eb0,
+ 0x0ebd,
+ 0x1fbe,
+ 0x207f,
+ 0x20a8,
+ 0x2102,
+ 0x2107,
+ 0x2124,
+ 0x2126,
+ 0x2128,
+ 0xfb3e,
+ 0xfe74,
+};
+
+@(static)
+space_ranges := [?]i32{
+ 0x0009, 0x000d, // tab and newline
+ 0x0020, 0x0020, // space
+ 0x0085, 0x0085, // next line
+ 0x00a0, 0x00a0,
+ 0x1680, 0x1680, // Ogham space mark
+ 0x2000, 0x200b, // en dash .. zero-width space
+ 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace)
+ 0x2028, 0x2029, 0x3000, 0x3000,
+ 0x202f, 0x202f, // narrow no-break space
+ 0x205f, 0x205f, // medium mathematical space
+ 0x3000, 0x3000, // ideographic space
+ 0xfeff, 0xfeff,
+};
+
+@(static)
+unicode_spaces := [?]i32{
+ 0x0009, // tab
+ 0x000a, // LF
+ 0x000d, // CR
+ 0x0020, // space
+ 0x0085, // next line
+ 0x00a0, // unknown
+ 0x1680, // Ogham space mark
+ 0x2000, // en dash .. zero-width space
+ 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace)
+ 0x2028, 0x2029, 0x3000, 0x3000,
+ 0x202f, // narrow no-break space
+ 0x205f, // medium mathematical space
+ 0x3000, // ideographic space
+ 0xfeff, // unknown
+};
+
+@(static)
+to_upper_ranges := [?]i32{
+ 0x0061, 0x007a, 468, // a-z A-Z
+ 0x00e0, 0x00f6, 468,
+ 0x00f8, 0x00fe, 468,
+ 0x0256, 0x0257, 295,
+ 0x0258, 0x0259, 298,
+ 0x028a, 0x028b, 283,
+ 0x03ad, 0x03af, 463,
+ 0x03b1, 0x03c1, 468,
+ 0x03c3, 0x03cb, 468,
+ 0x03cd, 0x03ce, 437,
+ 0x0430, 0x044f, 468,
+ 0x0451, 0x045c, 420,
+ 0x045e, 0x045f, 420,
+ 0x0561, 0x0586, 452,
+ 0x1f00, 0x1f07, 508,
+ 0x1f10, 0x1f15, 508,
+ 0x1f20, 0x1f27, 508,
+ 0x1f30, 0x1f37, 508,
+ 0x1f40, 0x1f45, 508,
+ 0x1f60, 0x1f67, 508,
+ 0x1f70, 0x1f71, 574,
+ 0x1f72, 0x1f75, 586,
+ 0x1f76, 0x1f77, 600,
+ 0x1f78, 0x1f79, 628,
+ 0x1f7a, 0x1f7b, 612,
+ 0x1f7c, 0x1f7d, 626,
+ 0x1f80, 0x1f87, 508,
+ 0x1f90, 0x1f97, 508,
+ 0x1fa0, 0x1fa7, 508,
+ 0x1fb0, 0x1fb1, 508,
+ 0x1fd0, 0x1fd1, 508,
+ 0x1fe0, 0x1fe1, 508,
+ 0x2170, 0x217f, 484,
+ 0x24d0, 0x24e9, 474,
+ 0xff41, 0xff5a, 468,
+};
+
+@(static)
+to_upper_singlets := [?]i32{
+ 0x00ff, 621,
+ 0x0101, 499,
+ 0x0103, 499,
+ 0x0105, 499,
+ 0x0107, 499,
+ 0x0109, 499,
+ 0x010b, 499,
+ 0x010d, 499,
+ 0x010f, 499,
+ 0x0111, 499,
+ 0x0113, 499,
+ 0x0115, 499,
+ 0x0117, 499,
+ 0x0119, 499,
+ 0x011b, 499,
+ 0x011d, 499,
+ 0x011f, 499,
+ 0x0121, 499,
+ 0x0123, 499,
+ 0x0125, 499,
+ 0x0127, 499,
+ 0x0129, 499,
+ 0x012b, 499,
+ 0x012d, 499,
+ 0x012f, 499,
+ 0x0131, 268, // I
+ 0x0133, 499,
+ 0x0135, 499,
+ 0x0137, 499,
+ 0x013a, 499,
+ 0x013c, 499,
+ 0x013e, 499,
+ 0x0140, 499,
+ 0x0142, 499,
+ 0x0144, 499,
+ 0x0146, 499,
+ 0x0148, 499,
+ 0x014b, 499,
+ 0x014d, 499,
+ 0x014f, 499,
+ 0x0151, 499,
+ 0x0153, 499,
+ 0x0155, 499,
+ 0x0157, 499,
+ 0x0159, 499,
+ 0x015b, 499,
+ 0x015d, 499,
+ 0x015f, 499,
+ 0x0161, 499,
+ 0x0163, 499,
+ 0x0165, 499,
+ 0x0167, 499,
+ 0x0169, 499,
+ 0x016b, 499,
+ 0x016d, 499,
+ 0x016f, 499,
+ 0x0171, 499,
+ 0x0173, 499,
+ 0x0175, 499,
+ 0x0177, 499,
+ 0x017a, 499,
+ 0x017c, 499,
+ 0x017e, 499,
+ 0x017f, 200, // S
+ 0x0183, 499,
+ 0x0185, 499,
+ 0x0188, 499,
+ 0x018c, 499,
+ 0x0192, 499,
+ 0x0199, 499,
+ 0x01a1, 499,
+ 0x01a3, 499,
+ 0x01a5, 499,
+ 0x01a8, 499,
+ 0x01ad, 499,
+ 0x01b0, 499,
+ 0x01b4, 499,
+ 0x01b6, 499,
+ 0x01b9, 499,
+ 0x01bd, 499,
+ 0x01c5, 499,
+ 0x01c6, 498,
+ 0x01c8, 499,
+ 0x01c9, 498,
+ 0x01cb, 499,
+ 0x01cc, 498,
+ 0x01ce, 499,
+ 0x01d0, 499,
+ 0x01d2, 499,
+ 0x01d4, 499,
+ 0x01d6, 499,
+ 0x01d8, 499,
+ 0x01da, 499,
+ 0x01dc, 499,
+ 0x01df, 499,
+ 0x01e1, 499,
+ 0x01e3, 499,
+ 0x01e5, 499,
+ 0x01e7, 499,
+ 0x01e9, 499,
+ 0x01eb, 499,
+ 0x01ed, 499,
+ 0x01ef, 499,
+ 0x01f2, 499,
+ 0x01f3, 498,
+ 0x01f5, 499,
+ 0x01fb, 499,
+ 0x01fd, 499,
+ 0x01ff, 499,
+ 0x0201, 499,
+ 0x0203, 499,
+ 0x0205, 499,
+ 0x0207, 499,
+ 0x0209, 499,
+ 0x020b, 499,
+ 0x020d, 499,
+ 0x020f, 499,
+ 0x0211, 499,
+ 0x0213, 499,
+ 0x0215, 499,
+ 0x0217, 499,
+ 0x0253, 290,
+ 0x0254, 294,
+ 0x025b, 297,
+ 0x0260, 295,
+ 0x0263, 293,
+ 0x0268, 291,
+ 0x0269, 289,
+ 0x026f, 289,
+ 0x0272, 287,
+ 0x0283, 282,
+ 0x0288, 282,
+ 0x0292, 281,
+ 0x03ac, 462,
+ 0x03cc, 436,
+ 0x03d0, 438,
+ 0x03d1, 443,
+ 0x03d5, 453,
+ 0x03d6, 446,
+ 0x03e3, 499,
+ 0x03e5, 499,
+ 0x03e7, 499,
+ 0x03e9, 499,
+ 0x03eb, 499,
+ 0x03ed, 499,
+ 0x03ef, 499,
+ 0x03f0, 414,
+ 0x03f1, 420,
+ 0x0461, 499,
+ 0x0463, 499,
+ 0x0465, 499,
+ 0x0467, 499,
+ 0x0469, 499,
+ 0x046b, 499,
+ 0x046d, 499,
+ 0x046f, 499,
+ 0x0471, 499,
+ 0x0473, 499,
+ 0x0475, 499,
+ 0x0477, 499,
+ 0x0479, 499,
+ 0x047b, 499,
+ 0x047d, 499,
+ 0x047f, 499,
+ 0x0481, 499,
+ 0x0491, 499,
+ 0x0493, 499,
+ 0x0495, 499,
+ 0x0497, 499,
+ 0x0499, 499,
+ 0x049b, 499,
+ 0x049d, 499,
+ 0x049f, 499,
+ 0x04a1, 499,
+ 0x04a3, 499,
+ 0x04a5, 499,
+ 0x04a7, 499,
+ 0x04a9, 499,
+ 0x04ab, 499,
+ 0x04ad, 499,
+ 0x04af, 499,
+ 0x04b1, 499,
+ 0x04b3, 499,
+ 0x04b5, 499,
+ 0x04b7, 499,
+ 0x04b9, 499,
+ 0x04bb, 499,
+ 0x04bd, 499,
+ 0x04bf, 499,
+ 0x04c2, 499,
+ 0x04c4, 499,
+ 0x04c8, 499,
+ 0x04cc, 499,
+ 0x04d1, 499,
+ 0x04d3, 499,
+ 0x04d5, 499,
+ 0x04d7, 499,
+ 0x04d9, 499,
+ 0x04db, 499,
+ 0x04dd, 499,
+ 0x04df, 499,
+ 0x04e1, 499,
+ 0x04e3, 499,
+ 0x04e5, 499,
+ 0x04e7, 499,
+ 0x04e9, 499,
+ 0x04eb, 499,
+ 0x04ef, 499,
+ 0x04f1, 499,
+ 0x04f3, 499,
+ 0x04f5, 499,
+ 0x04f9, 499,
+ 0x1e01, 499,
+ 0x1e03, 499,
+ 0x1e05, 499,
+ 0x1e07, 499,
+ 0x1e09, 499,
+ 0x1e0b, 499,
+ 0x1e0d, 499,
+ 0x1e0f, 499,
+ 0x1e11, 499,
+ 0x1e13, 499,
+ 0x1e15, 499,
+ 0x1e17, 499,
+ 0x1e19, 499,
+ 0x1e1b, 499,
+ 0x1e1d, 499,
+ 0x1e1f, 499,
+ 0x1e21, 499,
+ 0x1e23, 499,
+ 0x1e25, 499,
+ 0x1e27, 499,
+ 0x1e29, 499,
+ 0x1e2b, 499,
+ 0x1e2d, 499,
+ 0x1e2f, 499,
+ 0x1e31, 499,
+ 0x1e33, 499,
+ 0x1e35, 499,
+ 0x1e37, 499,
+ 0x1e39, 499,
+ 0x1e3b, 499,
+ 0x1e3d, 499,
+ 0x1e3f, 499,
+ 0x1e41, 499,
+ 0x1e43, 499,
+ 0x1e45, 499,
+ 0x1e47, 499,
+ 0x1e49, 499,
+ 0x1e4b, 499,
+ 0x1e4d, 499,
+ 0x1e4f, 499,
+ 0x1e51, 499,
+ 0x1e53, 499,
+ 0x1e55, 499,
+ 0x1e57, 499,
+ 0x1e59, 499,
+ 0x1e5b, 499,
+ 0x1e5d, 499,
+ 0x1e5f, 499,
+ 0x1e61, 499,
+ 0x1e63, 499,
+ 0x1e65, 499,
+ 0x1e67, 499,
+ 0x1e69, 499,
+ 0x1e6b, 499,
+ 0x1e6d, 499,
+ 0x1e6f, 499,
+ 0x1e71, 499,
+ 0x1e73, 499,
+ 0x1e75, 499,
+ 0x1e77, 499,
+ 0x1e79, 499,
+ 0x1e7b, 499,
+ 0x1e7d, 499,
+ 0x1e7f, 499,
+ 0x1e81, 499,
+ 0x1e83, 499,
+ 0x1e85, 499,
+ 0x1e87, 499,
+ 0x1e89, 499,
+ 0x1e8b, 499,
+ 0x1e8d, 499,
+ 0x1e8f, 499,
+ 0x1e91, 499,
+ 0x1e93, 499,
+ 0x1e95, 499,
+ 0x1ea1, 499,
+ 0x1ea3, 499,
+ 0x1ea5, 499,
+ 0x1ea7, 499,
+ 0x1ea9, 499,
+ 0x1eab, 499,
+ 0x1ead, 499,
+ 0x1eaf, 499,
+ 0x1eb1, 499,
+ 0x1eb3, 499,
+ 0x1eb5, 499,
+ 0x1eb7, 499,
+ 0x1eb9, 499,
+ 0x1ebb, 499,
+ 0x1ebd, 499,
+ 0x1ebf, 499,
+ 0x1ec1, 499,
+ 0x1ec3, 499,
+ 0x1ec5, 499,
+ 0x1ec7, 499,
+ 0x1ec9, 499,
+ 0x1ecb, 499,
+ 0x1ecd, 499,
+ 0x1ecf, 499,
+ 0x1ed1, 499,
+ 0x1ed3, 499,
+ 0x1ed5, 499,
+ 0x1ed7, 499,
+ 0x1ed9, 499,
+ 0x1edb, 499,
+ 0x1edd, 499,
+ 0x1edf, 499,
+ 0x1ee1, 499,
+ 0x1ee3, 499,
+ 0x1ee5, 499,
+ 0x1ee7, 499,
+ 0x1ee9, 499,
+ 0x1eeb, 499,
+ 0x1eed, 499,
+ 0x1eef, 499,
+ 0x1ef1, 499,
+ 0x1ef3, 499,
+ 0x1ef5, 499,
+ 0x1ef7, 499,
+ 0x1ef9, 499,
+ 0x1f51, 508,
+ 0x1f53, 508,
+ 0x1f55, 508,
+ 0x1f57, 508,
+ 0x1fb3, 509,
+ 0x1fc3, 509,
+ 0x1fe5, 507,
+ 0x1ff3, 509,
+};
+
+@(static)
+to_lower_ranges := [?]i32{
+ 0x0041, 0x005a, 532, // A-Z a-z
+ 0x00c0, 0x00d6, 532, // - -
+ 0x00d8, 0x00de, 532, // - -
+ 0x0189, 0x018a, 705, // - -
+ 0x018e, 0x018f, 702, // - -
+ 0x01b1, 0x01b2, 717, // - -
+ 0x0388, 0x038a, 537, // - -
+ 0x038e, 0x038f, 563, // - -
+ 0x0391, 0x03a1, 532, // - -
+ 0x03a3, 0x03ab, 532, // - -
+ 0x0401, 0x040c, 580, // - -
+ 0x040e, 0x040f, 580, // - -
+ 0x0410, 0x042f, 532, // - -
+ 0x0531, 0x0556, 548, // - -
+ 0x10a0, 0x10c5, 548, // - -
+ 0x1f08, 0x1f0f, 492, // - -
+ 0x1f18, 0x1f1d, 492, // - -
+ 0x1f28, 0x1f2f, 492, // - -
+ 0x1f38, 0x1f3f, 492, // - -
+ 0x1f48, 0x1f4d, 492, // - -
+ 0x1f68, 0x1f6f, 492, // - -
+ 0x1f88, 0x1f8f, 492, // - -
+ 0x1f98, 0x1f9f, 492, // - -
+ 0x1fa8, 0x1faf, 492, // - -
+ 0x1fb8, 0x1fb9, 492, // - -
+ 0x1fba, 0x1fbb, 426, // - -
+ 0x1fc8, 0x1fcb, 414, // - -
+ 0x1fd8, 0x1fd9, 492, // - -
+ 0x1fda, 0x1fdb, 400, // - -
+ 0x1fe8, 0x1fe9, 492, // - -
+ 0x1fea, 0x1feb, 388, // - -
+ 0x1ff8, 0x1ff9, 372, // - -
+ 0x1ffa, 0x1ffb, 374, // - -
+ 0x2160, 0x216f, 516, // - -
+ 0x24b6, 0x24cf, 526, // - -
+ 0xff21, 0xff3a, 532, // - -
+};
+
+@(static)
+to_lower_singlets := [?]i32{
+ 0x0100, 501,
+ 0x0102, 501,
+ 0x0104, 501,
+ 0x0106, 501,
+ 0x0108, 501,
+ 0x010a, 501,
+ 0x010c, 501,
+ 0x010e, 501,
+ 0x0110, 501,
+ 0x0112, 501,
+ 0x0114, 501,
+ 0x0116, 501,
+ 0x0118, 501,
+ 0x011a, 501,
+ 0x011c, 501,
+ 0x011e, 501,
+ 0x0120, 501,
+ 0x0122, 501,
+ 0x0124, 501,
+ 0x0126, 501,
+ 0x0128, 501,
+ 0x012a, 501,
+ 0x012c, 501,
+ 0x012e, 501,
+ 0x0130, 301, // i
+ 0x0132, 501,
+ 0x0134, 501,
+ 0x0136, 501,
+ 0x0139, 501,
+ 0x013b, 501,
+ 0x013d, 501,
+ 0x013f, 501,
+ 0x0141, 501,
+ 0x0143, 501,
+ 0x0145, 501,
+ 0x0147, 501,
+ 0x014a, 501,
+ 0x014c, 501,
+ 0x014e, 501,
+ 0x0150, 501,
+ 0x0152, 501,
+ 0x0154, 501,
+ 0x0156, 501,
+ 0x0158, 501,
+ 0x015a, 501,
+ 0x015c, 501,
+ 0x015e, 501,
+ 0x0160, 501,
+ 0x0162, 501,
+ 0x0164, 501,
+ 0x0166, 501,
+ 0x0168, 501,
+ 0x016a, 501,
+ 0x016c, 501,
+ 0x016e, 501,
+ 0x0170, 501,
+ 0x0172, 501,
+ 0x0174, 501,
+ 0x0176, 501,
+ 0x0178, 379,
+ 0x0179, 501,
+ 0x017b, 501,
+ 0x017d, 501,
+ 0x0181, 710,
+ 0x0182, 501,
+ 0x0184, 501,
+ 0x0186, 706,
+ 0x0187, 501,
+ 0x018b, 501,
+ 0x0190, 703,
+ 0x0191, 501,
+ 0x0193, 705,
+ 0x0194, 707,
+ 0x0196, 711,
+ 0x0197, 709,
+ 0x0198, 501,
+ 0x019c, 711,
+ 0x019d, 713,
+ 0x01a0, 501,
+ 0x01a2, 501,
+ 0x01a4, 501,
+ 0x01a7, 501,
+ 0x01a9, 718,
+ 0x01ac, 501,
+ 0x01ae, 718,
+ 0x01af, 501,
+ 0x01b3, 501,
+ 0x01b5, 501,
+ 0x01b7, 719,
+ 0x01b8, 501,
+ 0x01bc, 501,
+ 0x01c4, 502,
+ 0x01c5, 501,
+ 0x01c7, 502,
+ 0x01c8, 501,
+ 0x01ca, 502,
+ 0x01cb, 501,
+ 0x01cd, 501,
+ 0x01cf, 501,
+ 0x01d1, 501,
+ 0x01d3, 501,
+ 0x01d5, 501,
+ 0x01d7, 501,
+ 0x01d9, 501,
+ 0x01db, 501,
+ 0x01de, 501,
+ 0x01e0, 501,
+ 0x01e2, 501,
+ 0x01e4, 501,
+ 0x01e6, 501,
+ 0x01e8, 501,
+ 0x01ea, 501,
+ 0x01ec, 501,
+ 0x01ee, 501,
+ 0x01f1, 502,
+ 0x01f2, 501,
+ 0x01f4, 501,
+ 0x01fa, 501,
+ 0x01fc, 501,
+ 0x01fe, 501,
+ 0x0200, 501,
+ 0x0202, 501,
+ 0x0204, 501,
+ 0x0206, 501,
+ 0x0208, 501,
+ 0x020a, 501,
+ 0x020c, 501,
+ 0x020e, 501,
+ 0x0210, 501,
+ 0x0212, 501,
+ 0x0214, 501,
+ 0x0216, 501,
+ 0x0386, 538,
+ 0x038c, 564,
+ 0x03e2, 501,
+ 0x03e4, 501,
+ 0x03e6, 501,
+ 0x03e8, 501,
+ 0x03ea, 501,
+ 0x03ec, 501,
+ 0x03ee, 501,
+ 0x0460, 501,
+ 0x0462, 501,
+ 0x0464, 501,
+ 0x0466, 501,
+ 0x0468, 501,
+ 0x046a, 501,
+ 0x046c, 501,
+ 0x046e, 501,
+ 0x0470, 501,
+ 0x0472, 501,
+ 0x0474, 501,
+ 0x0476, 501,
+ 0x0478, 501,
+ 0x047a, 501,
+ 0x047c, 501,
+ 0x047e, 501,
+ 0x0480, 501,
+ 0x0490, 501,
+ 0x0492, 501,
+ 0x0494, 501,
+ 0x0496, 501,
+ 0x0498, 501,
+ 0x049a, 501,
+ 0x049c, 501,
+ 0x049e, 501,
+ 0x04a0, 501,
+ 0x04a2, 501,
+ 0x04a4, 501,
+ 0x04a6, 501,
+ 0x04a8, 501,
+ 0x04aa, 501,
+ 0x04ac, 501,
+ 0x04ae, 501,
+ 0x04b0, 501,
+ 0x04b2, 501,
+ 0x04b4, 501,
+ 0x04b6, 501,
+ 0x04b8, 501,
+ 0x04ba, 501,
+ 0x04bc, 501,
+ 0x04be, 501,
+ 0x04c1, 501,
+ 0x04c3, 501,
+ 0x04c7, 501,
+ 0x04cb, 501,
+ 0x04d0, 501,
+ 0x04d2, 501,
+ 0x04d4, 501,
+ 0x04d6, 501,
+ 0x04d8, 501,
+ 0x04da, 501,
+ 0x04dc, 501,
+ 0x04de, 501,
+ 0x04e0, 501,
+ 0x04e2, 501,
+ 0x04e4, 501,
+ 0x04e6, 501,
+ 0x04e8, 501,
+ 0x04ea, 501,
+ 0x04ee, 501,
+ 0x04f0, 501,
+ 0x04f2, 501,
+ 0x04f4, 501,
+ 0x04f8, 501,
+ 0x1e00, 501,
+ 0x1e02, 501,
+ 0x1e04, 501,
+ 0x1e06, 501,
+ 0x1e08, 501,
+ 0x1e0a, 501,
+ 0x1e0c, 501,
+ 0x1e0e, 501,
+ 0x1e10, 501,
+ 0x1e12, 501,
+ 0x1e14, 501,
+ 0x1e16, 501,
+ 0x1e18, 501,
+ 0x1e1a, 501,
+ 0x1e1c, 501,
+ 0x1e1e, 501,
+ 0x1e20, 501,
+ 0x1e22, 501,
+ 0x1e24, 501,
+ 0x1e26, 501,
+ 0x1e28, 501,
+ 0x1e2a, 501,
+ 0x1e2c, 501,
+ 0x1e2e, 501,
+ 0x1e30, 501,
+ 0x1e32, 501,
+ 0x1e34, 501,
+ 0x1e36, 501,
+ 0x1e38, 501,
+ 0x1e3a, 501,
+ 0x1e3c, 501,
+ 0x1e3e, 501,
+ 0x1e40, 501,
+ 0x1e42, 501,
+ 0x1e44, 501,
+ 0x1e46, 501,
+ 0x1e48, 501,
+ 0x1e4a, 501,
+ 0x1e4c, 501,
+ 0x1e4e, 501,
+ 0x1e50, 501,
+ 0x1e52, 501,
+ 0x1e54, 501,
+ 0x1e56, 501,
+ 0x1e58, 501,
+ 0x1e5a, 501,
+ 0x1e5c, 501,
+ 0x1e5e, 501,
+ 0x1e60, 501,
+ 0x1e62, 501,
+ 0x1e64, 501,
+ 0x1e66, 501,
+ 0x1e68, 501,
+ 0x1e6a, 501,
+ 0x1e6c, 501,
+ 0x1e6e, 501,
+ 0x1e70, 501,
+ 0x1e72, 501,
+ 0x1e74, 501,
+ 0x1e76, 501,
+ 0x1e78, 501,
+ 0x1e7a, 501,
+ 0x1e7c, 501,
+ 0x1e7e, 501,
+ 0x1e80, 501,
+ 0x1e82, 501,
+ 0x1e84, 501,
+ 0x1e86, 501,
+ 0x1e88, 501,
+ 0x1e8a, 501,
+ 0x1e8c, 501,
+ 0x1e8e, 501,
+ 0x1e90, 501,
+ 0x1e92, 501,
+ 0x1e94, 501,
+ 0x1ea0, 501,
+ 0x1ea2, 501,
+ 0x1ea4, 501,
+ 0x1ea6, 501,
+ 0x1ea8, 501,
+ 0x1eaa, 501,
+ 0x1eac, 501,
+ 0x1eae, 501,
+ 0x1eb0, 501,
+ 0x1eb2, 501,
+ 0x1eb4, 501,
+ 0x1eb6, 501,
+ 0x1eb8, 501,
+ 0x1eba, 501,
+ 0x1ebc, 501,
+ 0x1ebe, 501,
+ 0x1ec0, 501,
+ 0x1ec2, 501,
+ 0x1ec4, 501,
+ 0x1ec6, 501,
+ 0x1ec8, 501,
+ 0x1eca, 501,
+ 0x1ecc, 501,
+ 0x1ece, 501,
+ 0x1ed0, 501,
+ 0x1ed2, 501,
+ 0x1ed4, 501,
+ 0x1ed6, 501,
+ 0x1ed8, 501,
+ 0x1eda, 501,
+ 0x1edc, 501,
+ 0x1ede, 501,
+ 0x1ee0, 501,
+ 0x1ee2, 501,
+ 0x1ee4, 501,
+ 0x1ee6, 501,
+ 0x1ee8, 501,
+ 0x1eea, 501,
+ 0x1eec, 501,
+ 0x1eee, 501,
+ 0x1ef0, 501,
+ 0x1ef2, 501,
+ 0x1ef4, 501,
+ 0x1ef6, 501,
+ 0x1ef8, 501,
+ 0x1f59, 492,
+ 0x1f5b, 492,
+ 0x1f5d, 492,
+ 0x1f5f, 492,
+ 0x1fbc, 491,
+ 0x1fcc, 491,
+ 0x1fec, 493,
+ 0x1ffc, 491,
+};
+
+@(static)
+to_title_singlets := [?]i32{
+ 0x01c4, 501,
+ 0x01c6, 499,
+ 0x01c7, 501,
+ 0x01c9, 499,
+ 0x01ca, 501,
+ 0x01cc, 499,
+ 0x01f1, 501,
+ 0x01f3, 499,
+};
diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin
index f008c3881..50d24d562 100644
--- a/core/unicode/utf8/utf8.odin
+++ b/core/unicode/utf8/utf8.odin
@@ -350,3 +350,44 @@ rune_size :: proc(r: rune) -> int {
}
return -1;
}
+
+// full_rune reports if the bytes in b begin with a full utf-8 encoding of a rune or not
+// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
+full_rune :: proc(b: []byte) -> bool {
+ n := len(b);
+ if n == 0 {
+ return false;
+ }
+ x := _first[b[0]];
+ if n >= int(x & 7) {
+ return true;
+ }
+ accept := accept_ranges[x>>4];
+ if n > 1 && (b[1] < accept.lo || accept.hi < b[1]) {
+ return true;
+ } else if n > 2 && (b[2] < LOCB || HICB < b[2]) {
+ return true;
+ }
+ return false;
+}
+
+// full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not
+// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
+full_rune_in_string :: proc(s: string) -> bool {
+ return full_rune(transmute([]byte)s);
+}
+
+
+_first := [256]u8{
+ 0x00..0x7f = 0xf0, // ascii, size 1
+ 0x80..0xc1 = 0xf1, // invalid, size 1
+ 0xc2..0xdf = 0x02, // accept 1, size 2
+ 0xe0 = 0x13, // accept 1, size 3
+ 0xe1..0xec = 0x03, // accept 0, size 3
+ 0xed = 0x23, // accept 2, size 3
+ 0xee..0xef = 0x03, // accept 0, size 3
+ 0xf0 = 0x34, // accept 3, size 4
+ 0xf1..0xf3 = 0x04, // accept 0, size 4
+ 0xf4 = 0x44, // accept 4, size 4
+ 0xf5..0xff = 0xf1, // ascii, size 1
+};
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index 231ed6b91..d4f00475a 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -75,7 +75,7 @@ the_basics :: proc() {
// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
// literals 0x. A leading zero does not produce an octal constant (unlike C).
- // In Odin, if a number constant is possible to be represented by a type without
+ // In Odin, if a numeric constant can be represented by a type without
// precision loss, it will automatically convert to that type.
x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
@@ -85,7 +85,7 @@ the_basics :: proc() {
y = 1; // `1` is an untyped integer literal which can implicitly convert to `int`
z: f64; // `z` is typed of type `f64` (64-bit floating point number)
- z = 1; // `1` is an untyped integer literals which can be implicity conver to `f64`
+ z = 1; // `1` is an untyped integer literal which can be implicitly converted to `f64`
// No need for any suffixes or decimal places like in other languages
// CONSTANTS JUST WORK!!!
@@ -150,7 +150,7 @@ control_flow :: proc() {
i += 1;
}
- // If the condition is omitted, this produces an infinite loop:
+ // If the condition is omitted, an infinite loop is produced:
for {
break;
}
diff --git a/src/array.cpp b/src/array.cpp
index 6fe54b847..dc52eeb8d 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -43,11 +43,96 @@ template <typename T> void array_set_capacity (Array<T> *array, isize capac
template <typename T> Array<T> array_slice (Array<T> const &array, isize lo, isize hi);
template <typename T> Array<T> array_clone (gbAllocator const &a, Array<T> const &array);
-
-
template <typename T> void array_ordered_remove (Array<T> *array, isize index);
template <typename T> void array_unordered_remove(Array<T> *array, isize index);
+template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset);
+template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count);
+
+template <typename T> T *array_end_ptr(Array<T> *array);
+
+
+template <typename T>
+struct Slice {
+ T *data;
+ isize count;
+
+ T &operator[](isize index) {
+ #if !defined(NO_ARRAY_BOUNDS_CHECK)
+ GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+ #endif
+ return data[index];
+ }
+
+ T const &operator[](isize index) const {
+ #if !defined(NO_ARRAY_BOUNDS_CHECK)
+ GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+ #endif
+ return data[index];
+ }
+};
+
+template <typename T> Slice<T> slice_from_array(Array<T> const &a);
+
+
+
+template <typename T>
+Slice<T> slice_make(gbAllocator const &allocator, isize count) {
+ Slice<T> s = {};
+ s.data = gb_alloc_array(allocator, T, count);
+ s.count = count;
+ return s;
+}
+
+
+template <typename T>
+Slice<T> slice_from_array(Array<T> const &a) {
+ return {a.data, a.count};
+}
+template <typename T>
+Slice<T> slice_clone(gbAllocator const &allocator, Slice<T> const &a) {
+ T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T));
+ return {data, a.count};
+}
+
+template <typename T>
+Slice<T> slice_clone_from_array(gbAllocator const &allocator, Array<T> const &a) {
+ auto c = array_clone(allocator, a);
+ return {c.data, c.count};
+}
+
+
+template <typename T>
+void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset) {
+ gb_memmove(slice->data+offset, data.data, gb_size_of(T)*data.count);
+}
+template <typename T>
+void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset, isize count) {
+ gb_memmove(slice->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count));
+}
+
+
+
+template <typename T>
+void slice_ordered_remove(Slice<T> *array, isize index) {
+ GB_ASSERT(0 <= index && index < array->count);
+
+ isize bytes = gb_size_of(T) * (array->count-(index+1));
+ gb_memmove(array->data+index, array->data+index+1, bytes);
+ array->count -= 1;
+}
+
+template <typename T>
+void slice_unordered_remove(Slice<T> *array, isize index) {
+ GB_ASSERT(0 <= index && index < array->count);
+
+ isize n = array->count-1;
+ if (index != n) {
+ gb_memmove(array->data+index, array->data+n, gb_size_of(T));
+ }
+ array->count -= 1;
+}
+
template <typename T>
void array_copy(Array<T> *array, Array<T> const &data, isize offset) {
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 931dcf88e..b4c414f03 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -104,6 +104,37 @@ enum BuildModeKind {
BuildMode_Assembly,
};
+enum CommandKind : u32 {
+ Command_run = 1<<0,
+ Command_build = 1<<1,
+ Command_check = 1<<3,
+ Command_query = 1<<4,
+ Command_doc = 1<<5,
+ Command_version = 1<<6,
+ Command_test = 1<<7,
+
+ Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
+ Command__does_build = Command_run|Command_build|Command_test,
+ Command_all = ~(u32)0,
+};
+
+char const *odin_command_strings[32] = {
+ "run",
+ "build",
+ "check",
+ "query",
+ "doc",
+ "version",
+};
+
+
+
+enum CmdDocFlag : u32 {
+ CmdDocFlag_Short = 1<<0,
+ CmdDocFlag_AllPackages = 1<<1,
+};
+
+
// This stores the information for the specify architecture of this build
struct BuildContext {
@@ -124,6 +155,7 @@ struct BuildContext {
i64 word_size; // Size of a pointer, must be >= 4
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
+ CommandKind command_kind;
String command;
TargetMetrics metrics;
@@ -143,6 +175,8 @@ struct BuildContext {
bool generate_docs;
i32 optimization_level;
bool show_timings;
+ bool show_unused;
+ bool show_unused_with_location;
bool show_more_timings;
bool show_system_calls;
bool keep_temp_files;
@@ -151,6 +185,7 @@ struct BuildContext {
bool no_dynamic_literals;
bool no_output_files;
bool no_crt;
+ bool no_entry_point;
bool use_lld;
bool vet;
bool cross_compiling;
@@ -165,6 +200,9 @@ struct BuildContext {
bool ignore_microsoft_magic;
bool linker_map_file;
+ u32 cmd_doc_flags;
+ Array<String> extra_packages;
+
QueryDataSetSettings query_data_set_settings;
gbAffinity affinity;
@@ -297,6 +335,19 @@ bool is_excluded_target_filename(String name) {
String original_name = name;
name = remove_extension_from_path(name);
+ if (string_starts_with(name, str_lit("."))) {
+ // Ignore .*.odin files
+ return true;
+ }
+
+ String test_suffix = str_lit("_test");
+ if (build_context.command_kind != Command_test) {
+ if (string_ends_with(name, test_suffix) && name != test_suffix) {
+ // Ignore *_test.odin files
+ return true;
+ }
+ }
+
String str1 = {};
String str2 = {};
isize n = 0;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 1aafa6e1c..5234955fb 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -113,7 +113,7 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
return e->type;
}
-void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Ast *> const &inits, String context_name) {
+void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Slice<Ast *> const &inits, String context_name) {
if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
return;
}
@@ -121,8 +121,7 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
// an extra allocation
- auto operands = array_make<Operand>(ctx->allocator, 0, 2*lhs_count);
- defer (array_free(&operands));
+ auto operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false);
isize rhs_count = operands.count;
@@ -317,7 +316,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
break;
default:
error(e->token, "Only struct types can have custom atom operations");
- gb_free(heap_allocator(), ac.atom_op_table);
break;
}
}
@@ -638,7 +636,7 @@ String handle_link_name(CheckerContext *ctx, Token token, String link_name, Stri
error(token, "'link_name' and 'link_prefix' cannot be used together");
} else {
isize len = link_prefix.len + token.string.len;
- u8 *name = gb_alloc_array(ctx->allocator, u8, len+1);
+ u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1);
gb_memmove(name, &link_prefix[0], link_prefix.len);
gb_memmove(name+link_prefix.len, &token.string[0], token.string.len);
name[len] = 0;
@@ -975,7 +973,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
ast_node(pg, ProcGroup, d->init_expr);
- pge->entities = array_make<Entity*>(ctx->allocator, 0, pg->args.count);
+ pge->entities = array_make<Entity*>(permanent_allocator(), 0, pg->args.count);
// NOTE(bill): This must be set here to prevent cycles in checking if someone
// places the entity within itself
@@ -1009,11 +1007,10 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
continue;
}
- if (ptr_set_exists(&entity_set, e)) {
+ if (ptr_set_update(&entity_set, e)) {
error(arg, "Previous use of `%.*s` in procedure group", LIT(e->token.string));
continue;
}
- ptr_set_add(&entity_set, e);
array_add(&pge->entities, e);
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 02b54c80a..ea460ce09 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -73,7 +73,7 @@ void update_expr_type (CheckerContext *c, Ast *e, Type *type,
bool check_is_terminating (Ast *node, String const &label);
bool check_has_break (Ast *stmt, String const &label, bool implicit);
void check_stmt (CheckerContext *c, Ast *node, u32 flags);
-void check_stmt_list (CheckerContext *c, Array<Ast *> const &stmts, u32 flags);
+void check_stmt_list (CheckerContext *c, Slice<Ast *> const &stmts, u32 flags);
void check_init_constant (CheckerContext *c, Entity *e, Operand *operand);
bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value);
bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> *operands = nullptr);
@@ -89,9 +89,9 @@ Type * check_init_variable (CheckerContext *c, Entity *e, Operand *
Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc);
Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc);
bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type);
-void set_procedure_abi_types(gbAllocator a, Type *type);
+void set_procedure_abi_types(Type *type);
void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
-
+void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
@@ -133,7 +133,7 @@ void error_operand_no_value(Operand *o) {
}
-void check_scope_decls(CheckerContext *c, Array<Ast *> const &nodes, isize reserve_size) {
+void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) {
Scope *s = c->scope;
check_collect_entities(c, nodes);
@@ -267,7 +267,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
CheckerContext nctx = *c;
- Scope *scope = create_scope(base_entity->scope, a);
+ Scope *scope = create_scope(base_entity->scope);
scope->flags |= ScopeFlag_Proc;
nctx.scope = scope;
nctx.allow_polymorphic_types = true;
@@ -366,7 +366,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
u64 tags = base_entity->Procedure.tags;
Ast *ident = clone_ast(base_entity->identifier);
Token token = ident->Ident.token;
- DeclInfo *d = make_decl_info(nctx.allocator, scope, old_decl->parent);
+ DeclInfo *d = make_decl_info(scope, old_decl->parent);
d->gen_proc_type = final_proc_type;
d->type_expr = pl->type;
d->proc_lit = proc_lit;
@@ -1012,7 +1012,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
}
if (modify_type) {
- set_procedure_abi_types(c->allocator, source);
+ set_procedure_abi_types(source);
}
return true;
@@ -1075,8 +1075,10 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
if (e->parent_proc_decl != nullptr &&
e->parent_proc_decl != c->curr_proc_decl) {
if (e->kind == Entity_Variable) {
- error(n, "Nested procedures do not capture its parent's variables: %.*s", LIT(name));
- return nullptr;
+ if ((e->flags & EntityFlag_Static) == 0) {
+ error(n, "Nested procedures do not capture its parent's variables: %.*s", LIT(name));
+ return nullptr;
+ }
} else if (e->kind == Entity_Label) {
error(n, "Nested procedures do not capture its parent's labels: %.*s", LIT(name));
return nullptr;
@@ -1834,10 +1836,6 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
gbString err_str = nullptr;
- defer (if (err_str != nullptr) {
- gb_string_free(err_str);
- });
-
if (check_is_assignable_to(c, x, y->type) ||
check_is_assignable_to(c, y, x->type)) {
Type *err_type = x->type;
@@ -1867,8 +1865,8 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
}
gbString type_string = type_to_string(err_type);
defer (gb_string_free(type_string));
- err_str = gb_string_make(c->allocator,
- gb_bprintf("operator '%.*s' not defined for type '%s'", LIT(token_strings[op]), type_string));
+ err_str = gb_string_make(temporary_allocator(),
+ gb_bprintf("operator '%.*s' not defined for type '%s'", LIT(token_strings[op]), type_string));
}
} else {
gbString xt, yt;
@@ -1882,8 +1880,7 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
} else {
yt = type_to_string(y->type);
}
- err_str = gb_string_make(c->allocator,
- gb_bprintf("mismatched types '%s' and '%s'", xt, yt));
+ err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt));
gb_string_free(yt);
gb_string_free(xt);
}
@@ -2166,6 +2163,17 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
return true;
}
+ if (is_constant && is_type_untyped(src) && is_type_string(src)) {
+ if (is_type_u8_array(dst)) {
+ String s = operand->value.value_string;
+ return s.len == dst->Array.count;
+ }
+ if (is_type_rune_array(dst)) {
+ String s = operand->value.value_string;
+ return gb_utf8_strnlen(s.text, s.len) == dst->Array.count;
+ }
+ }
+
if (dst->kind == Type_Array && src->kind == Type_Array) {
if (are_types_identical(dst->Array.elem, src->Array.elem)) {
@@ -2960,6 +2968,19 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
if (check_is_assignable_to(c, operand, elem)) {
operand->mode = Addressing_Value;
} else {
+ if (operand->value.kind == ExactValue_String) {
+ String s = operand->value.value_string;
+ if (is_type_u8_array(t)) {
+ if (s.len == t->Array.count) {
+ break;
+ }
+ } else if (is_type_rune_array(t)) {
+ isize rune_count = gb_utf8_strnlen(s.text, s.len);
+ if (rune_count == t->Array.count) {
+ break;
+ }
+ }
+ }
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
return;
@@ -2979,8 +3000,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
case Type_Union:
if (!is_operand_nil(*operand) && !is_operand_undef(*operand)) {
isize count = t->Union.variants.count;
- ValidIndexAndScore *valids = gb_alloc_array(c->allocator, ValidIndexAndScore, count);
- defer (gb_free(c->allocator, valids));
+ ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count);
isize valid_count = 0;
isize first_success_index = -1;
for_array(i, t->Union.variants) {
@@ -3389,7 +3409,7 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
return value;
} else if (value.kind == ExactValue_Quaternion) {
// @QuaternionLayout
- Quaternion256 q = value.value_quaternion;
+ Quaternion256 q = *value.value_quaternion;
GB_ASSERT(sel.index.count == 1);
switch (sel.index[0]) {
@@ -3414,7 +3434,7 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
return empty_exact_value;
} else if (value.kind == ExactValue_Complex) {
// @QuaternionLayout
- Complex128 c = value.value_complex;
+ Complex128 c = *value.value_complex;
GB_ASSERT(sel.index.count == 1);
switch (sel.index[0]) {
@@ -3443,6 +3463,13 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
Entity *entity = nullptr;
Selection sel = {}; // NOTE(bill): Not used if it's an import name
+ if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) {
+ error(node, "Illegal use of -> selector shorthand outside of a call");
+ operand->mode = Addressing_Invalid;
+ operand->expr = node;
+ return nullptr;
+ }
+
operand->expr = node;
Ast *op_expr = se->expr;
@@ -4343,7 +4370,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
add_type_info_type(c, t);
- t = base_type(t);
if (o.mode != Addressing_Type) {
error(expr, "Expected a type for 'typeid_of'");
return false;
@@ -4351,6 +4377,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_Value;
operand->type = t_typeid;
+ operand->value = exact_value_typeid(t);
break;
}
@@ -4702,8 +4729,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (is_type_complex(x->type)) {
if (x->mode == Addressing_Constant) {
ExactValue v = exact_value_to_complex(x->value);
- f64 r = v.value_complex.real;
- f64 i = -v.value_complex.imag;
+ f64 r = v.value_complex->real;
+ f64 i = -v.value_complex->imag;
x->value = exact_value_complex(r, i);
x->mode = Addressing_Constant;
} else {
@@ -4712,10 +4739,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
} else if (is_type_quaternion(x->type)) {
if (x->mode == Addressing_Constant) {
ExactValue v = exact_value_to_quaternion(x->value);
- f64 r = v.value_quaternion.real;
- f64 i = -v.value_quaternion.imag;
- f64 j = -v.value_quaternion.jmag;
- f64 k = -v.value_quaternion.kmag;
+ f64 r = +v.value_quaternion->real;
+ f64 i = -v.value_quaternion->imag;
+ f64 j = -v.value_quaternion->jmag;
+ f64 k = -v.value_quaternion->kmag;
x->value = exact_value_quaternion(r, i, j, k);
x->mode = Addressing_Constant;
} else {
@@ -4739,7 +4766,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
gb_string_free(type_str);
return false;
}
- gbAllocator a = c->allocator;
+ gbAllocator a = permanent_allocator();
Type *tuple = alloc_type_tuple();
@@ -5132,8 +5159,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->value.value_float = gb_abs(operand->value.value_float);
break;
case ExactValue_Complex: {
- f64 r = operand->value.value_complex.real;
- f64 i = operand->value.value_complex.imag;
+ f64 r = operand->value.value_complex->real;
+ f64 i = operand->value.value_complex->imag;
operand->value = exact_value_float(gb_sqrt(r*r + i*i));
break;
@@ -5356,7 +5383,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
- scope = create_scope(c->scope, c->allocator);
+ scope = create_scope(c->scope);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
@@ -5389,7 +5416,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
- scope = create_scope(old_struct->Struct.scope->parent, c->allocator);
+ scope = create_scope(old_struct->Struct.scope->parent);
soa_struct->Struct.scope = scope;
for_array(i, old_struct->Struct.fields) {
@@ -6035,6 +6062,50 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
break;
+
+ case BuiltinProc_type_equal_proc:
+ {
+ Operand op = {};
+ Type *bt = check_type(c, ce->args[0]);
+ Type *type = base_type(bt);
+ if (type == nullptr || type == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_comparable(type)) {
+ gbString t = type_to_string(type);
+ error(ce->args[0], "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_equal_proc;
+ break;
+ }
+
+ case BuiltinProc_type_hasher_proc:
+ {
+ Operand op = {};
+ Type *bt = check_type(c, ce->args[0]);
+ Type *type = base_type(bt);
+ if (type == nullptr || type == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_valid_for_keys(type)) {
+ gbString t = type_to_string(type);
+ error(ce->args[0], "Expected a valid type for map keys for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ add_map_key_type_dependencies(c, type);
+
+ operand->mode = Addressing_Value;
+ operand->type = t_hasher_proc;
+ break;
+ }
}
return true;
@@ -6061,7 +6132,7 @@ isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs
}
-bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, Array<Operand> *operands, Array<Ast *> const &rhs) {
+bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, Array<Operand> *operands, Slice<Ast *> const &rhs) {
bool optional_ok = false;
isize tuple_index = 0;
for_array(i, rhs) {
@@ -6145,7 +6216,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
-bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Array<Ast *> const &rhs, bool allow_ok, bool is_variadic) {
+bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, bool allow_ok, bool is_variadic) {
bool optional_ok = false;
isize tuple_index = 0;
for_array(i, rhs) {
@@ -6540,10 +6611,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
CallArgumentError err = CallArgumentError_None;
isize param_count = pt->param_count;
- bool *visited = gb_alloc_array(c->allocator, bool, param_count);
- defer (gb_free(c->allocator, visited));
- auto ordered_operands = array_make<Operand>(c->allocator, param_count);
- defer (array_free(&ordered_operands));
+ bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
+ auto ordered_operands = array_make<Operand>(temporary_allocator(), param_count);
defer ({
for_array(i, ordered_operands) {
Operand const &o = ordered_operands[i];
@@ -6749,7 +6818,7 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize
}
-bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Array<Ast *> *clauses, bool print_err) {
+bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Slice<Ast *> *clauses, bool print_err) {
if (clauses != nullptr) {
for_array(i, *clauses) {
Ast *clause = (*clauses)[i];
@@ -6814,7 +6883,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, A
}
-CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Array<Ast *> const &args) {
+CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice<Ast *> const &args) {
ast_node(ce, CallExpr, call);
CallArgumentCheckerType *call_checker = check_call_arguments_internal;
@@ -7338,6 +7407,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
Entity *e = params->variables[i];
if (e->kind == Entity_Constant) {
check_expr_with_type_hint(c, &operands[i], fv->value, e->type);
+ continue;
}
}
@@ -7371,13 +7441,26 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
TypeTuple *tuple = get_record_polymorphic_params(original_type);
isize param_count = tuple->variables.count;
+ isize minimum_param_count = param_count;
+ for (minimum_param_count = tuple->variables.count-1; minimum_param_count >= 0; minimum_param_count--) {
+ Entity *e = tuple->variables[minimum_param_count];
+ if (e->kind != Entity_Constant) {
+ break;
+ }
+ if (e->Constant.param_value.kind == ParameterValue_Invalid) {
+ break;
+ }
+ }
Array<Operand> ordered_operands = operands;
- if (named_fields) {
- bool *visited = gb_alloc_array(c->allocator, bool, param_count);
+ if (!named_fields) {
+ ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
+ array_copy(&ordered_operands, operands, 0);
+ } else {
+ bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
// LEAK(bill)
- ordered_operands = array_make<Operand>(c->allocator, param_count);
+ ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
for_array(i, ce->args) {
Ast *arg = ce->args[i];
@@ -7440,26 +7523,55 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
return err;
}
- if (param_count < ordered_operands.count) {
- error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
- err = CallArgumentError_TooManyArguments;
- } else if (param_count > ordered_operands.count) {
- error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
- err = CallArgumentError_TooFewArguments;
+ if (minimum_param_count != param_count) {
+ if (param_count < ordered_operands.count) {
+ error(call, "Too many polymorphic type arguments, expected a maximum of %td, got %td", param_count, ordered_operands.count);
+ err = CallArgumentError_TooManyArguments;
+ } else if (minimum_param_count > ordered_operands.count) {
+ error(call, "Too few polymorphic type arguments, expected a minimum of %td, got %td", minimum_param_count, ordered_operands.count);
+ err = CallArgumentError_TooFewArguments;
+ }
+ } else {
+ if (param_count < ordered_operands.count) {
+ error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
+ err = CallArgumentError_TooManyArguments;
+ } else if (param_count > ordered_operands.count) {
+ error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
+ err = CallArgumentError_TooFewArguments;
+ }
}
if (err != 0) {
return err;
}
+ if (minimum_param_count != param_count) {
+ isize missing_count = 0;
+ // NOTE(bill): Replace missing operands with the default values (if possible)
+ for_array(i, ordered_operands) {
+ Operand *o = &ordered_operands[i];
+ if (o->expr == nullptr) {
+ Entity *e = tuple->variables[i];
+ if (e->kind == Entity_Constant) {
+ missing_count += 1;
+ o->mode = Addressing_Constant;
+ o->type = default_type(e->type);
+ o->expr = unparen_expr(e->Constant.param_value.original_ast_expr);
+ if (e->Constant.param_value.kind == ParameterValue_Constant) {
+ o->value = e->Constant.param_value.value;
+ }
+ }
+ }
+ }
+ }
+
i64 score = 0;
for (isize i = 0; i < param_count; i++) {
+ Entity *e = tuple->variables[i];
Operand *o = &ordered_operands[i];
if (o->mode == Addressing_Invalid) {
continue;
}
- Entity *e = tuple->variables[i];
-
if (e->kind == Entity_TypeName) {
if (o->mode != Addressing_Type) {
if (show_error) {
@@ -7506,8 +7618,6 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
}
{
- gbAllocator a = c->allocator;
-
bool failure = false;
Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure);
if (found_entity) {
@@ -7558,7 +7668,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
-ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Array<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
+ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
if (proc != nullptr &&
proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc);
@@ -7595,7 +7705,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
mix = arg->kind == Ast_FieldValue;
}
if (mix) {
- error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed");
+ error(arg, "Mixture of 'field = value' and value elements in a procedure call is not allowed");
fail = true;
}
}
@@ -7800,7 +7910,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
err_str = "used as a value";
break;
case Addressing_Type:
- err_str = "is not an expression";
+ err_str = "is not an expression but a type";
break;
case Addressing_Builtin:
err_str = "must be called";
@@ -8110,7 +8220,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_ast_node(bl, BasicLit, node);
Type *t = t_invalid;
- switch (bl->value.kind) {
+ switch (node->tav.value.kind) {
case ExactValue_String: t = t_untyped_string; break;
case ExactValue_Float: t = t_untyped_float; break;
case ExactValue_Complex: t = t_untyped_complex; break;
@@ -8128,7 +8238,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->mode = Addressing_Constant;
o->type = t;
- o->value = bl->value;
+ o->value = node->tav.value;
case_end;
case_ast_node(bd, BasicDirective, node);
@@ -8170,7 +8280,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
Type *type = alloc_type(Type_Proc);
check_open_scope(&ctx, pl->type);
{
- decl = make_decl_info(ctx.allocator, ctx.scope, ctx.decl);
+ decl = make_decl_info(ctx.scope, ctx.decl);
decl->proc_lit = node;
ctx.decl = decl;
defer (ctx.decl = ctx.decl->parent);
@@ -8467,7 +8577,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
if (cl->elems[0]->kind == Ast_FieldValue) {
- bool *fields_visited = gb_alloc_array(c->allocator, bool, field_count);
+ bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
@@ -9065,6 +9175,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
is_constant = false;
{ // Checker values
+ bool key_is_typeid = is_type_typeid(t->Map.key);
+ bool value_is_typeid = is_type_typeid(t->Map.value);
+
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind != Ast_FieldValue) {
@@ -9072,13 +9185,22 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
continue;
}
ast_node(fv, FieldValue, elem);
- check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+
+ if (key_is_typeid) {
+ check_expr_or_type(c, o, fv->field, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+ }
check_assignment(c, o, t->Map.key, str_lit("map literal"));
if (o->mode == Addressing_Invalid) {
continue;
}
- check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ if (value_is_typeid) {
+ check_expr_or_type(c, o, fv->value, t->Map.value);
+ } else {
+ check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ }
check_assignment(c, o, t->Map.value, str_lit("map literal"));
}
}
@@ -9349,6 +9471,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
add_package_dependency(c, "runtime", "type_assertion_check");
+ add_package_dependency(c, "runtime", "type_assertion_check2");
case_end;
case_ast_node(tc, TypeCast, node);
@@ -9447,8 +9570,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
//
// NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
+ bool allow_arrow_right_selector_expr;
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
Operand x = {};
ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
if (x.mode == Addressing_Invalid || x.type == t_invalid) {
o->mode = Addressing_Invalid;
o->type = t_invalid;
@@ -9543,13 +9671,17 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
- auto modified_args = array_make<Ast *>(heap_allocator(), ce->args.count+1);
+ auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
modified_args[0] = first_arg;
- array_copy(&modified_args, ce->args, 1);
+ slice_copy(&modified_args, ce->args, 1);
ce->args = modified_args;
se->modified_call = true;
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
check_expr_base(c, o, se->call, type_hint);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
o->expr = node;
return Expr_Expr;
case_end;
@@ -9634,7 +9766,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (is_type_map(t)) {
Operand key = {};
- check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
+ if (is_type_typeid(t->Map.key)) {
+ check_expr_or_type(c, &key, ie->index, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
+ }
check_assignment(c, &key, t->Map.key, str_lit("map index"));
if (key.mode == Addressing_Invalid) {
o->mode = Addressing_Invalid;
@@ -10033,7 +10169,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
error(x.expr, "Expected a constant string for the inline asm constraints parameter");
}
- Scope *scope = create_scope(c->scope, heap_allocator());
+ Scope *scope = create_scope(c->scope);
scope->flags |= ScopeFlag_Proc;
Type *params = alloc_type_tuple();
@@ -10143,14 +10279,14 @@ void check_expr_or_type(CheckerContext *c, Operand *o, Ast *e, Type *type_hint)
}
-gbString write_expr_to_string(gbString str, Ast *node);
+gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
-gbString write_struct_fields_to_string(gbString str, Array<Ast *> const &params) {
+gbString write_struct_fields_to_string(gbString str, Slice<Ast *> const &params) {
for_array(i, params) {
if (i > 0) {
str = gb_string_appendc(str, ", ");
}
- str = write_expr_to_string(str, params[i]);
+ str = write_expr_to_string(str, params[i], false);
}
return str;
}
@@ -10164,11 +10300,23 @@ gbString string_append_string(gbString str, String string) {
gbString string_append_token(gbString str, Token token) {
- return string_append_string(str, token.string);
+ if (token.kind == Token_String) {
+ str = gb_string_append_rune(str, '"');
+ } else if (token.kind == Token_Rune) {
+ str = gb_string_append_rune(str, '\'');
+ }
+ str = string_append_string(str, token.string);
+ if (token.kind == Token_String) {
+ str = gb_string_append_rune(str, '"');
+ } else if (token.kind == Token_Rune) {
+ str = gb_string_append_rune(str, '\'');
+ }
+
+ return str;
}
-gbString write_expr_to_string(gbString str, Ast *node) {
+gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
if (node == nullptr)
return str;
@@ -10206,21 +10354,30 @@ gbString write_expr_to_string(gbString str, Ast *node) {
str = gb_string_appendc(str, "proc{");
for_array(i, pg->args) {
if (i > 0) str = gb_string_appendc(str, ", ");
- str = write_expr_to_string(str, pg->args[i]);
+ str = write_expr_to_string(str, pg->args[i], shorthand);
}
str = gb_string_append_rune(str, '}');
case_end;
case_ast_node(pl, ProcLit, node);
- str = write_expr_to_string(str, pl->type);
+ str = write_expr_to_string(str, pl->type, shorthand);
+ if (pl->body) {
+ str = gb_string_appendc(str, " {...}");
+ } else {
+ str = gb_string_appendc(str, " ---");
+ }
case_end;
case_ast_node(cl, CompoundLit, node);
- str = write_expr_to_string(str, cl->type);
+ str = write_expr_to_string(str, cl->type, shorthand);
str = gb_string_append_rune(str, '{');
- for_array(i, cl->elems) {
- if (i > 0) str = gb_string_appendc(str, ", ");
- str = write_expr_to_string(str, cl->elems[i]);
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ for_array(i, cl->elems) {
+ if (i > 0) str = gb_string_appendc(str, ", ");
+ str = write_expr_to_string(str, cl->elems[i], shorthand);
+ }
}
str = gb_string_append_rune(str, '}');
case_end;
@@ -10229,71 +10386,71 @@ gbString write_expr_to_string(gbString str, Ast *node) {
case_ast_node(te, TagExpr, node);
str = gb_string_append_rune(str, '#');
str = string_append_token(str, te->name);
- str = write_expr_to_string(str, te->expr);
+ str = write_expr_to_string(str, te->expr, shorthand);
case_end;
case_ast_node(ue, UnaryExpr, node);
str = string_append_token(str, ue->op);
- str = write_expr_to_string(str, ue->expr);
+ str = write_expr_to_string(str, ue->expr, shorthand);
case_end;
case_ast_node(de, DerefExpr, node);
- str = write_expr_to_string(str, de->expr);
+ str = write_expr_to_string(str, de->expr, shorthand);
str = gb_string_append_rune(str, '^');
case_end;
case_ast_node(be, BinaryExpr, node);
- str = write_expr_to_string(str, be->left);
+ str = write_expr_to_string(str, be->left, shorthand);
str = gb_string_append_rune(str, ' ');
str = string_append_token(str, be->op);
str = gb_string_append_rune(str, ' ');
- str = write_expr_to_string(str, be->right);
+ str = write_expr_to_string(str, be->right, shorthand);
case_end;
case_ast_node(te, TernaryExpr, node);
- str = write_expr_to_string(str, te->cond);
+ str = write_expr_to_string(str, te->cond, shorthand);
str = gb_string_appendc(str, " ? ");
- str = write_expr_to_string(str, te->x);
+ str = write_expr_to_string(str, te->x, shorthand);
str = gb_string_appendc(str, " : ");
- str = write_expr_to_string(str, te->y);
+ str = write_expr_to_string(str, te->y, shorthand);
case_end;
case_ast_node(te, TernaryIfExpr, node);
- str = write_expr_to_string(str, te->x);
+ str = write_expr_to_string(str, te->x, shorthand);
str = gb_string_appendc(str, " if ");
- str = write_expr_to_string(str, te->cond);
+ str = write_expr_to_string(str, te->cond, shorthand);
str = gb_string_appendc(str, " else ");
- str = write_expr_to_string(str, te->y);
+ str = write_expr_to_string(str, te->y, shorthand);
case_end;
case_ast_node(te, TernaryWhenExpr, node);
- str = write_expr_to_string(str, te->x);
+ str = write_expr_to_string(str, te->x, shorthand);
str = gb_string_appendc(str, " when ");
- str = write_expr_to_string(str, te->cond);
+ str = write_expr_to_string(str, te->cond, shorthand);
str = gb_string_appendc(str, " else ");
- str = write_expr_to_string(str, te->y);
+ str = write_expr_to_string(str, te->y, shorthand);
case_end;
case_ast_node(pe, ParenExpr, node);
str = gb_string_append_rune(str, '(');
- str = write_expr_to_string(str, pe->expr);
+ str = write_expr_to_string(str, pe->expr, shorthand);
str = gb_string_append_rune(str, ')');
case_end;
case_ast_node(se, SelectorExpr, node);
- str = write_expr_to_string(str, se->expr);
+ str = write_expr_to_string(str, se->expr, shorthand);
str = string_append_token(str, se->token);
- str = write_expr_to_string(str, se->selector);
+ str = write_expr_to_string(str, se->selector, shorthand);
case_end;
case_ast_node(se, ImplicitSelectorExpr, node);
str = gb_string_append_rune(str, '.');
- str = write_expr_to_string(str, se->selector);
+ str = write_expr_to_string(str, se->selector, shorthand);
case_end;
case_ast_node(se, SelectorCallExpr, node);
- str = write_expr_to_string(str, se->expr);
+ str = write_expr_to_string(str, se->expr, shorthand);
str = gb_string_appendc(str, "(");
ast_node(ce, CallExpr, se->call);
isize start = se->modified_call ? 1 : 0;
@@ -10302,86 +10459,86 @@ gbString write_expr_to_string(gbString str, Ast *node) {
if (i > start) {
str = gb_string_appendc(str, ", ");
}
- str = write_expr_to_string(str, arg);
+ str = write_expr_to_string(str, arg, shorthand);
}
str = gb_string_appendc(str, ")");
case_end;
case_ast_node(ta, TypeAssertion, node);
- str = write_expr_to_string(str, ta->expr);
+ str = write_expr_to_string(str, ta->expr, shorthand);
str = gb_string_appendc(str, ".(");
- str = write_expr_to_string(str, ta->type);
+ str = write_expr_to_string(str, ta->type, shorthand);
str = gb_string_append_rune(str, ')');
case_end;
case_ast_node(tc, TypeCast, node);
str = string_append_token(str, tc->token);
str = gb_string_append_rune(str, '(');
- str = write_expr_to_string(str, tc->type);
+ str = write_expr_to_string(str, tc->type, shorthand);
str = gb_string_append_rune(str, ')');
- str = write_expr_to_string(str, tc->expr);
+ str = write_expr_to_string(str, tc->expr, shorthand);
case_end;
case_ast_node(ac, AutoCast, node);
str = string_append_token(str, ac->token);
str = gb_string_append_rune(str, ' ');
- str = write_expr_to_string(str, ac->expr);
+ str = write_expr_to_string(str, ac->expr, shorthand);
case_end;
case_ast_node(ie, IndexExpr, node);
- str = write_expr_to_string(str, ie->expr);
+ str = write_expr_to_string(str, ie->expr, shorthand);
str = gb_string_append_rune(str, '[');
- str = write_expr_to_string(str, ie->index);
+ str = write_expr_to_string(str, ie->index, shorthand);
str = gb_string_append_rune(str, ']');
case_end;
case_ast_node(se, SliceExpr, node);
- str = write_expr_to_string(str, se->expr);
+ str = write_expr_to_string(str, se->expr, shorthand);
str = gb_string_append_rune(str, '[');
- str = write_expr_to_string(str, se->low);
+ str = write_expr_to_string(str, se->low, shorthand);
str = string_append_token(str, se->interval);
- str = write_expr_to_string(str, se->high);
+ str = write_expr_to_string(str, se->high, shorthand);
str = gb_string_append_rune(str, ']');
case_end;
case_ast_node(e, Ellipsis, node);
str = gb_string_appendc(str, "..");
- str = write_expr_to_string(str, e->expr);
+ str = write_expr_to_string(str, e->expr, shorthand);
case_end;
case_ast_node(fv, FieldValue, node);
- str = write_expr_to_string(str, fv->field);
+ str = write_expr_to_string(str, fv->field, shorthand);
str = gb_string_appendc(str, " = ");
- str = write_expr_to_string(str, fv->value);
+ str = write_expr_to_string(str, fv->value, shorthand);
case_end;
case_ast_node(ht, HelperType, node);
str = gb_string_appendc(str, "#type ");
- str = write_expr_to_string(str, ht->type);
+ str = write_expr_to_string(str, ht->type, shorthand);
case_end;
case_ast_node(ht, DistinctType, node);
str = gb_string_appendc(str, "distinct ");
- str = write_expr_to_string(str, ht->type);
+ str = write_expr_to_string(str, ht->type, shorthand);
case_end;
case_ast_node(ht, OpaqueType, node);
str = gb_string_appendc(str, "opaque ");
- str = write_expr_to_string(str, ht->type);
+ str = write_expr_to_string(str, ht->type, shorthand);
case_end;
case_ast_node(pt, PolyType, node);
str = gb_string_append_rune(str, '$');
- str = write_expr_to_string(str, pt->type);
+ str = write_expr_to_string(str, pt->type, shorthand);
if (pt->specialization != nullptr) {
str = gb_string_append_rune(str, '/');
- str = write_expr_to_string(str, pt->specialization);
+ str = write_expr_to_string(str, pt->specialization, shorthand);
}
case_end;
case_ast_node(pt, PointerType, node);
str = gb_string_append_rune(str, '^');
- str = write_expr_to_string(str, pt->type);
+ str = write_expr_to_string(str, pt->type, shorthand);
case_end;
case_ast_node(at, ArrayType, node);
@@ -10391,40 +10548,44 @@ gbString write_expr_to_string(gbString str, Ast *node) {
at->count->UnaryExpr.op.kind == Token_Question) {
str = gb_string_appendc(str, "?");
} else {
- str = write_expr_to_string(str, at->count);
+ str = write_expr_to_string(str, at->count, shorthand);
}
str = gb_string_append_rune(str, ']');
- str = write_expr_to_string(str, at->elem);
+ str = write_expr_to_string(str, at->elem, shorthand);
case_end;
case_ast_node(at, DynamicArrayType, node);
str = gb_string_appendc(str, "[dynamic]");
- str = write_expr_to_string(str, at->elem);
+ str = write_expr_to_string(str, at->elem, shorthand);
case_end;
case_ast_node(bf, BitFieldType, node);
str = gb_string_appendc(str, "bit_field ");
if (bf->align) {
str = gb_string_appendc(str, "#align ");
- str = write_expr_to_string(str, bf->align);
+ str = write_expr_to_string(str, bf->align, shorthand);
}
str = gb_string_appendc(str, "{");
- str = write_struct_fields_to_string(str, bf->fields);
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ str = write_struct_fields_to_string(str, bf->fields);
+ }
str = gb_string_appendc(str, "}");
case_end;
case_ast_node(bs, BitSetType, node);
str = gb_string_appendc(str, "bit_set[");
- str = write_expr_to_string(str, bs->elem);
+ str = write_expr_to_string(str, bs->elem, shorthand);
str = gb_string_appendc(str, "]");
case_end;
case_ast_node(mt, MapType, node);
str = gb_string_appendc(str, "map[");
- str = write_expr_to_string(str, mt->key);
+ str = write_expr_to_string(str, mt->key, shorthand);
str = gb_string_append_rune(str, ']');
- str = write_expr_to_string(str, mt->value);
+ str = write_expr_to_string(str, mt->value, shorthand);
case_end;
case_ast_node(f, Field, node);
@@ -10444,7 +10605,7 @@ gbString write_expr_to_string(gbString str, Ast *node) {
for_array(i, f->names) {
Ast *name = f->names[i];
if (i > 0) str = gb_string_appendc(str, ", ");
- str = write_expr_to_string(str, name);
+ str = write_expr_to_string(str, name, shorthand);
}
if (f->names.count > 0) {
if (f->type == nullptr && f->default_value != nullptr) {
@@ -10454,14 +10615,14 @@ gbString write_expr_to_string(gbString str, Ast *node) {
}
if (f->type != nullptr) {
str = gb_string_append_rune(str, ' ');
- str = write_expr_to_string(str, f->type);
+ str = write_expr_to_string(str, f->type, shorthand);
}
if (f->default_value != nullptr) {
if (f->type != nullptr) {
str = gb_string_append_rune(str, ' ');
}
str = gb_string_appendc(str, "= ");
- str = write_expr_to_string(str, f->default_value);
+ str = write_expr_to_string(str, f->default_value, shorthand);
}
case_end;
@@ -10487,7 +10648,7 @@ gbString write_expr_to_string(gbString str, Ast *node) {
for_array(i, f->list) {
if (i > 0) str = gb_string_appendc(str, ", ");
if (has_name) {
- str = write_expr_to_string(str, f->list[i]);
+ str = write_expr_to_string(str, f->list[i], shorthand);
} else {
ast_node(field, Field, f->list[i]);
@@ -10501,7 +10662,7 @@ gbString write_expr_to_string(gbString str, Ast *node) {
str = gb_string_appendc(str, "#c_vararg ");
}
- str = write_expr_to_string(str, field->type);
+ str = write_expr_to_string(str, field->type, shorthand);
}
}
case_end;
@@ -10516,7 +10677,7 @@ gbString write_expr_to_string(gbString str, Ast *node) {
break;
}
- str = write_expr_to_string(str, ce->proc);
+ str = write_expr_to_string(str, ce->proc, shorthand);
str = gb_string_appendc(str, "(");
for_array(i, ce->args) {
@@ -10524,7 +10685,7 @@ gbString write_expr_to_string(gbString str, Ast *node) {
if (i > 0) {
str = gb_string_appendc(str, ", ");
}
- str = write_expr_to_string(str, arg);
+ str = write_expr_to_string(str, arg, shorthand);
}
str = gb_string_appendc(str, ")");
case_end;
@@ -10533,17 +10694,36 @@ gbString write_expr_to_string(gbString str, Ast *node) {
str = gb_string_appendc(str, "typeid");
if (tt->specialization) {
str = gb_string_appendc(str, "/");
- str = write_expr_to_string(str, tt->specialization);
+ str = write_expr_to_string(str, tt->specialization, shorthand);
}
case_end;
case_ast_node(pt, ProcType, node);
str = gb_string_appendc(str, "proc(");
- str = write_expr_to_string(str, pt->params);
+ str = write_expr_to_string(str, pt->params, shorthand);
str = gb_string_appendc(str, ")");
if (pt->results != nullptr) {
str = gb_string_appendc(str, " -> ");
- str = write_expr_to_string(str, pt->results);
+
+ bool parens_needed = false;
+ if (pt->results && pt->results->kind == Ast_FieldList) {
+ for_array(i, pt->results->FieldList.list) {
+ Ast *field = pt->results->FieldList.list[i];
+ ast_node(f, Field, field);
+ if (f->names.count != 0) {
+ parens_needed = true;
+ break;
+ }
+ }
+ }
+
+ if (parens_needed) {
+ str = gb_string_append_rune(str, '(');
+ }
+ str = write_expr_to_string(str, pt->results, shorthand);
+ if (parens_needed) {
+ str = gb_string_append_rune(str, ')');
+ }
}
case_end;
@@ -10553,7 +10733,11 @@ gbString write_expr_to_string(gbString str, Ast *node) {
if (st->is_packed) str = gb_string_appendc(str, "#packed ");
if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union ");
str = gb_string_append_rune(str, '{');
- str = write_struct_fields_to_string(str, st->fields);
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ str = write_struct_fields_to_string(str, st->fields);
+ }
str = gb_string_append_rune(str, '}');
case_end;
@@ -10561,30 +10745,38 @@ gbString write_expr_to_string(gbString str, Ast *node) {
case_ast_node(st, UnionType, node);
str = gb_string_appendc(str, "union ");
str = gb_string_append_rune(str, '{');
- str = write_struct_fields_to_string(str, st->variants);
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ str = write_struct_fields_to_string(str, st->variants);
+ }
str = gb_string_append_rune(str, '}');
case_end;
case_ast_node(et, EnumType, node);
str = gb_string_appendc(str, "enum ");
if (et->base_type != nullptr) {
- str = write_expr_to_string(str, et->base_type);
+ str = write_expr_to_string(str, et->base_type, shorthand);
str = gb_string_append_rune(str, ' ');
}
str = gb_string_append_rune(str, '{');
- for_array(i, et->fields) {
- if (i > 0) {
- str = gb_string_appendc(str, ", ");
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ for_array(i, et->fields) {
+ if (i > 0) {
+ str = gb_string_appendc(str, ", ");
+ }
+ str = write_expr_to_string(str, et->fields[i], shorthand);
}
- str = write_expr_to_string(str, et->fields[i]);
}
str = gb_string_append_rune(str, '}');
case_end;
case_ast_node(rt, RelativeType, node);
- str = write_expr_to_string(str, rt->tag);
+ str = write_expr_to_string(str, rt->tag, shorthand);
str = gb_string_appendc(str, "" );
- str = write_expr_to_string(str, rt->type);
+ str = write_expr_to_string(str, rt->type, shorthand);
case_end;
@@ -10594,12 +10786,12 @@ gbString write_expr_to_string(gbString str, Ast *node) {
if (i > 0) {
str = gb_string_appendc(str, ", ");
}
- str = write_expr_to_string(str, ia->param_types[i]);
+ str = write_expr_to_string(str, ia->param_types[i], shorthand);
}
str = gb_string_appendc(str, ")");
if (ia->return_type != nullptr) {
str = gb_string_appendc(str, " -> ");
- str = write_expr_to_string(str, ia->return_type);
+ str = write_expr_to_string(str, ia->return_type, shorthand);
}
if (ia->has_side_effects) {
str = gb_string_appendc(str, " #side_effects");
@@ -10612,9 +10804,13 @@ gbString write_expr_to_string(gbString str, Ast *node) {
str = gb_string_appendc(str, inline_asm_dialect_strings[ia->dialect]);
}
str = gb_string_appendc(str, " {");
- str = write_expr_to_string(str, ia->asm_string);
- str = gb_string_appendc(str, ", ");
- str = write_expr_to_string(str, ia->constraints_string);
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ str = write_expr_to_string(str, ia->asm_string, shorthand);
+ str = gb_string_appendc(str, ", ");
+ str = write_expr_to_string(str, ia->constraints_string, shorthand);
+ }
str = gb_string_appendc(str, "}");
case_end;
}
@@ -10623,5 +10819,8 @@ gbString write_expr_to_string(gbString str, Ast *node) {
}
gbString expr_to_string(Ast *expression) {
- return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression);
+ return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression, false);
+}
+gbString expr_to_string_shorthand(Ast *expression) {
+ return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression, true);
}
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index e6902f6a3..8a41dd12b 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -15,7 +15,7 @@ bool is_divigering_stmt(Ast *stmt) {
return t->kind == Type_Proc && t->Proc.diverging;
}
-void check_stmt_list(CheckerContext *ctx, Array<Ast *> const &stmts, u32 flags) {
+void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) {
if (stmts.count == 0) {
return;
}
@@ -78,7 +78,7 @@ void check_stmt_list(CheckerContext *ctx, Array<Ast *> const &stmts, u32 flags)
}
}
-bool check_is_terminating_list(Array<Ast *> const &stmts, String const &label) {
+bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) {
// Iterate backwards
for (isize n = stmts.count-1; n >= 0; n--) {
Ast *stmt = stmts[n];
@@ -96,7 +96,7 @@ bool check_is_terminating_list(Array<Ast *> const &stmts, String const &label) {
return false;
}
-bool check_has_break_list(Array<Ast *> const &stmts, String const &label, bool implicit) {
+bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) {
for_array(i, stmts) {
Ast *stmt = stmts[i];
if (check_has_break(stmt, label, implicit)) {
@@ -641,8 +641,7 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Oper
TypeAndToken *found = map_get(seen, key);
if (found != nullptr) {
isize count = multi_map_count(seen, key);
- TypeAndToken *taps = gb_alloc_array(ctx->allocator, TypeAndToken, count);
- defer (gb_free(ctx->allocator, taps));
+ TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
multi_map_get_all(seen, key, taps);
for (isize i = 0; i < count; i++) {
@@ -859,8 +858,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
token.pos = ast_token(ss->body).pos;
token.string = str_lit("true");
- x.expr = gb_alloc_item(ctx->allocator, Ast);
- x.expr->kind = Ast_Ident;
+ x.expr = alloc_ast_node(nullptr, Ast_Ident);
x.expr->Ident.token = token;
}
@@ -1025,8 +1023,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
GB_ASSERT(is_type_enum(et));
auto fields = et->Enum.fields;
- auto unhandled = array_make<Entity *>(ctx->allocator, 0, fields.count);
- defer (array_free(&unhandled));
+ auto unhandled = array_make<Entity *>(temporary_allocator(), 0, fields.count);
for_array(i, fields) {
Entity *f = fields[i];
@@ -1265,8 +1262,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
GB_ASSERT(is_type_union(ut));
auto variants = ut->Union.variants;
- auto unhandled = array_make<Type *>(ctx->allocator, 0, variants.count);
- defer (array_free(&unhandled));
+ auto unhandled = array_make<Type *>(temporary_allocator(), 0, variants.count);
for_array(i, variants) {
Type *t = variants[i];
@@ -1433,12 +1429,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
return;
}
+
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
// an extra allocation
- auto lhs_operands = array_make<Operand>(ctx->allocator, lhs_count);
- auto rhs_operands = array_make<Operand>(ctx->allocator, 0, 2*lhs_count);
- defer (array_free(&lhs_operands));
- defer (array_free(&rhs_operands));
+ auto lhs_operands = array_make<Operand>(temporary_allocator(), lhs_count);
+ auto rhs_operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
for_array(i, as->lhs) {
if (is_blank_ident(as->lhs[i])) {
@@ -1462,8 +1457,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
}
- auto lhs_to_ignore = array_make<bool>(ctx->allocator, lhs_count);
- defer (array_free(&lhs_to_ignore));
+ auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count);
isize max = gb_min(lhs_count, rhs_count);
// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
@@ -1642,8 +1636,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} else if (operands.count != result_count) {
error(node, "Expected %td return values, got %td", result_count, operands.count);
} else {
- isize max_count = rs->results.count;
- for (isize i = 0; i < max_count; i++) {
+ for (isize i = 0; i < result_count; i++) {
Entity *e = pt->results->Tuple.variables[i];
check_assignment(ctx, &operands[i], e->type, str_lit("return statement"));
}
@@ -1878,7 +1871,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
DeclInfo *d = decl_info_of_entity(e);
GB_ASSERT(d == nullptr);
add_entity(ctx->checker, ctx->scope, e->identifier, e);
- d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl);
+ d = make_decl_info(ctx->scope, ctx->decl);
add_entity_and_decl_info(ctx, e->identifier, e, d);
}
@@ -2036,7 +2029,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
case_ast_node(vd, ValueDecl, node);
if (vd->is_mutable) {
- Entity **entities = gb_alloc_array(ctx->allocator, Entity *, vd->names.count);
+ Entity **entities = gb_alloc_array(permanent_allocator(), Entity *, vd->names.count);
isize entity_count = 0;
isize new_name_count = 0;
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 93040e493..ab69c89bc 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -1,3 +1,4 @@
+ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
t = base_type(t);
@@ -116,7 +117,7 @@ bool does_field_type_allow_using(Type *t) {
return false;
}
-void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Array<Ast *> const &params,
+void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Slice<Ast *> const &params,
isize init_field_capacity, Type *struct_type, String context) {
*fields = array_make<Entity *>(heap_allocator(), 0, init_field_capacity);
*tags = array_make<String>(heap_allocator(), 0, init_field_capacity);
@@ -388,7 +389,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
if (st->polymorphic_params != nullptr) {
ast_node(field_list, FieldList, st->polymorphic_params);
- Array<Ast *> params = field_list->list;
+ Slice<Ast *> params = field_list->list;
if (params.count != 0) {
isize variable_count = 0;
for_array(i, params) {
@@ -399,7 +400,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
}
}
- auto entities = array_make<Entity *>(ctx->allocator, 0, variable_count);
+ auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
for_array(i, params) {
Ast *param = params[i];
@@ -408,32 +409,50 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
}
ast_node(p, Field, param);
Ast *type_expr = p->type;
+ Ast *default_value = unparen_expr(p->default_value);
Type *type = nullptr;
bool is_type_param = false;
bool is_type_polymorphic_type = false;
- if (type_expr == nullptr) {
+ if (type_expr == nullptr && default_value == nullptr) {
error(param, "Expected a type for this parameter");
continue;
}
- if (type_expr->kind == Ast_Ellipsis) {
- type_expr = type_expr->Ellipsis.expr;
- error(param, "A polymorphic parameter cannot be variadic");
+
+ if (type_expr != nullptr) {
+ if (type_expr->kind == Ast_Ellipsis) {
+ type_expr = type_expr->Ellipsis.expr;
+ error(param, "A polymorphic parameter cannot be variadic");
+ }
+ if (type_expr->kind == Ast_TypeidType) {
+ is_type_param = true;
+ Type *specialization = nullptr;
+ if (type_expr->TypeidType.specialization != nullptr) {
+ Ast *s = type_expr->TypeidType.specialization;
+ specialization = check_type(ctx, s);
+ }
+ type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
+ } else {
+ type = check_type(ctx, type_expr);
+ if (is_type_polymorphic(type)) {
+ is_type_polymorphic_type = true;
+ }
+ }
}
- if (type_expr->kind == Ast_TypeidType) {
- is_type_param = true;
- Type *specialization = nullptr;
- if (type_expr->TypeidType.specialization != nullptr) {
- Ast *s = type_expr->TypeidType.specialization;
- specialization = check_type(ctx, s);
+
+ ParameterValue param_value = {};
+ if (default_value != nullptr) {
+ Type *out_type = nullptr;
+ param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
+ if (type == nullptr && out_type != nullptr) {
+ type = out_type;
}
- type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
- } else {
- type = check_type(ctx, type_expr);
- if (is_type_polymorphic(type)) {
- is_type_polymorphic_type = true;
+ if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
+ error(default_value, "Invalid parameter value");
+ param_value = {};
}
}
+
if (type == nullptr) {
error(params[i], "Invalid parameter type");
type = t_invalid;
@@ -471,7 +490,14 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
Token token = name->Ident.token;
if (poly_operands != nullptr) {
- Operand operand = (*poly_operands)[entities.count];
+ Operand operand = {};
+ operand.type = t_invalid;
+ if (entities.count < poly_operands->count) {
+ operand = (*poly_operands)[entities.count];
+ } else if (param_value.kind != ParameterValue_Invalid) {
+ operand.mode = Addressing_Constant;
+ operand.value = param_value.value;
+ }
if (is_type_param) {
if (is_type_polymorphic(base_type(operand.type))) {
is_polymorphic = true;
@@ -486,6 +512,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
}
if (e == nullptr) {
e = alloc_entity_constant(scope, token, operand.type, operand.value);
+ e->Constant.param_value = param_value;
}
}
} else {
@@ -493,7 +520,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
e = alloc_entity_type_name(scope, token, type);
e->TypeName.is_type_alias = true;
} else {
- e = alloc_entity_constant(scope, token, type, empty_exact_value);
+ e = alloc_entity_constant(scope, token, type, param_value.value);
+ e->Constant.param_value = param_value;
}
}
@@ -568,7 +596,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
Entity *using_index_expr = nullptr;
- auto variants = array_make<Type *>(ctx->allocator, 0, variant_count);
+ auto variants = array_make<Type *>(permanent_allocator(), 0, variant_count);
union_type->Union.scope = ctx->scope;
@@ -579,7 +607,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
if (ut->polymorphic_params != nullptr) {
ast_node(field_list, FieldList, ut->polymorphic_params);
- Array<Ast *> params = field_list->list;
+ Slice<Ast *> params = field_list->list;
if (params.count != 0) {
isize variable_count = 0;
for_array(i, params) {
@@ -590,7 +618,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
}
}
- auto entities = array_make<Entity *>(ctx->allocator, 0, variable_count);
+ auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
for_array(i, params) {
Ast *param = params[i];
@@ -599,29 +627,45 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
}
ast_node(p, Field, param);
Ast *type_expr = p->type;
+ Ast *default_value = unparen_expr(p->default_value);
Type *type = nullptr;
bool is_type_param = false;
bool is_type_polymorphic_type = false;
- if (type_expr == nullptr) {
+ if (type_expr == nullptr && default_value == nullptr) {
error(param, "Expected a type for this parameter");
continue;
}
- if (type_expr->kind == Ast_Ellipsis) {
- type_expr = type_expr->Ellipsis.expr;
- error(param, "A polymorphic parameter cannot be variadic");
+ if (type_expr != nullptr) {
+ if (type_expr->kind == Ast_Ellipsis) {
+ type_expr = type_expr->Ellipsis.expr;
+ error(param, "A polymorphic parameter cannot be variadic");
+ }
+ if (type_expr->kind == Ast_TypeidType) {
+ is_type_param = true;
+ Type *specialization = nullptr;
+ if (type_expr->TypeidType.specialization != nullptr) {
+ Ast *s = type_expr->TypeidType.specialization;
+ specialization = check_type(ctx, s);
+ }
+ type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
+ } else {
+ type = check_type(ctx, type_expr);
+ if (is_type_polymorphic(type)) {
+ is_type_polymorphic_type = true;
+ }
+ }
}
- if (type_expr->kind == Ast_TypeidType) {
- is_type_param = true;
- Type *specialization = nullptr;
- if (type_expr->TypeidType.specialization != nullptr) {
- Ast *s = type_expr->TypeidType.specialization;
- specialization = check_type(ctx, s);
+
+ ParameterValue param_value = {};
+ if (default_value != nullptr) {
+ Type *out_type = nullptr;
+ param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
+ if (type == nullptr && out_type != nullptr) {
+ type = out_type;
}
- type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
- } else {
- type = check_type(ctx, type_expr);
- if (is_type_polymorphic(type)) {
- is_type_polymorphic_type = true;
+ if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
+ error(default_value, "Invalid parameter value");
+ param_value = {};
}
}
@@ -662,7 +706,14 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
Token token = name->Ident.token;
if (poly_operands != nullptr) {
- Operand operand = (*poly_operands)[entities.count];
+ Operand operand = {};
+ operand.type = t_invalid;
+ if (entities.count < poly_operands->count) {
+ operand = (*poly_operands)[entities.count];
+ } else if (param_value.kind != ParameterValue_Invalid) {
+ operand.mode = Addressing_Constant;
+ operand.value = param_value.value;
+ }
if (is_type_param) {
GB_ASSERT(operand.mode == Addressing_Type ||
operand.mode == Addressing_Invalid);
@@ -675,6 +726,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
} else {
// GB_ASSERT(operand.mode == Addressing_Constant);
e = alloc_entity_constant(scope, token, operand.type, operand.value);
+ e->Constant.param_value = param_value;
}
} else {
if (is_type_param) {
@@ -682,6 +734,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
e->TypeName.is_type_alias = true;
} else {
e = alloc_entity_constant(scope, token, type, empty_exact_value);
+ e->Constant.param_value = param_value;
}
}
@@ -816,7 +869,7 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
enum_type->Enum.base_type = base_type;
enum_type->Enum.scope = ctx->scope;
- auto fields = array_make<Entity *>(ctx->allocator, 0, et->fields.count);
+ auto fields = array_make<Entity *>(permanent_allocator(), 0, et->fields.count);
Type *constant_type = enum_type;
if (named_type != nullptr) {
@@ -933,9 +986,9 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node)
ast_node(bft, BitFieldType, node);
GB_ASSERT(is_type_bit_field(bit_field_type));
- auto fields = array_make<Entity*>(ctx->allocator, 0, bft->fields.count);
- auto sizes = array_make<u32> (ctx->allocator, 0, bft->fields.count);
- auto offsets = array_make<u32> (ctx->allocator, 0, bft->fields.count);
+ auto fields = array_make<Entity*>(permanent_allocator(), 0, bft->fields.count);
+ auto sizes = array_make<u32> (permanent_allocator(), 0, bft->fields.count);
+ auto offsets = array_make<u32> (permanent_allocator(), 0, bft->fields.count);
scope_reserve(ctx->scope, bft->fields.count);
@@ -1337,7 +1390,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper
if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) {
if (show_error) {
- set_procedure_abi_types(ctx->allocator, poly_type);
+ set_procedure_abi_types(poly_type);
}
return poly_type;
}
@@ -1463,7 +1516,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
bool success = true;
ast_node(field_list, FieldList, _params);
- Array<Ast *> params = field_list->list;
+ Slice<Ast *> params = field_list->list;
if (params.count == 0) {
if (success_) *success_ = success;
@@ -1496,7 +1549,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
bool is_variadic = false;
isize variadic_index = -1;
bool is_c_vararg = false;
- auto variables = array_make<Entity *>(ctx->allocator, 0, variable_count);
+ auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
for_array(i, params) {
Ast *param = params[i];
if (param->kind != Ast_Field) {
@@ -1822,7 +1875,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
return nullptr;
}
ast_node(field_list, FieldList, _results);
- Array<Ast *> results = field_list->list;
+ Slice<Ast *> results = field_list->list;
if (results.count == 0) {
return nullptr;
@@ -1838,7 +1891,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
}
}
- auto variables = array_make<Entity *>(ctx->allocator, 0, variable_count);
+ auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
for_array(i, results) {
ast_node(field, Field, results[i]);
Ast *default_value = unparen_expr(field->default_value);
@@ -2209,6 +2262,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
return new_type;
}
+ if (is_type_proc(original_type)) {
+ // NOTE(bill): Force a cast to prevent a possible type cycle
+ return t_rawptr;
+ }
+
if (cc == ProcCC_None || cc == ProcCC_PureNone || cc == ProcCC_InlineAsm) {
return new_type;
}
@@ -2221,7 +2279,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
return new_type;
}
if (build_context.ODIN_ARCH == "amd64") {
- if (is_type_integer_128bit(original_type)) {
+ bool is_128 = is_type_integer_128bit(original_type);
+ if (!is_128 && is_type_bit_set(original_type) && type_size_of(original_type) == 16) {
+ // is_128 = true;
+ }
+ if (is_128) {
if (build_context.ODIN_OS == "windows") {
return alloc_type_simd_vector(2, t_u64);
} else {
@@ -2332,6 +2394,11 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
return new_type;
}
+ if (is_type_proc(single_type)) {
+ // NOTE(bill): Force a cast to prevent a possible type cycle
+ return t_rawptr;
+ }
+
if (is_type_simd_vector(single_type)) {
return new_type;
}
@@ -2445,16 +2512,21 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type
return false;
}
-void set_procedure_abi_types(gbAllocator allocator, Type *type) {
+void set_procedure_abi_types(Type *type) {
type = base_type(type);
if (type->kind != Type_Proc) {
return;
}
- if (type->Proc.abi_types_set) {
+ if (type->Proc.abi_types_set || type->flags & TypeFlag_InProcessOfCheckingABI) {
return;
}
+ gbAllocator allocator = permanent_allocator();
+
+ u32 flags = type->flags;
+ type->flags |= TypeFlag_InProcessOfCheckingABI;
+
type->Proc.abi_compat_params = array_make<Type *>(allocator, cast(isize)type->Proc.param_count);
for (i32 i = 0; i < type->Proc.param_count; i++) {
Entity *e = type->Proc.params->Tuple.variables[i];
@@ -2466,7 +2538,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
case ProcCC_Odin:
case ProcCC_Contextless:
case ProcCC_Pure:
- if (is_type_pointer(new_type) & !is_type_pointer(e->type)) {
+ if (is_type_pointer(new_type) && !is_type_pointer(e->type) && !is_type_proc(e->type)) {
e->flags |= EntityFlag_ImplicitReference;
}
break;
@@ -2474,7 +2546,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
if (build_context.ODIN_OS == "linux" ||
build_context.ODIN_OS == "darwin") {
- if (is_type_pointer(new_type) & !is_type_pointer(e->type)) {
+ if (is_type_pointer(new_type) & !is_type_pointer(e->type) && !is_type_proc(e->type)) {
e->flags |= EntityFlag_ByVal;
}
}
@@ -2484,13 +2556,13 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
for (i32 i = 0; i < type->Proc.param_count; i++) {
Entity *e = type->Proc.params->Tuple.variables[i];
if (e->kind == Entity_Variable) {
- set_procedure_abi_types(allocator, e->type);
+ set_procedure_abi_types(e->type);
}
}
for (i32 i = 0; i < type->Proc.result_count; i++) {
Entity *e = type->Proc.results->Tuple.variables[i];
if (e->kind == Entity_Variable) {
- set_procedure_abi_types(allocator, e->type);
+ set_procedure_abi_types(e->type);
}
}
@@ -2499,6 +2571,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type);
type->Proc.abi_types_set = true;
+ type->flags = flags;
}
// NOTE(bill): 'operands' is for generating non generic procedure type
@@ -2711,30 +2784,29 @@ void init_map_entry_type(Type *type) {
if (type->Map.entry_type != nullptr) return;
// NOTE(bill): The preload types may have not been set yet
- GB_ASSERT(t_map_key != nullptr);
- gbAllocator a = heap_allocator();
+ GB_ASSERT(t_map_hash != nullptr);
Type *entry_type = alloc_type_struct();
/*
struct {
- hash: __MapKey;
- next: int;
- key: Key;
- value: Value;
+ hash: runtime.Map_Hash,
+ next: int,
+ key: Key,
+ value: Value,
}
*/
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
- Scope *s = create_scope(builtin_pkg->scope, a);
+ Scope *s = create_scope(builtin_pkg->scope);
- auto fields = array_make<Entity *>(a, 0, 3);
- array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), t_map_key, false, 0, EntityState_Resolved));
- array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved));
- array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 2, EntityState_Resolved));
+ auto fields = array_make<Entity *>(permanent_allocator(), 0, 4);
+ array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, cast(i32)fields.count, EntityState_Resolved));
+ array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, cast(i32)fields.count, EntityState_Resolved));
+ array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, cast(i32)fields.count, EntityState_Resolved));
+ array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, cast(i32)fields.count, EntityState_Resolved));
entry_type->Struct.fields = fields;
- // type_set_offsets(a, entry_type);
type->Map.entry_type = entry_type;
}
@@ -2757,15 +2829,14 @@ void init_map_internal_types(Type *type) {
entries: [dynamic]EntryType;
}
*/
- gbAllocator a = heap_allocator();
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
- Scope *s = create_scope(builtin_pkg->scope, a);
+ Scope *s = create_scope(builtin_pkg->scope);
Type *hashes_type = alloc_type_slice(t_int);
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
- auto fields = array_make<Entity *>(a, 0, 2);
+ auto fields = array_make<Entity *>(permanent_allocator(), 0, 2);
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved));
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved));
@@ -2777,6 +2848,47 @@ void init_map_internal_types(Type *type) {
type->Map.lookup_result_type = make_optional_ok_type(value);
}
+void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
+ key = core_type(key);
+
+ if (is_type_cstring(key)) {
+ add_package_dependency(ctx, "runtime", "default_hasher_cstring");
+ } else if (is_type_string(key)) {
+ add_package_dependency(ctx, "runtime", "default_hasher_string");
+ } else if (!is_type_polymorphic(key)) {
+ if (!is_type_comparable(key)) {
+ return;
+ }
+
+ if (is_type_simple_compare(key)) {
+ i64 sz = type_size_of(key);
+ if (1 <= sz && sz <= 16) {
+ char buf[20] = {};
+ gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz);
+ add_package_dependency(ctx, "runtime", buf);
+ return;
+ } else {
+ add_package_dependency(ctx, "runtime", "default_hasher_n");
+ return;
+ }
+ }
+
+ if (key->kind == Type_Struct) {
+ add_package_dependency(ctx, "runtime", "default_hasher_n");
+ for_array(i, key->Struct.fields) {
+ Entity *field = key->Struct.fields[i];
+ add_map_key_type_dependencies(ctx, field->type);
+ }
+ } else if (key->kind == Type_EnumeratedArray) {
+ add_package_dependency(ctx, "runtime", "default_hasher_n");
+ add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem);
+ } else if (key->kind == Type_Array) {
+ add_package_dependency(ctx, "runtime", "default_hasher_n");
+ add_map_key_type_dependencies(ctx, key->Array.elem);
+ }
+ }
+}
+
void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
GB_ASSERT(type->kind == Type_Map);
ast_node(mt, MapType, node);
@@ -2793,16 +2905,16 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
gb_string_free(str);
}
}
+ if (type_size_of(key) == 0) {
+ gbString str = type_to_string(key);
+ error(node, "Invalid type of a key for a map of size 0, got '%s'", str);
+ gb_string_free(str);
+ }
type->Map.key = key;
type->Map.value = value;
- if (is_type_string(key)) {
- add_package_dependency(ctx, "runtime", "default_hash_string");
- } else {
- add_package_dependency(ctx, "runtime", "default_hash_ptr");
- }
-
+ add_map_key_type_dependencies(ctx, key);
init_core_map_type(ctx->checker);
init_map_internal_types(type);
@@ -2833,7 +2945,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
- scope = create_scope(ctx->scope, ctx->allocator);
+ scope = create_scope(ctx->scope);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
@@ -2866,7 +2978,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
- scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+ scope = create_scope(old_struct->Struct.scope->parent);
soa_struct->Struct.scope = scope;
for_array(i, old_struct->Struct.fields) {
@@ -2927,7 +3039,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
soa_struct->Struct.soa_count = 0;
soa_struct->Struct.is_polymorphic = true;
- scope = create_scope(ctx->scope, ctx->allocator);
+ scope = create_scope(ctx->scope);
soa_struct->Struct.scope = scope;
} else if (is_type_array(elem)) {
Type *old_array = base_type(elem);
@@ -2941,7 +3053,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
- scope = create_scope(ctx->scope, ctx->allocator);
+ scope = create_scope(ctx->scope);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
@@ -2977,7 +3089,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
- scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+ scope = create_scope(old_struct->Struct.scope->parent);
soa_struct->Struct.scope = scope;
for_array(i, old_struct->Struct.fields) {
@@ -3044,7 +3156,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
soa_struct->Struct.soa_count = 0;
soa_struct->Struct.is_polymorphic = true;
- scope = create_scope(ctx->scope, ctx->allocator);
+ scope = create_scope(ctx->scope);
soa_struct->Struct.scope = scope;
} else if (is_type_array(elem)) {
Type *old_array = base_type(elem);
@@ -3058,7 +3170,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
- scope = create_scope(ctx->scope, ctx->allocator);
+ scope = create_scope(ctx->scope);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
@@ -3093,7 +3205,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
- scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+ scope = create_scope(old_struct->Struct.scope->parent);
soa_struct->Struct.scope = scope;
for_array(i, old_struct->Struct.fields) {
diff --git a/src/checker.cpp b/src/checker.cpp
index 5c93e12b6..c1a107c15 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -187,8 +187,8 @@ void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
array_init (&d->labels, heap_allocator());
}
-DeclInfo *make_decl_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
- DeclInfo *d = gb_alloc_item(a, DeclInfo);
+DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {
+ DeclInfo *d = gb_alloc_item(permanent_allocator(), DeclInfo);
init_decl_info(d, scope, parent);
return d;
}
@@ -219,8 +219,8 @@ bool decl_info_has_init(DeclInfo *d) {
-Scope *create_scope(Scope *parent, gbAllocator allocator, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
- Scope *s = gb_alloc_item(allocator, Scope);
+Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
+ Scope *s = gb_alloc_item(permanent_allocator(), Scope);
s->parent = parent;
string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
ptr_set_init(&s->imported, heap_allocator(), 0);
@@ -244,7 +244,7 @@ Scope *create_scope_from_file(CheckerContext *c, AstFile *f) {
GB_ASSERT(f->pkg != nullptr);
GB_ASSERT(f->pkg->scope != nullptr);
- Scope *s = create_scope(f->pkg->scope, c->allocator);
+ Scope *s = create_scope(f->pkg->scope);
array_reserve(&s->delayed_imports, f->imports.count);
array_reserve(&s->delayed_directives, f->directive_count);
@@ -264,7 +264,7 @@ Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) {
decl_count += pkg->files[i]->decls.count;
}
isize init_elements_capacity = 2*decl_count;
- Scope *s = create_scope(builtin_pkg->scope, c->allocator, init_elements_capacity);
+ Scope *s = create_scope(builtin_pkg->scope, init_elements_capacity);
s->flags |= ScopeFlag_Pkg;
s->pkg = pkg;
@@ -324,7 +324,7 @@ void check_open_scope(CheckerContext *c, Ast *node) {
GB_ASSERT(node->kind == Ast_Invalid ||
is_ast_stmt(node) ||
is_ast_type(node));
- Scope *scope = create_scope(c->scope, c->allocator);
+ Scope *scope = create_scope(c->scope);
add_scope(c, node, scope);
switch (node->kind) {
case Ast_ProcType:
@@ -368,9 +368,14 @@ void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entit
if (e->kind == Entity_Label) {
continue;
}
- if (e->kind == Entity_Variable &&
- !(e->scope->flags&ScopeFlag_File)) {
- continue;
+ if (e->kind == Entity_Variable) {
+ if (e->scope->flags&ScopeFlag_File) {
+ // Global variables are file to access
+ } else if (e->flags&EntityFlag_Static) {
+ // Allow static/thread_local variables to be referenced
+ } else {
+ continue;
+ }
}
}
@@ -690,31 +695,33 @@ void add_global_type_entity(String name, Type *type) {
void init_universal(void) {
BuildContext *bc = &build_context;
+
// NOTE(bill): No need to free these
- gbAllocator a = heap_allocator();
+ // gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
builtin_pkg = gb_alloc_item(a, AstPackage);
builtin_pkg->name = str_lit("builtin");
builtin_pkg->kind = Package_Normal;
- builtin_pkg->scope = create_scope(nullptr, a);
- builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
+ builtin_pkg->scope = create_scope(nullptr);
+ builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
builtin_pkg->scope->pkg = builtin_pkg;
intrinsics_pkg = gb_alloc_item(a, AstPackage);
intrinsics_pkg->name = str_lit("intrinsics");
intrinsics_pkg->kind = Package_Normal;
- intrinsics_pkg->scope = create_scope(nullptr, a);
- intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
+ intrinsics_pkg->scope = create_scope(nullptr);
+ intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
intrinsics_pkg->scope->pkg = intrinsics_pkg;
config_pkg = gb_alloc_item(a, AstPackage);
config_pkg->name = str_lit("config");
config_pkg->kind = Package_Normal;
- config_pkg->scope = create_scope(nullptr, a);
- config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
+ config_pkg->scope = create_scope(nullptr);
+ config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
config_pkg->scope->pkg = config_pkg;
@@ -724,6 +731,18 @@ void init_universal(void) {
}
add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]);
+ {
+ void set_procedure_abi_types(Type *type);
+
+ Type *equal_args[2] = {t_rawptr, t_rawptr};
+ t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless);
+ set_procedure_abi_types(t_equal_proc);
+
+ Type *hasher_args[2] = {t_rawptr, t_uintptr};
+ t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless);
+ set_procedure_abi_types(t_hasher_proc);
+ }
+
// Constants
add_global_constant(str_lit("true"), t_untyped_bool, exact_value_bool(true));
add_global_constant(str_lit("false"), t_untyped_bool, exact_value_bool(false));
@@ -742,6 +761,7 @@ void init_universal(void) {
add_global_constant(str_lit("ODIN_DEFAULT_TO_NIL_ALLOCATOR"), t_untyped_bool, exact_value_bool(bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR));
add_global_constant(str_lit("ODIN_USE_LLVM_API"), t_untyped_bool, exact_value_bool(bc->use_llvm_api));
add_global_constant(str_lit("ODIN_NO_DYNAMIC_LITERALS"), t_untyped_bool, exact_value_bool(bc->no_dynamic_literals));
+ add_global_constant(str_lit("ODIN_TEST"), t_untyped_bool, exact_value_bool(bc->command_kind == Command_test));
// Builtin Procedures
@@ -837,6 +857,8 @@ void init_checker_info(CheckerInfo *i) {
array_init(&i->variable_init_order, a);
array_init(&i->required_foreign_imports_through_force, a);
array_init(&i->required_global_variables, a);
+ array_init(&i->testing_procedures, a, 0, 0);
+
i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions;
if (i->allow_identifier_uses) {
@@ -870,7 +892,6 @@ CheckerContext make_checker_context(Checker *c) {
CheckerContext ctx = c->init_ctx;
ctx.checker = c;
ctx.info = &c->info;
- ctx.allocator = c->allocator;
ctx.scope = builtin_pkg->scope;
ctx.pkg = builtin_pkg;
@@ -895,6 +916,7 @@ bool init_checker(Checker *c, Parser *parser) {
gbAllocator a = heap_allocator();
init_checker_info(&c->info);
+ c->info.checker = c;
array_init(&c->procs_to_check, a);
array_init(&c->procs_with_deferred_to_check, a);
@@ -904,8 +926,6 @@ bool init_checker(Checker *c, Parser *parser) {
isize total_token_count = c->parser->total_token_count;
isize arena_size = 2 * item_size * total_token_count;
- c->allocator = heap_allocator();
-
c->init_ctx = make_checker_context(c);
return true;
}
@@ -1502,11 +1522,10 @@ void add_min_dep_type_info(Checker *c, Type *t) {
ti_index = type_info_index(&c->info, t, false);
}
GB_ASSERT(ti_index >= 0);
- if (ptr_set_exists(set, ti_index)) {
+ if (ptr_set_update(set, ti_index)) {
// Type Already exists
return;
}
- ptr_set_add(set, ti_index);
// Add nested types
if (t->kind == Type_Named) {
@@ -1680,8 +1699,6 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
CheckerInfo *info = &c->info;
auto *set = &info->minimum_dependency_set;
- String name = entity->token.string;
-
if (entity->type != nullptr &&
is_type_polymorphic(entity->type)) {
@@ -1691,12 +1708,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
}
}
- if (ptr_set_exists(set, entity)) {
+ if (ptr_set_update(set, entity)) {
return;
}
-
- ptr_set_add(set, entity);
DeclInfo *decl = decl_info_of_entity(entity);
if (decl == nullptr) {
return;
@@ -1715,16 +1730,15 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
if (fl != nullptr) {
GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
(fl->flags&EntityFlag_Used),
- "%.*s", LIT(name));
+ "%.*s", LIT(entity->token.string));
add_dependency_to_set(c, fl);
}
- }
- if (e->kind == Entity_Variable && e->Variable.is_foreign) {
+ } else if (e->kind == Entity_Variable && e->Variable.is_foreign) {
Entity *fl = e->Variable.foreign_library;
if (fl != nullptr) {
GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
(fl->flags&EntityFlag_Used),
- "%.*s", LIT(name));
+ "%.*s", LIT(entity->token.string));
add_dependency_to_set(c, fl);
}
}
@@ -1733,7 +1747,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
void generate_minimum_dependency_set(Checker *c, Entity *start) {
- ptr_set_init(&c->info.minimum_dependency_set, heap_allocator());
+ isize entity_count = c->info.entities.count;
+ isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
+
+ ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap);
ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator());
String required_runtime_entities[] = {
@@ -1769,6 +1786,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("memcpy"),
str_lit("memmove"),
+ str_lit("memory_equal"),
str_lit("memory_compare"),
str_lit("memory_compare_zero"),
@@ -1842,7 +1860,68 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
add_dependency_to_set(c, e);
}
- add_dependency_to_set(c, start);
+ for_array(i, c->info.entities) {
+ Entity *e = c->info.entities[i];
+ switch (e->kind) {
+ case Entity_Variable:
+ if (e->Variable.is_export) {
+ add_dependency_to_set(c, e);
+ }
+ break;
+ case Entity_Procedure:
+ if (e->Procedure.is_export) {
+ add_dependency_to_set(c, e);
+ }
+ break;
+ }
+ }
+
+ if (build_context.command_kind == Command_test) {
+ AstPackage *pkg = c->info.init_package;
+ Scope *s = pkg->scope;
+ for_array(i, s->elements.entries) {
+ Entity *e = s->elements.entries[i].value;
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+
+ if (e->file == nullptr || !e->file->is_test) {
+ continue;
+ }
+
+ String name = e->token.string;
+ String prefix = str_lit("test_");
+
+ if (!string_starts_with(name, prefix)) {
+ continue;
+ }
+
+ bool is_tester = false;
+ if (name != prefix) {
+ is_tester = true;
+ } else {
+ error(e->token, "Invalid testing procedure name: %.*s", LIT(name));
+ }
+
+ Type *t = base_type(e->type);
+ GB_ASSERT(t->kind == Type_Proc);
+ if (t->Proc.param_count == 0 && t->Proc.result_count == 0) {
+ // Good
+ } else {
+ gbString str = type_to_string(t);
+ error(e->token, "Testing procedures must have a signature type of proc(), got %s", str);
+ gb_string_free(str);
+ is_tester = false;
+ }
+
+ if (is_tester) {
+ add_dependency_to_set(c, e);
+ array_add(&c->info.testing_procedures, e);
+ }
+ }
+ } else {
+ add_dependency_to_set(c, start);
+ }
}
bool is_entity_a_dependency(Entity *e) {
@@ -1881,19 +1960,17 @@ void add_entity_dependency_from_procedure_parameters(Map<EntityGraphNode *> *M,
}
-Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
+Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) {
#define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
- gbAllocator a = heap_allocator();
-
Map<EntityGraphNode *> M = {}; // Key: Entity *
- map_init(&M, a, info->entities.count);
+ map_init(&M, allocator, info->entities.count);
defer (map_destroy(&M));
for_array(i, info->entities) {
Entity *e = info->entities[i];
DeclInfo *d = e->decl_info;
if (is_entity_a_dependency(e)) {
- EntityGraphNode *n = gb_alloc_item(a, EntityGraphNode);
+ EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode);
n->entity = e;
map_set(&M, hash_pointer(e), n);
}
@@ -1928,7 +2005,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
// This means that the entity graph node set will have to be thread safe
TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
- auto G = array_make<EntityGraphNode *>(a, 0, M.entries.count);
+ auto G = array_make<EntityGraphNode *>(allocator, 0, M.entries.count);
for_array(i, M.entries) {
auto *entry = &M.entries[i];
@@ -1949,17 +2026,27 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
EntityGraphNode *s = n->succ.entries[k].ptr;
// Ignore self-cycles
if (s != n) {
+ if (p->entity->kind == Entity_Procedure &&
+ s->entity->kind == Entity_Procedure) {
+ // NOTE(bill, 2020-11-15): Only care about variable initialization ordering
+ // TODO(bill): This is probably wrong!!!!
+ continue;
+ }
+ // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the
+ // the time to process
+
entity_graph_node_set_add(&p->succ, s);
entity_graph_node_set_add(&s->pred, p);
// Remove edge to 'n'
entity_graph_node_set_remove(&s->pred, n);
}
}
+
// Remove edge to 'n'
entity_graph_node_set_remove(&p->succ, n);
}
}
- } else {
+ } else if (e->kind == Entity_Variable) {
array_add(&G, n);
}
}
@@ -1972,6 +2059,28 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
GB_ASSERT(n->dep_count >= 0);
}
+ // f64 succ_count = 0.0;
+ // f64 pred_count = 0.0;
+ // f64 succ_capacity = 0.0;
+ // f64 pred_capacity = 0.0;
+ // f64 succ_max = 0.0;
+ // f64 pred_max = 0.0;
+ // for_array(i, G) {
+ // EntityGraphNode *n = G[i];
+ // succ_count += n->succ.entries.count;
+ // pred_count += n->pred.entries.count;
+ // succ_capacity += n->succ.entries.capacity;
+ // pred_capacity += n->pred.entries.capacity;
+
+ // succ_max = gb_max(succ_max, n->succ.entries.capacity);
+ // pred_max = gb_max(pred_max, n->pred.entries.capacity);
+
+ // }
+ // f64 count = cast(f64)G.count;
+ // gb_printf_err(">>>count pred: %f succ: %f\n", pred_count/count, succ_count/count);
+ // gb_printf_err(">>>capacity pred: %f succ: %f\n", pred_capacity/count, succ_capacity/count);
+ // gb_printf_err(">>>max pred: %f succ: %f\n", pred_max, succ_max);
+
return G;
#undef TIME_SECTION
@@ -2088,9 +2197,9 @@ void init_core_type_info(Checker *c) {
t_type_info_enum_value = type_info_enum_value->type;
t_type_info_enum_value_ptr = alloc_type_pointer(t_type_info_enum_value);
- GB_ASSERT(tis->fields.count == 4);
+ GB_ASSERT(tis->fields.count == 5);
- Entity *type_info_variant = tis->fields[3];
+ Entity *type_info_variant = tis->fields[4];
Type *tiv_type = type_info_variant->type;
GB_ASSERT(is_type_union(tiv_type));
@@ -2186,14 +2295,14 @@ void init_core_source_code_location(Checker *c) {
}
void init_core_map_type(Checker *c) {
- if (t_map_key == nullptr) {
- Entity *e = find_core_entity(c, str_lit("Map_Key"));
+ if (t_map_hash == nullptr) {
+ Entity *e = find_core_entity(c, str_lit("Map_Hash"));
if (e->state == EntityState_Unresolved) {
auto ctx = c->init_ctx;
check_entity_decl(&ctx, e, nullptr, nullptr);
}
- t_map_key = e->type;
- GB_ASSERT(t_map_key != nullptr);
+ t_map_hash = e->type;
+ GB_ASSERT(t_map_hash != nullptr);
}
if (t_map_header == nullptr) {
@@ -2579,7 +2688,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
if (valid && build_context.use_llvm_api) {
if (ac->atom_op_table == nullptr) {
- ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+ ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_index_get] = e;
}
@@ -2638,7 +2747,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
if (valid && build_context.use_llvm_api) {
if (ac->atom_op_table == nullptr) {
- ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+ ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_index_set] = e;
}
@@ -2720,7 +2829,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
if (valid && build_context.use_llvm_api) {
if (ac->atom_op_table == nullptr) {
- ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+ ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_slice] = e;
}
@@ -2809,7 +2918,7 @@ void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, De
}
-isize get_total_value_count(Array<Ast *> const &values) {
+isize get_total_value_count(Slice<Ast *> const &values) {
isize count = 0;
for_array(i, values) {
Type *t = type_of_expr(values[i]);
@@ -2967,8 +3076,8 @@ void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array<Ast *> *attr
}
void check_collect_value_decl(CheckerContext *c, Ast *decl) {
- if (decl->been_handled) return;
- decl->been_handled = true;
+ if (decl->state_flags & StateFlag_BeenHandled) return;
+ decl->state_flags |= StateFlag_BeenHandled;
ast_node(vd, ValueDecl, decl);
@@ -3023,7 +3132,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
} else {
entity_visibility_kind = kind;
}
- array_unordered_remove(elems, j);
+ slice_unordered_remove(elems, j);
j -= 1;
}
}
@@ -3072,7 +3181,10 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
Ast *init_expr = value;
- DeclInfo *d = make_decl_info(heap_allocator(), c->scope, c->decl);
+ DeclInfo *d = make_decl_info(c->scope, c->decl);
+ d->decl_node = decl;
+ d->comment = vd->comment;
+ d->docs = vd->docs;
d->entity = e;
d->type_expr = vd->type;
d->init_expr = init_expr;
@@ -3100,9 +3212,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
Token token = name->Ident.token;
Ast *fl = c->foreign_context.curr_library;
- DeclInfo *d = make_decl_info(c->allocator, c->scope, c->decl);
Entity *e = nullptr;
+ DeclInfo *d = make_decl_info(c->scope, c->decl);
+ d->decl_node = decl;
+ d->comment = vd->comment;
+ d->docs = vd->docs;
d->attributes = vd->attributes;
d->type_expr = vd->type;
d->init_expr = init;
@@ -3186,8 +3301,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
- if (decl->been_handled) return;
- decl->been_handled = true;
+ if (decl->state_flags & StateFlag_BeenHandled) return;
+ decl->state_flags |= StateFlag_BeenHandled;
ast_node(fb, ForeignBlockDecl, decl);
Ast *foreign_library = fb->foreign_library;
@@ -3207,7 +3322,7 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
}
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
-void check_collect_entities(CheckerContext *c, Array<Ast *> const &nodes) {
+void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
for_array(decl_index, nodes) {
Ast *decl = nodes[decl_index];
if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) {
@@ -3522,11 +3637,9 @@ struct ImportPathItem {
Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited) {
Array<ImportPathItem> empty_path = {};
- if (ptr_set_exists(visited, start)) {
+ if (ptr_set_update(visited, start)) {
return empty_path;
}
- ptr_set_add(visited, start);
-
String path = start->fullpath;
AstPackage **found = string_map_get(&c->info.packages, path);
@@ -3571,8 +3684,8 @@ Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage
}
#endif
void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
- if (decl->been_handled) return;
- decl->been_handled = true;
+ if (decl->state_flags & StateFlag_BeenHandled) return;
+ decl->state_flags |= StateFlag_BeenHandled;
ast_node(id, ImportDecl, decl);
Token token = id->relpath;
@@ -3612,10 +3725,8 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
GB_ASSERT(scope->flags&ScopeFlag_Pkg);
- if (ptr_set_exists(&parent_scope->imported, scope)) {
+ if (ptr_set_update(&parent_scope->imported, scope)) {
// error(token, "Multiple import of the same file within this scope");
- } else {
- ptr_set_add(&parent_scope->imported, scope);
}
String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
@@ -3686,8 +3797,8 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
}
void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
- if (decl->been_handled) return;
- decl->been_handled = true;
+ if (decl->state_flags & StateFlag_BeenHandled) return;
+ decl->state_flags |= StateFlag_BeenHandled;
ast_node(fl, ForeignImportDecl, decl);
@@ -3738,7 +3849,7 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
}
}
-bool collect_checked_packages_from_decl_list(Checker *c, Array<Ast *> const &decls) {
+bool collect_checked_packages_from_decl_list(Checker *c, Slice<Ast *> const &decls) {
bool new_files = false;
for_array(i, decls) {
Ast *decl = decls[i];
@@ -3760,7 +3871,7 @@ bool collect_checked_packages_from_decl_list(Checker *c, Array<Ast *> const &dec
}
// Returns true if a new package is present
-bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls);
+bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls);
bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws);
bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) {
@@ -3835,7 +3946,7 @@ bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) {
return false;
}
-bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls) {
+bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
if (collect_checked_packages_from_decl_list(ctx->checker, decls)) {
@@ -3968,10 +4079,9 @@ void check_import_entities(Checker *c) {
if (pkg == nullptr) {
continue;
}
- if (ptr_set_exists(&emitted, pkg)) {
+ if (ptr_set_update(&emitted, pkg)) {
continue;
}
- ptr_set_add(&emitted, pkg);
array_add(&package_order, n);
}
@@ -4162,7 +4272,7 @@ void calculate_global_init_order(Checker *c) {
CheckerInfo *info = &c->info;
TIME_SECTION("calculate_global_init_order: generate entity dependency graph");
- Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info);
+ Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info, heap_allocator());
defer ({
for_array(i, dep_graph) {
entity_graph_node_destroy(dep_graph[i], heap_allocator());
@@ -4214,11 +4324,9 @@ void calculate_global_init_order(Checker *c) {
// if (!decl_info_has_init(d)) {
// continue;
// }
- if (ptr_set_exists(&emitted, d)) {
+ if (ptr_set_update(&emitted, d)) {
continue;
}
- ptr_set_add(&emitted, d);
-
array_add(&info->variable_init_order, d);
}
@@ -4299,10 +4407,11 @@ void check_parsed_files(Checker *c) {
for_array(i, c->parser->packages) {
AstPackage *p = c->parser->packages[i];
Scope *scope = create_scope_from_package(&c->init_ctx, p);
- p->decl_info = make_decl_info(c->allocator, scope, c->init_ctx.decl);
+ p->decl_info = make_decl_info(scope, c->init_ctx.decl);
string_map_set(&c->info.packages, p->fullpath, p);
if (scope->flags&ScopeFlag_Init) {
+ c->info.init_package = p;
c->info.init_scope = scope;
}
if (p->kind == Package_Runtime) {
@@ -4572,7 +4681,7 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("check entry point");
- if (build_context.build_mode == BuildMode_Executable) {
+ if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
Scope *s = c->info.init_scope;
GB_ASSERT(s != nullptr);
GB_ASSERT(s->flags&ScopeFlag_Init);
diff --git a/src/checker.hpp b/src/checker.hpp
index 3d21a4cec..168c00d33 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -45,7 +45,7 @@ enum StmtFlag {
Stmt_TypeSwitch = 1<<4,
- Stmt_CheckScopeDecls = 1<<5,
+ Stmt_CheckScopeDecls = 1<<5,
};
enum BuiltinProcPkg {
@@ -132,6 +132,7 @@ struct DeclInfo {
Entity *entity;
+ Ast * decl_node;
Ast * type_expr;
Ast * init_expr;
Array<Ast *> attributes;
@@ -140,6 +141,9 @@ struct DeclInfo {
bool is_using;
bool where_clauses_evaluated;
+ CommentGroup *comment;
+ CommentGroup *docs;
+
PtrSet<Entity *> deps;
PtrSet<Type *> type_info_deps;
Array<BlockLabel> labels;
@@ -160,12 +164,13 @@ struct ProcInfo {
enum ScopeFlag : i32 {
- ScopeFlag_Pkg = 1<<1,
- ScopeFlag_Global = 1<<2,
- ScopeFlag_File = 1<<3,
- ScopeFlag_Init = 1<<4,
- ScopeFlag_Proc = 1<<5,
- ScopeFlag_Type = 1<<6,
+ ScopeFlag_Pkg = 1<<1,
+ ScopeFlag_Builtin = 1<<2,
+ ScopeFlag_Global = 1<<3,
+ ScopeFlag_File = 1<<4,
+ ScopeFlag_Init = 1<<5,
+ ScopeFlag_Proc = 1<<6,
+ ScopeFlag_Type = 1<<7,
ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes
@@ -247,8 +252,12 @@ struct AtomOpMapEntry {
};
+struct CheckerContext;
+
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
+ Checker *checker;
+
Map<ExprInfo> untyped; // Key: Ast * | Expression -> ExprInfo
// NOTE(bill): This needs to be a map and not on the Ast
// as it needs to be iterated across
@@ -268,6 +277,7 @@ struct CheckerInfo {
AstPackage * builtin_package;
AstPackage * runtime_package;
+ AstPackage * init_package;
Scope * init_scope;
Entity * entry_point;
PtrSet<Entity *> minimum_dependency_set;
@@ -278,6 +288,7 @@ struct CheckerInfo {
Map<AtomOpMapEntry> atom_op_map; // Key: Ast *
+ Array<Entity *> testing_procedures;
bool allow_identifier_uses;
Array<Ast *> identifier_uses; // only used by 'odin query'
@@ -301,7 +312,6 @@ struct CheckerContext {
ProcCallingConvention curr_proc_calling_convention;
bool in_proc_sig;
ForeignContext foreign_context;
- gbAllocator allocator;
CheckerTypePath *type_path;
isize type_level; // TODO(bill): Actually handle correctly
@@ -317,6 +327,7 @@ struct CheckerContext {
bool no_polymorphic_errors;
bool hide_polymorphic_errors;
bool in_polymorphic_specialization;
+ bool allow_arrow_right_selector_expr;
Scope * polymorphic_scope;
Ast *assignment_lhs_hint;
@@ -331,14 +342,11 @@ struct Checker {
Array<Entity *> procs_with_deferred_to_check;
CheckerContext *curr_ctx;
- gbAllocator allocator;
CheckerContext init_ctx;
};
-
-
gb_global AstPackage *builtin_pkg = nullptr;
gb_global AstPackage *intrinsics_pkg = nullptr;
gb_global AstPackage *config_pkg = nullptr;
@@ -387,7 +395,7 @@ void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
-void check_collect_entities(CheckerContext *c, Array<Ast *> const &nodes);
+void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index d0e009cc0..b2157b3c1 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -183,6 +183,9 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_field_index_of,
+ BuiltinProc_type_equal_proc,
+ BuiltinProc_type_hasher_proc,
+
BuiltinProc__type_end,
@@ -367,5 +370,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
};
diff --git a/src/common.cpp b/src/common.cpp
index 350127e1e..0147f27d5 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -56,6 +56,14 @@ gb_inline isize align_formula_isize(isize size, isize align) {
}
return size;
}
+gb_inline void *align_formula_ptr(void *ptr, isize align) {
+ if (align > 0) {
+ uintptr result = (cast(uintptr)ptr) + align-1;
+ return (void *)(result - result%align);
+ }
+ return ptr;
+}
+
GB_ALLOCATOR_PROC(heap_allocator_proc);
@@ -373,13 +381,16 @@ typedef struct Arena {
gbAllocator backing;
isize block_size;
gbMutex mutex;
-
isize total_used;
+ bool use_mutex;
} Arena;
#define ARENA_MIN_ALIGNMENT 16
#define ARENA_DEFAULT_BLOCK_SIZE (8*1024*1024)
+
+gb_global Arena permanent_arena = {};
+
void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) {
arena->backing = backing;
arena->block_size = block_size;
@@ -388,8 +399,9 @@ void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAUL
}
void arena_grow(Arena *arena, isize min_size) {
- // gb_mutex_lock(&arena->mutex);
- // defer (gb_mutex_unlock(&arena->mutex));
+ if (arena->use_mutex) {
+ gb_mutex_lock(&arena->mutex);
+ }
isize size = gb_max(arena->block_size, min_size);
size = ALIGN_UP(size, ARENA_MIN_ALIGNMENT);
@@ -399,11 +411,16 @@ void arena_grow(Arena *arena, isize min_size) {
GB_ASSERT(arena->ptr == ALIGN_DOWN_PTR(arena->ptr, ARENA_MIN_ALIGNMENT));
arena->end = arena->ptr + size;
array_add(&arena->blocks, arena->ptr);
+
+ if (arena->use_mutex) {
+ gb_mutex_unlock(&arena->mutex);
+ }
}
void *arena_alloc(Arena *arena, isize size, isize alignment) {
- // gb_mutex_lock(&arena->mutex);
- // defer (gb_mutex_unlock(&arena->mutex));
+ if (arena->use_mutex) {
+ gb_mutex_lock(&arena->mutex);
+ }
arena->total_used += size;
@@ -419,12 +436,17 @@ void *arena_alloc(Arena *arena, isize size, isize alignment) {
GB_ASSERT(arena->ptr <= arena->end);
GB_ASSERT(ptr == ALIGN_DOWN_PTR(ptr, align));
// zero_size(ptr, size);
+
+ if (arena->use_mutex) {
+ gb_mutex_unlock(&arena->mutex);
+ }
return ptr;
}
void arena_free_all(Arena *arena) {
- // gb_mutex_lock(&arena->mutex);
- // defer (gb_mutex_unlock(&arena->mutex));
+ if (arena->use_mutex) {
+ gb_mutex_lock(&arena->mutex);
+ }
for_array(i, arena->blocks) {
gb_free(arena->backing, arena->blocks[i]);
@@ -432,8 +454,11 @@ void arena_free_all(Arena *arena) {
array_clear(&arena->blocks);
arena->ptr = nullptr;
arena->end = nullptr;
-}
+ if (arena->use_mutex) {
+ gb_mutex_unlock(&arena->mutex);
+ }
+}
@@ -460,7 +485,14 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
// GB_PANIC("gbAllocation_Free not supported");
break;
case gbAllocation_Resize:
- GB_PANIC("gbAllocation_Resize: not supported");
+ if (size == 0) {
+ ptr = nullptr;
+ } else if (size <= old_size) {
+ ptr = old_memory;
+ } else {
+ ptr = arena_alloc(arena, size, alignment);
+ gb_memmove(ptr, old_memory, old_size);
+ }
break;
case gbAllocation_FreeAll:
arena_free_all(arena);
@@ -471,6 +503,97 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
}
+gbAllocator permanent_allocator() {
+ return arena_allocator(&permanent_arena);
+ // return heap_allocator();
+}
+
+
+
+struct Temp_Allocator {
+ u8 *data;
+ isize len;
+ isize curr_offset;
+ gbAllocator backup_allocator;
+ Array<void *> leaked_allocations;
+};
+
+gb_global Temp_Allocator temporary_allocator_data = {};
+
+void temp_allocator_init(Temp_Allocator *s, isize size) {
+ s->backup_allocator = heap_allocator();
+ s->data = cast(u8 *)gb_alloc_align(s->backup_allocator, size, 16);
+ s->curr_offset = 0;
+ s->leaked_allocations.allocator = s->backup_allocator;
+}
+
+void *temp_allocator_alloc(Temp_Allocator *s, isize size, isize alignment) {
+ size = align_formula_isize(size, alignment);
+ if (s->curr_offset+size <= s->len) {
+ u8 *start = s->data;
+ u8 *ptr = start + s->curr_offset;
+ ptr = cast(u8 *)align_formula_ptr(ptr, alignment);
+ // assume memory is zero
+
+ isize offset = ptr - start;
+ s->curr_offset = offset + size;
+ return ptr;
+ } else if (size <= s->len) {
+ u8 *start = s->data;
+ u8 *ptr = cast(u8 *)align_formula_ptr(start, alignment);
+ // assume memory is zero
+
+ isize offset = ptr - start;
+ s->curr_offset = offset + size;
+ return ptr;
+ }
+
+ void *ptr = gb_alloc_align(s->backup_allocator, size, alignment);
+ array_add(&s->leaked_allocations, ptr);
+ return ptr;
+}
+
+void temp_allocator_free_all(Temp_Allocator *s) {
+ s->curr_offset = 0;
+ for_array(i, s->leaked_allocations) {
+ gb_free(s->backup_allocator, s->leaked_allocations[i]);
+ }
+ array_clear(&s->leaked_allocations);
+ gb_zero_size(s->data, s->len);
+}
+
+GB_ALLOCATOR_PROC(temp_allocator_proc) {
+ void *ptr = nullptr;
+ Temp_Allocator *s = cast(Temp_Allocator *)allocator_data;
+ GB_ASSERT_NOT_NULL(s);
+
+ switch (type) {
+ case gbAllocation_Alloc:
+ return temp_allocator_alloc(s, size, alignment);
+ case gbAllocation_Free:
+ break;
+ case gbAllocation_Resize:
+ if (size == 0) {
+ ptr = nullptr;
+ } else if (size <= old_size) {
+ ptr = old_memory;
+ } else {
+ ptr = temp_allocator_alloc(s, size, alignment);
+ gb_memmove(ptr, old_memory, old_size);
+ }
+ break;
+ case gbAllocation_FreeAll:
+ temp_allocator_free_all(s);
+ break;
+ }
+
+ return ptr;
+}
+
+
+gbAllocator temporary_allocator() {
+ return {temp_allocator_proc, &temporary_allocator_data};
+}
diff --git a/src/docs.cpp b/src/docs.cpp
index 3bc0da649..aa1b89560 100644
--- a/src/docs.cpp
+++ b/src/docs.cpp
@@ -1,22 +1,102 @@
// Generates Documentation
-gbString expr_to_string(Ast *expression);
+gb_global int print_entity_kind_ordering[Entity_Count] = {
+ /*Invalid*/ -1,
+ /*Constant*/ 0,
+ /*Variable*/ 1,
+ /*TypeName*/ 4,
+ /*Procedure*/ 2,
+ /*ProcGroup*/ 3,
+ /*Builtin*/ -1,
+ /*ImportName*/ -1,
+ /*LibraryName*/ -1,
+ /*Nil*/ -1,
+ /*Label*/ -1,
+};
+gb_global char const *print_entity_names[Entity_Count] = {
+ /*Invalid*/ "",
+ /*Constant*/ "constants",
+ /*Variable*/ "variables",
+ /*TypeName*/ "types",
+ /*Procedure*/ "procedures",
+ /*ProcGroup*/ "proc_group",
+ /*Builtin*/ "",
+ /*ImportName*/ "import names",
+ /*LibraryName*/ "library names",
+ /*Nil*/ "",
+ /*Label*/ "",
+};
-String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
+
+GB_COMPARE_PROC(cmp_entities_for_printing) {
+ GB_ASSERT(a != nullptr);
+ GB_ASSERT(b != nullptr);
+ Entity *x = *cast(Entity **)a;
+ Entity *y = *cast(Entity **)b;
+ int res = 0;
+ res = string_compare(x->pkg->name, y->pkg->name);
+ if (res != 0) {
+ return res;
+ }
+ int ox = print_entity_kind_ordering[x->kind];
+ int oy = print_entity_kind_ordering[y->kind];
+ res = ox - oy;
+ if (res != 0) {
+ return res;
+ }
+ res = string_compare(x->token.string, y->token.string);
+ return res;
+}
+
+GB_COMPARE_PROC(cmp_ast_package_by_name) {
+ GB_ASSERT(a != nullptr);
+ GB_ASSERT(b != nullptr);
+ AstPackage *x = *cast(AstPackage **)a;
+ AstPackage *y = *cast(AstPackage **)b;
+ return string_compare(x->name, y->name);
+}
+
+void print_doc_line(i32 indent, char const *fmt, ...) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ va_list va;
+ va_start(va, fmt);
+ gb_printf_va(fmt, va);
+ va_end(va);
+ gb_printf("\n");
+}
+void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ va_list va;
+ va_start(va, fmt);
+ gb_printf_va(fmt, va);
+ va_end(va);
+}
+
+bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
+ if (g == nullptr) {
+ return false;
+ }
isize len = 0;
- for_array(i, g.list) {
- String comment = g.list[i].string;
+ for_array(i, g->list) {
+ String comment = g->list[i].string;
len += comment.len;
len += 1; // for \n
}
- if (len == 0) {
- return make_string(nullptr, 0);
+ if (len <= g->list.count) {
+ return false;
}
- u8 *text = gb_alloc_array(a, u8, len+1);
- len = 0;
- for_array(i, g.list) {
- String comment = g.list[i].string;
+ isize count = 0;
+ for_array(i, g->list) {
+ String comment = g->list[i].string;
+ String original_comment = comment;
+
+ bool slash_slash = comment[1] == '/';
+ bool slash_star = comment[1] == '*';
if (comment[1] == '/') {
comment.text += 2;
comment.len -= 2;
@@ -24,84 +104,216 @@ String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
comment.text += 2;
comment.len -= 4;
}
- comment = string_trim_whitespace(comment);
- gb_memmove(text+len, comment.text, comment.len);
- len += comment.len;
- text[len++] = '\n';
- }
- return make_string(text, len);
-}
-#if 0
-void print_type_spec(Ast *spec) {
- ast_node(ts, TypeSpec, spec);
- GB_ASSERT(ts->name->kind == Ast_Ident);
- String name = ts->name->Ident.string;
- if (name.len == 0) {
- return;
+ // Ignore the first space
+ if (comment.len > 0 && comment[0] == ' ') {
+ comment.text += 1;
+ comment.len -= 1;
+ }
+
+ if (slash_slash) {
+ if (string_starts_with(comment, str_lit("+"))) {
+ continue;
+ }
+ if (string_starts_with(comment, str_lit("@("))) {
+ continue;
+ }
+ }
+
+ if (slash_slash) {
+ print_doc_line(indent, "%.*s", LIT(comment));
+ count += 1;
+ } else {
+ isize pos = 0;
+ for (; pos < comment.len; pos++) {
+ isize end = pos;
+ for (; end < comment.len; end++) {
+ if (comment[end] == '\n') {
+ break;
+ }
+ }
+ String line = substring(comment, pos, end);
+ pos = end+1;
+ String trimmed_line = string_trim_whitespace(line);
+ if (trimmed_line.len == 0) {
+ if (count == 0) {
+ continue;
+ }
+ }
+ /*
+ * Remove comments with
+ * styles
+ * like this
+ */
+ if (string_starts_with(line, str_lit("* "))) {
+ line = substring(line, 2, line.len);
+ }
+
+ print_doc_line(indent, "%.*s", LIT(line));
+ count += 1;
+ }
+ }
}
- if (name[0] == '_') {
- return;
+
+ if (count > 0) {
+ print_doc_line(0, "");
+ return true;
}
- gb_printf("type %.*s\n", LIT(name));
+ return false;
}
-void print_proc_decl(AstProcDecl *pd) {
- GB_ASSERT(pd->name->kind == Ast_Ident);
- String name = pd->name->Ident.string;
- if (name.len == 0) {
- return;
+
+
+
+void print_doc_expr(Ast *expr) {
+ gbString s = nullptr;
+ if (build_context.cmd_doc_flags & CmdDocFlag_Short) {
+ s = expr_to_string_shorthand(expr);
+ } else {
+ s = expr_to_string(expr);
}
- if (name[0] == '_') {
+ gb_file_write(gb_file_get_standard(gbFileStandard_Output), s, gb_string_length(s));
+ gb_string_free(s);
+}
+
+
+void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
+ if (pkg == nullptr) {
return;
}
- String docs = alloc_comment_group_string(heap_allocator(), pd->docs);
- defer (gb_free(heap_allocator(), docs.text));
+ print_doc_line(0, "package %.*s", LIT(pkg->name));
- if (docs.len > 0) {
- gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len);
- } else {
- return;
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ if (f->pkg_decl) {
+ GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl);
+ print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs);
+ }
}
- ast_node(proc_type, ProcType, pd->type);
-
- gbString params = expr_to_string(proc_type->params);
- defer (gb_string_free(params));
- gb_printf("proc %.*s(%s)", LIT(name), params);
- if (proc_type->results != nullptr) {
- ast_node(fl, FieldList, proc_type->results);
- isize count = fl->list.count;
- if (count > 0) {
- gbString results = expr_to_string(proc_type->results);
- defer (gb_string_free(results));
- gb_printf(" -> ");
- if (count != 1) {
- gb_printf("(");
+ if (pkg->scope != nullptr) {
+ auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
+ defer (array_free(&entities));
+ for_array(i, pkg->scope->elements.entries) {
+ Entity *e = pkg->scope->elements.entries[i].value;
+ switch (e->kind) {
+ case Entity_Invalid:
+ case Entity_Builtin:
+ case Entity_Nil:
+ case Entity_Label:
+ continue;
+ case Entity_Constant:
+ case Entity_Variable:
+ case Entity_TypeName:
+ case Entity_Procedure:
+ case Entity_ProcGroup:
+ case Entity_ImportName:
+ case Entity_LibraryName:
+ // Fine
+ break;
+ }
+ array_add(&entities, e);
+ }
+ gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
+
+ bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
+
+ EntityKind curr_entity_kind = Entity_Invalid;
+ for_array(i, entities) {
+ Entity *e = entities[i];
+ if (e->pkg != pkg) {
+ continue;
+ }
+ if (!is_entity_exported(e)) {
+ continue;
+ }
+
+
+ if (curr_entity_kind != e->kind) {
+ if (curr_entity_kind != Entity_Invalid) {
+ print_doc_line(0, "");
+ }
+ curr_entity_kind = e->kind;
+ print_doc_line(1, "%s", print_entity_names[e->kind]);
+ }
+
+ Ast *type_expr = nullptr;
+ Ast *init_expr = nullptr;
+ Ast *decl_node = nullptr;
+ CommentGroup *comment = nullptr;
+ CommentGroup *docs = nullptr;
+ if (e->decl_info != nullptr) {
+ type_expr = e->decl_info->type_expr;
+ init_expr = e->decl_info->init_expr;
+ decl_node = e->decl_info->decl_node;
+ comment = e->decl_info->comment;
+ docs = e->decl_info->docs;
}
- gb_printf("%s", results);
- if (count != 1) {
- gb_printf(")");
+ GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
+
+ print_doc_line_no_newline(2, "%.*s", LIT(e->token.string));
+ if (type_expr != nullptr) {
+ gbString t = expr_to_string(type_expr);
+ gb_printf(": %s ", t);
+ gb_string_free(t);
+ } else {
+ gb_printf(" :");
+ }
+ if (e->kind == Entity_Variable) {
+ if (init_expr != nullptr) {
+ gb_printf("= ");
+ print_doc_expr(init_expr);
+ }
+ } else {
+ gb_printf(": ");
+ print_doc_expr(init_expr);
+ }
+
+ gb_printf(";\n");
+
+ if (show_docs) {
+ print_doc_comment_group_string(3, docs);
}
}
+ print_doc_line(0, "");
}
- gb_printf("\n\n");
-}
-#endif
-void print_declaration(Ast *decl) {
+
+ if (pkg->fullpath.len != 0) {
+ print_doc_line(0, "");
+ print_doc_line(1, "fullpath:");
+ print_doc_line(2, "%.*s", LIT(pkg->fullpath));
+ print_doc_line(1, "files:");
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ String filename = remove_directory_from_path(f->fullpath);
+ print_doc_line(2, "%.*s", LIT(filename));
+ }
+ }
+
}
-void generate_documentation(Parser *parser) {
- // for_array(file_index, parser->files) {
- // AstFile *file = parser->files[file_index];
- // Tokenizer *tokenizer = &file->tokenizer;
- // String fullpath = tokenizer->fullpath;
- // gb_printf("%.*s\n", LIT(fullpath));
-
- // for_array(decl_index, file->decls) {
- // Ast *decl = file->decls[decl_index];
- // print_declaration(decl);
- // }
- // }
+void generate_documentation(Checker *c) {
+ CheckerInfo *info = &c->info;
+
+ auto pkgs = array_make<AstPackage *>(permanent_allocator(), 0, info->packages.entries.count);
+ for_array(i, info->packages.entries) {
+ AstPackage *pkg = info->packages.entries[i].value;
+ if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
+ array_add(&pkgs, pkg);
+ } else {
+ if (pkg->kind == Package_Init) {
+ array_add(&pkgs, pkg);
+ } else if (pkg->is_extra) {
+ array_add(&pkgs, pkg);
+ }
+ }
+ }
+
+ gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
+
+ for_array(i, pkgs) {
+ print_doc_package(info, pkgs[i]);
+ }
}
diff --git a/src/entity.cpp b/src/entity.cpp
index 3d354b9c8..0aece39c3 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -120,6 +120,7 @@ struct Entity {
union {
struct {
ExactValue value;
+ ParameterValue param_value;
} Constant;
struct {
Ast *init_expr; // only used for some variables within procedure bodies
@@ -164,7 +165,7 @@ struct Entity {
Scope *scope;
} ImportName;
struct {
- Array<String> paths;
+ Slice<String> paths;
String name;
} LibraryName;
i32 Nil;
@@ -219,7 +220,7 @@ bool entity_has_deferred_procedure(Entity *e) {
gb_global u64 global_entity_id = 0;
Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) {
- gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
Entity *entity = gb_alloc_item(a, Entity);
entity->kind = kind;
entity->state = EntityState_Unresolved;
@@ -332,7 +333,7 @@ Entity *alloc_entity_import_name(Scope *scope, Token token, Type *type,
}
Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type,
- Array<String> paths, String name) {
+ Slice<String> paths, String name) {
Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
entity->LibraryName.paths = paths;
entity->LibraryName.name = name;
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index 9e22c0483..326f4d587 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -46,16 +46,16 @@ enum ExactValueKind {
struct ExactValue {
ExactValueKind kind;
union {
- bool value_bool;
- String value_string;
- BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
- f64 value_float;
- i64 value_pointer;
- Complex128 value_complex;
- Quaternion256 value_quaternion;
- Ast * value_compound;
- Ast * value_procedure;
- Type * value_typeid;
+ bool value_bool;
+ String value_string;
+ BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
+ f64 value_float;
+ i64 value_pointer;
+ Complex128 *value_complex;
+ Quaternion256 *value_quaternion;
+ Ast * value_compound;
+ Ast * value_procedure;
+ Type * value_typeid;
};
};
@@ -85,9 +85,9 @@ HashKey hash_exact_value(ExactValue v) {
case ExactValue_Pointer:
return hash_integer(v.value_pointer);
case ExactValue_Complex:
- return hashing_proc(&v.value_complex, gb_size_of(Complex128));
+ return hashing_proc(v.value_complex, gb_size_of(Complex128));
case ExactValue_Quaternion:
- return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256));
+ return hashing_proc(v.value_quaternion, gb_size_of(Quaternion256));
case ExactValue_Compound:
return hash_pointer(v.value_compound);
case ExactValue_Procedure:
@@ -139,17 +139,19 @@ ExactValue exact_value_float(f64 f) {
ExactValue exact_value_complex(f64 real, f64 imag) {
ExactValue result = {ExactValue_Complex};
- result.value_complex.real = real;
- result.value_complex.imag = imag;
+ result.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
+ result.value_complex->real = real;
+ result.value_complex->imag = imag;
return result;
}
ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
ExactValue result = {ExactValue_Quaternion};
- result.value_quaternion.real = real;
- result.value_quaternion.imag = imag;
- result.value_quaternion.jmag = jmag;
- result.value_quaternion.kmag = kmag;
+ result.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256);
+ result.value_quaternion->real = real;
+ result.value_quaternion->imag = imag;
+ result.value_quaternion->jmag = jmag;
+ result.value_quaternion->kmag = kmag;
return result;
}
@@ -373,6 +375,7 @@ ExactValue exact_value_to_complex(ExactValue v) {
// return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag);
}
ExactValue r = {ExactValue_Invalid};
+ v.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
return r;
}
ExactValue exact_value_to_quaternion(ExactValue v) {
@@ -382,11 +385,12 @@ ExactValue exact_value_to_quaternion(ExactValue v) {
case ExactValue_Float:
return exact_value_quaternion(v.value_float, 0, 0, 0);
case ExactValue_Complex:
- return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0);
+ return exact_value_quaternion(v.value_complex->real, v.value_complex->imag, 0, 0);
case ExactValue_Quaternion:
return v;
}
ExactValue r = {ExactValue_Invalid};
+ v.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256);
return r;
}
@@ -396,9 +400,9 @@ ExactValue exact_value_real(ExactValue v) {
case ExactValue_Float:
return v;
case ExactValue_Complex:
- return exact_value_float(v.value_complex.real);
+ return exact_value_float(v.value_complex->real);
case ExactValue_Quaternion:
- return exact_value_float(v.value_quaternion.real);
+ return exact_value_float(v.value_quaternion->real);
}
ExactValue r = {ExactValue_Invalid};
return r;
@@ -410,9 +414,9 @@ ExactValue exact_value_imag(ExactValue v) {
case ExactValue_Float:
return exact_value_i64(0);
case ExactValue_Complex:
- return exact_value_float(v.value_complex.imag);
+ return exact_value_float(v.value_complex->imag);
case ExactValue_Quaternion:
- return exact_value_float(v.value_quaternion.imag);
+ return exact_value_float(v.value_quaternion->imag);
}
ExactValue r = {ExactValue_Invalid};
return r;
@@ -425,7 +429,7 @@ ExactValue exact_value_jmag(ExactValue v) {
case ExactValue_Complex:
return exact_value_i64(0);
case ExactValue_Quaternion:
- return exact_value_float(v.value_quaternion.jmag);
+ return exact_value_float(v.value_quaternion->jmag);
}
ExactValue r = {ExactValue_Invalid};
return r;
@@ -438,7 +442,7 @@ ExactValue exact_value_kmag(ExactValue v) {
case ExactValue_Complex:
return exact_value_i64(0);
case ExactValue_Quaternion:
- return exact_value_float(v.value_quaternion.kmag);
+ return exact_value_float(v.value_quaternion->kmag);
}
ExactValue r = {ExactValue_Invalid};
return r;
@@ -532,15 +536,15 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision,
return i;
}
case ExactValue_Complex: {
- f64 real = v.value_complex.real;
- f64 imag = v.value_complex.imag;
+ f64 real = v.value_complex->real;
+ f64 imag = v.value_complex->imag;
return exact_value_complex(-real, -imag);
}
case ExactValue_Quaternion: {
- f64 real = v.value_quaternion.real;
- f64 imag = v.value_quaternion.imag;
- f64 jmag = v.value_quaternion.jmag;
- f64 kmag = v.value_quaternion.kmag;
+ f64 real = v.value_quaternion->real;
+ f64 imag = v.value_quaternion->imag;
+ f64 jmag = v.value_quaternion->jmag;
+ f64 kmag = v.value_quaternion->kmag;
return exact_value_quaternion(-real, -imag, -jmag, -kmag);
}
}
@@ -685,6 +689,8 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
+ case Token_AndNot: return exact_value_bool(x.value_bool & !y.value_bool);
+ case Token_Xor: return exact_value_bool((x.value_bool && !y.value_bool) || (!x.value_bool && y.value_bool));
default: goto error;
}
break;
@@ -730,10 +736,10 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Complex: {
y = exact_value_to_complex(y);
- f64 a = x.value_complex.real;
- f64 b = x.value_complex.imag;
- f64 c = y.value_complex.real;
- f64 d = y.value_complex.imag;
+ f64 a = x.value_complex->real;
+ f64 b = x.value_complex->imag;
+ f64 c = y.value_complex->real;
+ f64 d = y.value_complex->imag;
f64 real = 0;
f64 imag = 0;
switch (op) {
@@ -763,14 +769,14 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Quaternion: {
y = exact_value_to_quaternion(y);
- f64 xr = x.value_quaternion.real;
- f64 xi = x.value_quaternion.imag;
- f64 xj = x.value_quaternion.jmag;
- f64 xk = x.value_quaternion.kmag;
- f64 yr = y.value_quaternion.real;
- f64 yi = y.value_quaternion.imag;
- f64 yj = y.value_quaternion.jmag;
- f64 yk = y.value_quaternion.kmag;
+ f64 xr = x.value_quaternion->real;
+ f64 xi = x.value_quaternion->imag;
+ f64 xj = x.value_quaternion->jmag;
+ f64 xk = x.value_quaternion->kmag;
+ f64 yr = y.value_quaternion->real;
+ f64 yi = y.value_quaternion->imag;
+ f64 yj = y.value_quaternion->jmag;
+ f64 yk = y.value_quaternion->kmag;
f64 real = 0;
@@ -897,10 +903,10 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
}
case ExactValue_Complex: {
- f64 a = x.value_complex.real;
- f64 b = x.value_complex.imag;
- f64 c = y.value_complex.real;
- f64 d = y.value_complex.imag;
+ f64 a = x.value_complex->real;
+ f64 b = x.value_complex->imag;
+ f64 c = y.value_complex->real;
+ f64 d = y.value_complex->imag;
switch (op) {
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
@@ -945,7 +951,7 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
Entity *strip_entity_wrapping(Ast *expr);
Entity *strip_entity_wrapping(Entity *e);
-gbString write_expr_to_string(gbString str, Ast *node);
+gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) {
switch (v.kind) {
@@ -976,14 +982,16 @@ gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize st
case ExactValue_Float:
return gb_string_append_fmt(str, "%f", v.value_float);
case ExactValue_Complex:
- return gb_string_append_fmt(str, "%f+%fi", v.value_complex.real, v.value_complex.imag);
+ return gb_string_append_fmt(str, "%f+%fi", v.value_complex->real, v.value_complex->imag);
+ case ExactValue_Quaternion:
+ return gb_string_append_fmt(str, "%f+%fi+%fj+%fk", v.value_quaternion->real, v.value_quaternion->imag, v.value_quaternion->jmag, v.value_quaternion->kmag);
case ExactValue_Pointer:
return str;
case ExactValue_Compound:
- return write_expr_to_string(str, v.value_compound);
+ return write_expr_to_string(str, v.value_compound, false);
case ExactValue_Procedure:
- return write_expr_to_string(str, v.value_procedure);
+ return write_expr_to_string(str, v.value_procedure, false);
}
return str;
};
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 848f27628..9981b9e34 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -157,7 +157,7 @@ extern "C" {
#endif
#endif
-#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__)
+#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__)
#ifndef GB_ARCH_64_BIT
#define GB_ARCH_64_BIT 1
#endif
@@ -230,7 +230,7 @@ extern "C" {
#define GB_CACHE_LINE_SIZE 128
#endif
-#elif defined(__arm__)
+#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
#ifndef GB_CPU_ARM
#define GB_CPU_ARM 1
#endif
@@ -3702,6 +3702,12 @@ gb_inline void *gb_memcopy(void *dest, void const *source, isize n) {
void *dest_copy = dest;
__asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory");
+#elif defined(GB_CPU_ARM)
+ u8 *s = cast(u8 *)source;
+ u8 *d = cast(u8 *)dest;
+ for (isize i = 0; i < n; i++) {
+ *d++ = *s++;
+ }
#else
u8 *d = cast(u8 *)dest;
u8 const *s = cast(u8 const *)source;
@@ -4438,6 +4444,76 @@ gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) {
#endif
}
+#elif defined(GB_CPU_ARM)
+
+gb_inline i32 gb_atomic32_load (gbAtomic32 const volatile *a) {
+ return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
+}
+gb_inline void gb_atomic32_store(gbAtomic32 volatile *a, i32 value) {
+ __atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_compare_exchange(gbAtomic32 volatile *a, i32 expected, i32 desired) {
+ i32 expected_copy = expected;
+ auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+ if (result) {
+ return expected;
+ } else {
+ return expected_copy;
+ }
+}
+
+gb_inline i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) {
+ return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) {
+ return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) {
+ return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) {
+ return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_load(gbAtomic64 const volatile *a) {
+ return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
+}
+
+gb_inline void gb_atomic64_store(gbAtomic64 volatile *a, i64 value) {
+ __atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_compare_exchange(gbAtomic64 volatile *a, i64 expected, i64 desired) {
+ i64 expected_copy = expected;
+ auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+ if (result) {
+ return expected;
+ } else {
+ return expected_copy;
+ }
+}
+
+gb_inline i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) {
+ return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) {
+ return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) {
+ return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) {
+ return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+
#else
#error TODO(bill): Implement Atomics for this CPU
#endif
@@ -4563,7 +4639,11 @@ gb_inline void gb_yield_thread(void) {
#if defined(GB_SYSTEM_WINDOWS)
_mm_pause();
#elif defined(GB_SYSTEM_OSX)
+ #if defined(GB_CPU_X86)
__asm__ volatile ("" : : : "memory");
+ #elif defined(GB_CPU_ARM)
+ __asm__ volatile ("yield" : : : "memory");
+ #endif
#elif defined(GB_CPU_X86)
_mm_pause();
#else
@@ -4575,7 +4655,11 @@ gb_inline void gb_mfence(void) {
#if defined(GB_SYSTEM_WINDOWS)
_ReadWriteBarrier();
#elif defined(GB_SYSTEM_OSX)
+ #if defined(GB_CPU_X86)
__sync_synchronize();
+ #elif defined(GB_CPU_ARM)
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
+ #endif
#elif defined(GB_CPU_X86)
_mm_mfence();
#else
@@ -4587,7 +4671,12 @@ gb_inline void gb_sfence(void) {
#if defined(GB_SYSTEM_WINDOWS)
_WriteBarrier();
#elif defined(GB_SYSTEM_OSX)
+ #if defined(GB_CPU_X86)
__asm__ volatile ("" : : : "memory");
+ #elif defined(GB_CPU_ARM)
+ // TODO(bill): is this correct?
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
+ #endif
#elif defined(GB_CPU_X86)
_mm_sfence();
#else
@@ -5156,7 +5245,7 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
index = core * a->threads_per_core + thread_index;
thread = pthread_self();
-
+
cpuset_t mn;
CPU_ZERO(&mn);
@@ -5202,7 +5291,7 @@ void gb_affinity_init(gbAffinity *a) {
for (;;) {
// The 'temporary char'. Everything goes into this char,
// so that we can check against EOF at the end of this loop.
- char c;
+ int c;
#define AF__CHECK(letter) ((c = getc(cpu_info)) == letter)
if (AF__CHECK('c') && AF__CHECK('p') && AF__CHECK('u') && AF__CHECK(' ') &&
@@ -8808,6 +8897,14 @@ gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name)
return result;
}
+#elif defined(__aarch64__)
+ gb_inline u64 gb_rdtsc(void) {
+ int64_t virtual_timer_value;
+ asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
+ return virtual_timer_value;
+ }
+#else
+#error "gb_rdtsc not supported"
#endif
#if defined(GB_SYSTEM_WINDOWS)
diff --git a/src/ir.cpp b/src/ir.cpp
index 2b3bd35df..e46eb27fb 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -25,6 +25,9 @@ struct irModule {
Map<irDebugInfo *> debug_info; // Key: Unique pointer
Map<irValue *> anonymous_proc_lits; // Key: Ast *
+ Map<irValue *> equal_procs; // Key: Type *
+ Map<irValue *> hasher_procs; // Key: Type *
+
irDebugInfo * debug_compile_unit;
Array<irDebugInfo *> debug_location_stack;
@@ -161,6 +164,7 @@ struct irProcedure {
Ast * return_ptr_hint_ast;
bool return_ptr_hint_used;
+ bool ignore_dead_instr;
Array<irBranchBlocks> branch_blocks;
@@ -454,7 +458,6 @@ struct irValueSourceCodeLocation {
irValue *line;
irValue *column;
irValue *procedure;
- u64 hash;
};
@@ -525,6 +528,11 @@ struct irAddr {
Type *ir_type(irValue *value);
irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr);
+void ir_begin_procedure_body(irProcedure *proc);
+void ir_end_procedure_body(irProcedure *proc);
+irValue *ir_get_equal_proc_for_type(irModule *m, Type *type);
+irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type);
+
irAddr ir_addr(irValue *addr) {
irAddr v = {irAddr_Default, addr};
@@ -1159,7 +1167,7 @@ irValue *ir_instr_atomic_cxchg(irProcedure *p, Type *type, irValue *address, irV
GB_ASSERT(type->Tuple.variables.count == 2);
Type *elem = type->Tuple.variables[0]->type;
// LEAK TODO(bill): LLVM returns {T, i1} whilst Odin does {T, bool}, fix this mapping hack
- gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
Type *llvm_type = alloc_type_tuple();
array_init(&llvm_type->Tuple.variables, a, 0, 2);
array_add (&llvm_type->Tuple.variables, alloc_entity_field(nullptr, blank_token, elem, false, 0));
@@ -1799,7 +1807,7 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial
if (zero_initialized) {
ir_emit_zero_init(proc, instr, expr);
}
- set_procedure_abi_types(heap_allocator(), e->type);
+ set_procedure_abi_types(e->type);
// if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) {
// if (proc->module->generate_debug_info && proc->entity != nullptr) {
@@ -2132,7 +2140,7 @@ irDebugInfo *ir_add_debug_info_field(irModule *module, irDebugInfo *scope, Entit
if (e->token.string.len == 0) {
// If no name available for field, use its field index as its name.
isize max_len = 8;
- u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len);
+ u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len);
isize len = gb_snprintf(cast(char *)str, 8, "%d", index);
di->DerivedType.name = make_string(str, len-1);
}
@@ -3282,7 +3290,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
context_ptr = ir_find_or_generate_context_ptr(p);
}
- set_procedure_abi_types(heap_allocator(), pt);
+ set_procedure_abi_types(pt);
bool is_c_vararg = pt->Proc.c_vararg;
isize param_count = pt->Proc.param_count;
@@ -3293,7 +3301,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
GB_ASSERT_MSG(param_count == args.count, "%.*s %td == %td", LIT(p->entity->token.string), param_count, args.count);
}
- auto processed_args = array_make<irValue *>(heap_allocator(), 0, args.count);
+ auto processed_args = array_make<irValue *>(permanent_allocator(), 0, args.count);
for (isize i = 0; i < param_count; i++) {
Entity *e = pt->Proc.params->Tuple.variables[i];
@@ -3416,7 +3424,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
case DeferredProcedure_in_out:
{
auto out_args = ir_value_to_array(p, result);
- array_init(&result_as_args, heap_allocator(), in_args.count + out_args.count);
+ array_init(&result_as_args, permanent_allocator(), in_args.count + out_args.count);
array_copy(&result_as_args, in_args, 0);
array_copy(&result_as_args, out_args, in_args.count);
}
@@ -3587,65 +3595,69 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty
irValue *m = ir_emit_conv(proc, map_val_ptr, type_deref(ir_type(gep0)));
ir_emit_store(proc, gep0, m);
- ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_const_bool(is_type_string(key_type)));
i64 entry_size = type_size_of (map_type->Map.entry_type);
i64 entry_align = type_align_of (map_type->Map.entry_type);
- i64 value_offset = type_offset_of(map_type->Map.entry_type, 2);
+ i64 key_offset = type_offset_of(map_type->Map.entry_type, 2);
+ i64 key_size = type_size_of (map_type->Map.key);
+ i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
i64 value_size = type_size_of (map_type->Map.value);
+ ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_get_equal_proc_for_type(proc->module, key_type));
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 2), ir_const_int(entry_size));
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 3), ir_const_int(entry_align));
- ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(value_offset));
- ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(value_size));
+ ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(key_offset));
+ ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(key_size));
+ ir_emit_store(proc, ir_emit_struct_ep(proc, h, 6), ir_const_uintptr(value_offset));
+ ir_emit_store(proc, ir_emit_struct_ep(proc, h, 7), ir_const_int(value_size));
return ir_emit_load(proc, h);
}
-irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) {
- Type *hash_type = t_u64;
- irValue *v = ir_add_local_generated(proc, t_map_key, true);
- Type *t = base_type(ir_type(key));
- key = ir_emit_conv(proc, key, key_type);
-
- if (is_type_string(t)) {
- irValue *str = ir_emit_conv(proc, key, t_string);
- irValue *hashed_str = nullptr;
+irValue *ir_const_hash(irModule *m, irValue *key, Type *key_type) {
+ irValue *hashed_key = nullptr;
- if (str->kind == irValue_Constant) {
- ExactValue ev = str->Constant.value;
- GB_ASSERT(ev.kind == ExactValue_String);
- u64 hs = fnv64a(ev.value_string.text, ev.value_string.len);
- hashed_str = ir_value_constant(t_u64, exact_value_u64(hs));
+ if (key->kind == irValue_Constant) {
+ u64 hash = 0xcbf29ce484222325;
+ if (is_type_string(key_type)) {
+ GB_ASSERT(key->Constant.value.kind == ExactValue_String);
+ String s = key->Constant.value.value_string;
+ hash = fnv64a(s.text, s.len);
} else {
- auto args = array_make<irValue *>(ir_allocator(), 1);
- args[0] = str;
- hashed_str = ir_emit_runtime_call(proc, "default_hash_string", args);
+ return nullptr;
}
- ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_str);
+ // TODO(bill): other const hash types
- irValue *key_data = ir_emit_struct_ep(proc, v, 1);
- key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
- ir_emit_store(proc, key_data, str);
- } else {
- i64 sz = type_size_of(t);
- GB_ASSERT(sz <= 8);
- if (sz != 0) {
- auto args = array_make<irValue *>(ir_allocator(), 2);
- args[0] = ir_address_from_load_or_generate_local(proc, key);
- args[1] = ir_const_int(sz);
- irValue *hash = ir_emit_runtime_call(proc, "default_hash_ptr", args);
+ if (build_context.word_size == 4) {
+ hash &= 0xffffffffull;
+ }
+ hashed_key = ir_const_uintptr(hash);
+ }
+ return hashed_key;
+}
- irValue *hash_ptr = ir_emit_struct_ep(proc, v, 0);
- irValue *key_data = ir_emit_struct_ep(proc, v, 1);
- key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
+irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) {
+ Type *hash_type = t_u64;
+ irValue *v = ir_add_local_generated(proc, t_map_hash, true);
+ Type *t = base_type(ir_type(key));
+ key = ir_emit_conv(proc, key, key_type);
- ir_emit_store(proc, hash_ptr, hash);
- ir_emit_store(proc, key_data, key);
- }
+ irValue *key_ptr = ir_address_from_load_or_generate_local(proc, key);
+ key_ptr = ir_emit_conv(proc, key_ptr, t_rawptr);
+
+ irValue *hashed_key = ir_const_hash(proc->module, key, key_type);
+ if (hashed_key == nullptr) {
+ irValue *hasher = ir_get_hasher_proc_for_type(proc->module, key_type);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ args[0] = key_ptr;
+ args[1] = ir_value_constant(t_uintptr, exact_value_i64(0));
+ hashed_key = ir_emit_call(proc, hasher, args);
}
+ ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_key);
+ ir_emit_store(proc, ir_emit_struct_ep(proc, v, 1), key_ptr);
+
return ir_emit_load(proc, v);
}
@@ -3701,7 +3713,7 @@ irValue *ir_insert_dynamic_map_key_and_value(irProcedure *proc, irValue *addr, T
map_type = base_type(map_type);
irValue *h = ir_gen_map_header(proc, addr, map_type);
- irValue *key = ir_gen_map_key(proc, map_key, map_type->Map.key);
+ irValue *key = ir_gen_map_hash(proc, map_key, map_type->Map.key);
irValue *v = ir_emit_conv(proc, map_value, map_type->Map.value);
irValue *ptr = ir_add_local_generated(proc, ir_type(v), false);
@@ -4058,7 +4070,7 @@ irValue *ir_addr_load(irProcedure *proc, irAddr const &addr) {
Type *map_type = base_type(addr.map_type);
irValue *v = ir_add_local_generated(proc, map_type->Map.lookup_result_type, true);
irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
- irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key);
+ irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key);
auto args = array_make<irValue *>(ir_allocator(), 2);
args[0] = h;
@@ -4226,7 +4238,7 @@ irValue *ir_addr_get_ptr(irProcedure *proc, irAddr const &addr, bool allow_refer
if (allow_reference) {
Type *map_type = base_type(addr.map_type);
irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
- irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key);
+ irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key);
auto args = array_make<irValue *>(ir_allocator(), 2);
args[0] = h;
@@ -4537,7 +4549,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
Type *ft = base_complex_elem_type(t_left);
if (op == Token_Quo) {
- auto args = array_make<irValue *>(heap_allocator(), 2);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
args[0] = left;
args[1] = right;
@@ -4615,7 +4627,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
return ir_emit_load(proc, res);
} else if (op == Token_Mul) {
- auto args = array_make<irValue *>(heap_allocator(), 2);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
args[0] = left;
args[1] = right;
@@ -4625,7 +4637,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
default: GB_PANIC("Unknown float type"); break;
}
} else if (op == Token_Quo) {
- auto args = array_make<irValue *>(heap_allocator(), 2);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
args[0] = left;
args[1] = right;
@@ -4828,7 +4840,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
irValue *invalid_typeid = ir_value_constant(t_typeid, exact_value_i64(0));
return ir_emit_comp(proc, op_kind, x, invalid_typeid);
} else if (is_type_bit_field(t)) {
- auto args = array_make<irValue *>(heap_allocator(), 2);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
args[1] = ir_const_int(type_size_of(t));
@@ -4848,7 +4860,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
return ir_emit_comp(proc, op_kind, cap, v_zero);
}
} else if (is_type_struct(t) && type_has_nil(t)) {
- auto args = array_make<irValue *>(heap_allocator(), 2);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
args[1] = ir_const_int(type_size_of(t));
@@ -4859,6 +4871,244 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
return nullptr;
}
+irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) {
+ Type *original_type = type;
+ type = base_type(type);
+ Type *pt = alloc_type_pointer(type);
+
+ auto key = hash_type(type);
+ irValue **found = map_get(&m->equal_procs, key);
+ if (found) {
+ return *found;
+ }
+
+ static u32 proc_index = 0;
+
+ char buf[16] = {};
+ isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index);
+ char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+ String proc_name = make_string_c(str);
+
+
+ Ast *body = alloc_ast_node(nullptr, Ast_Invalid);
+ Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_equal_proc, 0);
+ e->Procedure.link_name = proc_name;
+ irValue *p = ir_value_procedure(m, e, t_equal_proc, nullptr, body, proc_name);
+ map_set(&m->values, hash_entity(e), p);
+ string_map_set(&m->members, proc_name, p);
+ map_set(&m->equal_procs, key, p);
+
+ irProcedure *proc = &p->Proc;
+ proc->is_startup = true;
+ proc->ignore_dead_instr = true;
+ ir_begin_procedure_body(proc);
+ // ir_start_block(proc, proc->decl_block);
+ GB_ASSERT(proc->curr_block != nullptr);
+
+ irValue *x = proc->params[0];
+ irValue *y = proc->params[1];
+ irValue *lhs = ir_emit_conv(proc, x, pt);
+ irValue *rhs = ir_emit_conv(proc, y, pt);
+
+ irBlock *block_same_ptr = ir_new_block(proc, nullptr, "same_ptr");
+ irBlock *block_diff_ptr = ir_new_block(proc, nullptr, "diff_ptr");
+
+ irValue *same_ptr = ir_emit_comp(proc, Token_CmpEq, lhs, rhs);
+ ir_emit_if(proc, same_ptr, block_same_ptr, block_diff_ptr);
+ ir_start_block(proc, block_same_ptr);
+ ir_emit(proc, ir_instr_return(proc, ir_const_bool(true)));
+
+ ir_start_block(proc, block_diff_ptr);
+
+ if (type->kind == Type_Struct) {
+ type_set_offsets(type);
+
+ irBlock *done = ir_new_block(proc, nullptr, "done"); // NOTE(bill): Append later
+
+ irBlock *block_false = ir_new_block(proc, nullptr, "bfalse");
+
+ for_array(i, type->Struct.fields) {
+ irBlock *next_block = ir_new_block(proc, nullptr, "btrue");
+
+ irValue *pleft = ir_emit_struct_ep(proc, lhs, cast(i32)i);
+ irValue *pright = ir_emit_struct_ep(proc, rhs, cast(i32)i);
+ irValue *left = ir_emit_load(proc, pleft);
+ irValue *right = ir_emit_load(proc, pright);
+ irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right);
+
+ ir_emit_if(proc, ok, next_block, block_false);
+
+ ir_emit_jump(proc, next_block);
+ ir_start_block(proc, next_block);
+ }
+
+ ir_emit_jump(proc, done);
+ ir_start_block(proc, block_false);
+
+ ir_emit(proc, ir_instr_return(proc, ir_const_bool(false)));
+
+ ir_emit_jump(proc, done);
+ ir_start_block(proc, done);
+ ir_emit(proc, ir_instr_return(proc, ir_const_bool(true)));
+ } else {
+ irValue *left = ir_emit_load(proc, lhs);
+ irValue *right = ir_emit_load(proc, rhs);
+ irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right);
+ ok = ir_emit_conv(proc, ok, t_bool);
+ ir_emit(proc, ir_instr_return(proc, ok));
+ }
+
+ ir_end_procedure_body(proc);
+
+ return p;
+}
+
+
+irValue *ir_simple_compare_hash(irProcedure *p, Type *type, irValue *data, irValue *seed) {
+ GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
+
+ i64 sz = type_size_of(type);
+ if (1 <= sz && sz <= 16) {
+ char name[20] = {};
+ gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
+
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ args[0] = data;
+ args[1] = seed;
+ return ir_emit_runtime_call(p, name, args);
+ }
+
+ auto args = array_make<irValue *>(permanent_allocator(), 3);
+ args[0] = data;
+ args[1] = seed;
+ args[2] = ir_const_int(type_size_of(type));
+ return ir_emit_runtime_call(p, "default_hasher_n", args);
+}
+
+irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) {
+ Type *original_type = type;
+ type = core_type(type);
+ Type *pt = alloc_type_pointer(type);
+
+ GB_ASSERT(is_type_valid_for_keys(type));
+
+ auto key = hash_type(type);
+ irValue **found = map_get(&m->hasher_procs, key);
+ if (found) {
+ return *found;
+ }
+
+ static u32 proc_index = 0;
+
+ char buf[16] = {};
+ isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index);
+ char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+ String proc_name = make_string_c(str);
+
+
+ Ast *body = alloc_ast_node(nullptr, Ast_Invalid);
+ Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_hasher_proc, 0);
+ e->Procedure.link_name = proc_name;
+ irValue *p = ir_value_procedure(m, e, t_hasher_proc, nullptr, body, proc_name);
+ map_set(&m->values, hash_entity(e), p);
+ string_map_set(&m->members, proc_name, p);
+ map_set(&m->hasher_procs, key, p);
+
+ irProcedure *proc = &p->Proc;
+ proc->is_startup = true;
+ proc->ignore_dead_instr = true;
+ ir_begin_procedure_body(proc);
+ defer (ir_end_procedure_body(proc));
+
+ // ir_start_block(proc, proc->decl_block);
+ GB_ASSERT(proc->curr_block != nullptr);
+
+ irValue *data = proc->params[0];
+ irValue *seed = proc->params[1];
+
+ if (is_type_simple_compare(type)) {
+ irValue *res = ir_simple_compare_hash(proc, type, data, seed);
+ ir_emit(proc, ir_instr_return(proc, res));
+ return p;
+ }
+
+ if (is_type_cstring(type)) {
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ args[0] = data;
+ args[1] = seed;
+ irValue *res = ir_emit_runtime_call(proc, "default_hasher_cstring", args);
+ ir_emit(proc, ir_instr_return(proc, res));
+ } else if (is_type_string(type)) {
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ args[0] = data;
+ args[1] = seed;
+ irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args);
+ ir_emit(proc, ir_instr_return(proc, res));
+ } else if (type->kind == Type_Struct) {
+ type_set_offsets(type);
+ data = ir_emit_conv(proc, data, t_u8_ptr);
+
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ for_array(i, type->Struct.fields) {
+ i64 offset = type->Struct.offsets[i];
+ Entity *field = type->Struct.fields[i];
+ irValue *field_hasher = ir_get_hasher_proc_for_type(m, field->type);
+ irValue *ptr = ir_emit_ptr_offset(proc, data, ir_const_uintptr(offset));
+
+ args[0] = ptr;
+ args[1] = seed;
+ seed = ir_emit_call(proc, field_hasher, args);
+ }
+ ir_emit(proc, ir_instr_return(proc, seed));
+ } else if (type->kind == Type_Array) {
+ irValue *pres = ir_add_local_generated(proc, t_uintptr, false);
+ ir_emit_store(proc, pres, seed);
+
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem);
+
+ auto loop_data = ir_loop_start(proc, type->Array.count, t_i32);
+
+ data = ir_emit_conv(proc, data, pt);
+
+ irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx);
+ args[0] = ptr;
+ args[1] = ir_emit_load(proc, pres);
+ irValue *new_seed = ir_emit_call(proc, elem_hasher, args);
+ ir_emit_store(proc, pres, new_seed);
+
+ ir_loop_end(proc, loop_data);
+
+ irValue *res = ir_emit_load(proc, pres);
+ ir_emit(proc, ir_instr_return(proc, res));
+ } else if (type->kind == Type_EnumeratedArray) {
+ irValue *pres = ir_add_local_generated(proc, t_uintptr, false);
+ ir_emit_store(proc, pres, seed);
+
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem);
+
+ auto loop_data = ir_loop_start(proc, type->Array.count, t_i32);
+
+ data = ir_emit_conv(proc, data, pt);
+
+ irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx);
+ args[0] = ptr;
+ args[1] = ir_emit_load(proc, pres);
+ irValue *new_seed = ir_emit_call(proc, elem_hasher, args);
+ ir_emit_store(proc, pres, new_seed);
+
+ ir_loop_end(proc, loop_data);
+
+ irValue *res = ir_emit_load(proc, pres);
+ ir_emit(proc, ir_instr_return(proc, res));
+ } else {
+ GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
+ }
+
+ return p;
+}
+
irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) {
Type *a = base_type(ir_type(left));
Type *b = base_type(ir_type(right));
@@ -4966,7 +5216,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
} else {
if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
// TODO(bill): Test to see if this is actually faster!!!!
- auto args = array_make<irValue *>(heap_allocator(), 3);
+ auto args = array_make<irValue *>(permanent_allocator(), 3);
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
args[1] = ir_emit_conv(proc, rhs, t_rawptr);
args[2] = ir_const_int(type_size_of(tl));
@@ -4992,6 +5242,30 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
}
}
+ if (is_type_struct(a) && is_type_comparable(a)) {
+ irValue *left_ptr = ir_address_from_load_or_generate_local(proc, left);
+ irValue *right_ptr = ir_address_from_load_or_generate_local(proc, right);
+ irValue *res = {};
+ if (is_type_simple_compare(a)) {
+ // TODO(bill): Test to see if this is actually faster!!!!
+ auto args = array_make<irValue *>(permanent_allocator(), 3);
+ args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
+ args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
+ args[2] = ir_const_int(type_size_of(a));
+ res = ir_emit_runtime_call(proc, "memory_equal", args);
+ } else {
+ irValue *value = ir_get_equal_proc_for_type(proc->module, a);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
+ args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
+ args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
+ res = ir_emit_call(proc, value, args);
+ }
+ if (op_kind == Token_NotEq) {
+ res = ir_emit_unary_arith(proc, Token_Not, res, ir_type(res));
+ }
+ return res;
+ }
+
if (is_type_string(a)) {
if (is_type_cstring(a)) {
left = ir_emit_conv(proc, left, t_string);
@@ -6636,7 +6910,7 @@ void ir_mangle_add_sub_type_name(irModule *m, Entity *field, String parent) {
return;
}
if (is_type_proc(field->type)) {
- set_procedure_abi_types(heap_allocator(), field->type);
+ set_procedure_abi_types(field->type);
}
String cn = field->token.string;
@@ -6733,7 +7007,7 @@ irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, i
String name = make_string(name_text, name_len-1);
Type *type = type_of_expr(expr);
- set_procedure_abi_types(heap_allocator(), type);
+ set_procedure_abi_types(type);
irValue *value = ir_value_procedure(m, nullptr, type, pl->type, pl->body, name);
value->Proc.tags = pl->tags;
@@ -6789,6 +7063,9 @@ void ir_gen_global_type_name(irModule *m, Entity *e, String name) {
if (!ir_min_dep_entity(m, e)) {
return;
}
+ if (is_type_proc(e->type)) {
+ return;
+ }
irValue *t = ir_value_type_name(name, e->type);
ir_module_add_value(m, e, t);
string_map_set(&m->members, name, t);
@@ -6884,7 +7161,7 @@ irValue *ir_find_global_variable(irProcedure *proc, String name) {
return *value;
}
-void ir_build_stmt_list(irProcedure *proc, Array<Ast *> stmts);
+void ir_build_stmt_list(irProcedure *proc, Slice<Ast *> stmts);
void ir_build_assign_op(irProcedure *proc, irAddr const &lhs, irValue *value, TokenKind op);
bool is_double_pointer(Type *t) {
@@ -6898,17 +7175,6 @@ bool is_double_pointer(Type *t) {
return is_type_pointer(td);
}
-
-u64 ir_generate_source_code_location_hash(TokenPos pos) {
- u64 h = 0xcbf29ce484222325;
- for (isize i = 0; i < pos.file.len; i++) {
- h = (h ^ u64(pos.file[i])) * 0x100000001b3;
- }
- h = h ^ (u64(pos.line) * 0x100000001b3);
- h = h ^ (u64(pos.column) * 0x100000001b3);
- return h;
-}
-
irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, TokenPos pos) {
gbAllocator a = ir_allocator();
irValue *v = ir_alloc_value(irValue_SourceCodeLocation);
@@ -6916,7 +7182,6 @@ irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, Token
v->SourceCodeLocation.line = ir_const_int(pos.line);
v->SourceCodeLocation.column = ir_const_int(pos.column);
v->SourceCodeLocation.procedure = ir_find_or_add_entity_string(proc->module, procedure);
- v->SourceCodeLocation.hash = ir_generate_source_code_location_hash(pos);
return v;
}
@@ -7355,7 +7620,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
// "Intrinsics"
case BuiltinProc_alloca:
{
- auto args = array_make<irValue *>(heap_allocator(), 2);
+ auto args = array_make<irValue *>(permanent_allocator(), 2);
args[0] = ir_emit_conv(proc, ir_build_expr(proc, ce->args[0]), t_i32);
args[1] = ir_build_expr(proc, ce->args[1]);
return ir_emit(proc, ir_instr_inline_code(proc, id, args, t_u8_ptr));
@@ -7459,7 +7724,11 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
return ir_emit(proc, ir_instr_atomic_cxchg(proc, type, address, old_value, new_value, id));
}
+ case BuiltinProc_type_equal_proc:
+ return ir_get_equal_proc_for_type(proc->module, ce->args[0]->tav.type);
+ case BuiltinProc_type_hasher_proc:
+ return ir_get_hasher_proc_for_type(proc->module, ce->args[0]->tav.type);
}
GB_PANIC("Unhandled built-in procedure");
@@ -7584,7 +7853,7 @@ irValue *ir_build_call_expr(irProcedure *proc, Ast *expr) {
Type *proc_type_ = base_type(ir_type(value));
GB_ASSERT(proc_type_->kind == Type_Proc);
TypeProc *pt = &proc_type_->Proc;
- set_procedure_abi_types(heap_allocator(), proc_type_);
+ set_procedure_abi_types(proc_type_);
if (is_call_expr_field_value(ce)) {
auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
@@ -7801,7 +8070,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
if (tv.value.kind != ExactValue_Invalid) {
// NOTE(bill): Edge case
- if (tv.value.kind != ExactValue_Compound &&
+ if (is_type_u8_array(tv.type) && tv.value.kind == ExactValue_String) {
+ return ir_add_module_constant(proc->module, tv.type, tv.value);
+ } else if (is_type_rune_array(tv.type) && tv.value.kind == ExactValue_String) {
+ return ir_add_module_constant(proc->module, tv.type, tv.value);
+ } else if (tv.value.kind != ExactValue_Compound &&
is_type_array(tv.type)) {
Type *elem = core_array_type(tv.type);
ExactValue value = convert_exact_value_for_type(tv.value, elem);
@@ -8207,7 +8480,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
irValue *addr = ir_address_from_load_or_generate_local(proc, right);
irValue *h = ir_gen_map_header(proc, addr, rt);
- irValue *key = ir_gen_map_key(proc, left, rt->Map.key);
+ irValue *key = ir_gen_map_hash(proc, left, rt->Map.key);
auto args = array_make<irValue *>(ir_allocator(), 2);
args[0] = h;
@@ -9024,8 +9297,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
if (cl->elems.count > 0) {
ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
- auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
- defer (array_free(&temp_data));
+ auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
// NOTE(bill): Separate value, gep, store into their own chunks
for_array(i, cl->elems) {
@@ -9123,8 +9395,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
if (cl->elems.count > 0) {
ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
- auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
- defer (array_free(&temp_data));
+ auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
// NOTE(bill): Separate value, gep, store into their own chunks
for_array(i, cl->elems) {
@@ -9232,8 +9503,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
irValue *data = ir_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32);
- auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
- defer (array_free(&temp_data));
+ auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
@@ -9574,7 +9844,7 @@ void ir_build_nested_proc(irProcedure *proc, AstProcLit *pd, Entity *e) {
name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid);
String name = make_string(name_text, name_len-1);
- set_procedure_abi_types(heap_allocator(), e->type);
+ set_procedure_abi_types(e->type);
irValue *value = ir_value_procedure(proc->module, e, e->type, pd->type, pd->body, name);
value->Proc.tags = pd->tags;
@@ -9673,7 +9943,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) {
return;
}
- set_procedure_abi_types(heap_allocator(), e->type);
+ set_procedure_abi_types(e->type);
irValue *value = ir_value_procedure(proc->module, e, e->type, pl->type, pl->body, name);
value->Proc.tags = pl->tags;
@@ -9692,7 +9962,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) {
}
}
-void ir_build_stmt_list(irProcedure *proc, Array<Ast *> stmts) {
+void ir_build_stmt_list(irProcedure *proc, Slice<Ast *> stmts) {
// NOTE(bill): Precollect constant entities
for_array(i, stmts) {
Ast *stmt = stmts[i];
@@ -9844,13 +10114,8 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, ir
elem = ir_emit_load(proc, elem);
irValue *entry = ir_emit_ptr_offset(proc, elem, idx);
- val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2));
-
- irValue *key_raw = ir_emit_struct_ep(proc, entry, 0);
- key_raw = ir_emit_struct_ep(proc, key_raw, 1);
- irValue *key = ir_emit_conv(proc, key_raw, alloc_type_pointer(expr_type->Map.key));
-
- idx = ir_emit_load(proc, key);
+ idx = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2));
+ val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 3));
break;
}
@@ -9995,7 +10260,7 @@ void ir_build_range_enum(irProcedure *proc, Type *enum_type, Type *val_type, irV
irValue *max_count = ir_const_int(enum_count);
irValue *ti = ir_type_info(proc, t);
- irValue *variant = ir_emit_struct_ep(proc, ti, 3);
+ irValue *variant = ir_emit_struct_ep(proc, ti, 4);
irValue *eti_ptr = ir_emit_conv(proc, variant, t_type_info_enum_ptr);
irValue *values = ir_emit_load(proc, ir_emit_struct_ep(proc, eti_ptr, 2));
irValue *values_data = ir_slice_elem(proc, values);
@@ -10179,7 +10444,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
String mangled_name = {};
{
- gbString str = gb_string_make_length(heap_allocator(), proc->name.text, proc->name.len);
+ gbString str = gb_string_make_length(permanent_allocator(), proc->name.text, proc->name.len);
str = gb_string_appendc(str, "-");
str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id);
mangled_name.text = cast(u8 *)str;
@@ -10902,7 +11167,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
ast_node(body, BlockStmt, ss->body);
- Array<Ast *> default_stmts = {};
+ Slice<Ast *> default_stmts = {};
irBlock *default_fall = nullptr;
irBlock *default_block = nullptr;
@@ -11349,6 +11614,9 @@ void ir_begin_procedure_body(irProcedure *proc) {
bool ir_remove_dead_instr(irProcedure *proc) {
+ if (proc->ignore_dead_instr) {
+ return false;
+ }
isize elimination_count = 0;
retry:
#if 1
@@ -11471,11 +11739,11 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) {
void ir_build_proc(irValue *value, irProcedure *parent) {
irProcedure *proc = &value->Proc;
- set_procedure_abi_types(heap_allocator(), proc->type);
+ set_procedure_abi_types(proc->type);
proc->parent = parent;
- if (proc->body != nullptr) {
+ if (proc->body != nullptr && proc->body->kind != Ast_Invalid) {
u64 prev_state_flags = proc->module->state_flags;
if (proc->tags != 0) {
@@ -11577,6 +11845,8 @@ void ir_init_module(irModule *m, Checker *c) {
map_init(&m->debug_info, heap_allocator());
map_init(&m->entity_names, heap_allocator());
map_init(&m->anonymous_proc_lits, heap_allocator());
+ map_init(&m->equal_procs, heap_allocator());
+ map_init(&m->hasher_procs, heap_allocator());
array_init(&m->procs, heap_allocator());
array_init(&m->procs_to_generate, heap_allocator());
array_init(&m->foreign_library_paths, heap_allocator());
@@ -11860,6 +12130,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
// Useful types
Type *t_i64_slice_ptr = alloc_type_pointer(alloc_type_slice(t_i64));
Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string));
+ Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
+ Type *t_type_info_flags = type_info_flags_entity->type;
i32 type_info_member_types_index = 0;
i32 type_info_member_names_index = 0;
@@ -11879,11 +12151,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
irValue *tag = nullptr;
irValue *ti_ptr = ir_emit_array_epi(proc, ir_global_type_info_data, cast(i32)entry_index);
- irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 3);
+ irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 4);
+
+ irValue *type_info_flags = ir_value_constant(t_type_info_flags, exact_value_i64(type_info_flags_of_type(t)));
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 0), ir_const_int(type_size_of(t)));
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 1), ir_const_int(type_align_of(t)));
- ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), ir_typeid(proc->module, t));
+ ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), type_info_flags);
+ ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 3), ir_typeid(proc->module, t));
switch (t->kind) {
@@ -11897,6 +12172,21 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name);
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip);
+
+ if (t->Named.type_name->pkg) {
+ irValue *name = ir_const_string(proc->module, t->Named.type_name->pkg->name);
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), name);
+ }
+
+ String proc_name = {};
+ if (t->Named.type_name->parent_proc_decl) {
+ DeclInfo *decl = t->Named.type_name->parent_proc_decl;
+ if (decl->entity && decl->entity->kind == Entity_Procedure) {
+ proc_name = decl->entity->token.string;
+ }
+ }
+ irValue *loc = ir_emit_source_code_location(proc, proc_name, t->Named.type_name->token.pos);
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), loc);
break;
}
@@ -12234,8 +12524,13 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union);
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align);
+ if (is_type_comparable(t) && !is_type_simple_compare(t)) {
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 8), ir_get_equal_proc_for_type(proc->module, t));
+ }
+
+
if (t->Struct.soa_kind != StructSoa_None) {
- irValue *kind = ir_emit_struct_ep(proc, tag, 8);
+ irValue *kind = ir_emit_struct_ep(proc, tag, 9);
Type *kind_type = type_deref(ir_type(kind));
irValue *soa_kind = ir_value_constant(kind_type, exact_value_i64(t->Struct.soa_kind));
@@ -12244,8 +12539,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
ir_emit_store(proc, kind, soa_kind);
- ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 9), soa_type);
- ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_len);
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_type);
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 11), soa_len);
}
}
@@ -12308,10 +12603,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
irValue *key = ir_emit_struct_ep(proc, tag, 0);
irValue *value = ir_emit_struct_ep(proc, tag, 1);
irValue *generated_struct = ir_emit_struct_ep(proc, tag, 2);
+ irValue *key_equal = ir_emit_struct_ep(proc, tag, 3);
+ irValue *key_hasher = ir_emit_struct_ep(proc, tag, 4);
ir_emit_store(proc, key, ir_get_type_info_ptr(proc, t->Map.key));
ir_emit_store(proc, value, ir_get_type_info_ptr(proc, t->Map.value));
ir_emit_store(proc, generated_struct, ir_get_type_info_ptr(proc, t->Map.generated_struct_type));
+ ir_emit_store(proc, key_equal, ir_get_equal_proc_for_type(proc->module, t->Map.key));
+ ir_emit_store(proc, key_hasher, ir_get_hasher_proc_for_type(proc->module, t->Map.key));
break;
}
@@ -12612,7 +12911,7 @@ void ir_gen_tree(irGen *s) {
Ast *type_expr = pl->type;
- set_procedure_abi_types(heap_allocator(), e->type);
+ set_procedure_abi_types(e->type);
irValue *p = ir_value_procedure(m, e, e->type, type_expr, body, name);
p->Proc.tags = pl->tags;
p->Proc.inlining = pl->inlining;
@@ -12646,7 +12945,7 @@ void ir_gen_tree(irGen *s) {
#if defined(GB_SYSTEM_WINDOWS)
- if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) {
+ if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main && !build_context.no_entry_point) {
// DllMain :: proc(inst: rawptr, reason: u32, reserved: rawptr) -> i32
String name = str_lit("DllMain");
Type *proc_params = alloc_type_tuple();
@@ -12717,7 +13016,7 @@ void ir_gen_tree(irGen *s) {
ir_emit_return(proc, v_one32);
}
#endif
- if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
+ if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) && !build_context.no_entry_point) {
// main :: proc(argc: i32, argv: ^^u8) -> i32
String name = str_lit("main");
@@ -12784,11 +13083,18 @@ void ir_gen_tree(irGen *s) {
ir_fill_slice(proc, global_args, argv, ir_emit_conv(proc, argc, t_int));
ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime));
- {
+ Array<irValue *> empty_args = {};
+ if (build_context.command_kind == Command_test) {
+ for_array(i, m->info->testing_procedures) {
+ Entity *e = m->info->testing_procedures[i];
+ irValue **found = map_get(&proc->module->values, hash_entity(e));
+ GB_ASSERT(found != nullptr);
+ ir_emit_call(proc, *found, empty_args);
+ }
+ } else {
irValue **found = map_get(&proc->module->values, hash_entity(entry_point));
if (found != nullptr) {
- Array<irValue *> args = {};
- ir_emit_call(proc, *found, args);
+ ir_emit_call(proc, *found, empty_args);
}
}
@@ -12796,7 +13102,7 @@ void ir_gen_tree(irGen *s) {
}
#if defined(GB_SYSTEM_WINDOWS)
- if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt) {
+ if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt && !build_context.no_entry_point) {
s->print_chkstk = true;
{
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index ceb95c5c3..a58ddbe0f 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -76,7 +76,6 @@ void ir_write_u64(irFileBuffer *f, u64 i) {
}
void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) {
if (x.len == 2) {
- gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
u64 words[2] = {};
BigInt y = x;
if (swap_endian) {
@@ -88,9 +87,8 @@ void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_en
y.d.words = words;
}
- String s = big_int_to_string(a, &y, 10);
+ String s = big_int_to_string(temporary_allocator(), &y, 10);
ir_write_string(f, s);
- gb_free(a, s.text);
} else {
i64 i = 0;
if (x.neg) {
@@ -296,7 +294,7 @@ void ir_print_alignment_prefix_hack(irFileBuffer *f, i64 alignment) {
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
- set_procedure_abi_types(heap_allocator(), t);
+ set_procedure_abi_types(t);
GB_ASSERT(is_type_proc(t));
t = base_type(t);
@@ -325,7 +323,7 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
- set_procedure_abi_types(heap_allocator(), t);
+ set_procedure_abi_types(t);
i64 word_bits = 8*build_context.word_size;
t = base_type(t);
@@ -736,6 +734,28 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
i64 count = type->Array.count;
Type *elem = type->Array.elem;
+
+ if (is_type_rune_array(type)) {
+ Rune rune;
+ isize offset = 0;
+ isize width = 1;
+ String s = value.value_string;
+ ir_write_byte(f, '[');
+ for (i64 i = 0; i < count && offset < s.len; i++) {
+ width = gb_utf8_decode(s.text+offset, s.len-offset, &rune);
+ if (i > 0) ir_write_str_lit(f, ", ");
+ ir_print_type(f, m, elem);
+ ir_write_byte(f, ' ');
+ ir_print_exact_value(f, m, exact_value_i64(rune), elem);
+ offset += width;
+ }
+ GB_ASSERT(offset == s.len);
+
+ ir_write_byte(f, ']');
+ return;
+ }
+
+
ir_write_byte(f, '[');
for (i64 i = 0; i < count; i++) {
@@ -747,7 +767,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
ir_write_byte(f, ']');
return;
- } else if (is_type_array(type) &&
+ } else if (is_type_array(type) &&
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_String &&
value.kind != ExactValue_Compound) {
@@ -798,7 +818,11 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
GB_ASSERT(is_type_array(type));
ir_write_str_lit(f, "c\"");
ir_print_escape_string(f, str, false, false);
- ir_write_str_lit(f, "\\00\"");
+ if (type->Array.count == str.len) {
+ ir_write_str_lit(f, "\"");
+ } else {
+ ir_write_str_lit(f, "\\00\"");
+ }
} else if (is_type_cstring(t)) {
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
// of the .ll file
@@ -812,7 +836,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
ir_write_str_lit(f, ", ");
ir_print_type(f, m, t_i32);
ir_write_str_lit(f, " 0, i32 0)");
- }else {
+ } else {
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
// of the .ll file
irValue *str_array = ir_add_global_string_array(m, str);
@@ -929,9 +953,9 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
ir_write_byte(f, ' ');
ir_write_byte(f, '{');
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
- ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
+ ir_print_exact_value(f, m, exact_value_float(value.value_complex->real), ft);
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
- ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
+ ir_print_exact_value(f, m, exact_value_float(value.value_complex->imag), ft);
ir_write_byte(f, '}');
break;
}
@@ -944,13 +968,13 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
ir_write_byte(f, ' ');
ir_write_byte(f, '{');
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
- ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft);
+ ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->imag), ft);
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
- ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft);
+ ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->jmag), ft);
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
- ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft);
+ ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->kmag), ft);
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
- ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft);
+ ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->real), ft);
ir_write_byte(f, '}');
break;
}
@@ -1406,7 +1430,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
irValue *line = value->SourceCodeLocation.line;
irValue *column = value->SourceCodeLocation.column;
irValue *procedure = value->SourceCodeLocation.procedure;
- u64 hash = value->SourceCodeLocation.hash;
ir_write_byte(f, '{');
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, file, t_string);
@@ -1416,8 +1439,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
ir_print_type(f, m, t_int); ir_write_byte(f, ' '); ir_print_value(f, m, column, t_int);
ir_write_string(f, str_lit(", "));
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, procedure, t_string);
- ir_write_string(f, str_lit(", "));
- ir_print_type(f, m, t_u64); ir_write_byte(f, ' '); ir_write_u64(f, hash);
ir_write_byte(f, '}');
break;
}
@@ -1551,7 +1572,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
break;
case BuiltinProc_cpu_relax:
- ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
+ } else {
+ // ir_write_str_lit(f, "call void asm sideeffect \"yield\", \"\"()");
+ }
break;
default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
}
@@ -2189,7 +2214,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
irInstrCall *call = &instr->Call;
Type *proc_type = base_type(ir_type(call->value));
GB_ASSERT(is_type_proc(proc_type));
- set_procedure_abi_types(heap_allocator(), proc_type);
+ set_procedure_abi_types(proc_type);
bool is_c_vararg = proc_type->Proc.c_vararg;
Type *result_type = call->type;
@@ -2396,7 +2421,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
- set_procedure_abi_types(heap_allocator(), proc->type);
+ set_procedure_abi_types(proc->type);
if (proc->body == nullptr) {
ir_write_str_lit(f, "declare ");
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
new file mode 100644
index 000000000..77d4b42b0
--- /dev/null
+++ b/src/llvm_abi.cpp
@@ -0,0 +1,959 @@
+enum lbArgKind {
+ lbArg_Direct,
+ lbArg_Indirect,
+ lbArg_Ignore,
+};
+
+struct lbArgType {
+ lbArgKind kind;
+ LLVMTypeRef type;
+ LLVMTypeRef cast_type; // Optional
+ LLVMTypeRef pad_type; // Optional
+ LLVMAttributeRef attribute; // Optional
+};
+
+lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) {
+ return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr};
+}
+lbArgType lb_arg_type_direct(LLVMTypeRef type) {
+ return lb_arg_type_direct(type, nullptr, nullptr, nullptr);
+}
+
+lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) {
+ return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr};
+}
+
+lbArgType lb_arg_type_ignore(LLVMTypeRef type) {
+ return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr};
+}
+
+struct lbFunctionType {
+ LLVMContextRef ctx;
+ ProcCallingConvention calling_convention;
+ Array<lbArgType> args;
+ lbArgType ret;
+};
+
+i64 llvm_align_formula(i64 off, i64 a) {
+ return (off + a - 1) / a * a;
+}
+
+
+bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) {
+ if (type == nullptr) {
+ return false;
+ }
+ return LLVMGetTypeKind(type) == kind;
+}
+
+LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) {
+ unsigned arg_count = cast(unsigned)ft->args.count;
+ unsigned offset = 0;
+
+ LLVMTypeRef ret = nullptr;
+ if (ft->ret.kind == lbArg_Direct) {
+ if (ft->ret.cast_type != nullptr) {
+ ret = ft->ret.cast_type;
+ } else {
+ ret = ft->ret.type;
+ }
+ } else if (ft->ret.kind == lbArg_Indirect) {
+ offset += 1;
+ ret = LLVMVoidTypeInContext(ft->ctx);
+ } else if (ft->ret.kind == lbArg_Ignore) {
+ ret = LLVMVoidTypeInContext(ft->ctx);
+ }
+ GB_ASSERT_MSG(ret != nullptr, "%d", ft->ret.kind);
+
+ unsigned maximum_arg_count = offset+arg_count;
+ LLVMTypeRef *args = gb_alloc_array(heap_allocator(), LLVMTypeRef, maximum_arg_count);
+ if (offset == 1) {
+ GB_ASSERT(ft->ret.kind == lbArg_Indirect);
+ args[0] = LLVMPointerType(ft->ret.type, 0);
+ }
+
+ unsigned arg_index = offset;
+ for (unsigned i = 0; i < arg_count; i++) {
+ lbArgType *arg = &ft->args[i];
+ if (arg->kind == lbArg_Direct) {
+ LLVMTypeRef arg_type = nullptr;
+ if (ft->args[i].cast_type != nullptr) {
+ arg_type = arg->cast_type;
+ } else {
+ arg_type = arg->type;
+ }
+ args[arg_index++] = arg_type;
+ } else if (arg->kind == lbArg_Indirect) {
+ GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
+ args[arg_index++] = LLVMPointerType(arg->type, 0);
+ } else if (arg->kind == lbArg_Ignore) {
+ // ignore
+ }
+ }
+ unsigned total_arg_count = arg_index;
+ LLVMTypeRef func_type = LLVMFunctionType(ret, args, total_arg_count, is_var_arg);
+ return LLVMPointerType(func_type, 0);
+}
+
+
+void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCallingConvention calling_convention) {
+ if (ft == nullptr) {
+ return;
+ }
+ unsigned arg_count = cast(unsigned)ft->args.count;
+ unsigned offset = 0;
+ if (ft->ret.kind == lbArg_Indirect) {
+ offset += 1;
+ }
+
+ LLVMContextRef c = ft->ctx;
+ LLVMAttributeRef noalias_attr = lb_create_enum_attribute(c, "noalias", true);
+ LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(c, "nonnull", true);
+ LLVMAttributeRef nocapture_attr = lb_create_enum_attribute(c, "nocapture", true);
+
+ unsigned arg_index = offset;
+ for (unsigned i = 0; i < arg_count; i++) {
+ lbArgType *arg = &ft->args[i];
+ if (arg->kind == lbArg_Ignore) {
+ continue;
+ }
+
+ if (arg->attribute) {
+ LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute);
+ }
+
+ arg_index++;
+ }
+
+ if (offset != 0 && ft->ret.kind == lbArg_Indirect && ft->ret.attribute != nullptr) {
+ LLVMAddAttributeAtIndex(fn, offset, ft->ret.attribute);
+ LLVMAddAttributeAtIndex(fn, offset, noalias_attr);
+ }
+
+ lbCallingConventionKind cc_kind = lbCallingConvention_C;
+ // TODO(bill): Clean up this logic
+ if (build_context.metrics.os != TargetOs_js) {
+ cc_kind = lb_calling_convention_map[calling_convention];
+ }
+ LLVMSetFunctionCallConv(fn, cc_kind);
+ if (calling_convention == ProcCC_Odin) {
+ unsigned context_index = offset+arg_count;
+ LLVMAddAttributeAtIndex(fn, context_index, noalias_attr);
+ LLVMAddAttributeAtIndex(fn, context_index, nonnull_attr);
+ LLVMAddAttributeAtIndex(fn, context_index, nocapture_attr);
+ }
+
+}
+
+i64 lb_sizeof(LLVMTypeRef type);
+i64 lb_alignof(LLVMTypeRef type);
+
+i64 lb_sizeof(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMVoidTypeKind:
+ return 0;
+ case LLVMIntegerTypeKind:
+ {
+ unsigned w = LLVMGetIntTypeWidth(type);
+ return (w + 7)/8;
+ }
+ case LLVMFloatTypeKind:
+ return 4;
+ case LLVMDoubleTypeKind:
+ return 8;
+ case LLVMPointerTypeKind:
+ return build_context.word_size;
+ case LLVMStructTypeKind:
+ {
+ unsigned field_count = LLVMCountStructElementTypes(type);
+ i64 offset = 0;
+ if (LLVMIsPackedStruct(type)) {
+ for (unsigned i = 0; i < field_count; i++) {
+ LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
+ offset += lb_sizeof(field);
+ }
+ } else {
+ for (unsigned i = 0; i < field_count; i++) {
+ LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
+ i64 align = lb_alignof(field);
+ offset = llvm_align_formula(offset, align);
+ offset += lb_sizeof(field);
+ }
+ offset = llvm_align_formula(offset, lb_alignof(type));
+ }
+ return offset;
+ }
+ break;
+ case LLVMArrayTypeKind:
+ {
+ LLVMTypeRef elem = LLVMGetElementType(type);
+ i64 elem_size = lb_sizeof(elem);
+ i64 count = LLVMGetArrayLength(type);
+ i64 size = count * elem_size;
+ return size;
+ }
+ break;
+
+ case LLVMX86_MMXTypeKind:
+ return 8;
+ case LLVMVectorTypeKind:
+ {
+ LLVMTypeRef elem = LLVMGetElementType(type);
+ i64 elem_size = lb_sizeof(elem);
+ i64 count = LLVMGetVectorSize(type);
+ i64 size = count * elem_size;
+ return gb_clamp(next_pow2(size), 1, build_context.max_align);
+ }
+
+ }
+ GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type));
+
+ return 0;
+}
+
+i64 lb_alignof(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMVoidTypeKind:
+ return 1;
+ case LLVMIntegerTypeKind:
+ {
+ unsigned w = LLVMGetIntTypeWidth(type);
+ return gb_clamp((w + 7)/8, 1, build_context.max_align);
+ }
+ case LLVMFloatTypeKind:
+ return 4;
+ case LLVMDoubleTypeKind:
+ return 8;
+ case LLVMPointerTypeKind:
+ return build_context.word_size;
+ case LLVMStructTypeKind:
+ {
+ if (LLVMIsPackedStruct(type)) {
+ return 1;
+ } else {
+ unsigned field_count = LLVMCountStructElementTypes(type);
+ i64 max_align = 1;
+ for (unsigned i = 0; i < field_count; i++) {
+ LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
+ i64 field_align = lb_alignof(field);
+ max_align = gb_max(max_align, field_align);
+ }
+ return max_align;
+ }
+ }
+ break;
+ case LLVMArrayTypeKind:
+ return lb_alignof(LLVMGetElementType(type));
+
+ case LLVMX86_MMXTypeKind:
+ return 8;
+ case LLVMVectorTypeKind:
+ {
+ LLVMTypeRef elem = LLVMGetElementType(type);
+ i64 elem_size = lb_sizeof(elem);
+ i64 count = LLVMGetVectorSize(type);
+ i64 size = count * elem_size;
+ return gb_clamp(next_pow2(size), 1, build_context.max_align);
+ }
+
+ }
+ GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type));
+
+ // LLVMValueRef v = LLVMAlignOf(type);
+ // GB_ASSERT(LLVMIsConstant(v));
+ // return LLVMConstIntGetSExtValue(v);
+ return 1;
+}
+
+#if 0
+Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) {
+ Type **found = map_get(&m->llvm_types, hash_pointer(type));
+ if (found) {
+ return *found;
+ }
+ GB_ASSERT_MSG(level < 64, "%s %d", LLVMPrintTypeToString(type), is_return);
+
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMVoidTypeKind:
+ return nullptr;
+ case LLVMIntegerTypeKind:
+ {
+ unsigned w = LLVMGetIntTypeWidth(type);
+ if (w == 1) {
+ return t_llvm_bool;
+ }
+ unsigned bytes = (w + 7)/8;
+ switch (bytes) {
+ case 1: return t_u8;
+ case 2: return t_u16;
+ case 4: return t_u32;
+ case 8: return t_u64;
+ case 16: return t_u128;
+ }
+ GB_PANIC("Unhandled integer type");
+ }
+ case LLVMFloatTypeKind:
+ return t_f32;
+ case LLVMDoubleTypeKind:
+ return t_f64;
+ case LLVMPointerTypeKind:
+ {
+ LLVMTypeRef elem = LLVMGetElementType(type);
+ if (lb_is_type_kind(elem, LLVMFunctionTypeKind)) {
+ unsigned param_count = LLVMCountParamTypes(elem);
+ LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count);
+ defer (gb_free(heap_allocator(), params));
+ LLVMGetParamTypes(elem, params);
+
+ Type **param_types = gb_alloc_array(heap_allocator(), Type *, param_count);
+ defer (gb_free(heap_allocator(), param_types));
+
+ for (unsigned i = 0; i < param_count; i++) {
+ param_types[i] = lb_abi_to_odin_type(m, params[i], false, level+1);
+ }
+
+ LLVMTypeRef ret = LLVMGetReturnType(elem);
+ Type *ret_type = lb_abi_to_odin_type(m, ret, true, level+1);
+
+ bool is_c_vararg = !!LLVMIsFunctionVarArg(elem);
+ return alloc_type_proc_from_types(param_types, param_count, ret_type, is_c_vararg);
+ }
+ return alloc_type_pointer(lb_abi_to_odin_type(m, elem, false, level+1));
+ }
+ case LLVMFunctionTypeKind:
+ GB_PANIC("LLVMFunctionTypeKind should not be seen on its own");
+ break;
+
+ case LLVMStructTypeKind:
+ {
+ unsigned field_count = LLVMCountStructElementTypes(type);
+ Type **fields = gb_alloc_array(heap_allocator(), Type *, field_count);
+ for (unsigned i = 0; i < field_count; i++) {
+ LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(type, i);
+ if (lb_is_type_kind(field_type, LLVMPointerTypeKind) && level > 0) {
+ fields[i] = t_rawptr;
+ } else {
+ fields[i] = lb_abi_to_odin_type(m, field_type, false, level+1);
+ }
+ }
+ if (is_return) {
+ return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type), false);
+ } else {
+ return alloc_type_struct_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type));
+ }
+ }
+ break;
+ case LLVMArrayTypeKind:
+ {
+
+ i64 count = LLVMGetArrayLength(type);
+ Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1);
+ return alloc_type_array(elem, count);
+ }
+ break;
+
+ case LLVMX86_MMXTypeKind:
+ return t_vector_x86_mmx;
+ case LLVMVectorTypeKind:
+ {
+ i64 count = LLVMGetVectorSize(type);
+ Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1);
+ return alloc_type_simd_vector(count, elem);
+ }
+
+ }
+ GB_PANIC("Unhandled type for lb_abi_to_odin_type -> %s", LLVMPrintTypeToString(type));
+
+ return 0;
+}
+#endif
+
+
+#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention)
+typedef LB_ABI_INFO(lbAbiInfoType);
+
+
+// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
+namespace lbAbi386 {
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+
+ LB_ABI_INFO(abi_info) {
+ lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+ ft->ctx = c;
+ ft->args = compute_arg_types(c, arg_types, arg_count);
+ ft->ret = compute_return_type(c, return_type, return_is_defined);
+ ft->calling_convention = calling_convention;
+ return ft;
+ }
+
+ lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+ if (!is_return && lb_sizeof(type) > 8) {
+ return lb_arg_type_indirect(type, nullptr);
+ }
+
+ if (build_context.metrics.os == TargetOs_windows &&
+ build_context.word_size == 8 &&
+ lb_is_type_kind(type, LLVMIntegerTypeKind) &&
+ type == LLVMIntTypeInContext(c, 128)) {
+ // NOTE(bill): Because Windows AMD64 is weird
+ LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
+ return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
+ }
+
+ LLVMAttributeRef attr = nullptr;
+ LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+ if (type == i1) {
+ attr = lb_create_enum_attribute(c, "zeroext", true);
+ }
+ return lb_arg_type_direct(type, nullptr, nullptr, attr);
+ }
+
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+ auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+ for (unsigned i = 0; i < arg_count; i++) {
+ LLVMTypeRef t = arg_types[i];
+ LLVMTypeKind kind = LLVMGetTypeKind(t);
+ i64 sz = lb_sizeof(t);
+ if (kind == LLVMStructTypeKind) {
+ if (sz == 0) {
+ args[i] = lb_arg_type_ignore(t);
+ } else {
+ args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true));
+ }
+ } else {
+ args[i] = non_struct(c, t, false);
+ }
+ }
+ return args;
+ }
+
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+ if (!return_is_defined) {
+ return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ } else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
+ i64 sz = lb_sizeof(return_type);
+ switch (sz) {
+ case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
+ case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
+ case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+ case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+ }
+ return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true));
+ }
+ return non_struct(c, return_type, true);
+ }
+};
+
+namespace lbAbiAmd64Win64 {
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+
+
+ LB_ABI_INFO(abi_info) {
+ lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+ ft->ctx = c;
+ ft->args = compute_arg_types(c, arg_types, arg_count);
+ ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
+ ft->calling_convention = calling_convention;
+ return ft;
+ }
+
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+ auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+ for (unsigned i = 0; i < arg_count; i++) {
+ LLVMTypeRef t = arg_types[i];
+ LLVMTypeKind kind = LLVMGetTypeKind(t);
+ if (kind == LLVMStructTypeKind) {
+ i64 sz = lb_sizeof(t);
+ switch (sz) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ args[i] = lb_arg_type_direct(t, LLVMIntTypeInContext(c, 8*cast(unsigned)sz), nullptr, nullptr);
+ break;
+ default:
+ args[i] = lb_arg_type_indirect(t, nullptr);
+ break;
+ }
+ } else {
+ args[i] = lbAbi386::non_struct(c, t, false);
+ }
+ }
+ return args;
+ }
+};
+
+// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
+namespace lbAbiAmd64SysV {
+ enum RegClass {
+ RegClass_NoClass,
+ RegClass_Int,
+ RegClass_SSEFs,
+ RegClass_SSEFv,
+ RegClass_SSEDs,
+ RegClass_SSEDv,
+ RegClass_SSEInt8,
+ RegClass_SSEInt16,
+ RegClass_SSEInt32,
+ RegClass_SSEInt64,
+ RegClass_SSEUp,
+ RegClass_X87,
+ RegClass_X87Up,
+ RegClass_ComplexX87,
+ RegClass_Memory,
+ };
+
+ bool is_sse(RegClass reg_class) {
+ switch (reg_class) {
+ case RegClass_SSEFs:
+ case RegClass_SSEFv:
+ case RegClass_SSEDv:
+ return true;
+ }
+ return false;
+ }
+
+ void all_mem(Array<RegClass> *cs) {
+ for_array(i, *cs) {
+ (*cs)[i] = RegClass_Memory;
+ }
+ }
+
+ enum Amd64TypeAttributeKind {
+ Amd64TypeAttribute_None,
+ Amd64TypeAttribute_ByVal,
+ Amd64TypeAttribute_StructRect,
+ };
+
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+ void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
+ void fixup(LLVMTypeRef t, Array<RegClass> *cls);
+ lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind);
+ Array<RegClass> classify(LLVMTypeRef t);
+ LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes);
+
+ LB_ABI_INFO(abi_info) {
+ lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+ ft->ctx = c;
+ ft->calling_convention = calling_convention;
+
+ ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+ for (unsigned i = 0; i < arg_count; i++) {
+ ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal);
+ }
+
+ if (return_is_defined) {
+ ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect);
+ } else {
+ ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ }
+
+ return ft;
+ }
+
+ bool is_mem_cls(Array<RegClass> const &cls, Amd64TypeAttributeKind attribute_kind) {
+ if (attribute_kind == Amd64TypeAttribute_ByVal) {
+ if (cls.count == 0) {
+ return false;
+ }
+ auto first = cls[0];
+ return first == RegClass_Memory || first == RegClass_X87 || first == RegClass_ComplexX87;
+ } else if (attribute_kind == Amd64TypeAttribute_StructRect) {
+ if (cls.count == 0) {
+ return false;
+ }
+ return cls[0] == RegClass_Memory;
+ }
+ return false;
+ }
+
+ bool is_register(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMIntegerTypeKind:
+ case LLVMFloatTypeKind:
+ case LLVMDoubleTypeKind:
+ case LLVMPointerTypeKind:
+ return true;
+ }
+ return false;
+ }
+
+ lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind) {
+ if (is_register(type)) {
+ LLVMAttributeRef attribute = nullptr;
+ if (type == LLVMInt1TypeInContext(c)) {
+ attribute = lb_create_enum_attribute(c, "zeroext", true);
+ }
+ return lb_arg_type_direct(type, nullptr, nullptr, attribute);
+ }
+
+ auto cls = classify(type);
+ if (is_mem_cls(cls, attribute_kind)) {
+ LLVMAttributeRef attribute = nullptr;
+ if (attribute_kind == Amd64TypeAttribute_ByVal) {
+ attribute = lb_create_enum_attribute(c, "byval", true);
+ } else if (attribute_kind == Amd64TypeAttribute_StructRect) {
+ attribute = lb_create_enum_attribute(c, "sret", true);
+ }
+ return lb_arg_type_indirect(type, attribute);
+ } else {
+ return lb_arg_type_direct(type, llreg(c, cls), nullptr, nullptr);
+ }
+ }
+
+ lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
+ LLVMAttributeRef attr = nullptr;
+ LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+ if (type == i1) {
+ attr = lb_create_enum_attribute(c, "zeroext", true);
+ }
+ return lb_arg_type_direct(type, nullptr, nullptr, attr);
+ }
+
+ Array<RegClass> classify(LLVMTypeRef t) {
+ i64 sz = lb_sizeof(t);
+ i64 words = (sz + 7)/8;
+ auto reg_classes = array_make<RegClass>(heap_allocator(), cast(isize)words);
+ if (words > 4) {
+ all_mem(&reg_classes);
+ } else {
+ classify_with(t, &reg_classes, 0, 0);
+ fixup(t, &reg_classes);
+ }
+ return reg_classes;
+ }
+
+ void unify(Array<RegClass> *cls, i64 i, RegClass newv) {
+ RegClass &oldv = (*cls)[i];
+ if (oldv == newv) {
+ return;
+ } else if (oldv == RegClass_NoClass) {
+ oldv = newv;
+ } else if (newv == RegClass_NoClass) {
+ return;
+ } else if (oldv == RegClass_Memory || newv == RegClass_Memory) {
+ return;
+ } else if (oldv == RegClass_Int || newv == RegClass_Int) {
+ return;
+ } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 ||
+ newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) {
+ oldv = RegClass_Memory;
+ } else {
+ oldv = newv;
+ }
+ }
+
+ void fixup(LLVMTypeRef t, Array<RegClass> *cls) {
+ i64 i = 0;
+ i64 e = cls->count;
+ if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) {
+ RegClass &oldv = (*cls)[i];
+ if (is_sse(oldv)) {
+ for (i++; i < e; i++) {
+ if (oldv != RegClass_SSEUp) {
+ all_mem(cls);
+ return;
+ }
+ }
+ } else {
+ all_mem(cls);
+ return;
+ }
+ } else {
+ while (i < e) {
+ RegClass &oldv = (*cls)[i];
+ if (oldv == RegClass_Memory) {
+ all_mem(cls);
+ return;
+ } else if (oldv == RegClass_X87Up) {
+ // NOTE(bill): Darwin
+ all_mem(cls);
+ return;
+ } else if (oldv == RegClass_SSEUp) {
+ oldv = RegClass_SSEDv;
+ } else if (is_sse(oldv)) {
+ i++;
+ while (i != e && oldv == RegClass_SSEUp) {
+ i++;
+ }
+ } else if (oldv == RegClass_X87) {
+ i++;
+ while (i != e && oldv == RegClass_X87Up) {
+ i++;
+ }
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ unsigned llvec_len(Array<RegClass> const &reg_classes, isize offset) {
+ unsigned len = 1;
+ for (isize i = offset+1; i < reg_classes.count; i++) {
+ if (reg_classes[offset] != RegClass_SSEFv && reg_classes[i] != RegClass_SSEUp) {
+ break;
+ }
+ len++;
+ }
+ return len;
+ }
+
+
+ LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) {
+ auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count);
+ for_array(i, reg_classes) {
+ RegClass reg_class = reg_classes[i];
+ switch (reg_class) {
+ case RegClass_Int:
+ array_add(&types, LLVMIntTypeInContext(c, 64));
+ break;
+ case RegClass_SSEFv:
+ case RegClass_SSEDv:
+ case RegClass_SSEInt8:
+ case RegClass_SSEInt16:
+ case RegClass_SSEInt32:
+ case RegClass_SSEInt64:
+ {
+ unsigned elems_per_word = 0;
+ LLVMTypeRef elem_type = nullptr;
+ switch (reg_class) {
+ case RegClass_SSEFv:
+ elems_per_word = 2;
+ elem_type = LLVMFloatTypeInContext(c);
+ break;
+ case RegClass_SSEDv:
+ elems_per_word = 1;
+ elem_type = LLVMDoubleTypeInContext(c);
+ break;
+ case RegClass_SSEInt8:
+ elems_per_word = 64/8;
+ elem_type = LLVMIntTypeInContext(c, 8);
+ break;
+ case RegClass_SSEInt16:
+ elems_per_word = 64/16;
+ elem_type = LLVMIntTypeInContext(c, 16);
+ break;
+ case RegClass_SSEInt32:
+ elems_per_word = 64/32;
+ elem_type = LLVMIntTypeInContext(c, 32);
+ break;
+ case RegClass_SSEInt64:
+ elems_per_word = 64/64;
+ elem_type = LLVMIntTypeInContext(c, 64);
+ break;
+ }
+
+ unsigned vec_len = llvec_len(reg_classes, i);
+ LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
+ array_add(&types, vec_type);
+ i += vec_len;
+ continue;
+ }
+ break;
+ case RegClass_SSEFs:
+ array_add(&types, LLVMFloatTypeInContext(c));
+ break;
+ case RegClass_SSEDs:
+ array_add(&types, LLVMDoubleTypeInContext(c));
+ break;
+ default:
+ GB_PANIC("Unhandled RegClass");
+ }
+ }
+
+ GB_ASSERT(types.count != 0);
+ if (types.count == 1) {
+ return types[0];
+ }
+ return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
+ }
+
+ void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
+ i64 t_align = lb_alignof(t);
+ i64 t_size = lb_sizeof(t);
+
+ i64 misalign = off % t_align;
+ if (misalign != 0) {
+ i64 e = (off + t_size + 7) / 8;
+ for (i64 i = off / 8; i < e; i++) {
+ unify(cls, ix+i, RegClass_Memory);
+ }
+ return;
+ }
+
+ switch (LLVMGetTypeKind(t)) {
+ case LLVMIntegerTypeKind:
+ case LLVMPointerTypeKind:
+ unify(cls, ix + off/8, RegClass_Int);
+ break;
+ case LLVMFloatTypeKind:
+ unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs);
+ break;
+ case LLVMDoubleTypeKind:
+ unify(cls, ix + off/8, RegClass_SSEDs);
+ break;
+ case LLVMStructTypeKind:
+ {
+ LLVMBool packed = LLVMIsPackedStruct(t);
+ unsigned field_count = LLVMCountStructElementTypes(t);
+
+ i64 field_off = off;
+ for (unsigned field_index = 0; field_index < field_count; field_index++) {
+ LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(t, field_index);
+ if (!packed) {
+ field_off = llvm_align_formula(field_off, lb_alignof(field_type));
+ }
+ classify_with(field_type, cls, ix, field_off);
+ field_off += lb_sizeof(field_type);
+ }
+ }
+ break;
+ case LLVMArrayTypeKind:
+ {
+ i64 len = LLVMGetArrayLength(t);
+ LLVMTypeRef elem = LLVMGetElementType(t);
+ i64 elem_sz = lb_sizeof(elem);
+ for (i64 i = 0; i < len; i++) {
+ classify_with(elem, cls, ix, off + i*elem_sz);
+ }
+ }
+ break;
+ case LLVMVectorTypeKind:
+ {
+ i64 len = LLVMGetVectorSize(t);
+ LLVMTypeRef elem = LLVMGetElementType(t);
+ i64 elem_sz = lb_sizeof(elem);
+ LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
+ RegClass reg = RegClass_NoClass;
+ switch (elem_kind) {
+ case LLVMIntegerTypeKind:
+ switch (LLVMGetIntTypeWidth(elem)) {
+ case 8: reg = RegClass_SSEInt8;
+ case 16: reg = RegClass_SSEInt16;
+ case 32: reg = RegClass_SSEInt32;
+ case 64: reg = RegClass_SSEInt64;
+ default:
+ GB_PANIC("Unhandled integer width for vector type");
+ }
+ break;
+ case LLVMFloatTypeKind:
+ reg = RegClass_SSEFv;
+ break;
+ case LLVMDoubleTypeKind:
+ reg = RegClass_SSEDv;
+ break;
+ default:
+ GB_PANIC("Unhandled vector element type");
+ }
+
+ for (i64 i = 0; i < len; i++) {
+ unify(cls, ix + (off + i*elem_sz)/8, reg);
+ // NOTE(bill): Everything after the first one is the upper
+ // half of a register
+ reg = RegClass_SSEUp;
+ }
+ }
+ break;
+ default:
+ GB_PANIC("Unhandled type");
+ break;
+ }
+ }
+
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+ auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+ for (unsigned i = 0; i < arg_count; i++) {
+ LLVMTypeRef t = arg_types[i];
+ LLVMTypeKind kind = LLVMGetTypeKind(t);
+ if (kind == LLVMStructTypeKind) {
+ i64 sz = lb_sizeof(t);
+ if (sz == 0) {
+ args[i] = lb_arg_type_ignore(t);
+ } else {
+ args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true));
+ }
+ } else {
+ args[i] = non_struct(c, t);
+ }
+ }
+ return args;
+ }
+
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+ if (!return_is_defined) {
+ return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ } else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
+ i64 sz = lb_sizeof(return_type);
+ switch (sz) {
+ case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
+ case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
+ case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+ case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+ }
+ return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true));
+ } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
+ return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr);
+ }
+ return non_struct(c, return_type);
+ }
+};
+
+
+namespace lbAbiAarch64 {
+ LB_ABI_INFO(abi_info) {
+ lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+ ft->ctx = c;
+ // ft->args = compute_arg_types(c, arg_types, arg_count);
+ // ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
+ // ft->calling_convention = calling_convention;
+ return ft;
+ }
+}
+
+
+LB_ABI_INFO(lb_get_abi_info) {
+ switch (calling_convention) {
+ case ProcCC_None:
+ case ProcCC_PureNone:
+ case ProcCC_InlineAsm:
+ {
+ lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+ ft->ctx = c;
+ ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+ for (unsigned i = 0; i < arg_count; i++) {
+ ft->args[i] = lb_arg_type_direct(arg_types[i]);
+ }
+ if (return_is_defined) {
+ ft->ret = lb_arg_type_direct(return_type);
+ } else {
+ ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ }
+ ft->calling_convention = calling_convention;
+ return ft;
+ }
+ }
+
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ if (build_context.metrics.os == TargetOs_windows) {
+ return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ } else {
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ }
+ } else if (build_context.metrics.arch == TargetArch_386) {
+ return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ } else if (build_context.metrics.arch == TargetArch_wasm32) {
+ return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ }
+ GB_PANIC("Unsupported ABI");
+ return {};
+}
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 4abc65ab4..d92108044 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -1,4 +1,11 @@
#include "llvm_backend.hpp"
+#include "llvm_abi.cpp"
+
+#ifdef USE_NEW_LLVM_ABI_SYSTEM
+#define USE_LLVM_ABI 1
+#else
+#define USE_LLVM_ABI 0
+#endif
gb_global lbAddr lb_global_type_info_data = {};
gb_global lbAddr lb_global_type_info_member_types = {};
@@ -135,9 +142,9 @@ lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
case lbAddr_Map: {
Type *map_type = base_type(addr.map.type);
lbValue h = lb_gen_map_header(p, addr.addr, map_type);
- lbValue key = lb_gen_map_key(p, addr.map.key, map_type->Map.key);
+ lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = h;
args[1] = key;
@@ -202,7 +209,7 @@ void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index, lbValue le
lbValue line = lb_const_int(p->module, t_int, token.pos.line);
lbValue column = lb_const_int(p->module, t_int, token.pos.column);
- auto args = array_make<lbValue>(heap_allocator(), 5);
+ auto args = array_make<lbValue>(permanent_allocator(), 5);
args[0] = file;
args[1] = line;
args[2] = column;
@@ -226,7 +233,7 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu
high = lb_emit_conv(p, high, t_int);
if (!lower_value_used) {
- auto args = array_make<lbValue>(heap_allocator(), 5);
+ auto args = array_make<lbValue>(permanent_allocator(), 5);
args[0] = file;
args[1] = line;
args[2] = column;
@@ -238,7 +245,7 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu
// No need to convert unless used
low = lb_emit_conv(p, low, t_int);
- auto args = array_make<lbValue>(heap_allocator(), 6);
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = file;
args[1] = line;
args[2] = column;
@@ -350,7 +357,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
}
- auto args = array_make<lbValue>(heap_allocator(), gb_max(arg_count, param_count));
+ auto args = array_make<lbValue>(permanent_allocator(), gb_max(arg_count, param_count));
args[0] = ptr;
args[1] = index;
args[2] = value;
@@ -459,7 +466,11 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
GB_ASSERT(value.value != nullptr);
value = lb_emit_conv(p, value, lb_addr_type(addr));
- LLVMBuildStore(p->builder, value.value, addr.addr.value);
+ if (USE_LLVM_ABI) {
+ lb_emit_store(p, addr.addr, value);
+ } else {
+ LLVMBuildStore(p->builder, value.value, addr.addr.value);
+ }
}
void lb_const_store(lbValue ptr, lbValue value) {
@@ -480,11 +491,25 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
Type *ca = core_type(a);
if (ca->kind == Type_Basic) {
GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type));
- } else {
- GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type));
}
- LLVMBuildStore(p->builder, value.value, ptr.value);
+ if (is_type_proc(a)) {
+ // NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be
+ // stored as regular pointer with no procedure information
+
+ LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value));
+ LLVMValueRef v = LLVMBuildPointerCast(p->builder, value.value, src_t, "");
+ LLVMBuildStore(p->builder, v, ptr.value);
+ } else {
+ Type *ca = core_type(a);
+ if (ca->kind == Type_Basic || ca->kind == Type_Proc) {
+ GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type));
+ } else {
+ GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type));
+ }
+
+ LLVMBuildStore(p->builder, value.value, ptr.value);
+ }
}
lbValue lb_emit_load(lbProcedure *p, lbValue value) {
@@ -567,9 +592,9 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
Type *map_type = base_type(addr.map.type);
lbAddr v = lb_add_local_generated(p, map_type->Map.lookup_result_type, true);
lbValue h = lb_gen_map_header(p, addr.addr, map_type);
- lbValue key = lb_gen_map_key(p, addr.map.key, map_type->Map.key);
+ lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = h;
args[1] = key;
@@ -633,12 +658,14 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
res.value = LLVMBuildZExtOrBitCast(p->builder, field, lb_type(p->module, int_type), "");
return res;
} else if (addr.kind == lbAddr_Context) {
+ lbValue a = addr.addr;
+ a.value = LLVMBuildPointerCast(p->builder, a.value, lb_type(p->module, t_context_ptr), "");
+
if (addr.ctx.sel.index.count > 0) {
- lbValue a = addr.addr;
lbValue b = lb_emit_deep_field_gep(p, a, addr.ctx.sel);
return lb_emit_load(p, b);
} else {
- return lb_emit_load(p, addr.addr);
+ return lb_emit_load(p, a);
}
} else if (addr.kind == lbAddr_SoaVariable) {
Type *t = type_deref(addr.addr.type);
@@ -748,7 +775,6 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
}
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
- gbAllocator a = heap_allocator();
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
lb_emit_store(p, underlying, variant);
@@ -758,10 +784,9 @@ void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant
void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) {
unsigned field_count = LLVMCountStructElementTypes(src);
- LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+ LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
LLVMGetStructElementTypes(src, fields);
LLVMStructSetBody(dst, fields, field_count, LLVMIsPackedStruct(src));
- gb_free(heap_allocator(), fields);
}
LLVMTypeRef lb_alignment_prefix_type_hack(lbModule *m, i64 alignment) {
@@ -796,8 +821,6 @@ bool lb_is_elem_const(Ast *elem, Type *elem_type) {
}
String lb_mangle_name(lbModule *m, Entity *e) {
- gbAllocator a = heap_allocator();
-
String name = e->token.string;
AstPackage *pkg = e->pkg;
@@ -823,7 +846,7 @@ String lb_mangle_name(lbModule *m, Entity *e) {
max_len += 21;
}
- char *new_name = gb_alloc_array(a, char, max_len);
+ char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
isize new_name_len = gb_snprintf(
new_name, max_len,
"%.*s.%.*s", LIT(pkgn), LIT(name)
@@ -874,7 +897,7 @@ String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p) {
if (p != nullptr) {
isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
- char *name_text = gb_alloc_array(heap_allocator(), char, name_len);
+ char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
u32 guid = ++p->module->nested_type_name_guid;
name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid);
@@ -884,7 +907,7 @@ String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p) {
} else {
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
- char *name_text = gb_alloc_array(heap_allocator(), char, name_len);
+ char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
static u32 guid = 0;
guid += 1;
name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid);
@@ -1093,7 +1116,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return type;
}
- case Basic_typeid: return LLVMIntType(8*cast(unsigned)build_context.word_size);
+ case Basic_typeid: return LLVMIntTypeInContext(m->ctx, 8*cast(unsigned)build_context.word_size);
// Endian Specific Types
case Basic_i16le: return LLVMInt16TypeInContext(ctx);
@@ -1132,7 +1155,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
switch (base->kind) {
case Type_Basic:
- return lb_type(m, base);
+ return lb_type_internal(m, base);
case Type_Named:
case Type_Generic:
@@ -1141,7 +1164,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
break;
case Type_Opaque:
- return lb_type(m, base->Opaque.elem);
+ return lb_type_internal(m, base->Opaque.elem);
case Type_Pointer:
case Type_Array:
@@ -1152,21 +1175,21 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_Enum:
case Type_BitSet:
case Type_SimdVector:
- return lb_type(m, base);
+ return lb_type_internal(m, base);
// TODO(bill): Deal with this correctly. Can this be named?
case Type_Proc:
- return lb_type(m, base);
+ return lb_type_internal(m, base);
case Type_Tuple:
- return lb_type(m, base);
+ return lb_type_internal(m, base);
}
LLVMTypeRef *found = map_get(&m->types, hash_type(base));
if (found) {
LLVMTypeKind kind = LLVMGetTypeKind(*found);
if (kind == LLVMStructTypeKind) {
- char const *name = alloc_cstring(heap_allocator(), lb_get_entity_name(m, type->Named.type_name));
+ char const *name = alloc_cstring(permanent_allocator(), lb_get_entity_name(m, type->Named.type_name));
LLVMTypeRef llvm_type = LLVMGetTypeByName(m->mod, name);
if (llvm_type != nullptr) {
return llvm_type;
@@ -1183,7 +1206,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_Union:
case Type_BitField:
{
- char const *name = alloc_cstring(heap_allocator(), lb_get_entity_name(m, type->Named.type_name));
+ char const *name = alloc_cstring(permanent_allocator(), lb_get_entity_name(m, type->Named.type_name));
LLVMTypeRef llvm_type = LLVMGetTypeByName(m->mod, name);
if (llvm_type != nullptr) {
return llvm_type;
@@ -1196,7 +1219,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
- return lb_type(m, base);
+ return lb_type_internal(m, base);
}
case Type_Pointer:
@@ -1240,7 +1263,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
{
if (type->Struct.is_raw_union) {
unsigned field_count = 2;
- LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+ LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count);
i64 alignment = type_align_of(type);
unsigned size_of_union = cast(unsigned)type_size_of(type);
fields[0] = lb_alignment_prefix_type_hack(m, alignment);
@@ -1253,16 +1276,18 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
offset = 1;
}
+ m->internal_type_level += 1;
+ defer (m->internal_type_level -= 1);
+
unsigned field_count = cast(unsigned)(type->Struct.fields.count + offset);
- LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
- GB_ASSERT(fields != nullptr);
- defer (gb_free(heap_allocator(), fields));
+ LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
for_array(i, type->Struct.fields) {
Entity *field = type->Struct.fields[i];
fields[i+offset] = lb_type(m, field->type);
}
+
if (type->Struct.custom_align > 0) {
fields[0] = lb_alignment_prefix_type_hack(m, type->Struct.custom_align);
}
@@ -1315,12 +1340,15 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return lb_type(m, type->Tuple.variables[0]->type);
} else {
unsigned field_count = cast(unsigned)(type->Tuple.variables.count);
- LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
- defer (gb_free(heap_allocator(), fields));
+ LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
for_array(i, type->Tuple.variables) {
Entity *field = type->Tuple.variables[i];
- fields[i] = lb_type(m, field->type);
+
+ LLVMTypeRef param_type = nullptr;
+ param_type = lb_type(m, field->type);
+
+ fields[i] = param_type;
}
return LLVMStructTypeInContext(ctx, fields, field_count, type->Tuple.is_packed);
@@ -1328,64 +1356,150 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_Proc:
{
- set_procedure_abi_types(heap_allocator(), type);
+ if (USE_LLVM_ABI) {
+ if (m->internal_type_level > 256) { // TODO HACK(bill): is this really enough?
+ return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0);
+ } else {
+ unsigned param_count = 0;
+ if (type->Proc.calling_convention == ProcCC_Odin) {
+ param_count += 1;
+ }
- LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx);
- if (type->Proc.return_by_pointer) {
- // Void
- } else if (type->Proc.abi_compat_result_type != nullptr) {
- return_type = lb_type(m, type->Proc.abi_compat_result_type);
- }
+ if (type->Proc.param_count != 0) {
+ GB_ASSERT(type->Proc.params->kind == Type_Tuple);
+ for_array(i, type->Proc.params->Tuple.variables) {
+ Entity *e = type->Proc.params->Tuple.variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+ param_count += 1;
+ }
+ }
+ m->internal_type_level += 1;
+ defer (m->internal_type_level -= 1);
+
+ LLVMTypeRef ret = nullptr;
+ LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count);
+ if (type->Proc.result_count != 0) {
+ Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
+ ret = lb_type(m, single_ret);
+ if (ret != nullptr) {
+ if (is_type_boolean(single_ret) &&
+ is_calling_convention_none(type->Proc.calling_convention) &&
+ type_size_of(single_ret) <= 1) {
+ ret = LLVMInt1TypeInContext(m->ctx);
+ }
+ }
+ }
- isize extra_param_count = 0;
- if (type->Proc.return_by_pointer) {
- extra_param_count += 1;
- }
- if (type->Proc.calling_convention == ProcCC_Odin) {
- extra_param_count += 1;
- }
+ isize param_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;
+ }
- isize param_count = type->Proc.abi_compat_params.count + extra_param_count;
- auto param_types = array_make<LLVMTypeRef>(heap_allocator(), 0, param_count);
- defer (array_free(&param_types));
+ Type *e_type = reduce_tuple_to_single_type(e->type);
+
+ LLVMTypeRef param_type = nullptr;
+ if (is_type_boolean(e_type) &&
+ type_size_of(e_type) <= 1) {
+ param_type = LLVMInt1TypeInContext(m->ctx);
+ } else {
+ if (is_type_proc(e_type)) {
+ param_type = lb_type(m, t_rawptr);
+ } else {
+ param_type = lb_type(m, e_type);
+ }
+ }
- if (type->Proc.return_by_pointer) {
- array_add(&param_types, LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0));
- }
+ params[param_index++] = param_type;
+ }
+ }
+ if (param_index < param_count) {
+ params[param_index++] = lb_type(m, t_rawptr);
+ // params[param_index++] = lb_type(m, t_context_ptr);
+ }
+ GB_ASSERT(param_index == param_count);
- for_array(i, type->Proc.abi_compat_params) {
- Type *param = type->Proc.abi_compat_params[i];
- if (param == nullptr) {
- continue;
- }
- if (type->Proc.params->Tuple.variables[i]->flags & EntityFlag_CVarArg) {
- GB_ASSERT(i+1 == type->Proc.abi_compat_params.count);
- break;
+
+ lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention);
+ map_set(&m->function_type_map, hash_type(type), ft);
+ LLVMTypeRef new_abi_fn_ptr_type = lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg);
+ LLVMTypeRef new_abi_fn_type = LLVMGetElementType(new_abi_fn_ptr_type);
+
+ // LLVMTypeRef new_ret = LLVMGetReturnType(new_abi_fn_type);
+ // LLVMTypeRef old_ret = LLVMGetReturnType(old_abi_fn_type);
+ // unsigned new_count = LLVMCountParamTypes(new_abi_fn_type);
+ // unsigned old_count = LLVMCountParamTypes(old_abi_fn_type);
+ // GB_ASSERT_MSG(new_count == old_count, "%u %u, %s %s", new_count, old_count, LLVMPrintTypeToString(new_abi_fn_type), LLVMPrintTypeToString(old_abi_fn_type));
+ return new_abi_fn_ptr_type;
}
- if (is_type_tuple(param)) {
- param = base_type(param);
- for_array(j, param->Tuple.variables) {
- Entity *v = param->Tuple.variables[j];
- if (v->kind != Entity_Variable) {
- // Sanity check
+ } else {
+ LLVMTypeRef old_abi_fn_type = nullptr;
+
+ set_procedure_abi_types(type);
+ LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx);
+ if (type->Proc.return_by_pointer) {
+ // Void
+ } else if (type->Proc.abi_compat_result_type != nullptr) {
+ return_type = lb_type(m, type->Proc.abi_compat_result_type);
+ }
+
+ isize extra_param_count = 0;
+ if (type->Proc.return_by_pointer) {
+ extra_param_count += 1;
+ }
+ if (type->Proc.calling_convention == ProcCC_Odin) {
+ extra_param_count += 1;
+ }
+
+ isize param_count = type->Proc.abi_compat_params.count + extra_param_count;
+ auto param_types = array_make<LLVMTypeRef>(temporary_allocator(), 0, param_count);
+
+ if (type->Proc.return_by_pointer) {
+ array_add(&param_types, LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0));
+ }
+
+ for_array(i, type->Proc.abi_compat_params) {
+ Type *param = type->Proc.abi_compat_params[i];
+ if (param == nullptr) {
continue;
}
- array_add(&param_types, lb_type(m, v->type));
+ if (type->Proc.params->Tuple.variables[i]->flags & EntityFlag_CVarArg) {
+ GB_ASSERT(i+1 == type->Proc.abi_compat_params.count);
+ break;
+ }
+ if (is_type_tuple(param)) {
+ param = base_type(param);
+ for_array(j, param->Tuple.variables) {
+ Entity *v = param->Tuple.variables[j];
+ if (v->kind != Entity_Variable) {
+ // Sanity check
+ continue;
+ }
+ LLVMTypeRef t = lb_type(m, v->type);
+ array_add(&param_types, t);
+ }
+ } else {
+ array_add(&param_types, lb_type(m, param));
+ }
+ }
+ if (type->Proc.calling_convention == ProcCC_Odin) {
+ array_add(&param_types, lb_type(m, t_rawptr));
+ // array_add(&param_types, lb_type(m, t_context_ptr));
}
- } else {
- array_add(&param_types, lb_type(m, param));
- }
- }
- if (type->Proc.calling_convention == ProcCC_Odin) {
- array_add(&param_types, lb_type(m, t_context_ptr));
- }
- LLVMTypeRef t = LLVMFunctionType(return_type, param_types.data, cast(unsigned)param_types.count, type->Proc.c_vararg);
- return LLVMPointerType(t, 0);
+ old_abi_fn_type = LLVMFunctionType(return_type, param_types.data, cast(unsigned)param_types.count, type->Proc.c_vararg);
+ return LLVMPointerType(old_abi_fn_type, 0);
+ }
}
+
break;
case Type_BitFieldValue:
- return LLVMIntType(type->BitFieldValue.bits);
+ return LLVMIntTypeInContext(m->ctx, type->BitFieldValue.bits);
case Type_BitField:
{
@@ -1393,12 +1507,11 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
{
GB_ASSERT(type->BitField.fields.count == type->BitField.sizes.count);
unsigned field_count = cast(unsigned)type->BitField.fields.count;
- LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
- defer (gb_free(heap_allocator(), fields));
+ LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
for_array(i, type->BitField.sizes) {
u32 size = type->BitField.sizes[i];
- fields[i] = LLVMIntType(size);
+ fields[i] = LLVMIntTypeInContext(m->ctx, size);
}
internal_type = LLVMStructTypeInContext(ctx, fields, field_count, true);
@@ -1417,7 +1530,11 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
break;
case Type_BitSet:
- return LLVMIntType(8*cast(unsigned)type_size_of(type));
+ {
+ Type *ut = bit_set_to_int(type);
+ return lb_type(m, ut);
+ }
+
case Type_SimdVector:
if (type->SimdVector.is_x86_mmx) {
return LLVMX86MMXTypeInContext(ctx);
@@ -1451,10 +1568,17 @@ LLVMTypeRef lb_type(lbModule *m, Type *type) {
return *found;
}
- LLVMTypeRef llvm_type = lb_type_internal(m, type);
-
- map_set(&m->types, hash_type(type), llvm_type);
+ LLVMTypeRef llvm_type = nullptr;
+ m->internal_type_level += 1;
+ llvm_type = lb_type_internal(m, type);
+ m->internal_type_level -= 1;
+ if (USE_LLVM_ABI && m->internal_type_level == 0) {
+ map_set(&m->types, hash_type(type), llvm_type);
+ if (is_type_named(type)) {
+ map_set(&m->llvm_types, hash_pointer(llvm_type), type);
+ }
+ }
return llvm_type;
}
@@ -1855,7 +1979,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
case Type_Proc:
{
return nullptr;
- // set_procedure_abi_types(heap_allocator(), type);
+ // set_procedure_abi_types(type);
// LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx);
// isize offset = 0;
@@ -1895,7 +2019,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
break;
case Type_BitFieldValue:
return nullptr;
- // return LLVMIntType(type->BitFieldValue.bits);
+ // return LLVMIntTypeInContext(m->ctx, type->BitFieldValue.bits);
case Type_BitField:
{
@@ -1909,7 +2033,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
// for_array(i, type->BitField.sizes) {
// u32 size = type->BitField.sizes[i];
- // fields[i] = LLVMIntType(size);
+ // fields[i] = LLVMIntTypeInContext(m->ctx, size);
// }
// internal_type = LLVMStructTypeInContext(ctx, fields, field_count, true);
@@ -1929,7 +2053,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
break;
case Type_BitSet:
return nullptr;
- // return LLVMIntType(8*cast(unsigned)type_size_of(type));
+ // return LLVMIntTypeInContext(m->ctx, 8*cast(unsigned)type_size_of(type));
case Type_SimdVector:
return nullptr;
// if (type->SimdVector.is_x86_mmx) {
@@ -1998,7 +2122,7 @@ lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) {
unsigned kind = LLVMGetEnumAttributeKindForName(name, gb_strlen(name));
- GB_ASSERT(kind != 0);
+ GB_ASSERT_MSG(kind != 0, "unknown attribute: %s", name);
return LLVMCreateEnumAttribute(ctx, kind, value);
}
@@ -2032,7 +2156,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
}
- lbProcedure *p = gb_alloc_item(heap_allocator(), lbProcedure);
+ lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure);
p->module = m;
entity->code_gen_module = m;
@@ -2046,7 +2170,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
Type *pt = base_type(entity->type);
GB_ASSERT(pt->kind == Type_Proc);
- set_procedure_abi_types(heap_allocator(), entity->type);
+ set_procedure_abi_types(entity->type);
p->type = entity->type;
p->type_expr = decl->type_expr;
@@ -2069,18 +2193,32 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
}
- char *c_link_name = alloc_cstring(heap_allocator(), p->name);
+ char *c_link_name = alloc_cstring(permanent_allocator(), p->name);
LLVMTypeRef func_ptr_type = lb_type(m, p->type);
LLVMTypeRef func_type = LLVMGetElementType(func_ptr_type);
p->value = LLVMAddFunction(m->mod, c_link_name, func_type);
- lbCallingConventionKind cc_kind = lbCallingConvention_C;
- // TODO(bill): Clean up this logic
- if (build_context.metrics.os != TargetOs_js) {
- cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
+ lbFunctionType **ft_found = map_get(&m->function_type_map, hash_type(p->type));
+ if (USE_LLVM_ABI && ft_found) {
+ lbFunctionType *abi_ft = *ft_found;
+ p->abi_function_type = abi_ft;
+ lb_add_function_type_attributes(p->value, abi_ft, abi_ft->calling_convention);
+ } else {
+ lbCallingConventionKind cc_kind = lbCallingConvention_C;
+ // TODO(bill): Clean up this logic
+ if (build_context.metrics.os != TargetOs_js) {
+ cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
+ }
+ LLVMSetFunctionCallConv(p->value, cc_kind);
}
- LLVMSetFunctionCallConv(p->value, cc_kind);
+
+ // lbCallingConventionKind cc_kind = lbCallingConvention_C;
+ // // TODO(bill): Clean up this logic
+ // if (build_context.metrics.os != TargetOs_js) {
+ // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
+ // }
+ // LLVMSetFunctionCallConv(p->value, cc_kind);
lbValue proc_value = {p->value, p->type};
lb_add_entity(m, entity, proc_value);
lb_add_member(m, p->name, proc_value);
@@ -2092,19 +2230,19 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
LLVMSetVisibility(p->value, LLVMDefaultVisibility);
if (build_context.metrics.os == TargetOs_js) {
- char const *export_name = alloc_cstring(heap_allocator(), p->name);
+ char const *export_name = alloc_cstring(permanent_allocator(), p->name);
LLVMAddTargetDependentFunctionAttr(p->value, "wasm-export-name", export_name);
}
}
if (p->is_foreign) {
if (build_context.metrics.os == TargetOs_js) {
- char const *import_name = alloc_cstring(heap_allocator(), p->name);
+ char const *import_name = alloc_cstring(permanent_allocator(), p->name);
char const *module_name = "env";
if (entity->Procedure.foreign_library != nullptr) {
Entity *foreign_library = entity->Procedure.foreign_library;
GB_ASSERT(foreign_library->kind == Entity_LibraryName);
if (foreign_library->LibraryName.paths.count > 0) {
- module_name = alloc_cstring(heap_allocator(), foreign_library->LibraryName.paths[0]);
+ module_name = alloc_cstring(permanent_allocator(), foreign_library->LibraryName.paths[0]);
}
}
LLVMAddTargetDependentFunctionAttr(p->value, "wasm-import-name", import_name);
@@ -2115,8 +2253,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
// NOTE(bill): offset==0 is the return value
isize offset = 1;
if (pt->Proc.return_by_pointer) {
- lb_add_proc_attribute_at_index(p, 1, "sret");
- lb_add_proc_attribute_at_index(p, 1, "noalias");
+ if (!USE_LLVM_ABI) {
+ lb_add_proc_attribute_at_index(p, 1, "sret");
+ lb_add_proc_attribute_at_index(p, 1, "noalias");
+ }
offset = 2;
}
@@ -2149,7 +2289,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
}
}
- if (pt->Proc.calling_convention == ProcCC_Odin) {
+ if (!USE_LLVM_ABI && pt->Proc.calling_convention == ProcCC_Odin) {
lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
lb_add_proc_attribute_at_index(p, offset+parameter_index, "nonnull");
lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture");
@@ -2191,7 +2331,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
GB_ASSERT(found == nullptr);
}
- lbProcedure *p = gb_alloc_item(heap_allocator(), lbProcedure);
+ lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure);
p->module = m;
p->name = link_name;
@@ -2205,7 +2345,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
p->is_export = false;
p->is_entry_point = false;
- gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
p->children.allocator = a;
p->params.allocator = a;
p->defer_stmts.allocator = a;
@@ -2214,7 +2354,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
p->context_stack.allocator = a;
- char *c_link_name = alloc_cstring(heap_allocator(), p->name);
+ char *c_link_name = alloc_cstring(permanent_allocator(), p->name);
LLVMTypeRef func_ptr_type = lb_type(m, p->type);
LLVMTypeRef func_type = LLVMGetElementType(func_ptr_type);
@@ -2241,7 +2381,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
}
isize parameter_index = 0;
- if (pt->Proc.param_count) {
+ if (!USE_LLVM_ABI && pt->Proc.param_count) {
TypeTuple *params = &pt->Proc.params->Tuple;
for (isize i = 0; i < pt->Proc.param_count; i++) {
Entity *e = params->variables[i];
@@ -2396,6 +2536,69 @@ void lb_start_block(lbProcedure *p, lbBlock *b) {
p->curr_block = b;
}
+LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRef dst_type) {
+ LLVMContextRef ctx = p->module->ctx;
+
+ LLVMTypeRef src_type = LLVMTypeOf(val);
+
+ if (src_type == dst_type) {
+ return val;
+ }
+
+ i64 src_size = lb_sizeof(src_type);
+ i64 dst_size = lb_sizeof(dst_type);
+
+ if (dst_type == LLVMInt1TypeInContext(ctx)) {
+ GB_ASSERT(lb_is_type_kind(src_type, LLVMIntegerTypeKind));
+ return LLVMBuildICmp(p->builder, LLVMIntNE, val, LLVMConstNull(src_type), "");
+ } else if (src_type == LLVMInt1TypeInContext(ctx)) {
+ GB_ASSERT(lb_is_type_kind(src_type, LLVMIntegerTypeKind));
+ return LLVMBuildZExtOrBitCast(p->builder, val, dst_type, "");
+ }
+
+ if (src_size != dst_size && (lb_is_type_kind(src_type, LLVMVectorTypeKind) ^ lb_is_type_kind(dst_type, LLVMVectorTypeKind))) {
+ // Okay
+ } else {
+ GB_ASSERT_MSG(src_size == dst_size, "%s == %s", LLVMPrintTypeToString(src_type), LLVMPrintTypeToString(dst_type));
+ }
+
+ LLVMTypeKind src_kind = LLVMGetTypeKind(src_type);
+ LLVMTypeKind dst_kind = LLVMGetTypeKind(dst_type);
+ if (src_kind == dst_kind) {
+ if (src_kind == LLVMPointerTypeKind) {
+ return LLVMBuildPointerCast(p->builder, val, dst_type, "");
+ } else if (src_kind != LLVMStructTypeKind) {
+ return LLVMBuildBitCast(p->builder, val, dst_type, "");
+ }
+ } else {
+ if (src_kind == LLVMPointerTypeKind && dst_kind == LLVMIntegerTypeKind) {
+ return LLVMBuildPtrToInt(p->builder, val, dst_type, "");
+ } else if (src_kind == LLVMIntegerTypeKind && dst_kind == LLVMPointerTypeKind) {
+ return LLVMBuildIntToPtr(p->builder, val, dst_type, "");
+ }
+ }
+
+ if (LLVMIsALoadInst(val)) {
+ LLVMValueRef val_ptr = LLVMGetOperand(val, 0);
+ val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), "");
+ return LLVMBuildLoad(p->builder, val_ptr, "");
+ } else {
+ GB_ASSERT(p->decl_block != p->curr_block);
+ LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
+
+ LLVMValueRef ptr = LLVMBuildAlloca(p->builder, dst_type, "");
+ LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
+ i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
+ max_align = gb_max(max_align, 4);
+ LLVMSetAlignment(ptr, cast(unsigned)max_align);
+
+ LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), "");
+ LLVMBuildStore(p->builder, val, nptr);
+
+ return LLVMBuildLoad(p->builder, ptr, "");
+ }
+}
+
void lb_begin_procedure_body(lbProcedure *p) {
DeclInfo *decl = decl_info_of_entity(p->entity);
@@ -2428,82 +2631,186 @@ void lb_begin_procedure_body(lbProcedure *p) {
GB_ASSERT(p->type != nullptr);
- i32 parameter_index = 0;
+ if (p->abi_function_type) {
+ lbFunctionType *ft = p->abi_function_type;
+ unsigned param_offset = 0;
- lbValue return_ptr_value = {};
- if (p->type->Proc.return_by_pointer) {
- // NOTE(bill): this must be parameter 0
- Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
- Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
- e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
+ lbValue return_ptr_value = {};
+ if (ft->ret.kind == lbArg_Indirect) {
+ // NOTE(bill): this must be parameter 0
+ Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
+ Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
+ e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
- return_ptr_value.value = LLVMGetParam(p->value, 0);
- return_ptr_value.type = ptr_type;
- p->return_ptr = lb_addr(return_ptr_value);
+ return_ptr_value.value = LLVMGetParam(p->value, 0);
+ return_ptr_value.type = ptr_type;
+ p->return_ptr = lb_addr(return_ptr_value);
- lb_add_entity(p->module, e, return_ptr_value);
+ lb_add_entity(p->module, e, return_ptr_value);
- parameter_index += 1;
- }
+ param_offset += 1;
+ }
- if (p->type->Proc.params != nullptr) {
- TypeTuple *params = &p->type->Proc.params->Tuple;
- auto abi_types = p->type->Proc.abi_compat_params;
+ if (p->type->Proc.params != nullptr) {
+ TypeTuple *params = &p->type->Proc.params->Tuple;
- for_array(i, params->variables) {
- Entity *e = params->variables[i];
- if (e->kind != Entity_Variable) {
- continue;
- }
- Type *abi_type = e->type;
- if (abi_types.count > 0) {
- abi_type = abi_types[i];
- }
- if (e->token.string != "") {
- lb_add_param(p, e, nullptr, abi_type, parameter_index);
- }
- if (is_type_tuple(abi_type)) {
- parameter_index += cast(i32)abi_type->Tuple.variables.count;
- } else {
- parameter_index += 1;
+ unsigned param_index = 0;
+ for_array(i, params->variables) {
+ Entity *e = params->variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+
+ lbArgType *arg_type = &ft->args[param_index];
+ if (arg_type->kind == lbArg_Ignore) {
+ continue;
+ } else if (arg_type->kind == lbArg_Direct) {
+ lbParamPasskind kind = lbParamPass_Value;
+ LLVMTypeRef param_type = lb_type(p->module, e->type);
+ if (param_type != arg_type->type) {
+ kind = lbParamPass_BitCast;
+ }
+ LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index);
+
+ value = OdinLLVMBuildTransmute(p, value, param_type);
+
+ lbValue param = {};
+ param.value = value;
+ param.type = e->type;
+ array_add(&p->params, param);
+
+ if (e->token.string.len != 0) {
+ lbAddr l = lb_add_local(p, e->type, e, false, param_index);
+ lb_addr_store(p, l, param);
+ }
+
+ param_index += 1;
+ } else if (arg_type->kind == lbArg_Indirect) {
+ LLVMValueRef value_ptr = LLVMGetParam(p->value, param_offset+param_index);
+ LLVMValueRef value = LLVMBuildLoad(p->builder, value_ptr, "");
+
+ lbValue param = {};
+ param.value = value;
+ param.type = e->type;
+ array_add(&p->params, param);
+
+ lbValue ptr = {};
+ ptr.value = value_ptr;
+ ptr.type = alloc_type_pointer(e->type);
+
+ lb_add_entity(p->module, e, ptr);
+ param_index += 1;
+ }
}
}
- }
+ if (p->type->Proc.has_named_results) {
+ GB_ASSERT(p->type->Proc.result_count > 0);
+ TypeTuple *results = &p->type->Proc.results->Tuple;
- if (p->type->Proc.has_named_results) {
- GB_ASSERT(p->type->Proc.result_count > 0);
- TypeTuple *results = &p->type->Proc.results->Tuple;
+ for_array(i, results->variables) {
+ Entity *e = results->variables[i];
+ GB_ASSERT(e->kind == Entity_Variable);
- for_array(i, results->variables) {
- Entity *e = results->variables[i];
- GB_ASSERT(e->kind == Entity_Variable);
+ if (e->token.string != "") {
+ GB_ASSERT(!is_blank_ident(e->token));
- if (e->token.string != "") {
- GB_ASSERT(!is_blank_ident(e->token));
+ lbAddr res = {};
+ if (return_ptr_value.value) {
+ lbValue ptr = return_ptr_value;
+ if (results->variables.count != 1) {
+ ptr = lb_emit_struct_ep(p, ptr, cast(i32)i);
+ }
- lbAddr res = {};
- if (p->type->Proc.return_by_pointer) {
- lbValue ptr = return_ptr_value;
- if (results->variables.count != 1) {
- ptr = lb_emit_struct_ep(p, ptr, cast(i32)i);
+ res = lb_addr(ptr);
+ lb_add_entity(p->module, e, ptr);
+ } else {
+ res = lb_add_local(p, e->type, e);
}
- res = lb_addr(ptr);
- lb_add_entity(p->module, e, ptr);
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
+ lb_addr_store(p, res, c);
+ }
+ }
+ }
+ }
+ } else {
+ i32 parameter_index = 0;
+ lbValue return_ptr_value = {};
+ if (p->type->Proc.return_by_pointer) {
+ // NOTE(bill): this must be parameter 0
+ Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
+ Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
+ e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
+
+ return_ptr_value.value = LLVMGetParam(p->value, 0);
+ return_ptr_value.type = ptr_type;
+ p->return_ptr = lb_addr(return_ptr_value);
+
+ lb_add_entity(p->module, e, return_ptr_value);
+
+ parameter_index += 1;
+ }
+
+ if (p->type->Proc.params != nullptr) {
+ TypeTuple *params = &p->type->Proc.params->Tuple;
+ auto abi_types = p->type->Proc.abi_compat_params;
+
+ for_array(i, params->variables) {
+ Entity *e = params->variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+ Type *abi_type = e->type;
+ if (abi_types.count > 0) {
+ abi_type = abi_types[i];
+ }
+ if (e->token.string != "") {
+ lb_add_param(p, e, nullptr, abi_type, parameter_index);
+ }
+ if (is_type_tuple(abi_type)) {
+ parameter_index += cast(i32)abi_type->Tuple.variables.count;
} else {
- res = lb_add_local(p, e->type, e);
+ parameter_index += 1;
}
+ }
+ }
+
- if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
- lb_addr_store(p, res, c);
+ if (p->type->Proc.has_named_results) {
+ GB_ASSERT(p->type->Proc.result_count > 0);
+ TypeTuple *results = &p->type->Proc.results->Tuple;
+
+ for_array(i, results->variables) {
+ Entity *e = results->variables[i];
+ GB_ASSERT(e->kind == Entity_Variable);
+
+ if (e->token.string != "") {
+ GB_ASSERT(!is_blank_ident(e->token));
+
+ lbAddr res = {};
+ if (p->type->Proc.return_by_pointer) {
+ lbValue ptr = return_ptr_value;
+ if (results->variables.count != 1) {
+ ptr = lb_emit_struct_ep(p, ptr, cast(i32)i);
+ }
+
+ res = lb_addr(ptr);
+ lb_add_entity(p->module, e, ptr);
+ } else {
+ res = lb_add_local(p, e->type, e);
+ }
+
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
+ lb_addr_store(p, res, c);
+ }
}
}
}
}
-
if (p->type->Proc.calling_convention == ProcCC_Odin) {
Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false);
e->flags |= EntityFlag_NoAlias;
@@ -2559,7 +2866,7 @@ void lb_add_edge(lbBlock *from, lbBlock *to) {
lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append) {
- lbBlock *b = gb_alloc_item(heap_allocator(), lbBlock);
+ lbBlock *b = gb_alloc_item(permanent_allocator(), lbBlock);
b->block = LLVMCreateBasicBlockInContext(p->module->ctx, name);
b->appended = false;
if (append) {
@@ -2658,16 +2965,34 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p
char const *name = "";
if (e != nullptr) {
- // name = alloc_cstring(heap_allocator(), e->token.string);
+ // name = alloc_cstring(permanent_allocator(), e->token.string);
}
LLVMTypeRef llvm_type = lb_type(p->module, type);
LLVMValueRef ptr = LLVMBuildAlloca(p->builder, llvm_type, name);
- LLVMSetAlignment(ptr, 16); // TODO(bill): Make this configurable
+
+ // unsigned alignment = 16; // TODO(bill): Make this configurable
+ unsigned alignment = cast(unsigned)lb_alignof(llvm_type);
+ LLVMSetAlignment(ptr, alignment);
LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
if (zero_init) {
- LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr);
+ LLVMTypeKind kind = LLVMGetTypeKind(llvm_type);
+
+ switch (kind) {
+ case LLVMStructTypeKind:
+ case LLVMArrayTypeKind:
+ {
+ // NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too
+ LLVMTypeRef type_i8 = LLVMInt8TypeInContext(p->module->ctx);
+ LLVMTypeRef type_i32 = LLVMInt32TypeInContext(p->module->ctx);
+ i32 sz = cast(i32)type_size_of(type);
+ LLVMBuildMemSet(p->builder, ptr, LLVMConstNull(type_i8), LLVMConstInt(type_i32, sz, false), alignment);
+ }
+ break;
+ default:
+ LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr);
+ }
}
lbValue val = {};
@@ -2706,13 +3031,13 @@ void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
isize name_len = p->name.len + 1 + pd_name.len + 1 + 10 + 1;
- char *name_text = gb_alloc_array(heap_allocator(), char, name_len);
+ char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
i32 guid = cast(i32)p->children.count;
name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%d", LIT(p->name), LIT(pd_name), guid);
String name = make_string(cast(u8 *)name_text, name_len-1);
- set_procedure_abi_types(heap_allocator(), e->type);
+ set_procedure_abi_types(e->type);
e->Procedure.link_name = name;
@@ -2849,7 +3174,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
return;
}
- set_procedure_abi_types(heap_allocator(), e->type);
+ set_procedure_abi_types(e->type);
e->Procedure.link_name = name;
lbProcedure *nested_proc = lb_create_procedure(p->module, e);
@@ -2869,7 +3194,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
}
-void lb_build_stmt_list(lbProcedure *p, Array<Ast *> const &stmts) {
+void lb_build_stmt_list(lbProcedure *p, Slice<Ast *> const &stmts) {
for_array(i, stmts) {
Ast *stmt = stmts[i];
switch (stmt->kind) {
@@ -2905,7 +3230,7 @@ lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
- lbTargetList *tl = gb_alloc_item(heap_allocator(), lbTargetList);
+ lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
tl->prev = p->target_list;
tl->break_ = break_;
tl->continue_ = continue_;
@@ -3063,13 +3388,8 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
elem = lb_emit_load(p, elem);
lbValue entry = lb_emit_ptr_offset(p, elem, idx);
- val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2));
-
- lbValue key_raw = lb_emit_struct_ep(p, entry, 0);
- key_raw = lb_emit_struct_ep(p, key_raw, 1);
- lbValue key = lb_emit_conv(p, key_raw, alloc_type_pointer(expr_type->Map.key));
-
- idx = lb_emit_load(p, key);
+ idx = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2));
+ val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 3));
break;
}
@@ -3126,7 +3446,7 @@ void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset);
lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int);
- auto args = array_make<lbValue>(heap_allocator(), 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = lb_emit_string(p, str_elem, str_len);
lbValue rune_and_len = lb_emit_runtime_call(p, "string_decode_rune", args);
lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
@@ -3217,7 +3537,7 @@ void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValu
lbValue max_count = lb_const_int(m, t_int, enum_count);
lbValue ti = lb_type_info(m, t);
- lbValue variant = lb_emit_struct_ep(p, ti, 3);
+ lbValue variant = lb_emit_struct_ep(p, ti, 4);
lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr);
lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2));
lbValue values_data = lb_slice_elem(p, values);
@@ -3600,7 +3920,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss) {
ast_node(body, BlockStmt, ss->body);
- Array<Ast *> default_stmts = {};
+ Slice<Ast *> default_stmts = {};
lbBlock *default_fall = nullptr;
lbBlock *default_block = nullptr;
@@ -4001,14 +4321,14 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
String mangled_name = {};
{
- gbString str = gb_string_make_length(heap_allocator(), p->name.text, p->name.len);
+ gbString str = gb_string_make_length(permanent_allocator(), p->name.text, p->name.len);
str = gb_string_appendc(str, "-");
str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id);
mangled_name.text = cast(u8 *)str;
mangled_name.len = gb_string_length(str);
}
- char *c_name = alloc_cstring(heap_allocator(), mangled_name);
+ char *c_name = alloc_cstring(permanent_allocator(), mangled_name);
LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name);
LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
@@ -4055,8 +4375,8 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
}
}
} else { // Tuple(s)
- auto lvals = array_make<lbAddr>(heap_allocator(), 0, vd->names.count);
- auto inits = array_make<lbValue>(heap_allocator(), 0, vd->names.count);
+ auto lvals = array_make<lbAddr>(permanent_allocator(), 0, vd->names.count);
+ auto inits = array_make<lbValue>(permanent_allocator(), 0, vd->names.count);
for_array(i, vd->names) {
Ast *name = vd->names[i];
@@ -4093,7 +4413,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
case_ast_node(as, AssignStmt, node);
if (as->op.kind == Token_Eq) {
- auto lvals = array_make<lbAddr>(heap_allocator(), 0, as->lhs.count);
+ auto lvals = array_make<lbAddr>(permanent_allocator(), 0, as->lhs.count);
for_array(i, as->lhs) {
Ast *lhs = as->lhs[i];
@@ -4111,7 +4431,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
lbValue init = lb_build_expr(p, rhs);
lb_addr_store(p, lvals[0], init);
} else {
- auto inits = array_make<lbValue>(heap_allocator(), 0, lvals.count);
+ auto inits = array_make<lbValue>(permanent_allocator(), 0, lvals.count);
for_array(i, as->rhs) {
lbValue init = lb_build_expr(p, as->rhs[i]);
@@ -4125,7 +4445,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
}
}
} else {
- auto inits = array_make<lbValue>(heap_allocator(), 0, lvals.count);
+ auto inits = array_make<lbValue>(permanent_allocator(), 0, lvals.count);
for_array(i, as->rhs) {
lbValue init = lb_build_expr(p, as->rhs[i]);
@@ -4217,7 +4537,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
}
} else {
- auto results = array_make<lbValue>(heap_allocator(), 0, return_count);
+ auto results = array_make<lbValue>(permanent_allocator(), 0, return_count);
if (res_count != 0) {
for (isize res_index = 0; res_index < res_count; res_index++) {
@@ -4276,27 +4596,53 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
}
- if (p->type->Proc.return_by_pointer) {
- if (res.value != nullptr) {
- lb_addr_store(p, p->return_ptr, res);
+ if (p->abi_function_type) {
+ if (p->abi_function_type->ret.kind == lbArg_Indirect) {
+ if (res.value != nullptr) {
+ LLVMBuildStore(p->builder, res.value, p->return_ptr.addr.value);
+ } else {
+ LLVMBuildStore(p->builder, LLVMConstNull(p->abi_function_type->ret.type), p->return_ptr.addr.value);
+ }
+
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+
+ LLVMBuildRetVoid(p->builder);
} else {
- lb_addr_store(p, p->return_ptr, lb_const_nil(p->module, p->type->Proc.abi_compat_result_type));
+ LLVMValueRef ret_val = res.value;
+ ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.type);
+ if (p->abi_function_type->ret.cast_type != nullptr) {
+ ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.cast_type);
+ }
+
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ LLVMBuildRet(p->builder, ret_val);
}
+ } else {
+ GB_ASSERT(!USE_LLVM_ABI);
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ if (p->type->Proc.return_by_pointer) {
+ if (res.value != nullptr) {
+ lb_addr_store(p, p->return_ptr, res);
+ } else {
+ lb_addr_store(p, p->return_ptr, lb_const_nil(p->module, p->type->Proc.abi_compat_result_type));
+ }
- LLVMBuildRetVoid(p->builder);
- } else {
- GB_ASSERT_MSG(res.value != nullptr, "%.*s", LIT(p->name));
- Type *abi_rt = p->type->Proc.abi_compat_result_type;
- if (!are_types_identical(res.type, abi_rt)) {
- res = lb_emit_transmute(p, res, abi_rt);
- }
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ LLVMBuildRetVoid(p->builder);
+ } else {
+ GB_ASSERT_MSG(res.value != nullptr, "%.*s", LIT(p->name));
- LLVMBuildRet(p->builder, res.value);
+ Type *abi_rt = p->type->Proc.abi_compat_result_type;
+ if (!are_types_identical(res.type, abi_rt)) {
+ res = lb_emit_transmute(p, res, abi_rt);
+ }
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ LLVMBuildRet(p->builder, res.value);
+ }
}
+
+
case_end;
case_ast_node(is, IfStmt, node);
@@ -4496,9 +4842,8 @@ lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
y = lb_emit_conv(p, y, t);
if (is_type_float(t)) {
- gbAllocator a = heap_allocator();
i64 sz = 8*type_size_of(t);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = x;
args[1] = y;
switch (sz) {
@@ -4514,9 +4859,8 @@ lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
y = lb_emit_conv(p, y, t);
if (is_type_float(t)) {
- gbAllocator a = heap_allocator();
i64 sz = 8*type_size_of(t);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = x;
args[1] = y;
switch (sz) {
@@ -4552,7 +4896,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
isize max_len = 7+8+1;
- char *name = gb_alloc_array(heap_allocator(), char, max_len);
+ char *name = gb_alloc_array(permanent_allocator(), char, max_len);
isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index);
len -= 1;
m->global_array_index++;
@@ -4594,7 +4938,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
char *name = nullptr;
{
isize max_len = 7+8+1;
- name = gb_alloc_array(heap_allocator(), char, max_len);
+ name = gb_alloc_array(permanent_allocator(), char, max_len);
isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index);
len -= 1;
m->global_array_index++;
@@ -4634,7 +4978,7 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr
return -1;
}
-lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) {
+lbValue lb_typeid(lbModule *m, Type *type) {
type = default_type(type);
u64 id = cast(u64)lb_type_info_index(m->info, type);
@@ -4686,6 +5030,7 @@ lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) {
u64 data = 0;
if (build_context.word_size == 4) {
+ GB_ASSERT(id <= (1u<<24u));
data |= (id &~ (1u<<24)) << 0u; // index
data |= (kind &~ (1u<<5)) << 24u; // kind
data |= (named &~ (1u<<1)) << 29u; // kind
@@ -4693,6 +5038,7 @@ lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) {
data |= (reserved &~ (1u<<1)) << 31u; // kind
} else {
GB_ASSERT(build_context.word_size == 8);
+ GB_ASSERT(id <= (1ull<<56u));
data |= (id &~ (1ull<<56)) << 0ul; // index
data |= (kind &~ (1ull<<5)) << 56ull; // kind
data |= (named &~ (1ull<<1)) << 61ull; // kind
@@ -4700,10 +5046,9 @@ lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) {
data |= (reserved &~ (1ull<<1)) << 63ull; // kind
}
-
lbValue res = {};
- res.value = LLVMConstInt(lb_type(m, typeid_type), data, false);
- res.type = typeid_type;
+ res.value = LLVMConstInt(lb_type(m, t_typeid), data, false);
+ res.type = t_typeid;
return res;
}
@@ -4738,7 +5083,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
value = convert_exact_value_for_type(value, type);
if (value.kind == ExactValue_Typeid) {
- return lb_typeid(m, value.value_typeid, original_type);
+ return lb_typeid(m, value.value_typeid);
}
if (value.kind == ExactValue_Invalid) {
@@ -4804,7 +5149,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
} else {
isize max_len = 7+8+1;
- char *str = gb_alloc_array(heap_allocator(), char, max_len);
+ char *str = gb_alloc_array(permanent_allocator(), char, max_len);
isize len = gb_snprintf(str, max_len, "csba$%x", m->global_array_index);
m->global_array_index++;
@@ -4835,10 +5180,44 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
} else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
+ if (is_type_rune_array(type) && value.kind == ExactValue_String) {
+ i64 count = type->Array.count;
+ Type *elem = type->Array.elem;
+ LLVMTypeRef et = lb_type(m, elem);
+
+ Rune rune;
+ isize offset = 0;
+ isize width = 1;
+ String s = value.value_string;
+
+ LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
+
+ for (i64 i = 0; i < count && offset < s.len; i++) {
+ width = gb_utf8_decode(s.text+offset, s.len-offset, &rune);
+ offset += width;
+
+ elems[i] = LLVMConstInt(et, rune, true);
+
+ }
+ GB_ASSERT(offset == s.len);
+
+ res.value = LLVMConstArray(et, elems, cast(unsigned)count);
+ return res;
+ }
+ GB_PANIC("HERE!\n");
+
LLVMValueRef data = LLVMConstStringInContext(ctx,
cast(char const *)value.value_string.text,
cast(unsigned)value.value_string.len,
- false);
+ false /*DontNullTerminate*/);
+ res.value = data;
+ return res;
+ } else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
+ GB_ASSERT(type->Array.count == value.value_string.len);
+ LLVMValueRef data = LLVMConstStringInContext(ctx,
+ cast(char const *)value.value_string.text,
+ cast(unsigned)value.value_string.len,
+ true /*DontNullTerminate*/);
res.value = data;
return res;
} else if (is_type_array(type) &&
@@ -4852,7 +5231,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
lbValue single_elem = lb_const_value(m, elem, value, allow_local);
- LLVMValueRef *elems = gb_alloc_array(heap_allocator(), LLVMValueRef, count);
+ LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) {
elems[i] = single_elem.value;
}
@@ -4905,7 +5284,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
isize byte_len = gb_size_of(u64)*len;
u8 *old_bytes = cast(u8 *)words;
// TODO(bill): Use a different allocator here for a temporary allocation
- u8 *new_bytes = cast(u8 *)gb_alloc_align(heap_allocator(), byte_len, gb_align_of(u64));
+ u8 *new_bytes = cast(u8 *)gb_alloc_align(permanent_allocator(), byte_len, gb_align_of(u64));
for (i64 i = 0; i < sz; i++) {
new_bytes[i] = old_bytes[sz-1-i];
}
@@ -4937,12 +5316,12 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
LLVMValueRef values[2] = {};
switch (8*type_size_of(type)) {
case 64:
- values[0] = lb_const_f32(m, cast(f32)value.value_complex.real);
- values[1] = lb_const_f32(m, cast(f32)value.value_complex.imag);
+ values[0] = lb_const_f32(m, cast(f32)value.value_complex->real);
+ values[1] = lb_const_f32(m, cast(f32)value.value_complex->imag);
break;
case 128:
- values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_complex.real);
- values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_complex.imag);
+ values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->real);
+ values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->imag);
break;
}
@@ -4956,17 +5335,17 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
switch (8*type_size_of(type)) {
case 128:
// @QuaternionLayout
- values[3] = lb_const_f32(m, cast(f32)value.value_quaternion.real);
- values[0] = lb_const_f32(m, cast(f32)value.value_quaternion.imag);
- values[1] = lb_const_f32(m, cast(f32)value.value_quaternion.jmag);
- values[2] = lb_const_f32(m, cast(f32)value.value_quaternion.kmag);
+ values[3] = lb_const_f32(m, cast(f32)value.value_quaternion->real);
+ values[0] = lb_const_f32(m, cast(f32)value.value_quaternion->imag);
+ values[1] = lb_const_f32(m, cast(f32)value.value_quaternion->jmag);
+ values[2] = lb_const_f32(m, cast(f32)value.value_quaternion->kmag);
break;
case 256:
// @QuaternionLayout
- values[3] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.real);
- values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.imag);
- values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.jmag);
- values[2] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.kmag);
+ values[3] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->real);
+ values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->imag);
+ values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->jmag);
+ values[2] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->kmag);
break;
}
@@ -4991,9 +5370,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
if (cl->elems[0]->kind == Ast_FieldValue) {
// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
-
- LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->Array.count);
- defer (gb_free(heap_allocator(), values));
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count);
isize value_index = 0;
for (i64 i = 0; i < type->Array.count; i++) {
@@ -5050,8 +5427,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
} else {
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
- LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->Array.count);
- defer (gb_free(heap_allocator(), values));
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->Array.count);
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
@@ -5074,9 +5450,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
if (cl->elems[0]->kind == Ast_FieldValue) {
// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
-
- LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->EnumeratedArray.count);
- defer (gb_free(heap_allocator(), values));
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count);
isize value_index = 0;
@@ -5137,8 +5511,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
} else {
GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
- LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->EnumeratedArray.count);
- defer (gb_free(heap_allocator(), values));
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, type->EnumeratedArray.count);
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
@@ -5163,8 +5536,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
GB_ASSERT(elem_type_can_be_constant(elem_type));
isize total_elem_count = type->SimdVector.count;
- LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, total_elem_count);
- defer (gb_free(heap_allocator(), values));
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count);
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
@@ -5190,12 +5562,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
isize value_count = type->Struct.fields.count + offset;
- LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, value_count);
- bool *visited = gb_alloc_array(heap_allocator(), bool, value_count);
- defer (gb_free(heap_allocator(), values));
- defer (gb_free(heap_allocator(), visited));
-
-
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count);
+ bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count);
if (cl->elems.count > 0) {
if (cl->elems[0]->kind == Ast_FieldValue) {
@@ -5293,34 +5661,23 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
break;
case ExactValue_Typeid:
- return lb_typeid(m, value.value_typeid, original_type);
+ return lb_typeid(m, value.value_typeid);
}
return lb_const_nil(m, original_type);
}
-u64 lb_generate_source_code_location_hash(TokenPos const &pos) {
- u64 h = 0xcbf29ce484222325;
- for (isize i = 0; i < pos.file.len; i++) {
- h = (h ^ u64(pos.file[i])) * 0x100000001b3;
- }
- h = h ^ (u64(pos.line) * 0x100000001b3);
- h = h ^ (u64(pos.column) * 0x100000001b3);
- return h;
-}
-
lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos) {
lbModule *m = p->module;
- LLVMValueRef fields[5] = {};
+ LLVMValueRef fields[4] = {};
fields[0]/*file*/ = lb_find_or_add_entity_string(p->module, pos.file).value;
fields[1]/*line*/ = lb_const_int(m, t_int, pos.line).value;
fields[2]/*column*/ = lb_const_int(m, t_int, pos.column).value;
fields[3]/*procedure*/ = lb_find_or_add_entity_string(p->module, procedure).value;
- fields[4]/*hash*/ = lb_const_int(m, t_u64, lb_generate_source_code_location_hash(pos)).value;
lbValue res = {};
- res.value = LLVMConstNamedStruct(lb_type(m, t_source_code_location), fields, 5);
+ res.value = LLVMConstNamedStruct(lb_type(m, t_source_code_location), fields, gb_count_of(fields));
res.type = t_source_code_location;
return res;
}
@@ -5523,7 +5880,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty
Type *ft = base_complex_elem_type(type);
if (op == Token_Quo) {
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = lhs;
args[1] = rhs;
@@ -5597,7 +5954,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty
return lb_addr_load(p, res);
} else if (op == Token_Mul) {
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = lhs;
args[1] = rhs;
@@ -5607,7 +5964,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty
default: GB_PANIC("Unknown float type"); break;
}
} else if (op == Token_Quo) {
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = lhs;
args[1] = rhs;
@@ -5803,10 +6160,10 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
lbValue right = {};
if (be->left->tav.mode == Addressing_Type) {
- left = lb_typeid(p->module, be->left->tav.type, t_typeid);
+ left = lb_typeid(p->module, be->left->tav.type);
}
if (be->right->tav.mode == Addressing_Type) {
- right = lb_typeid(p->module, be->right->tav.type, t_typeid);
+ right = lb_typeid(p->module, be->right->tav.type);
}
if (left.value == nullptr) left = lb_build_expr(p, be->left);
if (right.value == nullptr) right = lb_build_expr(p, be->right);
@@ -5831,9 +6188,9 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
{
lbValue addr = lb_address_from_load_or_generate_local(p, right);
lbValue h = lb_gen_map_header(p, addr, rt);
- lbValue key = lb_gen_map_key(p, left, rt->Map.key);
+ lbValue key = lb_gen_map_hash(p, left, rt->Map.key);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = h;
args[1] = key;
@@ -6079,7 +6436,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) {
lbValue c = lb_emit_conv(p, value, t_cstring);
- auto args = array_make<lbValue>(heap_allocator(), 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = c;
lbValue s = lb_emit_runtime_call(p, "cstring_to_string", args);
return lb_emit_conv(p, s, dst);
@@ -6096,7 +6453,6 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
// float -> float
if (is_type_float(src) && is_type_float(dst)) {
- gbAllocator a = heap_allocator();
i64 sz = type_size_of(src);
i64 dz = type_size_of(dst);
@@ -6322,8 +6678,6 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
-
-
// []byte/[]u8 <-> string
if (is_type_u8_slice(src) && is_type_string(dst)) {
return lb_emit_transmute(p, value, t);
@@ -6373,6 +6727,34 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return lb_addr_load(p, result);
}
+
+ i64 src_sz = type_size_of(src);
+ i64 dst_sz = type_size_of(dst);
+
+ if (src_sz == dst_sz) {
+ // bit_set <-> integer
+ if (is_type_integer(src) && is_type_bit_set(dst)) {
+ lbValue res = lb_emit_conv(p, value, bit_set_to_int(dst));
+ res.type = dst;
+ return res;
+ }
+ if (is_type_bit_set(src) && is_type_integer(dst)) {
+ lbValue bs = value;
+ bs.type = bit_set_to_int(src);
+ return lb_emit_conv(p, bs, dst);
+ }
+
+ // typeid <-> integer
+ if (is_type_integer(src) && is_type_typeid(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+ if (is_type_typeid(src) && is_type_integer(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+ }
+
+
+
if (is_type_untyped(src)) {
if (is_type_string(src) && is_type_string(dst)) {
lbAddr result = lb_add_local_generated(p, t, false);
@@ -6453,6 +6835,14 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
i64 sz = type_size_of(src);
i64 dz = type_size_of(dst);
+ if (sz != dz) {
+ LLVMTypeRef s = lb_type(m, src);
+ LLVMTypeRef d = lb_type(m, dst);
+ i64 llvm_sz = lb_sizeof(s);
+ i64 llvm_dz = lb_sizeof(d);
+ GB_ASSERT_MSG(llvm_sz == llvm_dz, "%s %s", LLVMPrintTypeToString(s), LLVMPrintTypeToString(d));
+ }
+
GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: '%s' to '%s'", type_to_string(src_type), type_to_string(t));
// NOTE(bill): Casting between an integer and a pointer cannot be done through a bitcast
@@ -6504,8 +6894,7 @@ void lb_emit_init_context(lbProcedure *p, lbAddr addr) {
GB_ASSERT(addr.ctx.sel.index.count == 0);
lbModule *m = p->module;
- gbAllocator a = heap_allocator();
- auto args = array_make<lbValue>(a, 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = addr.addr;
lb_emit_runtime_call(p, "__init_context", args);
}
@@ -6569,12 +6958,11 @@ lbValue lb_copy_value_to_ptr(lbProcedure *p, lbValue val, Type *new_type, i64 al
lbAddr ptr = lb_add_local_generated(p, new_type, false);
LLVMSetAlignment(ptr.addr.value, cast(unsigned)alignment);
lb_addr_store(p, ptr, val);
- ptr.kind = lbAddr_Context;
+ // ptr.kind = lbAddr_Context;
return ptr.addr;
}
lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
- gbAllocator a = heap_allocator();
GB_ASSERT(is_type_pointer(s.type));
Type *t = base_type(type_deref(s.type));
Type *result_type = nullptr;
@@ -6682,7 +7070,6 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
return lb_emit_load(p, ptr);
}
- gbAllocator a = heap_allocator();
Type *t = base_type(s.type);
Type *result_type = nullptr;
@@ -6788,7 +7175,6 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) {
GB_ASSERT(sel.index.count > 0);
Type *type = type_deref(e.type);
- gbAllocator a = heap_allocator();
for_array(i, sel.index) {
i32 index = cast(i32)sel.index[i];
@@ -6959,14 +7345,14 @@ Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) {
GB_ASSERT(t->kind == Type_Tuple);
auto *rt = &t->Tuple;
if (rt->variables.count > 0) {
- array = array_make<lbValue>(heap_allocator(), rt->variables.count);
+ array = array_make<lbValue>(permanent_allocator(), rt->variables.count);
for_array(i, rt->variables) {
lbValue elem = lb_emit_struct_ev(p, value, cast(i32)i);
array[i] = elem;
}
}
} else {
- array = array_make<lbValue>(heap_allocator(), 1);
+ array = array_make<lbValue>(permanent_allocator(), 1);
array[0] = value;
}
return array;
@@ -6983,7 +7369,7 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
arg_count += 1;
}
- LLVMValueRef *args = gb_alloc_array(heap_allocator(), LLVMValueRef, arg_count);
+ LLVMValueRef *args = gb_alloc_array(permanent_allocator(), LLVMValueRef, arg_count);
isize arg_index = 0;
if (return_ptr.value != nullptr) {
args[arg_index++] = return_ptr.value;
@@ -6993,17 +7379,37 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
args[arg_index++] = arg.value;
}
if (context_ptr.addr.value != nullptr) {
- args[arg_index++] = context_ptr.addr.value;
+ LLVMValueRef cp = context_ptr.addr.value;
+ cp = LLVMBuildPointerCast(p->builder, cp, lb_type(p->module, t_rawptr), "");
+ args[arg_index++] = cp;
}
-
LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder);
GB_ASSERT(curr_block != p->decl_block->block);
- LLVMValueRef ret = LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(p->module, value.type)), value.value, args, arg_count, "");;
- lbValue res = {};
- res.value = ret;
- res.type = abi_rt;
- return res;
+ if (USE_LLVM_ABI) {
+
+ LLVMTypeRef ftp = lb_type(p->module, value.type);
+ LLVMTypeRef ft = LLVMGetElementType(ftp);
+ LLVMValueRef fn = value.value;
+ if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) {
+ fn = LLVMBuildPointerCast(p->builder, fn, ftp, "");
+ }
+ LLVMTypeRef fnp = LLVMGetElementType(LLVMTypeOf(fn));
+ GB_ASSERT_MSG(lb_is_type_kind(fnp, LLVMFunctionTypeKind), "%s", LLVMPrintTypeToString(fnp));
+
+ LLVMValueRef ret = LLVMBuildCall2(p->builder, ft, fn, args, arg_count, "");;
+ lbValue res = {};
+ res.value = ret;
+ res.type = abi_rt;
+ return res;
+ } else {
+
+ LLVMValueRef ret = LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(p->module, value.type)), value.value, args, arg_count, "");;
+ lbValue res = {};
+ res.value = ret;
+ res.type = abi_rt;
+ return res;
+ }
}
lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args) {
@@ -7048,7 +7454,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
LLVMBuildUnreachable(p->builder);
});
- set_procedure_abi_types(heap_allocator(), pt);
+ set_procedure_abi_types(pt);
bool is_c_vararg = pt->Proc.c_vararg;
isize param_count = pt->Proc.param_count;
@@ -7059,95 +7465,191 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count);
}
- auto processed_args = array_make<lbValue>(heap_allocator(), 0, args.count);
+ lbValue result = {};
- for (isize i = 0; i < param_count; i++) {
- Entity *e = pt->Proc.params->Tuple.variables[i];
- if (e->kind != Entity_Variable) {
- // array_add(&processed_args, args[i]);
- continue;
+ auto processed_args = array_make<lbValue>(permanent_allocator(), 0, args.count);
+
+ if (USE_LLVM_ABI) {
+ lbFunctionType **ft_found = nullptr;
+ ft_found = map_get(&m->function_type_map, hash_type(pt));
+ if (!ft_found) {
+ LLVMTypeRef llvm_proc_type = lb_type(p->module, pt);
+ ft_found = map_get(&m->function_type_map, hash_type(pt));
}
- GB_ASSERT(e->flags & EntityFlag_Param);
-
- Type *original_type = e->type;
- Type *new_type = pt->Proc.abi_compat_params[i];
- Type *arg_type = args[i].type;
-
- if (are_types_identical(arg_type, new_type)) {
- // NOTE(bill): Done
- array_add(&processed_args, args[i]);
- } else if (!are_types_identical(original_type, new_type)) {
- if (is_type_pointer(new_type) && !is_type_pointer(original_type)) {
- Type *av = core_type(type_deref(new_type));
- if (are_types_identical(av, core_type(original_type))) {
- if (e->flags&EntityFlag_ImplicitReference) {
- array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i]));
- } else if (!is_type_pointer(arg_type)) {
- array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16));
- }
+ GB_ASSERT(ft_found != nullptr);
+
+ lbFunctionType *ft = *ft_found;
+ bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
+
+ unsigned param_index = 0;
+ for (isize i = 0; i < param_count; i++) {
+ Entity *e = pt->Proc.params->Tuple.variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+ GB_ASSERT(e->flags & EntityFlag_Param);
+
+ Type *original_type = e->type;
+ lbArgType *arg = &ft->args[param_index];
+ if (arg->kind == lbArg_Ignore) {
+ continue;
+ }
+
+ lbValue x = lb_emit_conv(p, args[i], original_type);
+ LLVMTypeRef xt = lb_type(p->module, x.type);
+
+ if (arg->kind == lbArg_Direct) {
+ LLVMTypeRef abi_type = arg->cast_type;
+ if (!abi_type) {
+ abi_type = arg->type;
+ }
+ if (xt == abi_type) {
+ array_add(&processed_args, x);
} else {
- array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
+ x.value = OdinLLVMBuildTransmute(p, x.value, abi_type);
+ array_add(&processed_args, x);
}
- } else if (new_type == t_llvm_bool) {
- array_add(&processed_args, lb_emit_conv(p, args[i], new_type));
- } else if (is_type_integer(new_type) || is_type_float(new_type) || is_type_boolean(new_type)) {
- array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
- } else if (is_type_simd_vector(new_type)) {
- array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
- } else if (is_type_tuple(new_type)) {
- Type *abi_type = pt->Proc.abi_compat_params[i];
- Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type);
- lbValue x = {};
- i64 st_sz = type_size_of(st);
- i64 arg_sz = type_size_of(args[i].type);
- if (st_sz == arg_sz) {
- x = lb_emit_transmute(p, args[i], st);
+
+ } else if (arg->kind == lbArg_Indirect) {
+ lbValue ptr = {};
+ if (is_calling_convention_odin(pt->Proc.calling_convention)) {
+ // NOTE(bill): Odin parameters are immutable so the original value can be passed if possible
+ // i.e. `T const &` in C++
+ ptr = lb_address_from_load_or_generate_local(p, x);
} else {
- // NOTE(bill): struct{f32, f32, f32} != struct{#simd[2]f32, f32}
- GB_ASSERT(st_sz > arg_sz);
- lbAddr xx = lb_add_local_generated(p, st, false);
- lbValue pp = lb_emit_conv(p, xx.addr, alloc_type_pointer(args[i].type));
- lb_emit_store(p, pp, args[i]);
- x = lb_addr_load(p, xx);
+ ptr = lb_copy_value_to_ptr(p, x, original_type, 16);
}
- for (isize j = 0; j < new_type->Tuple.variables.count; j++) {
- lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j);
- array_add(&processed_args, xx);
+ array_add(&processed_args, ptr);
+ }
+
+ param_index += 1;
+ }
+
+ if (inlining == ProcInlining_none) {
+ inlining = p->inlining;
+ }
+
+ Type *rt = reduce_tuple_to_single_type(results);
+ if (return_by_pointer) {
+ lbValue return_ptr = {};
+ if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) {
+ if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) {
+ return_ptr = p->return_ptr_hint_value;
+ p->return_ptr_hint_used = true;
}
}
+ if (return_ptr.value == nullptr) {
+ lbAddr r = lb_add_local_generated(p, rt, true);
+ return_ptr = r.addr;
+ }
+ GB_ASSERT(is_type_pointer(return_ptr.type));
+ lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
+ result = lb_emit_load(p, return_ptr);
+ } else if (rt != nullptr) {
+ result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining);
+ if (ft->ret.cast_type) {
+ result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.cast_type);
+ }
+ result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.type);
+ result.type = rt;
+ if (LLVMTypeOf(result.value) == LLVMInt1TypeInContext(p->module->ctx)) {
+ result.type = t_llvm_bool;
+ }
+ if (!is_type_tuple(rt)) {
+ result = lb_emit_conv(p, result, rt);
+ }
} else {
- lbValue x = lb_emit_conv(p, args[i], new_type);
- array_add(&processed_args, x);
+ lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining);
}
- }
- if (inlining == ProcInlining_none) {
- inlining = p->inlining;
- }
-
- lbValue result = {};
+ } else {
+ for (isize i = 0; i < param_count; i++) {
+ Entity *e = pt->Proc.params->Tuple.variables[i];
+ if (e->kind != Entity_Variable) {
+ // array_add(&processed_args, args[i]);
+ continue;
+ }
+ GB_ASSERT(e->flags & EntityFlag_Param);
- Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type);
- Type *rt = reduce_tuple_to_single_type(results);
- if (pt->Proc.return_by_pointer) {
- lbValue return_ptr = {};
- if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) {
- if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) {
- return_ptr = p->return_ptr_hint_value;
- p->return_ptr_hint_used = true;
+ Type *original_type = e->type;
+ Type *new_type = pt->Proc.abi_compat_params[i];
+ Type *arg_type = args[i].type;
+
+ if (are_types_identical(arg_type, new_type)) {
+ // NOTE(bill): Done
+ array_add(&processed_args, args[i]);
+ } else if (!are_types_identical(original_type, new_type)) {
+ if (is_type_pointer(new_type) && !is_type_pointer(original_type)) {
+ Type *av = core_type(type_deref(new_type));
+ if (are_types_identical(av, core_type(original_type))) {
+ if (e->flags&EntityFlag_ImplicitReference) {
+ array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i]));
+ } else if (!is_type_pointer(arg_type)) {
+ array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16));
+ }
+ } else {
+ array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
+ }
+ } else if (new_type == t_llvm_bool) {
+ array_add(&processed_args, lb_emit_conv(p, args[i], new_type));
+ } else if (is_type_integer(new_type) || is_type_float(new_type) || is_type_boolean(new_type)) {
+ array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
+ } else if (is_type_simd_vector(new_type)) {
+ array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
+ } else if (is_type_tuple(new_type)) {
+ Type *abi_type = pt->Proc.abi_compat_params[i];
+ Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type);
+ lbValue x = {};
+ i64 st_sz = type_size_of(st);
+ i64 arg_sz = type_size_of(args[i].type);
+ if (st_sz == arg_sz) {
+ x = lb_emit_transmute(p, args[i], st);
+ } else {
+ // NOTE(bill): struct{f32, f32, f32} != struct{#simd[2]f32, f32}
+ GB_ASSERT(st_sz > arg_sz);
+ lbAddr xx = lb_add_local_generated(p, st, false);
+ lbValue pp = lb_emit_conv(p, xx.addr, alloc_type_pointer(args[i].type));
+ lb_emit_store(p, pp, args[i]);
+ x = lb_addr_load(p, xx);
+ }
+ for (isize j = 0; j < new_type->Tuple.variables.count; j++) {
+ lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j);
+ array_add(&processed_args, xx);
+ }
+ }
+ } else {
+ lbValue x = lb_emit_conv(p, args[i], new_type);
+ array_add(&processed_args, x);
}
}
- if (return_ptr.value == nullptr) {
- lbAddr r = lb_add_local_generated(p, rt, true);
- return_ptr = r.addr;
+
+ if (inlining == ProcInlining_none) {
+ inlining = p->inlining;
}
- GB_ASSERT(is_type_pointer(return_ptr.type));
- lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
- result = lb_emit_load(p, return_ptr);
- } else {
- result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining);
- if (abi_rt != rt) {
- result = lb_emit_transmute(p, result, rt);
+
+
+ Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type);
+ Type *rt = reduce_tuple_to_single_type(results);
+ if (pt->Proc.return_by_pointer) {
+ lbValue return_ptr = {};
+ if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) {
+ if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) {
+ return_ptr = p->return_ptr_hint_value;
+ p->return_ptr_hint_used = true;
+ }
+ }
+ if (return_ptr.value == nullptr) {
+ lbAddr r = lb_add_local_generated(p, rt, true);
+ return_ptr = r.addr;
+ }
+ GB_ASSERT(is_type_pointer(return_ptr.type));
+ lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
+ result = lb_emit_load(p, return_ptr);
+ } else {
+ result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining);
+ if (abi_rt != rt) {
+ result = lb_emit_transmute(p, result, rt);
+ }
}
}
@@ -7176,7 +7678,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
case DeferredProcedure_in_out:
{
auto out_args = lb_value_to_array(p, result);
- array_init(&result_as_args, heap_allocator(), in_args.count + out_args.count);
+ array_init(&result_as_args, permanent_allocator(), in_args.count + out_args.count);
array_copy(&result_as_args, in_args, 0);
array_copy(&result_as_args, out_args, in_args.count);
}
@@ -7285,7 +7787,7 @@ lbValue lb_string_len(lbProcedure *p, lbValue string) {
lbValue lb_cstring_len(lbProcedure *p, lbValue value) {
GB_ASSERT(is_type_cstring(value.type));
- auto args = array_make<lbValue>(heap_allocator(), 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = lb_emit_conv(p, value, t_cstring);
return lb_emit_runtime_call(p, "cstring_len", args);
}
@@ -7323,7 +7825,6 @@ lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da) {
}
lbValue lb_map_entries(lbProcedure *p, lbValue value) {
- gbAllocator a = heap_allocator();
Type *t = base_type(value.type);
GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
init_map_internal_types(t);
@@ -7334,7 +7835,6 @@ lbValue lb_map_entries(lbProcedure *p, lbValue value) {
}
lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value) {
- gbAllocator a = heap_allocator();
Type *t = base_type(type_deref(value.type));
GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
init_map_internal_types(t);
@@ -7458,7 +7958,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
GB_ASSERT(is_type_typeid(tav.type));
- auto args = array_make<lbValue>(heap_allocator(), 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = lb_build_expr(p, arg);
return lb_emit_runtime_call(p, "__type_info_of", args);
}
@@ -7466,16 +7966,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_typeid_of: {
Ast *arg = ce->args[0];
TypeAndValue tav = type_and_value_of_expr(arg);
- if (tav.mode == Addressing_Type) {
- Type *t = default_type(type_of_expr(arg));
- return lb_typeid(p->module, t);
- }
- Type *t = base_type(tav.type);
- GB_ASSERT(are_types_identical(t, t_type_info_ptr));
-
- auto args = array_make<lbValue>(heap_allocator(), 1);
- args[0] = lb_emit_conv(p, lb_build_expr(p, arg), t_type_info_ptr);
- return lb_emit_runtime_call(p, "__typeid_of", args);
+ GB_ASSERT(tav.mode == Addressing_Type);
+ Type *t = default_type(type_of_expr(arg));
+ return lb_typeid(p->module, t);
}
case BuiltinProc_len: {
@@ -7542,7 +8035,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
unsigned mask_len = cast(unsigned)index_count;
- LLVMValueRef *mask_elems = gb_alloc_array(heap_allocator(), LLVMValueRef, index_count);
+ LLVMValueRef *mask_elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, index_count);
for (isize i = 1; i < ce->args.count; i++) {
TypeAndValue tv = type_and_value_of_expr(ce->args[i]);
GB_ASSERT(is_type_integer(tv.type));
@@ -7772,7 +8265,6 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_abs: {
- gbAllocator a = heap_allocator();
lbValue x = lb_build_expr(p, ce->args[0]);
Type *t = x.type;
if (is_type_unsigned(t)) {
@@ -7780,7 +8272,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
if (is_type_quaternion(t)) {
i64 sz = 8*type_size_of(t);
- auto args = array_make<lbValue>(heap_allocator(), 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = x;
switch (sz) {
case 128: return lb_emit_runtime_call(p, "abs_quaternion128", args);
@@ -7789,7 +8281,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_PANIC("Unknown complex type");
} else if (is_type_complex(t)) {
i64 sz = 8*type_size_of(t);
- auto args = array_make<lbValue>(heap_allocator(), 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = x;
switch (sz) {
case 64: return lb_emit_runtime_call(p, "abs_complex64", args);
@@ -7798,7 +8290,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_PANIC("Unknown complex type");
} else if (is_type_float(t)) {
i64 sz = 8*type_size_of(t);
- auto args = array_make<lbValue>(heap_allocator(), 1);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = x;
switch (sz) {
case 32: return lb_emit_runtime_call(p, "abs_f32", args);
@@ -7838,8 +8330,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
{
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
LLVMValueRef the_asm = LLVMGetInlineAsm(func_type,
- "pause", 5,
- "", 0,
+ cast(char *)"pause", 5,
+ cast(char *)"", 0,
/*HasSideEffects*/true, /*IsAlignStack*/false,
LLVMInlineAsmDialectATT
);
@@ -8054,7 +8546,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_ASSERT(tv.type->kind == Type_Tuple);
Type *fix_typed = alloc_type_tuple();
- array_init(&fix_typed->Tuple.variables, heap_allocator(), 2);
+ array_init(&fix_typed->Tuple.variables, permanent_allocator(), 2);
fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0];
fix_typed->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1);
@@ -8063,6 +8555,13 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = fix_typed;
return res;
}
+
+
+ case BuiltinProc_type_equal_proc:
+ return lb_get_equal_proc_for_type(p->module, ce->args[0]->tav.type);
+
+ case BuiltinProc_type_hasher_proc:
+ return lb_get_hasher_proc_for_type(p->module, ce->args[0]->tav.type);
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
@@ -8169,10 +8668,10 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
Type *proc_type_ = base_type(value.type);
GB_ASSERT(proc_type_->kind == Type_Proc);
TypeProc *pt = &proc_type_->Proc;
- set_procedure_abi_types(heap_allocator(), proc_type_);
+ set_procedure_abi_types(proc_type_);
if (is_call_expr_field_value(ce)) {
- auto args = array_make<lbValue>(heap_allocator(), pt->param_count);
+ auto args = array_make<lbValue>(permanent_allocator(), pt->param_count);
for_array(arg_index, ce->args) {
Ast *arg = ce->args[arg_index];
@@ -8241,7 +8740,7 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
param_count = pt->params->Tuple.variables.count;
}
- auto args = array_make<lbValue>(heap_allocator(), cast(isize)gb_max(param_count, arg_count));
+ auto args = array_make<lbValue>(permanent_allocator(), cast(isize)gb_max(param_count, arg_count));
isize variadic_index = pt->variadic_index;
bool variadic = pt->variadic && variadic_index >= 0;
bool vari_expand = ce->ellipsis.pos.line != 0;
@@ -8349,7 +8848,6 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
if (variadic && !vari_expand && !is_c_vararg) {
// variadic call argument generation
- gbAllocator allocator = heap_allocator();
Type *slice_type = param_tuple->variables[variadic_index]->type;
Type *elem_type = base_type(slice_type)->Slice.elem;
lbAddr slice = lb_add_local_generated(p, slice_type, true);
@@ -8625,7 +9123,7 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
lbValue invalid_typeid = lb_const_value(p->module, t_typeid, exact_value_i64(0));
return lb_emit_comp(p, op_kind, x, invalid_typeid);
} else if (is_type_bit_field(t)) {
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
lbValue lhs = lb_address_from_load_or_generate_local(p, x);
args[0] = lb_emit_conv(p, lhs, t_rawptr);
args[1] = lb_const_int(p->module, t_int, type_size_of(t));
@@ -8654,7 +9152,7 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
}
}
} else if (is_type_struct(t) && type_has_nil(t)) {
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
lbValue lhs = lb_address_from_load_or_generate_local(p, x);
args[0] = lb_emit_conv(p, lhs, t_rawptr);
args[1] = lb_const_int(p->module, t_int, type_size_of(t));
@@ -8665,6 +9163,230 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
return {};
}
+lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
+ Type *original_type = type;
+ type = base_type(type);
+ GB_ASSERT(is_type_comparable(type));
+
+ Type *pt = alloc_type_pointer(type);
+ LLVMTypeRef ptr_type = lb_type(m, pt);
+
+ auto key = hash_type(type);
+ lbProcedure **found = map_get(&m->equal_procs, key);
+ lbProcedure *compare_proc = nullptr;
+ if (found) {
+ compare_proc = *found;
+ GB_ASSERT(compare_proc != nullptr);
+ return {compare_proc->value, compare_proc->type};
+ }
+
+ static u32 proc_index = 0;
+
+ char buf[16] = {};
+ isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index);
+ char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+ String proc_name = make_string_c(str);
+
+ lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
+ map_set(&m->equal_procs, key, p);
+ lb_begin_procedure_body(p);
+
+ LLVMValueRef x = LLVMGetParam(p->value, 0);
+ LLVMValueRef y = LLVMGetParam(p->value, 1);
+ x = LLVMBuildPointerCast(p->builder, x, ptr_type, "");
+ y = LLVMBuildPointerCast(p->builder, y, ptr_type, "");
+ lbValue lhs = {x, pt};
+ lbValue rhs = {y, pt};
+
+
+ lbBlock *block_same_ptr = lb_create_block(p, "same_ptr");
+ lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr");
+
+ lbValue same_ptr = lb_emit_comp(p, Token_CmpEq, lhs, rhs);
+ lb_emit_if(p, same_ptr, block_same_ptr, block_diff_ptr);
+ lb_start_block(p, block_same_ptr);
+ LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false));
+
+ lb_start_block(p, block_diff_ptr);
+
+ if (type->kind == Type_Struct) {
+ type_set_offsets(type);
+
+ lbBlock *block_false = lb_create_block(p, "bfalse");
+ lbValue res = lb_const_bool(m, t_bool, true);
+
+ for_array(i, type->Struct.fields) {
+ lbBlock *next_block = lb_create_block(p, "btrue");
+
+ lbValue pleft = lb_emit_struct_ep(p, lhs, cast(i32)i);
+ lbValue pright = lb_emit_struct_ep(p, rhs, cast(i32)i);
+ lbValue left = lb_emit_load(p, pleft);
+ lbValue right = lb_emit_load(p, pright);
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right);
+
+ lb_emit_if(p, ok, next_block, block_false);
+
+ lb_emit_jump(p, next_block);
+ lb_start_block(p, next_block);
+ }
+
+ LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false));
+
+ lb_start_block(p, block_false);
+
+ LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false));
+ } else {
+ lbValue left = lb_emit_load(p, lhs);
+ lbValue right = lb_emit_load(p, rhs);
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right);
+ ok = lb_emit_conv(p, ok, t_bool);
+ LLVMBuildRet(p->builder, ok.value);
+ }
+
+ lb_end_procedure_body(p);
+
+ compare_proc = p;
+ return {compare_proc->value, compare_proc->type};
+}
+
+lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
+ GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
+
+ i64 sz = type_size_of(type);
+
+ if (1 <= sz && sz <= 16) {
+ char name[20] = {};
+ gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ args[0] = data;
+ args[1] = seed;
+ return lb_emit_runtime_call(p, name, args);
+ }
+
+ auto args = array_make<lbValue>(permanent_allocator(), 3);
+ args[0] = data;
+ args[1] = seed;
+ args[2] = lb_const_int(p->module, t_int, type_size_of(type));
+ return lb_emit_runtime_call(p, "default_hasher_n", args);
+}
+
+lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
+ Type *original_type = type;
+ type = core_type(type);
+ GB_ASSERT(is_type_valid_for_keys(type));
+
+ Type *pt = alloc_type_pointer(type);
+ LLVMTypeRef ptr_type = lb_type(m, pt);
+
+ auto key = hash_type(type);
+ lbProcedure **found = map_get(&m->hasher_procs, key);
+ if (found) {
+ GB_ASSERT(*found != nullptr);
+ return {(*found)->value, (*found)->type};
+ }
+
+ static u32 proc_index = 0;
+
+ char buf[16] = {};
+ isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index);
+ char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+ String proc_name = make_string_c(str);
+
+ lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_hasher_proc);
+ map_set(&m->hasher_procs, key, p);
+ lb_begin_procedure_body(p);
+ defer (lb_end_procedure_body(p));
+
+ LLVMValueRef x = LLVMGetParam(p->value, 0);
+ LLVMValueRef y = LLVMGetParam(p->value, 1);
+ lbValue data = {x, t_rawptr};
+ lbValue seed = {y, t_uintptr};
+
+ if (is_type_simple_compare(type)) {
+ lbValue res = lb_simple_compare_hash(p, type, data, seed);
+ LLVMBuildRet(p->builder, res.value);
+ return {p->value, p->type};
+ }
+
+ if (type->kind == Type_Struct) {
+ type_set_offsets(type);
+ data = lb_emit_conv(p, data, t_u8_ptr);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ for_array(i, type->Struct.fields) {
+ i64 offset = type->Struct.offsets[i];
+ Entity *field = type->Struct.fields[i];
+ lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type);
+ lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset));
+
+ args[0] = ptr;
+ args[1] = seed;
+ seed = lb_emit_call(p, field_hasher, args);
+ }
+ LLVMBuildRet(p->builder, seed.value);
+ } else if (type->kind == Type_Array) {
+ lbAddr pres = lb_add_local_generated(p, t_uintptr, false);
+ lb_addr_store(p, pres, seed);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem);
+
+ auto loop_data = lb_loop_start(p, type->Array.count, t_i32);
+
+ data = lb_emit_conv(p, data, pt);
+
+ lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx);
+ args[0] = ptr;
+ args[1] = lb_addr_load(p, pres);
+ lbValue new_seed = lb_emit_call(p, elem_hasher, args);
+ lb_addr_store(p, pres, new_seed);
+
+ lb_loop_end(p, loop_data);
+
+ lbValue res = lb_addr_load(p, pres);
+ LLVMBuildRet(p->builder, res.value);
+ } else if (type->kind == Type_EnumeratedArray) {
+ lbAddr res = lb_add_local_generated(p, t_uintptr, false);
+ lb_addr_store(p, res, seed);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem);
+
+ auto loop_data = lb_loop_start(p, type->EnumeratedArray.count, t_i32);
+
+ data = lb_emit_conv(p, data, pt);
+
+ lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx);
+ args[0] = ptr;
+ args[1] = lb_addr_load(p, res);
+ lbValue new_seed = lb_emit_call(p, elem_hasher, args);
+ lb_addr_store(p, res, new_seed);
+
+ lb_loop_end(p, loop_data);
+
+ lbValue vres = lb_addr_load(p, res);
+ LLVMBuildRet(p->builder, vres.value);
+ } else if (is_type_cstring(type)) {
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ args[0] = data;
+ args[1] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args);
+ LLVMBuildRet(p->builder, res.value);
+ } else if (is_type_string(type)) {
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ args[0] = data;
+ args[1] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
+ LLVMBuildRet(p->builder, res.value);
+ } else {
+ GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
+ }
+
+ return {p->value, p->type};
+}
+
+
lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
Type *a = core_type(left.type);
@@ -8689,8 +9411,6 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
} else if (lb_is_const(right) || lb_is_const_nil(right)) {
right = lb_emit_conv(p, right, left.type);
} else {
- gbAllocator a = heap_allocator();
-
Type *lt = left.type;
Type *rt = right.type;
@@ -8770,7 +9490,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
} else {
if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
// TODO(bill): Test to see if this is actually faster!!!!
- auto args = array_make<lbValue>(heap_allocator(), 3);
+ auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, lhs, t_rawptr);
args[1] = lb_emit_conv(p, rhs, t_rawptr);
args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
@@ -8796,6 +9516,31 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
}
+
+ if (is_type_struct(a) && is_type_comparable(a)) {
+ lbValue left_ptr = lb_address_from_load_or_generate_local(p, left);
+ lbValue right_ptr = lb_address_from_load_or_generate_local(p, right);
+ lbValue res = {};
+ if (is_type_simple_compare(a)) {
+ // TODO(bill): Test to see if this is actually faster!!!!
+ auto args = array_make<lbValue>(permanent_allocator(), 3);
+ args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
+ args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
+ args[2] = lb_const_int(p->module, t_int, type_size_of(a));
+ res = lb_emit_runtime_call(p, "memory_equal", args);
+ } else {
+ lbValue value = lb_get_equal_proc_for_type(p->module, a);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
+ args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
+ res = lb_emit_call(p, value, args);
+ }
+ if (op_kind == Token_NotEq) {
+ res = lb_emit_unary_arith(p, Token_Not, res, res.type);
+ }
+ return res;
+ }
+
if (is_type_string(a)) {
if (is_type_cstring(a)) {
left = lb_emit_conv(p, left, t_string);
@@ -8813,7 +9558,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
GB_ASSERT(runtime_procedure != nullptr);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = left;
args[1] = right;
return lb_emit_runtime_call(p, runtime_procedure, args);
@@ -8838,7 +9583,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
GB_ASSERT(runtime_procedure != nullptr);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = left;
args[1] = right;
return lb_emit_runtime_call(p, runtime_procedure, args);
@@ -8863,7 +9608,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
GB_ASSERT(runtime_procedure != nullptr);
- auto args = array_make<lbValue>(heap_allocator(), 2);
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = left;
args[1] = right;
return lb_emit_runtime_call(p, runtime_procedure, args);
@@ -9012,14 +9757,14 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
// NOTE(bill): Generate a new name
// parent$count
isize name_len = prefix_name.len + 1 + 8 + 1;
- char *name_text = gb_alloc_array(heap_allocator(), char, name_len);
+ char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
i32 name_id = cast(i32)m->anonymous_proc_lits.entries.count;
name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id);
String name = make_string((u8 *)name_text, name_len-1);
Type *type = type_of_expr(expr);
- set_procedure_abi_types(heap_allocator(), type);
+ set_procedure_abi_types(type);
Token token = {};
@@ -9115,7 +9860,7 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
Type *dst_type = tuple->Tuple.variables[0]->type;
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make<lbValue>(heap_allocator(), 6);
+ auto args = array_make<lbValue>(permanent_allocator(), 7);
args[0] = ok;
args[1] = lb_const_string(m, pos.file);
@@ -9124,7 +9869,8 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
args[4] = lb_typeid(m, src_type);
args[5] = lb_typeid(m, dst_type);
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[6] = lb_emit_conv(p, value_, t_rawptr);
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
}
return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
@@ -9176,7 +9922,7 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
// NOTE(bill): Panic on invalid conversion
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make<lbValue>(heap_allocator(), 6);
+ auto args = array_make<lbValue>(permanent_allocator(), 7);
args[0] = ok;
args[1] = lb_const_string(m, pos.file);
@@ -9185,7 +9931,8 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
args[4] = any_typeid;
args[5] = dst_typeid;
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[6] = lb_emit_struct_ev(p, value, 0);;
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
}
@@ -9460,7 +10207,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
return addr.addr;
} else if (ue_expr->kind == Ast_TypeAssertion) {
- gbAllocator a = heap_allocator();
GB_ASSERT(is_type_pointer(tv.type));
ast_node(ta, TypeAssertion, ue_expr);
@@ -9482,7 +10228,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbValue dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
- auto args = array_make<lbValue>(heap_allocator(), 6);
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = ok;
args[1] = lb_find_or_add_entity_string(p->module, pos.file);
@@ -9507,7 +10253,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
- auto args = array_make<lbValue>(heap_allocator(), 6);
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = ok;
args[1] = lb_find_or_add_entity_string(p->module, pos.file);
@@ -9658,7 +10404,6 @@ lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) {
lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) {
GB_ASSERT_MSG(is_type_pointer(map_val_ptr.type), "%s", type_to_string(map_val_ptr.type));
- gbAllocator a = heap_allocator();
lbAddr h = lb_add_local_generated(p, t_map_header, false); // all the values will be initialzed later
map_type = base_type(map_type);
GB_ASSERT(map_type->kind == Type_Map);
@@ -9671,65 +10416,96 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) {
lbValue m = lb_emit_conv(p, map_val_ptr, type_deref(gep0.type));
lb_emit_store(p, gep0, m);
- lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 1), lb_const_bool(p->module, t_bool, is_type_string(key_type)));
-
i64 entry_size = type_size_of (map_type->Map.entry_type);
i64 entry_align = type_align_of (map_type->Map.entry_type);
- i64 value_offset = type_offset_of(map_type->Map.entry_type, 2);
+
+ i64 key_offset = type_offset_of(map_type->Map.entry_type, 2);
+ i64 key_size = type_size_of (map_type->Map.key);
+
+ i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
i64 value_size = type_size_of (map_type->Map.value);
+ lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 1), lb_get_equal_proc_for_type(p->module, key_type));
lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 2), lb_const_int(p->module, t_int, entry_size));
lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 3), lb_const_int(p->module, t_int, entry_align));
- lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 4), lb_const_int(p->module, t_uintptr, value_offset));
- lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 5), lb_const_int(p->module, t_int, value_size));
+ lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 4), lb_const_int(p->module, t_uintptr, key_offset));
+ lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 5), lb_const_int(p->module, t_int, key_size));
+ lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 6), lb_const_int(p->module, t_uintptr, value_offset));
+ lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 7), lb_const_int(p->module, t_int, value_size));
return lb_addr_load(p, h);
}
-lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) {
- Type *hash_type = t_u64;
- lbAddr v = lb_add_local_generated(p, t_map_key, true);
- lbValue vp = lb_addr_get_ptr(p, v);
- Type *t = base_type(key.type);
- key = lb_emit_conv(p, key, key_type);
+lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
+ if (true) {
+ return {};
+ }
+
+ lbValue hashed_key = {};
- if (is_type_string(t)) {
- lbValue str = lb_emit_conv(p, key, t_string);
- lbValue hashed_str = {};
- if (lb_is_const(str)) {
- String v = lb_get_const_string(p->module, str);
- u64 hs = fnv64a(v.text, v.len);
- hashed_str = lb_const_int(p->module, t_u64, hs);
+ if (lb_is_const(key)) {
+ u64 hash = 0xcbf29ce484222325;
+ if (is_type_cstring(key_type)) {
+ size_t length = 0;
+ char const *text = LLVMGetAsString(key.value, &length);
+ hash = fnv64a(text, cast(isize)length);
+ } else if (is_type_string(key_type)) {
+ unsigned data_indices[] = {0};
+ unsigned len_indices[] = {1};
+ LLVMValueRef data = LLVMConstExtractValue(key.value, data_indices, gb_count_of(data_indices));
+ LLVMValueRef len = LLVMConstExtractValue(key.value, len_indices, gb_count_of(len_indices));
+ isize length = LLVMConstIntGetSExtValue(len);
+ char const *text = nullptr;
+ if (length != 0) {
+ if (LLVMGetConstOpcode(data) != LLVMGetElementPtr) {
+ return {};
+ }
+ // TODO(bill): THIS IS BROKEN! THIS NEEDS FIXING :P
+
+ size_t ulength = 0;
+ text = LLVMGetAsString(data, &ulength);
+ gb_printf_err("%td %td %s\n", length, ulength, text);
+ length = gb_min(length, cast(isize)ulength);
+ }
+ hash = fnv64a(text, cast(isize)length);
} else {
- auto args = array_make<lbValue>(heap_allocator(), 1);
- args[0] = str;
- hashed_str = lb_emit_runtime_call(p, "default_hash_string", args);
+ return {};
}
- lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_str);
+ // TODO(bill): other const hash types
- lbValue key_data = lb_emit_struct_ep(p, vp, 1);
- key_data = lb_emit_conv(p, key_data, alloc_type_pointer(key_type));
- lb_emit_store(p, key_data, str);
- } else {
- i64 sz = type_size_of(t);
- GB_ASSERT(sz <= 8);
- if (sz != 0) {
- auto args = array_make<lbValue>(heap_allocator(), 2);
- args[0] = lb_address_from_load_or_generate_local(p, key);
- args[1] = lb_const_int(p->module, t_int, sz);
- lbValue hash = lb_emit_runtime_call(p, "default_hash_ptr", args);
+ if (build_context.word_size == 4) {
+ hash &= 0xffffffffull;
+ }
+ hashed_key = lb_const_int(m, t_uintptr, hash);
+ }
+
+ return hashed_key;
+}
+
+lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type) {
+ Type *hash_type = t_u64;
+ lbAddr v = lb_add_local_generated(p, t_map_hash, true);
+ lbValue vp = lb_addr_get_ptr(p, v);
+ Type *t = base_type(key.type);
+ key = lb_emit_conv(p, key, key_type);
+ lbValue key_ptr = lb_address_from_load_or_generate_local(p, key);
+ key_ptr = lb_emit_conv(p, key_ptr, t_rawptr);
- lbValue hash_ptr = lb_emit_struct_ep(p, vp, 0);
- lbValue key_data = lb_emit_struct_ep(p, vp, 1);
- key_data = lb_emit_conv(p, key_data, alloc_type_pointer(key_type));
+ lbValue hashed_key = lb_const_hash(p->module, key, key_type);
+ if (hashed_key.value == nullptr) {
+ lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type);
- lb_emit_store(p, hash_ptr, hash);
- lb_emit_store(p, key_data, key);
- }
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ args[0] = key_ptr;
+ args[1] = lb_const_int(p->module, t_uintptr, 0);
+ hashed_key = lb_emit_call(p, hasher, args);
}
+ lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_key);
+ lb_emit_store(p, lb_emit_struct_ep(p, vp, 1), key_ptr);
+
return lb_addr_load(p, v);
}
@@ -9739,13 +10515,13 @@ void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_
GB_ASSERT(map_type->kind == Type_Map);
lbValue h = lb_gen_map_header(p, addr.addr, map_type);
- lbValue key = lb_gen_map_key(p, map_key, map_type->Map.key);
+ lbValue key = lb_gen_map_hash(p, map_key, map_type->Map.key);
lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
lbAddr value_addr = lb_add_local_generated(p, v.type, false);
lb_addr_store(p, value_addr, v);
- auto args = array_make<lbValue>(heap_allocator(), 4);
+ auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = h;
args[1] = key;
args[2] = lb_emit_conv(p, value_addr.addr, t_rawptr);
@@ -9904,7 +10680,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(ta, TypeAssertion, expr);
- gbAllocator a = heap_allocator();
TokenPos pos = ast_token(expr).pos;
lbValue e = lb_build_expr(p, ta->expr);
Type *t = type_deref(e.type);
@@ -9941,7 +10716,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_ast_node(ie, IndexExpr, expr);
Type *t = base_type(type_of_expr(ie->expr));
- gbAllocator a = heap_allocator();
bool deref = is_type_pointer(t);
t = base_type(type_deref(t));
@@ -10141,7 +10915,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(se, SliceExpr, expr);
- gbAllocator a = heap_allocator();
lbValue low = lb_const_int(p->module, t_int, 0);
lbValue high = {};
@@ -10440,9 +11213,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
if (cl->elems.count == 0) {
break;
}
- gbAllocator a = heap_allocator();
{
- auto args = array_make<lbValue>(a, 3);
+ 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);
@@ -10463,8 +11235,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
if (cl->elems.count > 0) {
lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
- auto temp_data = array_make<lbCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
- defer (array_free(&temp_data));
+ 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) {
@@ -10563,8 +11334,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
if (cl->elems.count > 0) {
lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
- auto temp_data = array_make<lbCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
- defer (array_free(&temp_data));
+ 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) {
@@ -10672,8 +11442,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
lbValue data = lb_slice_elem(p, slice);
- auto temp_data = array_make<lbCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
- defer (array_free(&temp_data));
+ auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
@@ -10766,14 +11535,13 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
break;
}
Type *et = bt->DynamicArray.elem;
- gbAllocator a = heap_allocator();
lbValue size = lb_const_int(p->module, t_int, type_size_of(et));
lbValue align = lb_const_int(p->module, t_int, type_align_of(et));
i64 item_count = gb_max(cl->max_count, cl->elems.count);
{
- auto args = array_make<lbValue>(a, 5);
+ 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;
@@ -10827,7 +11595,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
}
{
- auto args = array_make<lbValue>(a, 6);
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = lb_emit_conv(p, v.addr, t_rawptr);
args[1] = size;
args[2] = align;
@@ -10968,12 +11736,16 @@ void lb_init_module(lbModule *m, Checker *c) {
gb_mutex_init(&m->mutex);
gbAllocator a = heap_allocator();
map_init(&m->types, a);
+ map_init(&m->llvm_types, a);
map_init(&m->values, a);
string_map_init(&m->members, a);
map_init(&m->procedure_values, a);
string_map_init(&m->procedures, a);
string_map_init(&m->const_strings, a);
map_init(&m->anonymous_proc_lits, a);
+ map_init(&m->function_type_map, a);
+ map_init(&m->equal_procs, a);
+ map_init(&m->hasher_procs, a);
array_init(&m->procedures_to_generate, a);
array_init(&m->foreign_library_paths, a);
@@ -11038,7 +11810,7 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
type = default_type(type);
isize max_len = 7+8+1;
- u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len);
+ u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len);
isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", m->global_generated_index);
m->global_generated_index++;
String name = make_string(str, len-1);
@@ -11118,12 +11890,11 @@ lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool
}
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id) {
- gbAllocator a = heap_allocator();
Token token = {Token_Ident};
isize name_len = prefix.len + 1 + 20;
auto suffix_id = cast(unsigned long long)id;
- char *text = gb_alloc_array(a, char, name_len+1);
+ char *text = gb_alloc_array(permanent_allocator(), char, name_len+1);
gb_snprintf(text, name_len,
"%.*s-%llu", LIT(prefix), suffix_id);
text[name_len] = 0;
@@ -11145,7 +11916,6 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String
void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
lbModule *m = p->module;
LLVMContextRef ctx = m->ctx;
- gbAllocator a = heap_allocator();
CheckerInfo *info = m->info;
{
@@ -11168,6 +11938,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
// Useful types
Type *t_i64_slice_ptr = alloc_type_pointer(alloc_type_slice(t_i64));
Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string));
+ Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
+ Type *t_type_info_flags = type_info_flags_entity->type;
+
i32 type_info_member_types_index = 0;
i32 type_info_member_names_index = 0;
@@ -11187,19 +11960,43 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
lbValue tag = {};
lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data.addr, cast(i32)entry_index);
- lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 3);
+ lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4);
+
+ lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, type_info_flags_of_type(t));
lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 0), lb_const_int(m, t_int, type_size_of(t)));
lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 1), lb_const_int(m, t_int, type_align_of(t)));
- lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 2), lb_typeid(m, t));
+ lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 2), type_info_flags);
+ lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 3), lb_typeid(m, t));
switch (t->kind) {
case Type_Named: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr);
- LLVMValueRef vals[2] = {
+
+ LLVMValueRef pkg_name = nullptr;
+ if (t->Named.type_name->pkg) {
+ pkg_name = lb_const_string(m, t->Named.type_name->pkg->name).value;
+ } else {
+ pkg_name = LLVMConstNull(lb_type(m, t_string));
+ }
+
+ String proc_name = {};
+ if (t->Named.type_name->parent_proc_decl) {
+ DeclInfo *decl = t->Named.type_name->parent_proc_decl;
+ if (decl->entity && decl->entity->kind == Entity_Procedure) {
+ proc_name = decl->entity->token.string;
+ }
+ }
+ TokenPos pos = t->Named.type_name->token.pos;
+
+ lbValue loc = lb_emit_source_code_location(p, proc_name, pos);
+
+ LLVMValueRef vals[4] = {
lb_const_string(p->module, t->Named.type_name->token.string).value,
lb_get_type_info_ptr(m, t->Named.base).value,
+ pkg_name,
+ loc.value
};
lbValue res = {};
@@ -11523,10 +12320,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
str_lit("$enum_values"), cast(i64)entry_index);
- LLVMValueRef *name_values = gb_alloc_array(heap_allocator(), LLVMValueRef, fields.count);
- LLVMValueRef *value_values = gb_alloc_array(heap_allocator(), LLVMValueRef, fields.count);
- defer (gb_free(heap_allocator(), name_values));
- defer (gb_free(heap_allocator(), value_values));
+ LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
+ LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
GB_ASSERT(is_type_integer(t->Enum.base_type));
@@ -11610,7 +12405,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_Struct: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr);
- LLVMValueRef vals[11] = {};
+ LLVMValueRef vals[12] = {};
{
@@ -11620,18 +12415,22 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
vals[5] = is_packed.value;
vals[6] = is_raw_union.value;
vals[7] = is_custom_align.value;
+ if (is_type_comparable(t) && !is_type_simple_compare(t)) {
+ vals[8] = lb_get_equal_proc_for_type(m, t).value;
+ }
+
if (t->Struct.soa_kind != StructSoa_None) {
- lbValue kind = lb_emit_struct_ep(p, tag, 8);
+ lbValue kind = lb_emit_struct_ep(p, tag, 9);
Type *kind_type = type_deref(kind.type);
lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
lbValue soa_type = lb_type_info(m, t->Struct.soa_elem);
lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
- vals[8] = soa_kind.value;
- vals[9] = soa_type.value;
- vals[10] = soa_len.value;
+ vals[9] = soa_kind.value;
+ vals[10] = soa_type.value;
+ vals[11] = soa_len.value;
}
}
@@ -11703,10 +12502,12 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
init_map_internal_types(t);
- LLVMValueRef vals[3] = {
+ LLVMValueRef vals[5] = {
lb_get_type_info_ptr(m, t->Map.key).value,
lb_get_type_info_ptr(m, t->Map.value).value,
lb_get_type_info_ptr(m, t->Map.generated_struct_type).value,
+ lb_get_equal_proc_for_type(m, t->Map.key).value,
+ lb_get_hasher_proc_for_type(m, t->Map.key).value
};
lbValue res = {};
@@ -11875,10 +12676,6 @@ void lb_generate_code(lbGenerator *gen) {
LLVMModuleRef mod = gen->module.mod;
CheckerInfo *info = gen->info;
- Arena temp_arena = {};
- arena_init(&temp_arena, heap_allocator());
- gbAllocator temp_allocator = arena_allocator(&temp_arena);
-
auto *min_dep_set = &info->minimum_dependency_set;
@@ -11891,8 +12688,8 @@ void lb_generate_code(lbGenerator *gen) {
LLVMInitializeNativeTarget();
- char const *target_triple = alloc_cstring(heap_allocator(), build_context.metrics.target_triplet);
- char const *target_data_layout = alloc_cstring(heap_allocator(), build_context.metrics.target_data_layout);
+ char const *target_triple = alloc_cstring(permanent_allocator(), build_context.metrics.target_triplet);
+ char const *target_data_layout = alloc_cstring(permanent_allocator(), build_context.metrics.target_data_layout);
LLVMSetTarget(mod, target_triple);
LLVMTargetRef target = {};
@@ -11914,7 +12711,7 @@ void lb_generate_code(lbGenerator *gen) {
if (build_context.microarch == "native") {
llvm_cpu = host_cpu_name;
} else {
- llvm_cpu = alloc_cstring(heap_allocator(), build_context.microarch);
+ llvm_cpu = alloc_cstring(permanent_allocator(), build_context.microarch);
}
if (gb_strcmp(llvm_cpu, host_cpu_name) == 0) {
llvm_features = LLVMGetHostCPUFeatures();
@@ -11957,6 +12754,10 @@ void lb_generate_code(lbGenerator *gen) {
1, "", 0,
LLVMDWARFEmissionFull, 0, true,
true
+ #if LLVM_VERSION_MAJOR > 10
+ , "", 0,
+ "", 0
+ #endif
);
}
@@ -12084,8 +12885,9 @@ void lb_generate_code(lbGenerator *gen) {
lbValue var;
lbValue init;
DeclInfo *decl;
+ bool is_initialized;
};
- auto global_variables = array_make<GlobalVariable>(heap_allocator(), 0, global_variable_max_count);
+ auto global_variables = array_make<GlobalVariable>(permanent_allocator(), 0, global_variable_max_count);
for_array(i, info->variable_init_order) {
DeclInfo *d = info->variable_init_order[i];
@@ -12112,7 +12914,7 @@ void lb_generate_code(lbGenerator *gen) {
lbValue g = {};
- g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(heap_allocator(), name));
+ g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
g.type = alloc_type_pointer(e->type);
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(g.value, true);
@@ -12146,15 +12948,22 @@ void lb_generate_code(lbGenerator *gen) {
var.var = g;
var.decl = decl;
- if (decl->init_expr != nullptr && !is_type_any(e->type)) {
+ if (decl->init_expr != nullptr) {
TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
- if (tav.mode != Addressing_Invalid) {
- if (tav.value.kind != ExactValue_Invalid) {
- ExactValue v = tav.value;
- lbValue init = lb_const_value(m, tav.type, v);
- LLVMSetInitializer(g.value, init.value);
+ if (!is_type_any(e->type)) {
+ if (tav.mode != Addressing_Invalid) {
+ if (tav.value.kind != ExactValue_Invalid) {
+ ExactValue v = tav.value;
+ lbValue init = lb_const_value(m, tav.type, v);
+ LLVMSetInitializer(g.value, init.value);
+ var.is_initialized = true;
+ }
}
}
+ if (!var.is_initialized &&
+ (is_type_untyped_nil(tav.type) || is_type_untyped_undef(tav.type))) {
+ var.is_initialized = true;
+ }
}
array_add(&global_variables, var);
@@ -12166,9 +12975,6 @@ void lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Global Procedures and Types");
for_array(i, info->entities) {
- // arena_free_all(&temp_arena);
- // gbAllocator a = temp_allocator;
-
Entity *e = info->entities[i];
String name = e->token.string;
DeclInfo *decl = e->decl_info;
@@ -12229,101 +13035,85 @@ void lb_generate_code(lbGenerator *gen) {
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod);
defer (LLVMDisposePassManager(default_function_pass_manager));
- /*{
- LLVMAddMemCpyOptPass(default_function_pass_manager);
- LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager);
- LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager);
- LLVMAddAggressiveInstCombinerPass(default_function_pass_manager);
- LLVMAddConstantPropagationPass(default_function_pass_manager);
- LLVMAddAggressiveDCEPass(default_function_pass_manager);
- LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager);
- LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager);
- LLVMAddCFGSimplificationPass(default_function_pass_manager);
- // LLVMAddUnifyFunctionExitNodesPass(default_function_pass_manager);
-
- if (build_context.optimization_level >= 2) {
- LLVMAddAggressiveInstCombinerPass(default_function_pass_manager);
- LLVMAddEarlyCSEPass(default_function_pass_manager);
- LLVMAddEarlyCSEMemSSAPass(default_function_pass_manager);
- LLVMAddLowerExpectIntrinsicPass(default_function_pass_manager);
-
- LLVMAddAlignmentFromAssumptionsPass(default_function_pass_manager);
- LLVMAddLoopRotatePass(default_function_pass_manager);
- LLVMAddDeadStoreEliminationPass(default_function_pass_manager);
- LLVMAddScalarizerPass(default_function_pass_manager);
- LLVMAddReassociatePass(default_function_pass_manager);
- LLVMAddAddDiscriminatorsPass(default_function_pass_manager);
- LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager);
- LLVMAddCorrelatedValuePropagationPass(default_function_pass_manager);
-
- LLVMAddSLPVectorizePass(default_function_pass_manager);
- LLVMAddLoopVectorizePass(default_function_pass_manager);
-
- }
- }*/
- if (build_context.optimization_level == 0 && false) {
- auto dfpm = default_function_pass_manager;
-
- LLVMAddMemCpyOptPass(dfpm);
- LLVMAddPromoteMemoryToRegisterPass(dfpm);
- LLVMAddMergedLoadStoreMotionPass(dfpm);
- LLVMAddAggressiveInstCombinerPass(dfpm);
- LLVMAddConstantPropagationPass(dfpm);
- LLVMAddAggressiveDCEPass(dfpm);
- LLVMAddMergedLoadStoreMotionPass(dfpm);
- LLVMAddPromoteMemoryToRegisterPass(dfpm);
- LLVMAddCFGSimplificationPass(dfpm);
- LLVMAddScalarizerPass(dfpm);
- } else {
+ {
auto dfpm = default_function_pass_manager;
- LLVMAddMemCpyOptPass(dfpm);
- LLVMAddPromoteMemoryToRegisterPass(dfpm);
- LLVMAddMergedLoadStoreMotionPass(dfpm);
- LLVMAddAggressiveInstCombinerPass(dfpm);
- LLVMAddConstantPropagationPass(dfpm);
- LLVMAddAggressiveDCEPass(dfpm);
- LLVMAddMergedLoadStoreMotionPass(dfpm);
- LLVMAddPromoteMemoryToRegisterPass(dfpm);
- LLVMAddCFGSimplificationPass(dfpm);
-
+ if (build_context.optimization_level == 0) {
+ LLVMAddMemCpyOptPass(dfpm);
+ LLVMAddPromoteMemoryToRegisterPass(dfpm);
+ LLVMAddMergedLoadStoreMotionPass(dfpm);
+ LLVMAddAggressiveInstCombinerPass(dfpm);
+ LLVMAddEarlyCSEPass(dfpm);
+ LLVMAddEarlyCSEMemSSAPass(dfpm);
+ LLVMAddConstantPropagationPass(dfpm);
+ LLVMAddAggressiveDCEPass(dfpm);
+ LLVMAddMergedLoadStoreMotionPass(dfpm);
+ LLVMAddPromoteMemoryToRegisterPass(dfpm);
+ LLVMAddCFGSimplificationPass(dfpm);
+
+
+ // LLVMAddInstructionCombiningPass(dfpm);
+ LLVMAddSLPVectorizePass(dfpm);
+ LLVMAddLoopVectorizePass(dfpm);
+
+ LLVMAddScalarizerPass(dfpm);
+ LLVMAddLoopIdiomPass(dfpm);
+ } else {
+ bool do_extra_passes = true;
- // LLVMAddInstructionCombiningPass(dfpm);
- LLVMAddSLPVectorizePass(dfpm);
- LLVMAddLoopVectorizePass(dfpm);
- LLVMAddEarlyCSEPass(dfpm);
- LLVMAddEarlyCSEMemSSAPass(dfpm);
+ int repeat_count = cast(int)build_context.optimization_level;
+ do {
+ LLVMAddMemCpyOptPass(dfpm);
+ LLVMAddPromoteMemoryToRegisterPass(dfpm);
+ LLVMAddMergedLoadStoreMotionPass(dfpm);
+ LLVMAddAlignmentFromAssumptionsPass(dfpm);
+ LLVMAddEarlyCSEPass(dfpm);
+ LLVMAddEarlyCSEMemSSAPass(dfpm);
+ LLVMAddConstantPropagationPass(dfpm);
+ if (do_extra_passes) {
+ LLVMAddAggressiveInstCombinerPass(dfpm);
+ LLVMAddAggressiveDCEPass(dfpm);
+ }
+ LLVMAddMergedLoadStoreMotionPass(dfpm);
+ LLVMAddPromoteMemoryToRegisterPass(dfpm);
+ LLVMAddCFGSimplificationPass(dfpm);
+ LLVMAddTailCallEliminationPass(dfpm);
- LLVMAddScalarizerPass(dfpm);
- LLVMAddLoopIdiomPass(dfpm);
+ if (do_extra_passes) {
+ LLVMAddSLPVectorizePass(dfpm);
+ LLVMAddLoopVectorizePass(dfpm);
- // LLVMAddAggressiveInstCombinerPass(dfpm);
- // LLVMAddLowerExpectIntrinsicPass(dfpm);
+ LLVMAddConstantPropagationPass(dfpm);
+ LLVMAddScalarizerPass(dfpm);
+ LLVMAddLoopIdiomPass(dfpm);
- // LLVMAddPartiallyInlineLibCallsPass(dfpm);
+ LLVMAddAggressiveInstCombinerPass(dfpm);
+ LLVMAddLowerExpectIntrinsicPass(dfpm);
- // LLVMAddAlignmentFromAssumptionsPass(dfpm);
- // LLVMAddDeadStoreEliminationPass(dfpm);
- // LLVMAddReassociatePass(dfpm);
- // LLVMAddAddDiscriminatorsPass(dfpm);
- // LLVMAddPromoteMemoryToRegisterPass(dfpm);
- // LLVMAddCorrelatedValuePropagationPass(dfpm);
- // LLVMAddMemCpyOptPass(dfpm);
+ LLVMAddDeadStoreEliminationPass(dfpm);
+ LLVMAddReassociatePass(dfpm);
+ LLVMAddAddDiscriminatorsPass(dfpm);
+ LLVMAddPromoteMemoryToRegisterPass(dfpm);
+ LLVMAddCorrelatedValuePropagationPass(dfpm);
+ }
+ } while (repeat_count --> 0);
+ }
}
LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod);
defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy));
{
- LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy);
- LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy);
- LLVMAddAggressiveInstCombinerPass(default_function_pass_manager_without_memcpy);
- LLVMAddConstantPropagationPass(default_function_pass_manager_without_memcpy);
- LLVMAddAggressiveDCEPass(default_function_pass_manager_without_memcpy);
- LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy);
- LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy);
- LLVMAddCFGSimplificationPass(default_function_pass_manager_without_memcpy);
- // LLVMAddUnifyFunctionExitNodesPass(default_function_pass_manager_without_memcpy);
+ auto dfpm = default_function_pass_manager_without_memcpy;
+ LLVMAddPromoteMemoryToRegisterPass(dfpm);
+ LLVMAddMergedLoadStoreMotionPass(dfpm);
+ LLVMAddAggressiveInstCombinerPass(dfpm);
+ LLVMAddConstantPropagationPass(dfpm);
+ LLVMAddAggressiveDCEPass(dfpm);
+ LLVMAddMergedLoadStoreMotionPass(dfpm);
+ LLVMAddPromoteMemoryToRegisterPass(dfpm);
+ LLVMAddCFGSimplificationPass(dfpm);
+ // LLVMAddUnifyFunctionExitNodesPass(dfpm);
}
TIME_SECTION("LLVM Runtime Creation");
@@ -12374,7 +13164,12 @@ void lb_generate_code(lbGenerator *gen) {
auto *var = &global_variables[i];
if (var->decl->init_expr != nullptr) {
lbValue init = lb_build_expr(p, var->decl->init_expr);
- if (!lb_is_const(init)) {
+ if (lb_is_const(init)) {
+ if (!var->is_initialized) {
+ LLVMSetInitializer(var->var.value, init.value);
+ var->is_initialized = true;
+ }
+ } else {
var->init = init;
}
}
@@ -12392,6 +13187,7 @@ void lb_generate_code(lbGenerator *gen) {
}
if (var->init.value != nullptr) {
+ GB_ASSERT(!var->is_initialized);
Type *t = type_deref(var->var.type);
if (is_type_any(t)) {
@@ -12408,6 +13204,8 @@ void lb_generate_code(lbGenerator *gen) {
} else {
lb_emit_store(p, var->var, lb_emit_conv(p, var->init, t));
}
+
+ var->is_initialized = true;
}
}
@@ -12453,12 +13251,12 @@ void lb_generate_code(lbGenerator *gen) {
if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) {
name = str_lit("mainCRTStartup");
} else {
- array_init(&params->Tuple.variables, heap_allocator(), 2);
+ array_init(&params->Tuple.variables, permanent_allocator(), 2);
params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true);
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), alloc_type_pointer(t_cstring), false, true);
}
- array_init(&results->Tuple.variables, heap_allocator(), 1);
+ array_init(&results->Tuple.variables, permanent_allocator(), 1);
results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"), t_i32, false, true);
Type *proc_type = alloc_type_proc(nullptr,
@@ -12471,11 +13269,21 @@ void lb_generate_code(lbGenerator *gen) {
lb_begin_procedure_body(p);
- lbValue *found = map_get(&m->values, hash_entity(entry_point));
- GB_ASSERT(found != nullptr);
-
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, "");
- LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
+
+ if (build_context.command_kind == Command_test) {
+ for_array(i, m->info->testing_procedures) {
+ Entity *e = m->info->testing_procedures[i];
+ lbValue *found = map_get(&m->values, hash_entity(e));
+ GB_ASSERT(found != nullptr);
+ lb_emit_call(p, *found, {});
+ }
+ } else {
+ lbValue *found = map_get(&m->values, hash_entity(entry_point));
+ GB_ASSERT(found != nullptr);
+ LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
+ }
+
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
lb_end_procedure_body(p);
@@ -12490,6 +13298,9 @@ void lb_generate_code(lbGenerator *gen) {
LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
}
+
+ String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll"));
+
TIME_SECTION("LLVM Procedure Generation");
for_array(i, m->procedures_to_generate) {
lbProcedure *p = m->procedures_to_generate[i];
@@ -12520,7 +13331,11 @@ void lb_generate_code(lbGenerator *gen) {
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
LLVMDumpValue(p->value);
gb_printf_err("\n\n\n\n");
- LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
+ if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
+ gb_printf_err("LLVM Error: %s\n", llvm_error);
+ }
+ LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
+ gb_exit(1);
}
}
@@ -12541,6 +13356,15 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ for_array(i, m->equal_procs.entries) {
+ lbProcedure *p = m->equal_procs.entries[i].value;
+ LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+ }
+ for_array(i, m->hasher_procs.entries) {
+ lbProcedure *p = m->hasher_procs.entries[i].value;
+ LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+ }
+
TIME_SECTION("LLVM Module Pass");
@@ -12567,35 +13391,41 @@ void lb_generate_code(lbGenerator *gen) {
llvm_error = nullptr;
defer (LLVMDisposeMessage(llvm_error));
- String filepath_ll = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".ll"));
- defer (gb_free(heap_allocator(), filepath_ll.text));
-
String filepath_obj = {};
LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile;
if (build_context.build_mode == BuildMode_Assembly) {
- filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".S"));
+ filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".S"));
code_gen_file_type = LLVMAssemblyFile;
} else {
switch (build_context.metrics.os) {
case TargetOs_windows:
- filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".obj"));
+ filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".obj"));
break;
case TargetOs_darwin:
case TargetOs_linux:
case TargetOs_essence:
- filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".o"));
+ filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".o"));
break;
case TargetOs_js:
- filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".wasm-obj"));
+ filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".wasm-obj"));
break;
}
}
LLVMDIBuilderFinalize(m->debug_builder);
- if (LLVMVerifyModule(mod, LLVMAbortProcessAction, &llvm_error)) {
- gb_printf_err("LLVM Error: %s\n", llvm_error);
+ if (LLVMVerifyModule(mod, LLVMReturnStatusAction, &llvm_error)) {
+ gb_printf_err("LLVM Error:\n%s\n", llvm_error);
+ if (build_context.keep_temp_files) {
+ TIME_SECTION("LLVM Print Module to File");
+ if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
+ gb_printf_err("LLVM Error: %s\n", llvm_error);
+ gb_exit(1);
+ return;
+ }
+ }
+ gb_exit(1);
return;
}
llvm_error = nullptr;
@@ -12603,6 +13433,7 @@ void lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Print Module to File");
if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
+ gb_exit(1);
return;
}
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 23b2c7654..34a5dfacf 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -1,3 +1,5 @@
+#if defined(LLVM_BACKEND_SUPPORT)
+#if defined(GB_SYSTEM_WINDOWS)
#include "llvm-c/Core.h"
#include "llvm-c/ExecutionEngine.h"
#include "llvm-c/Target.h"
@@ -12,6 +14,23 @@
#include "llvm-c/Transforms/Scalar.h"
#include "llvm-c/Transforms/Utils.h"
#include "llvm-c/Transforms/Vectorize.h"
+#else
+#include <llvm-c/Core.h>
+#include <llvm-c/ExecutionEngine.h>
+#include <llvm-c/Target.h>
+#include <llvm-c/Analysis.h>
+#include <llvm-c/Object.h>
+#include <llvm-c/BitWriter.h>
+#include <llvm-c/DebugInfo.h>
+#include <llvm-c/Transforms/AggressiveInstCombine.h>
+#include <llvm-c/Transforms/InstCombine.h>
+#include <llvm-c/Transforms/IPO.h>
+#include <llvm-c/Transforms/PassManagerBuilder.h>
+#include <llvm-c/Transforms/Scalar.h>
+#include <llvm-c/Transforms/Utils.h>
+#include <llvm-c/Transforms/Vectorize.h>
+#endif
+#endif
struct lbProcedure;
@@ -74,6 +93,8 @@ struct lbModule {
gbMutex mutex;
Map<LLVMTypeRef> types; // Key: Type *
+ Map<Type *> llvm_types; // Key: LLVMTypeRef
+ i32 internal_type_level;
Map<lbValue> values; // Key: Entity *
StringMap<lbValue> members;
@@ -83,6 +104,10 @@ struct lbModule {
StringMap<LLVMValueRef> const_strings;
Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
+ Map<struct lbFunctionType *> function_type_map; // Key: Type *
+
+ Map<lbProcedure *> equal_procs; // Key: Type *
+ Map<lbProcedure *> hasher_procs; // Key: Type *
u32 global_array_index;
u32 global_generated_index;
@@ -199,6 +224,7 @@ struct lbProcedure {
bool is_entry_point;
bool is_startup;
+ lbFunctionType *abi_function_type;
LLVMValueRef value;
LLVMBuilderRef builder;
@@ -301,7 +327,7 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_ini
void lb_add_foreign_library_path(lbModule *m, Entity *e);
-lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type=t_typeid);
+lbValue lb_typeid(lbModule *m, Type *type);
lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value);
lbValue lb_address_from_load(lbProcedure *p, lbValue value);
@@ -344,7 +370,7 @@ String lb_get_const_string(lbModule *m, lbValue value);
lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type);
-lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type);
+lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type);
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node);
@@ -354,6 +380,9 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To
lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
+lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type);
+lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type);
+lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
diff --git a/src/main.cpp b/src/main.cpp
index 95491d6b2..b55908aef 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -11,15 +11,19 @@
gb_global Timings global_timings = {0};
#if defined(LLVM_BACKEND_SUPPORT)
+#if defined(GB_SYSTEM_WINDOWS)
#include "llvm-c/Types.h"
+#else
+#include <llvm-c/Types.h>
+#endif
#endif
#include "parser.hpp"
#include "checker.hpp"
#include "parser.cpp"
-#include "docs.cpp"
#include "checker.cpp"
+#include "docs.cpp"
#if defined(LLVM_BACKEND_SUPPORT)
@@ -85,6 +89,10 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
exit_code = -1;
}
+ if (exit_code) {
+ exit(exit_code);
+ }
+
return exit_code;
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
@@ -133,6 +141,10 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
// exit_code = status;
+ if (exit_code) {
+ exit(exit_code);
+ }
+
return exit_code;
#endif
}
@@ -141,29 +153,23 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
#if defined(LLVM_BACKEND_SUPPORT)
-i32 linker_stage(lbGenerator *gen) {
- i32 exit_code = 0;
-
+void linker_stage(lbGenerator *gen) {
Timings *timings = &global_timings;
String output_base = gen->output_base;
if (build_context.metrics.os == TargetOs_js) {
timings_start_section(timings, str_lit("wasm-ld"));
- exit_code = system_exec_command_line_app("wasm-ld",
+ system_exec_command_line_app("wasm-ld",
"\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm-obj\" -o \"%.*s.wasm\" %.*s %.*s",
LIT(build_context.ODIN_ROOT),
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
- if (exit_code != 0) {
- return exit_code;
- }
- return exit_code;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
#ifdef GB_SYSTEM_UNIX
system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
@@ -194,7 +200,7 @@ i32 linker_stage(lbGenerator *gen) {
if (find_result.windows_sdk_version == 0) {
gb_printf_err("Windows SDK not found.\n");
- return 1;
+ exit(1);
}
if (build_context.ignore_microsoft_magic) {
@@ -259,17 +265,13 @@ i32 linker_stage(lbGenerator *gen) {
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
if (build_context.has_resource) {
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
LIT(output_base),
LIT(build_context.resource_filepath)
);
- if (exit_code != 0) {
- return exit_code;
- }
-
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
@@ -284,7 +286,7 @@ i32 linker_stage(lbGenerator *gen) {
lib_str
);
} else {
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
@@ -300,7 +302,7 @@ i32 linker_stage(lbGenerator *gen) {
);
}
} else { // lld
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
@@ -341,11 +343,11 @@ i32 linker_stage(lbGenerator *gen) {
String lib_name = lib;
lib_name = remove_extension_from_path(lib_name);
lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
- } else if (string_ends_with(lib, str_lit(".a"))) {
- // static libs, absolute full path relative to the file in which the lib was imported from
- lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
- } else if (string_ends_with(lib, str_lit(".dylib"))) {
+ } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
+ // For:
+ // object
// dynamic lib
+ // static libs, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
@@ -382,20 +384,33 @@ i32 linker_stage(lbGenerator *gen) {
// Unlike the Win32 linker code, the output_ext includes the dot, because
// typically executable files on *NIX systems don't have extensions.
String output_ext = {};
- char const *link_settings = "";
+ gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
char const *linker;
if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time.
+ // Clang, for some reason, won't let us pass the '-init' flag that lets us do this,
+ // so use ld instead.
+ // :UseLDForShared
+ linker = "ld";
+ link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
// Shared libraries are .dylib on MacOS and .so on Linux.
#if defined(GB_SYSTEM_OSX)
output_ext = STR_LIT(".dylib");
- link_settings = "-dylib -dynamic";
+ link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
#else
output_ext = STR_LIT(".so");
- link_settings = "-shared";
+ link_settings = gb_string_appendc(link_settings, "-shared ");
#endif
} else {
- // TODO: Do I need anything here?
- link_settings = "";
+ #if defined(GB_SYSTEM_OSX)
+ linker = "ld";
+ #else
+ // TODO(zangent): Figure out how to make ld work on Linux.
+ // It probably has to do with including the entire CRT, but
+ // that's quite a complicated issue to solve while remaining distro-agnostic.
+ // Clang can figure out linker flags for us, and that's good enough _for now_.
+ linker = "clang -Wno-unused-command-line-argument";
+ #endif
}
if (build_context.out_filepath.len > 0) {
@@ -406,17 +421,7 @@ i32 linker_stage(lbGenerator *gen) {
}
}
- #if defined(GB_SYSTEM_OSX)
- linker = "ld";
- #else
- // TODO(zangent): Figure out how to make ld work on Linux.
- // It probably has to do with including the entire CRT, but
- // that's quite a complicated issue to solve while remaining distro-agnostic.
- // Clang can figure out linker flags for us, and that's good enough _for now_.
- linker = "clang -Wno-unused-command-line-argument";
- #endif
-
- exit_code = system_exec_command_line_app("ld-link",
+ system_exec_command_line_app("ld-link",
"%s %s -o \"%.*s%.*s\" %s "
" %s "
" %.*s "
@@ -431,37 +436,30 @@ i32 linker_stage(lbGenerator *gen) {
" -e _main "
#endif
, linker, object_files, LIT(output_base), LIT(output_ext),
- lib_str,
- #if defined(GB_SYSTEM_OSX)
- "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
- #else
- "-lc -lm",
- #endif
+ #if defined(GB_SYSTEM_OSX)
+ "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
+ #else
+ "-lc -lm",
+ #endif
+ lib_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
link_settings);
- if (exit_code != 0) {
- return exit_code;
- }
#if defined(GB_SYSTEM_OSX)
if (build_context.ODIN_DEBUG) {
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
// to the symbols in the object file
- exit_code = system_exec_command_line_app("dsymutil",
+ system_exec_command_line_app("dsymutil",
"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
);
-
- if (exit_code != 0) {
- return exit_code;
- }
}
#endif
#endif
}
- return exit_code;
+ return;
}
#endif
@@ -519,7 +517,7 @@ void usage(String argv0) {
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
print_usage_line(1, "check parse and type check .odin file");
print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
- print_usage_line(1, "docs generate documentation for a .odin file");
+ print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
print_usage_line(1, "version print version");
print_usage_line(0, "");
print_usage_line(0, "For more information of flags, apply the flag to see what is possible");
@@ -565,6 +563,8 @@ enum BuildFlagKind {
BuildFlag_OutFile,
BuildFlag_OptimizationLevel,
BuildFlag_ShowTimings,
+ BuildFlag_ShowUnused,
+ BuildFlag_ShowUnusedWithLocation,
BuildFlag_ShowMoreTimings,
BuildFlag_ShowSystemCalls,
BuildFlag_ThreadCount,
@@ -578,6 +578,7 @@ enum BuildFlagKind {
BuildFlag_NoBoundsCheck,
BuildFlag_NoDynamicLiterals,
BuildFlag_NoCRT,
+ BuildFlag_NoEntryPoint,
BuildFlag_UseLLD,
BuildFlag_Vet,
BuildFlag_UseLLVMApi,
@@ -593,6 +594,9 @@ enum BuildFlagKind {
BuildFlag_GlobalDefinitions,
BuildFlag_GoToDefinitions,
+ BuildFlag_Short,
+ BuildFlag_AllPackages,
+
#if defined(GB_SYSTEM_WINDOWS)
BuildFlag_IgnoreVsSearch,
BuildFlag_ResourceFile,
@@ -618,10 +622,13 @@ struct BuildFlag {
BuildFlagKind kind;
String name;
BuildFlagParamKind param_kind;
+ u32 command_support;
+ bool allow_mulitple;
};
-void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
- BuildFlag flag = {kind, name, param_kind};
+
+void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_mulitple=false) {
+ BuildFlag flag = {kind, name, param_kind, command_support, allow_mulitple};
array_add(build_flags, flag);
}
@@ -662,44 +669,50 @@ ExactValue build_param_to_exact_value(String name, String param) {
bool parse_build_flags(Array<String> args) {
auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
- add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
- add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer);
- add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String);
-
- add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None);
-
- add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None);
+ add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
+ add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all);
+ add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
+ add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
+ add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
+ add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
+
+ add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None);
+ add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query);
+
+ add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc);
+
#if defined(GB_SYSTEM_WINDOWS)
- add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None);
- add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String);
+ add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
#endif
GB_ASSERT(args.count >= 3);
@@ -729,11 +742,19 @@ bool parse_build_flags(Array<String> args) {
String param = {};
if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
+ bool is_supported = true;
bool found = false;
+ BuildFlag found_bf = {};
for_array(build_flag_index, build_flags) {
BuildFlag bf = build_flags[build_flag_index];
if (bf.name == name) {
found = true;
+ found_bf = bf;
+ if ((bf.command_support & build_context.command_kind) == 0) {
+ is_supported = false;
+ break;
+ }
+
if (set_flags[bf.kind]) {
gb_printf_err("Previous flag set: '%.*s'\n", LIT(name));
bad_flags = true;
@@ -835,7 +856,7 @@ bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_String);
String path = value.value_string;
path = string_trim_whitespace(path);
- if (is_import_path_valid(path)) {
+ if (is_build_flag_path_valid(path)) {
#if defined(GB_SYSTEM_WINDOWS)
String ext = path_extension(path);
if (ext == ".exe") {
@@ -857,6 +878,15 @@ bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_Invalid);
build_context.show_timings = true;
break;
+ case BuildFlag_ShowUnused:
+ GB_ASSERT(value.kind == ExactValue_Invalid);
+ build_context.show_unused = true;
+ break;
+ case BuildFlag_ShowUnusedWithLocation:
+ GB_ASSERT(value.kind == ExactValue_Invalid);
+ build_context.show_unused = true;
+ build_context.show_unused_with_location = true;
+ break;
case BuildFlag_ShowMoreTimings:
GB_ASSERT(value.kind == ExactValue_Invalid);
build_context.show_timings = true;
@@ -1094,6 +1124,10 @@ bool parse_build_flags(Array<String> args) {
build_context.no_crt = true;
break;
+ case BuildFlag_NoEntryPoint:
+ build_context.no_entry_point = true;
+ break;
+
case BuildFlag_UseLLD:
build_context.use_lld = true;
break;
@@ -1165,6 +1199,15 @@ bool parse_build_flags(Array<String> args) {
}
break;
+ case BuildFlag_Short:
+ build_context.cmd_doc_flags |= CmdDocFlag_Short;
+ break;
+ case BuildFlag_AllPackages:
+ build_context.cmd_doc_flags |= CmdDocFlag_AllPackages;
+ break;
+
+
+
#if defined(GB_SYSTEM_WINDOWS)
case BuildFlag_IgnoreVsSearch:
GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -1175,7 +1218,7 @@ bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_String);
String path = value.value_string;
path = string_trim_whitespace(path);
- if (is_import_path_valid(path)) {
+ if (is_build_flag_path_valid(path)) {
if(!string_ends_with(path, str_lit(".rc"))) {
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
bad_flags = true;
@@ -1193,7 +1236,7 @@ bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_String);
String path = value.value_string;
path = string_trim_whitespace(path);
- if (is_import_path_valid(path)) {
+ if (is_build_flag_path_valid(path)) {
// #if defined(GB_SYSTEM_WINDOWS)
// String ext = path_extension(path);
// if (ext != ".pdb") {
@@ -1228,26 +1271,35 @@ bool parse_build_flags(Array<String> args) {
}
}
-
- switch (bf.kind) {
- case BuildFlag_Define:
- // Allow for multiple
- break;
- default:
+ if (!bf.allow_mulitple) {
set_flags[bf.kind] = ok;
- break;
}
}
break;
}
}
- if (!found) {
+ if (found && !is_supported) {
+ gb_printf_err("Unknown flag for 'odin %.*s': '%.*s'\n", LIT(build_context.command), LIT(name));
+ gb_printf_err("'%.*s' is supported with the following commands:\n", LIT(name));
+ gb_printf_err("\t");
+ i32 count = 0;
+ for (u32 i = 0; i < 32; i++) {
+ if (found_bf.command_support & (1<<i)) {
+ if (count > 0) {
+ gb_printf_err(", ");
+ }
+ gb_printf_err("%s", odin_command_strings[i]);
+ count += 1;
+ }
+ }
+ gb_printf_err("\n");
+ bad_flags = true;
+ } else if (!found) {
gb_printf_err("Unknown flag: '%.*s'\n", LIT(name));
bad_flags = true;
}
}
-
if (build_context.query_data_set_settings.ok) {
if (build_context.query_data_set_settings.kind == QueryDataSet_Invalid) {
gb_printf_err("'odin query' requires a flag determining the kind of query data set to be returned\n");
@@ -1474,22 +1526,39 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
} else if (command == "check") {
print_usage_line(1, "check parse and type check .odin file");
+ } else if (command == "test") {
+ print_usage_line(1, "test build ands runs 'test_*' procedures in the initial package");
} else if (command == "query") {
print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program");
- } else if (command == "docs") {
- print_usage_line(1, "docs generate documentation for a .odin file");
+ } else if (command == "doc") {
+ print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin doc core/path");
+ print_usage_line(3, "odin doc core/path core/path/filepath");
} else if (command == "version") {
print_usage_line(1, "version print version");
}
+ bool doc = command == "doc";
bool build = command == "build";
- bool run_or_build = command == "run" || command == "build";
- bool check = command == "run" || command == "build" || command == "check";
+ bool run_or_build = command == "run" || command == "build" || command == "test";
+ bool check_only = command == "check";
+ bool check = run_or_build || command == "check";
print_usage_line(0, "");
print_usage_line(1, "Flags");
print_usage_line(0, "");
+ if (doc) {
+ print_usage_line(1, "-short");
+ print_usage_line(2, "Show shortened documentation for the packages");
+ print_usage_line(0, "");
+
+ print_usage_line(1, "-all-packages");
+ print_usage_line(2, "Generates documentation for all packages used in the current project");
+ print_usage_line(0, "");
+ }
+
if (run_or_build) {
print_usage_line(1, "-out:<filepath>");
print_usage_line(2, "Set the file name of the outputted executable");
@@ -1518,6 +1587,15 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(0, "");
}
+ if (check_only) {
+ print_usage_line(1, "-show-unused");
+ print_usage_line(2, "Shows unused package declarations within the current project");
+ print_usage_line(0, "");
+ print_usage_line(1, "-show-unused-with-location");
+ print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location");
+ print_usage_line(0, "");
+ }
+
if (run_or_build) {
print_usage_line(1, "-keep-temp-files");
print_usage_line(2, "Keeps the temporary files generated during compilation");
@@ -1596,6 +1674,23 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-extra-linker-flags:<string>");
print_usage_line(2, "Adds extra linker specific flags in a string");
print_usage_line(0, "");
+
+ print_usage_line(1, "-microarch:<string>");
+ print_usage_line(2, "Specifies the specific micro-architecture for the build in a string");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "-microarch:sandybridge");
+ print_usage_line(3, "-microarch:native");
+ print_usage_line(0, "");
+ }
+
+ if (check) {
+ print_usage_line(1, "-disallow-do");
+ print_usage_line(2, "Disallows the 'do' keyword in the project");
+ print_usage_line(0, "");
+
+ print_usage_line(1, "-default-to-nil-allocator");
+ print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing");
+ print_usage_line(0, "");
}
if (run_or_build) {
@@ -1629,6 +1724,80 @@ void print_show_help(String const arg0, String const &command) {
}
}
+void print_show_unused(Checker *c) {
+ CheckerInfo *info = &c->info;
+
+ auto unused = array_make<Entity *>(permanent_allocator(), 0, info->entities.count);
+ for_array(i, info->entities) {
+ Entity *e = info->entities[i];
+ if (e == nullptr) {
+ continue;
+ }
+ if (e->pkg == nullptr || e->pkg->scope == nullptr) {
+ continue;
+ }
+ if (e->pkg->scope->flags & ScopeFlag_Builtin) {
+ continue;
+ }
+ switch (e->kind) {
+ case Entity_Invalid:
+ case Entity_Builtin:
+ case Entity_Nil:
+ case Entity_Label:
+ continue;
+ case Entity_Constant:
+ case Entity_Variable:
+ case Entity_TypeName:
+ case Entity_Procedure:
+ case Entity_ProcGroup:
+ case Entity_ImportName:
+ case Entity_LibraryName:
+ // Fine
+ break;
+ }
+ if ((e->scope->flags & (ScopeFlag_Pkg|ScopeFlag_File)) == 0) {
+ continue;
+ }
+ if (e->token.string.len == 0) {
+ continue;
+ }
+ if (e->token.string == "_") {
+ continue;
+ }
+ if (ptr_set_exists(&info->minimum_dependency_set, e)) {
+ continue;
+ }
+ array_add(&unused, e);
+ }
+
+ gb_sort_array(unused.data, unused.count, cmp_entities_for_printing);
+
+ print_usage_line(0, "Unused Package Declarations");
+
+ AstPackage *curr_pkg = nullptr;
+ EntityKind curr_entity_kind = Entity_Invalid;
+ for_array(i, unused) {
+ Entity *e = unused[i];
+ if (curr_pkg != e->pkg) {
+ curr_pkg = e->pkg;
+ curr_entity_kind = Entity_Invalid;
+ print_usage_line(0, "");
+ print_usage_line(0, "package %.*s", LIT(curr_pkg->name));
+ }
+ if (curr_entity_kind != e->kind) {
+ curr_entity_kind = e->kind;
+ print_usage_line(1, "%s", print_entity_names[e->kind]);
+ }
+ if (build_context.show_unused_with_location) {
+ TokenPos pos = e->token.pos;
+ print_usage_line(2, "%.*s(%td:%td) %.*s", LIT(pos.file), pos.line, pos.column, LIT(e->token.string));
+ } else {
+ print_usage_line(2, "%.*s", LIT(e->token.string));
+ }
+ }
+ print_usage_line(0, "");
+}
+
int main(int arg_count, char const **arg_ptr) {
if (arg_count < 2) {
usage(make_string_c(arg_ptr[0]));
@@ -1640,18 +1809,24 @@ int main(int arg_count, char const **arg_ptr) {
timings_init(timings, str_lit("Total Time"), 128);
defer (timings_destroy(timings));
+ arena_init(&permanent_arena, heap_allocator());
+ temp_allocator_init(&temporary_allocator_data, 16*1024*1024);
+ arena_init(&global_ast_arena, heap_allocator());
+ permanent_arena.use_mutex = true;
+
init_string_buffer_memory();
init_string_interner();
init_global_error_collector();
init_keyword_hash_table();
global_big_int_init();
- arena_init(&global_ast_arena, heap_allocator());
array_init(&library_collections, heap_allocator());
// NOTE(bill): 'core' cannot be (re)defined by the user
add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
map_init(&build_context.defined_values, heap_allocator());
+ build_context.extra_packages.allocator = heap_allocator();
+
Array<String> args = setup_args(arg_count, arg_ptr);
@@ -1660,11 +1835,15 @@ int main(int arg_count, char const **arg_ptr) {
String run_args_string = {};
bool run_output = false;
- if (command == "run") {
+ if (command == "run" || command == "test") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
+ build_context.command_kind = Command_run;
+ if (command == "test") {
+ build_context.command_kind = Command_test;
+ }
Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
defer (array_free(&run_args));
@@ -1684,17 +1863,20 @@ int main(int arg_count, char const **arg_ptr) {
run_args_string = string_join_and_quote(heap_allocator(), run_args);
init_filename = args[2];
run_output = true;
+
} else if (command == "build") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
+ build_context.command_kind = Command_build;
init_filename = args[2];
} else if (command == "check") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
+ build_context.command_kind = Command_check;
build_context.no_output_files = true;
init_filename = args[2];
} else if (command == "query") {
@@ -1702,22 +1884,41 @@ int main(int arg_count, char const **arg_ptr) {
usage(args[0]);
return 1;
}
+ build_context.command_kind = Command_query;
build_context.no_output_files = true;
build_context.query_data_set_settings.ok = true;
init_filename = args[2];
- } else if (command == "docs") {
+ } else if (command == "doc") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
+ build_context.command_kind = Command_doc;
init_filename = args[2];
+ for (isize i = 3; i < args.count; i++) {
+ auto arg = args[i];
+ if (string_starts_with(arg, str_lit("-"))) {
+ break;
+ }
+ array_add(&build_context.extra_packages, arg);
+ }
+ isize extra_count = build_context.extra_packages.count;
+ if (extra_count > 0) {
+ gb_memmove(args.data + 3, args.data + 3 + extra_count, extra_count * gb_size_of(*args.data));
+ args.count -= extra_count;
+ }
+
+
+ build_context.no_output_files = true;
build_context.generate_docs = true;
- #if 1
+ build_context.no_entry_point = true; // ignore entry point
+ #if 0
print_usage_line(0, "Documentation generation is not yet supported");
return 1;
#endif
} else if (command == "version") {
+ build_context.command_kind = Command_version;
gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION));
#ifdef NIGHTLY
@@ -1792,10 +1993,8 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
}
- if (build_context.generate_docs) {
- // generate_documentation(&parser);
- return 0;
- }
+ temp_allocator_free_all(&temporary_allocator_data);
+
timings_start_section(timings, str_lit("type check"));
Checker checker = {0};
@@ -1809,8 +2008,21 @@ int main(int arg_count, char const **arg_ptr) {
check_parsed_files(&checker);
}
+ temp_allocator_free_all(&temporary_allocator_data);
+
+ if (build_context.generate_docs) {
+ if (global_error_collector.count != 0) {
+ return 1;
+ }
+ generate_documentation(&checker);
+ return 0;
+ }
if (build_context.no_output_files) {
+ if (build_context.show_unused) {
+ print_show_unused(&checker);
+ }
+
if (build_context.query_data_set_settings.ok) {
generate_and_print_query_data(&checker, timings);
} else {
@@ -1839,15 +2051,12 @@ int main(int arg_count, char const **arg_ptr) {
}
lb_generate_code(&gen);
+ temp_allocator_free_all(&temporary_allocator_data);
+
switch (build_context.build_mode) {
case BuildMode_Executable:
case BuildMode_DynamicLibrary:
- {
- i32 linker_stage_exit_count = linker_stage(&gen);
- if (linker_stage_exit_count != 0) {
- return linker_stage_exit_count;
- }
- }
+ linker_stage(&gen);
break;
}
@@ -1864,9 +2073,6 @@ int main(int arg_count, char const **arg_ptr) {
SIZE_T virtual_mem_used_by_me = pmc.PrivateUsage;
gb_printf_err("virtual_memory_used: %tu B\n", virtual_mem_used_by_me);
- gb_printf_err("total_allocated_node_memory: %lld B\n", total_allocated_node_memory.value);
- gb_printf_err("total_subtype_node_memory_test: %lld B\n", total_subtype_node_memory_test.value);
- gb_printf_err("fraction: %.6f\n", (f64)total_subtype_node_memory_test.value/(f64)total_allocated_node_memory.value);
Parser *p = checker.parser;
isize lines = p->total_line_count;
isize tokens = p->total_token_count;
@@ -1916,31 +2122,29 @@ int main(int arg_count, char const **arg_ptr) {
timings_start_section(timings, str_lit("llvm ir gen"));
ir_gen_tree(&ir_gen);
+ temp_allocator_free_all(&temporary_allocator_data);
+
timings_start_section(timings, str_lit("llvm ir opt tree"));
ir_opt_tree(&ir_gen);
+ temp_allocator_free_all(&temporary_allocator_data);
+
timings_start_section(timings, str_lit("llvm ir print"));
print_llvm_ir(&ir_gen);
+ temp_allocator_free_all(&temporary_allocator_data);
+
String output_name = ir_gen.output_name;
String output_base = ir_gen.output_base;
build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
- i32 exit_code = 0;
-
timings_start_section(timings, str_lit("llvm-opt"));
- exit_code = exec_llvm_opt(output_base);
- if (exit_code != 0) {
- return exit_code;
- }
+ exec_llvm_opt(output_base);
timings_start_section(timings, str_lit("llvm-llc"));
- exit_code = exec_llvm_llc(output_base);
- if (exit_code != 0) {
- return exit_code;
- }
+ exec_llvm_llc(output_base);
if (build_context.build_mode == BuildMode_Object) {
// Ignore the linker
@@ -1949,7 +2153,7 @@ int main(int arg_count, char const **arg_ptr) {
}
remove_temp_files(output_base);
- return exit_code;
+ return 0;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
@@ -2046,17 +2250,13 @@ int main(int arg_count, char const **arg_ptr) {
if (!build_context.use_lld) { // msvc
if (build_context.has_resource) {
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
LIT(output_base),
LIT(build_context.resource_filepath)
);
- if (exit_code != 0) {
- return exit_code;
- }
-
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
@@ -2071,7 +2271,7 @@ int main(int arg_count, char const **arg_ptr) {
lib_str
);
} else {
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
@@ -2087,7 +2287,7 @@ int main(int arg_count, char const **arg_ptr) {
);
}
} else { // lld
- exit_code = system_exec_command_line_app("msvc-link",
+ system_exec_command_line_app("msvc-link",
"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
@@ -2104,10 +2304,6 @@ int main(int arg_count, char const **arg_ptr) {
);
}
- if (exit_code != 0) {
- return exit_code;
- }
-
if (build_context.show_timings) {
show_timings(&checker, timings);
}
@@ -2142,13 +2338,14 @@ int main(int arg_count, char const **arg_ptr) {
String lib_name = lib;
lib_name = remove_extension_from_path(lib_name);
lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
- } else if (string_ends_with(lib, str_lit(".a"))) {
- // static libs, absolute full path relative to the file in which the lib was imported from
- lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
- } else if (string_ends_with(lib, str_lit(".dylib"))) {
- // dynamic lib
- lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
- } else {
+
+ } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
+ // For:
+ // object
+ // dynamic lib
+ // static libs, absolute full path relative to the file in which the lib was imported from
+ lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+ } else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
@@ -2176,22 +2373,36 @@ int main(int arg_count, char const **arg_ptr) {
// Unlike the Win32 linker code, the output_ext includes the dot, because
// typically executable files on *NIX systems don't have extensions.
String output_ext = {};
- char const *link_settings = "";
+ gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
char const *linker;
if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time.
+ // Clang, for some reason, won't let us pass the '-init' flag that lets us do this,
+ // so use ld instead.
+ // :UseLDForShared
+ linker = "ld";
+ link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
// Shared libraries are .dylib on MacOS and .so on Linux.
#if defined(GB_SYSTEM_OSX)
output_ext = STR_LIT(".dylib");
- link_settings = "-dylib -dynamic";
+ link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
#else
output_ext = STR_LIT(".so");
- link_settings = "-shared";
+ link_settings = gb_string_appendc(link_settings, "-shared ");
#endif
} else {
- // TODO: Do I need anything here?
- link_settings = "";
+ #if defined(GB_SYSTEM_OSX)
+ linker = "ld";
+ #else
+ // TODO(zangent): Figure out how to make ld work on Linux.
+ // It probably has to do with including the entire CRT, but
+ // that's quite a complicated issue to solve while remaining distro-agnostic.
+ // Clang can figure out linker flags for us, and that's good enough _for now_.
+ linker = "clang -Wno-unused-command-line-argument";
+ #endif
}
+
if (build_context.out_filepath.len > 0) {
//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
isize pos = string_extension_position(build_context.out_filepath);
@@ -2200,17 +2411,8 @@ int main(int arg_count, char const **arg_ptr) {
}
}
- #if defined(GB_SYSTEM_OSX)
- linker = "ld";
- #else
- // TODO(zangent): Figure out how to make ld work on Linux.
- // It probably has to do with including the entire CRT, but
- // that's quite a complicated issue to solve while remaining distro-agnostic.
- // Clang can figure out linker flags for us, and that's good enough _for now_.
- linker = "clang -Wno-unused-command-line-argument";
- #endif
- exit_code = system_exec_command_line_app("ld-link",
+ system_exec_command_line_app("ld-link",
"%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
" %s "
" %.*s "
@@ -2226,29 +2428,22 @@ int main(int arg_count, char const **arg_ptr) {
#endif
, linker, LIT(output_base), LIT(output_base), LIT(output_ext),
lib_str,
- #if defined(GB_SYSTEM_OSX)
- "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
- #else
- "-lc -lm",
- #endif
+ #if defined(GB_SYSTEM_OSX)
+ "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
+ #else
+ "-lc -lm",
+ #endif
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
link_settings);
- if (exit_code != 0) {
- return exit_code;
- }
#if defined(GB_SYSTEM_OSX)
if (build_context.ODIN_DEBUG) {
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
// to the symbols in the object file
- exit_code = system_exec_command_line_app("dsymutil",
+ system_exec_command_line_app("dsymutil",
"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
);
-
- if (exit_code != 0) {
- return exit_code;
- }
}
#endif
diff --git a/src/parser.cpp b/src/parser.cpp
index b89d9f9f5..46af47c2d 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -108,8 +108,25 @@ Token ast_token(Ast *node) {
return empty_token;
}
+
+isize ast_node_size(AstKind kind) {
+ return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
+
+}
+// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
+Ast *alloc_ast_node(AstFile *f, AstKind kind) {
+ gbAllocator a = ast_allocator(f);
+
+ isize size = ast_node_size(kind);
+
+ Ast *node = cast(Ast *)gb_alloc(a, size);
+ node->kind = kind;
+ node->file = f;
+ return node;
+}
+
Ast *clone_ast(Ast *node);
-Array<Ast *> clone_ast_array(Array<Ast *> array) {
+Array<Ast *> clone_ast_array(Array<Ast *> const &array) {
Array<Ast *> result = {};
if (array.count > 0) {
result = array_make<Ast *>(ast_allocator(nullptr), array.count);
@@ -119,13 +136,23 @@ Array<Ast *> clone_ast_array(Array<Ast *> array) {
}
return result;
}
+Slice<Ast *> clone_ast_array(Slice<Ast *> const &array) {
+ Slice<Ast *> result = {};
+ if (array.count > 0) {
+ result = slice_clone(permanent_allocator(), array);
+ for_array(i, array) {
+ result[i] = clone_ast(array[i]);
+ }
+ }
+ return result;
+}
Ast *clone_ast(Ast *node) {
if (node == nullptr) {
return nullptr;
}
Ast *n = alloc_ast_node(node->file, node->kind);
- gb_memmove(n, node, gb_size_of(Ast));
+ gb_memmove(n, node, ast_node_size(node->kind));
switch (n->kind) {
default: GB_PANIC("Unhandled Ast %.*s", LIT(ast_strings[n->kind])); break;
@@ -463,23 +490,6 @@ bool ast_node_expect(Ast *node, AstKind kind) {
return true;
}
-
-gb_global gbAtomic64 total_allocated_node_memory = {0};
-gb_global gbAtomic64 total_subtype_node_memory_test = {0};
-
-// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
-Ast *alloc_ast_node(AstFile *f, AstKind kind) {
- gbAllocator a = ast_allocator(f);
-
- gb_atomic64_fetch_add(&total_allocated_node_memory, cast(i64)(gb_size_of(Ast)));
- gb_atomic64_fetch_add(&total_subtype_node_memory_test, cast(i64)(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind]));
-
- Ast *node = gb_alloc_item(a, Ast);
- node->kind = kind;
- node->file = f;
- return node;
-}
-
Ast *ast_bad_expr(AstFile *f, Token begin, Token end) {
Ast *result = alloc_ast_node(f, Ast_BadExpr);
result->BadExpr.begin = begin;
@@ -537,10 +547,10 @@ Ast *ast_paren_expr(AstFile *f, Ast *expr, Token open, Token close) {
return result;
}
-Ast *ast_call_expr(AstFile *f, Ast *proc, Array<Ast *> args, Token open, Token close, Token ellipsis) {
+Ast *ast_call_expr(AstFile *f, Ast *proc, Array<Ast *> const &args, Token open, Token close, Token ellipsis) {
Ast *result = alloc_ast_node(f, Ast_CallExpr);
result->CallExpr.proc = proc;
- result->CallExpr.args = args;
+ result->CallExpr.args = slice_from_array(args);
result->CallExpr.open = open;
result->CallExpr.close = close;
result->CallExpr.ellipsis = ellipsis;
@@ -624,7 +634,8 @@ Ast *ast_undef(AstFile *f, Token token) {
Ast *ast_basic_lit(AstFile *f, Token basic_lit) {
Ast *result = alloc_ast_node(f, Ast_BasicLit);
result->BasicLit.token = basic_lit;
- result->BasicLit.value = exact_value_from_basic_literal(basic_lit);
+ result->tav.mode = Addressing_Constant;
+ result->tav.value = exact_value_from_basic_literal(basic_lit);
return result;
}
@@ -643,12 +654,12 @@ Ast *ast_ellipsis(AstFile *f, Token token, Ast *expr) {
}
-Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast *> args) {
+Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast *> const &args) {
Ast *result = alloc_ast_node(f, Ast_ProcGroup);
result->ProcGroup.token = token;
result->ProcGroup.open = open;
result->ProcGroup.close = close;
- result->ProcGroup.args = args;
+ result->ProcGroup.args = slice_from_array(args);
return result;
}
@@ -658,7 +669,7 @@ Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token,
result->ProcLit.body = body;
result->ProcLit.tags = tags;
result->ProcLit.where_token = where_token;
- result->ProcLit.where_clauses = where_clauses;
+ result->ProcLit.where_clauses = slice_from_array(where_clauses);
return result;
}
@@ -670,10 +681,10 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) {
return result;
}
-Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> elems, Token open, Token close) {
+Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> const &elems, Token open, Token close) {
Ast *result = alloc_ast_node(f, Ast_CompoundLit);
result->CompoundLit.type = type;
- result->CompoundLit.elems = elems;
+ result->CompoundLit.elems = slice_from_array(elems);
result->CompoundLit.open = open;
result->CompoundLit.close = close;
return result;
@@ -736,7 +747,7 @@ Ast *ast_inline_asm_expr(AstFile *f, Token token, Token open, Token close,
result->InlineAsmExpr.token = token;
result->InlineAsmExpr.open = open;
result->InlineAsmExpr.close = close;
- result->InlineAsmExpr.param_types = param_types;
+ result->InlineAsmExpr.param_types = slice_from_array(param_types);
result->InlineAsmExpr.return_type = return_type;
result->InlineAsmExpr.asm_string = asm_string;
result->InlineAsmExpr.constraints_string = constraints_string;
@@ -768,18 +779,18 @@ Ast *ast_expr_stmt(AstFile *f, Ast *expr) {
return result;
}
-Ast *ast_assign_stmt(AstFile *f, Token op, Array<Ast *> lhs, Array<Ast *> rhs) {
+Ast *ast_assign_stmt(AstFile *f, Token op, Array<Ast *> const &lhs, Array<Ast *> const &rhs) {
Ast *result = alloc_ast_node(f, Ast_AssignStmt);
result->AssignStmt.op = op;
- result->AssignStmt.lhs = lhs;
- result->AssignStmt.rhs = rhs;
+ result->AssignStmt.lhs = slice_from_array(lhs);
+ result->AssignStmt.rhs = slice_from_array(rhs);
return result;
}
-Ast *ast_block_stmt(AstFile *f, Array<Ast *> stmts, Token open, Token close) {
+Ast *ast_block_stmt(AstFile *f, Array<Ast *> const &stmts, Token open, Token close) {
Ast *result = alloc_ast_node(f, Ast_BlockStmt);
- result->BlockStmt.stmts = stmts;
+ result->BlockStmt.stmts = slice_from_array(stmts);
result->BlockStmt.open = open;
result->BlockStmt.close = close;
return result;
@@ -805,10 +816,10 @@ Ast *ast_when_stmt(AstFile *f, Token token, Ast *cond, Ast *body, Ast *else_stmt
}
-Ast *ast_return_stmt(AstFile *f, Token token, Array<Ast *> results) {
+Ast *ast_return_stmt(AstFile *f, Token token, Array<Ast *> const &results) {
Ast *result = alloc_ast_node(f, Ast_ReturnStmt);
result->ReturnStmt.token = token;
- result->ReturnStmt.results = results;
+ result->ReturnStmt.results = slice_from_array(results);
return result;
}
@@ -866,11 +877,11 @@ Ast *ast_type_switch_stmt(AstFile *f, Token token, Ast *tag, Ast *body) {
return result;
}
-Ast *ast_case_clause(AstFile *f, Token token, Array<Ast *> list, Array<Ast *> stmts) {
+Ast *ast_case_clause(AstFile *f, Token token, Array<Ast *> const &list, Array<Ast *> const &stmts) {
Ast *result = alloc_ast_node(f, Ast_CaseClause);
result->CaseClause.token = token;
- result->CaseClause.list = list;
- result->CaseClause.stmts = stmts;
+ result->CaseClause.list = slice_from_array(list);
+ result->CaseClause.stmts = slice_from_array(stmts);
return result;
}
@@ -889,10 +900,10 @@ Ast *ast_branch_stmt(AstFile *f, Token token, Ast *label) {
return result;
}
-Ast *ast_using_stmt(AstFile *f, Token token, Array<Ast *> list) {
+Ast *ast_using_stmt(AstFile *f, Token token, Array<Ast *> const &list) {
Ast *result = alloc_ast_node(f, Ast_UsingStmt);
result->UsingStmt.token = token;
- result->UsingStmt.list = list;
+ result->UsingStmt.list = slice_from_array(list);
return result;
}
@@ -905,10 +916,10 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) {
return result;
}
-Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, Token tag,
+Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast *default_value, u32 flags, Token tag,
CommentGroup *docs, CommentGroup *comment) {
Ast *result = alloc_ast_node(f, Ast_Field);
- result->Field.names = names;
+ result->Field.names = slice_from_array(names);
result->Field.type = type;
result->Field.default_value = default_value;
result->Field.flags = flags;
@@ -918,10 +929,10 @@ Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u3
return result;
}
-Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> list) {
+Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> const &list) {
Ast *result = alloc_ast_node(f, Ast_FieldList);
result->FieldList.token = token;
- result->FieldList.list = list;
+ result->FieldList.list = slice_from_array(list);
return result;
}
@@ -1002,7 +1013,7 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
return result;
}
-Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_count,
+Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
Ast *polymorphic_params, bool is_packed, bool is_raw_union,
Ast *align,
Token where_token, Array<Ast *> const &where_clauses) {
@@ -1015,38 +1026,38 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c
result->StructType.is_raw_union = is_raw_union;
result->StructType.align = align;
result->StructType.where_token = where_token;
- result->StructType.where_clauses = where_clauses;
+ result->StructType.where_clauses = slice_from_array(where_clauses);
return result;
}
-Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
+Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_UnionType);
result->UnionType.token = token;
- result->UnionType.variants = variants;
+ result->UnionType.variants = slice_from_array(variants);
result->UnionType.polymorphic_params = polymorphic_params;
result->UnionType.align = align;
result->UnionType.no_nil = no_nil;
- result->UnionType.maybe = maybe;
+ result->UnionType.maybe = maybe;
result->UnionType.where_token = where_token;
- result->UnionType.where_clauses = where_clauses;
+ result->UnionType.where_clauses = slice_from_array(where_clauses);
return result;
}
-Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array<Ast *> fields) {
+Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array<Ast *> const &fields) {
Ast *result = alloc_ast_node(f, Ast_EnumType);
result->EnumType.token = token;
result->EnumType.base_type = base_type;
- result->EnumType.fields = fields;
+ result->EnumType.fields = slice_from_array(fields);
return result;
}
-Ast *ast_bit_field_type(AstFile *f, Token token, Array<Ast *> fields, Ast *align) {
+Ast *ast_bit_field_type(AstFile *f, Token token, Array<Ast *> const &fields, Ast *align) {
Ast *result = alloc_ast_node(f, Ast_BitFieldType);
result->BitFieldType.token = token;
- result->BitFieldType.fields = fields;
+ result->BitFieldType.fields = slice_from_array(fields);
result->BitFieldType.align = align;
return result;
}
@@ -1069,7 +1080,7 @@ Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) {
Ast *ast_foreign_block_decl(AstFile *f, Token token, Ast *foreign_library, Ast *body,
- CommentGroup *docs) {
+ CommentGroup *docs) {
Ast *result = alloc_ast_node(f, Ast_ForeignBlockDecl);
result->ForeignBlockDecl.token = token;
result->ForeignBlockDecl.foreign_library = foreign_library;
@@ -1087,12 +1098,12 @@ Ast *ast_label_decl(AstFile *f, Token token, Ast *name) {
return result;
}
-Ast *ast_value_decl(AstFile *f, Array<Ast *> names, Ast *type, Array<Ast *> values, bool is_mutable,
- CommentGroup *docs, CommentGroup *comment) {
+Ast *ast_value_decl(AstFile *f, Array<Ast *> const &names, Ast *type, Array<Ast *> const &values, bool is_mutable,
+ CommentGroup *docs, CommentGroup *comment) {
Ast *result = alloc_ast_node(f, Ast_ValueDecl);
- result->ValueDecl.names = names;
+ result->ValueDecl.names = slice_from_array(names);
result->ValueDecl.type = type;
- result->ValueDecl.values = values;
+ result->ValueDecl.values = slice_from_array(values);
result->ValueDecl.is_mutable = is_mutable;
result->ValueDecl.docs = docs;
result->ValueDecl.comment = comment;
@@ -1111,7 +1122,7 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C
}
Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name,
- CommentGroup *docs, CommentGroup *comment) {
+ CommentGroup *docs, CommentGroup *comment) {
Ast *result = alloc_ast_node(f, Ast_ImportDecl);
result->ImportDecl.token = token;
result->ImportDecl.is_using = is_using;
@@ -1123,10 +1134,10 @@ Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Toke
}
Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, Token library_name,
- CommentGroup *docs, CommentGroup *comment) {
+ CommentGroup *docs, CommentGroup *comment) {
Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl);
result->ForeignImportDecl.token = token;
- result->ForeignImportDecl.filepaths = filepaths;
+ result->ForeignImportDecl.filepaths = slice_from_array(filepaths);
result->ForeignImportDecl.library_name = library_name;
result->ForeignImportDecl.docs = docs;
result->ForeignImportDecl.comment = comment;
@@ -1136,11 +1147,11 @@ Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, To
}
-Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array<Ast *> elems) {
+Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array<Ast *> const &elems) {
Ast *result = alloc_ast_node(f, Ast_Attribute);
result->Attribute.token = token;
result->Attribute.open = open;
- result->Attribute.elems = elems;
+ result->Attribute.elems = slice_from_array(elems);
result->Attribute.close = close;
return result;
}
@@ -1182,6 +1193,12 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
Array<Token> list = {};
list.allocator = heap_allocator();
isize end_line = f->curr_token.pos.line;
+ if (f->curr_token_index == 1 &&
+ f->prev_token.kind == Token_Comment &&
+ f->prev_token.pos.line+1 == f->curr_token.pos.line) {
+ // NOTE(bill): Special logic for the first comment in the file
+ array_add(&list, f->prev_token);
+ }
while (f->curr_token.kind == Token_Comment &&
f->curr_token.pos.line <= end_line+n) {
array_add(&list, consume_comment(f, &end_line));
@@ -1192,7 +1209,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
CommentGroup *comments = nullptr;
if (list.count > 0) {
comments = gb_alloc_item(heap_allocator(), CommentGroup);
- comments->list = list;
+ comments->list = slice_from_array(list);
array_add(&f->comments, comments);
}
return comments;
@@ -1894,12 +1911,18 @@ Ast *parse_operand(AstFile *f, bool lhs) {
case Token_opaque: {
Token token = expect_token(f, Token_opaque);
+ warning(token, "opaque is deprecated, please use #opaque");
Ast *type = parse_type(f);
return ast_opaque_type(f, token, type);
}
case Token_Hash: {
Token token = expect_token(f, Token_Hash);
+ if (allow_token(f, Token_opaque)) {
+ Ast *type = parse_type(f);
+ return ast_opaque_type(f, token, type);
+ }
+
Token name = expect_token(f, Token_Ident);
if (name.string == "type") {
return ast_helper_type(f, token, parse_type(f));
@@ -2201,7 +2224,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
if (allow_token(f, Token_OpenParen)) {
isize param_count = 0;
- polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, false, true);
+ polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, true, true);
if (param_count == 0) {
syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter");
polymorphic_params = nullptr;
@@ -2262,7 +2285,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Ast *fields = parse_struct_field_list(f, &name_count);
Token close = expect_token(f, Token_CloseBrace);
- Array<Ast *> decls = {};
+ Slice<Ast *> decls = {};
if (fields != nullptr) {
GB_ASSERT(fields->kind == Ast_FieldList);
decls = fields->FieldList.list;
@@ -2284,7 +2307,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
if (allow_token(f, Token_OpenParen)) {
isize param_count = 0;
- polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, false, true);
+ polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, true, true);
if (param_count == 0) {
syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric");
polymorphic_params = nullptr;
@@ -2586,7 +2609,15 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) {
f->expr_level--;
close_paren = expect_closing(f, Token_CloseParen, str_lit("argument list"));
- return ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
+
+ Ast *call = ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
+
+ Ast *o = unparen_expr(operand);
+ if (o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) {
+ return ast_selector_call_expr(f, o->SelectorExpr.token, o, call);
+ }
+
+ return call;
}
Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
@@ -2638,11 +2669,10 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
case Token_ArrowRight: {
Token token = advance_token(f);
- // syntax_error(token, "Selector expressions use '.' rather than '->'");
- Ast *sel = ast_selector_expr(f, token, operand, parse_ident(f));
- Ast *call = parse_call_expr(f, sel);
- operand = ast_selector_call_expr(f, token, sel, call);
+ operand = ast_selector_expr(f, token, operand, parse_ident(f));
+ // Ast *call = parse_call_expr(f, sel);
+ // operand = ast_selector_call_expr(f, token, sel, call);
break;
}
@@ -3317,11 +3347,10 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
return FieldPrefix_no_alias;
} else if (f->curr_token.string == "c_vararg") {
return FieldPrefix_c_var_arg;
+ } else if (f->curr_token.string == "const") {
+ return FieldPrefix_const;
}
break;
-
- case Token_const:
- return FieldPrefix_const;
}
return FieldPrefix_Unknown;
}
@@ -4723,18 +4752,14 @@ void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, AstForeignFi
// NOTE(bill): Returns true if it's added
-bool try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) {
- if (build_context.generate_docs) {
- return false;
- }
-
+AstPackage *try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) {
String const FILE_EXT = str_lit(".odin");
gb_mutex_lock(&p->file_add_mutex);
defer (gb_mutex_unlock(&p->file_add_mutex));
if (string_set_exists(&p->imported_files, path)) {
- return false;
+ return nullptr;
}
string_set_add(&p->imported_files, path);
@@ -4757,7 +4782,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
pkg->is_single_file = true;
parser_add_file_to_process(p, pkg, fi, pos);
parser_add_package(p, pkg);
- return true;
+ return pkg;
}
@@ -4773,22 +4798,22 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
switch (rd_err) {
case ReadDirectory_InvalidPath:
syntax_error(pos, "Invalid path: %.*s", LIT(rel_path));
- return false;
+ return nullptr;
case ReadDirectory_NotExists:
syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path));
- return false;
+ return nullptr;
case ReadDirectory_Permission:
syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
- return false;
+ return nullptr;
case ReadDirectory_NotDir:
syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path));
- return false;
+ return nullptr;
case ReadDirectory_Empty:
syntax_error(pos, "Empty directory: %.*s", LIT(rel_path));
- return false;
+ return nullptr;
case ReadDirectory_Unknown:
syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
- return false;
+ return nullptr;
}
for_array(list_index, list) {
@@ -4810,7 +4835,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
parser_add_package(p, pkg);
- return true;
+ return pkg;
}
gb_global Rune illegal_import_runes[] = {
@@ -4829,7 +4854,7 @@ bool is_import_path_valid(String path) {
u8 *curr = start;
while (curr < end) {
isize width = 1;
- Rune r = curr[0];
+ Rune r = *curr;
if (r >= 0x80) {
width = gb_utf8_decode(curr, end-curr, &r);
if (r == GB_RUNE_INVALID && width == 1) {
@@ -4854,6 +4879,45 @@ bool is_import_path_valid(String path) {
return false;
}
+bool is_build_flag_path_valid(String path) {
+ if (path.len > 0) {
+ u8 *start = path.text;
+ u8 *end = path.text + path.len;
+ u8 *curr = start;
+ isize index = 0;
+ while (curr < end) {
+ isize width = 1;
+ Rune r = *curr;
+ if (r >= 0x80) {
+ width = gb_utf8_decode(curr, end-curr, &r);
+ if (r == GB_RUNE_INVALID && width == 1) {
+ return false;
+ }
+ else if (r == GB_RUNE_BOM && curr-start > 0) {
+ return false;
+ }
+ }
+
+ for (isize i = 0; i < gb_count_of(illegal_import_runes); i++) {
+#if defined(GB_SYSTEM_WINDOWS)
+ if (r == '\\') {
+ break;
+ }
+#endif
+ if (r == illegal_import_runes[i]) {
+ return false;
+ }
+ }
+
+ curr += width;
+ index += 1;
+ }
+
+ return true;
+ }
+ return false;
+}
+
bool is_package_name_reserved(String const &name) {
if (name == "builtin") {
@@ -4974,7 +5038,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir,
-void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> &decls);
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice<Ast *> &decls);
void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) {
if (ws->body != nullptr) {
@@ -4995,7 +5059,7 @@ void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenS
}
}
-void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> &decls) {
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice<Ast *> &decls) {
for_array(i, decls) {
Ast *node = decls[i];
if (!is_ast_decl(node) &&
@@ -5034,8 +5098,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *>
} else if (node->kind == Ast_ForeignImportDecl) {
ast_node(fl, ForeignImportDecl, node);
- fl->fullpaths.allocator = heap_allocator();
- array_reserve(&fl->fullpaths, fl->filepaths.count);
+ auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count);
for_array(fp_idx, fl->filepaths) {
String file_str = fl->filepaths[fp_idx].string;
@@ -5049,14 +5112,17 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *>
}
fullpath = foreign_path;
}
- array_add(&fl->fullpaths, fullpath);
+ array_add(&fullpaths, fullpath);
}
- if (fl->fullpaths.count == 0) {
+ if (fullpaths.count == 0) {
syntax_error(decls[i], "No foreign paths found");
decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]);
goto end;
}
+ fl->fullpaths = slice_from_array(fullpaths);
+
+
} else if (node->kind == Ast_WhenStmt) {
ast_node(ws, WhenStmt, node);
parse_setup_file_when_stmt(p, f, base_dir, ws);
@@ -5218,12 +5284,12 @@ bool parse_file(Parser *p, AstFile *f) {
f->pkg_decl = pd;
if (f->error_count == 0) {
- f->decls = array_make<Ast *>(heap_allocator());
+ auto decls = array_make<Ast *>(heap_allocator());
while (f->curr_token.kind != Token_EOF) {
Ast *stmt = parse_stmt(f);
if (stmt && stmt->kind != Ast_EmptyStmt) {
- array_add(&f->decls, stmt);
+ array_add(&decls, stmt);
if (stmt->kind == Ast_ExprStmt &&
stmt->ExprStmt.expr != nullptr &&
stmt->ExprStmt.expr->kind == Ast_ProcLit) {
@@ -5232,6 +5298,8 @@ bool parse_file(Parser *p, AstFile *f) {
}
}
+ f->decls = slice_from_array(decls);
+
parse_setup_file_decls(p, f, base_dir, f->decls);
}
@@ -5325,7 +5393,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
}
TokenPos init_pos = {};
- if (!build_context.generate_docs) {
+ {
String s = get_fullpath_core(heap_allocator(), str_lit("runtime"));
try_add_import_path(p, s, s, init_pos, Package_Runtime);
}
@@ -5333,6 +5401,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init);
p->init_fullpath = init_fullpath;
+ for_array(i, build_context.extra_packages) {
+ String path = build_context.extra_packages[i];
+ String fullpath = path_to_full_path(heap_allocator(), path); // LEAK?
+ if (!path_is_directory(fullpath)) {
+ String const ext = str_lit(".odin");
+ if (!string_ends_with(fullpath, ext)) {
+ error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
+ return ParseFile_WrongExtension;
+ }
+ }
+ AstPackage *pkg = try_add_import_path(p, fullpath, fullpath, init_pos, Package_Normal);
+ if (pkg) {
+ pkg->is_extra = true;
+ }
+ }
+
thread_pool_start(&parser_thread_pool);
thread_pool_wait_to_process(&parser_thread_pool);
diff --git a/src/parser.hpp b/src/parser.hpp
index 8e210876f..cd0d59a48 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -46,7 +46,7 @@ enum ParseFileError {
};
struct CommentGroup {
- Array<Token> list; // Token_Comment
+ Slice<Token> list; // Token_Comment
};
@@ -98,8 +98,8 @@ struct AstFile {
bool in_foreign_block;
bool allow_type;
- Array<Ast *> decls;
- Array<Ast *> imports; // 'import' 'using import'
+ Slice<Ast *> decls;
+ Array<Ast *> imports; // 'import'
isize directive_count;
Ast * curr_proc;
@@ -107,6 +107,8 @@ struct AstFile {
f64 time_to_tokenize; // seconds
f64 time_to_parse; // seconds
+ bool is_test;
+
CommentGroup *lead_comment; // Comment (block) before the decl
CommentGroup *line_comment; // Comment after the semicolon
CommentGroup *docs; // current docs
@@ -148,6 +150,7 @@ struct AstPackage {
Scope * scope;
DeclInfo *decl_info;
bool used;
+ bool is_extra;
};
@@ -217,14 +220,16 @@ enum ProcCallingConvention {
ProcCC_ForeignBlockDefault = -1,
};
-enum StateFlag {
+enum StateFlag : u16 {
StateFlag_bounds_check = 1<<0,
StateFlag_no_bounds_check = 1<<1,
StateFlag_no_deferred = 1<<5,
+
+ StateFlag_BeenHandled = 1<<15,
};
-enum ViralStateFlag {
+enum ViralStateFlag : u16 {
ViralStateFlag_ContainsDeferredProcedure = 1<<0,
};
@@ -275,7 +280,6 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
AST_KIND(Undef, "undef", Token) \
AST_KIND(BasicLit, "basic literal", struct { \
Token token; \
- ExactValue value; \
}) \
AST_KIND(BasicDirective, "basic directive", struct { \
Token token; \
@@ -289,7 +293,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
Token token; \
Token open; \
Token close; \
- Array<Ast *> args; \
+ Slice<Ast *> args; \
}) \
AST_KIND(ProcLit, "procedure literal", struct { \
Ast *type; \
@@ -297,12 +301,12 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
u64 tags; \
ProcInlining inlining; \
Token where_token; \
- Array<Ast *> where_clauses; \
+ Slice<Ast *> where_clauses; \
DeclInfo *decl; \
}) \
AST_KIND(CompoundLit, "compound literal", struct { \
Ast *type; \
- Array<Ast *> elems; \
+ Slice<Ast *> elems; \
Token open, close; \
i64 max_count; \
}) \
@@ -325,7 +329,7 @@ AST_KIND(_ExprBegin, "", bool) \
}) \
AST_KIND(CallExpr, "call expression", struct { \
Ast * proc; \
- Array<Ast *> args; \
+ Slice<Ast *> args; \
Token open; \
Token close; \
Token ellipsis; \
@@ -342,7 +346,7 @@ AST_KIND(_ExprBegin, "", bool) \
AST_KIND(InlineAsmExpr, "inline asm expression", struct { \
Token token; \
Token open, close; \
- Array<Ast *> param_types; \
+ Slice<Ast *> param_types; \
Ast *return_type; \
Ast *asm_string; \
Ast *constraints_string; \
@@ -362,11 +366,11 @@ AST_KIND(_StmtBegin, "", bool) \
}) \
AST_KIND(AssignStmt, "assign statement", struct { \
Token op; \
- Array<Ast *> lhs, rhs; \
+ Slice<Ast *> lhs, rhs; \
}) \
AST_KIND(_ComplexStmtBegin, "", bool) \
AST_KIND(BlockStmt, "block statement", struct { \
- Array<Ast *> stmts; \
+ Slice<Ast *> stmts; \
Ast *label; \
Token open, close; \
}) \
@@ -388,7 +392,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
}) \
AST_KIND(ReturnStmt, "return statement", struct { \
Token token; \
- Array<Ast *> results; \
+ Slice<Ast *> results; \
}) \
AST_KIND(ForStmt, "for statement", struct { \
Token token; \
@@ -418,8 +422,8 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
}) \
AST_KIND(CaseClause, "case clause", struct { \
Token token; \
- Array<Ast *> list; \
- Array<Ast *> stmts; \
+ Slice<Ast *> list; \
+ Slice<Ast *> stmts; \
Entity *implicit_entity; \
}) \
AST_KIND(SwitchStmt, "switch statement", struct { \
@@ -436,12 +440,12 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
Ast *tag; \
Ast *body; \
bool partial; \
-}) \
+ }) \
AST_KIND(DeferStmt, "defer statement", struct { Token token; Ast *stmt; }) \
AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \
AST_KIND(UsingStmt, "using statement", struct { \
Token token; \
- Array<Ast *> list; \
+ Slice<Ast *> list; \
}) \
AST_KIND(_ComplexStmtEnd, "", bool) \
AST_KIND(_StmtEnd, "", bool) \
@@ -459,9 +463,9 @@ AST_KIND(_DeclBegin, "", bool) \
Ast *name; \
}) \
AST_KIND(ValueDecl, "value declaration", struct { \
- Array<Ast *> names; \
+ Slice<Ast *> names; \
Ast * type; \
- Array<Ast *> values; \
+ Slice<Ast *> values; \
Array<Ast *> attributes; \
CommentGroup *docs; \
CommentGroup *comment; \
@@ -486,10 +490,10 @@ AST_KIND(_DeclBegin, "", bool) \
}) \
AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \
Token token; \
- Array<Token> filepaths; \
+ Slice<Token> filepaths; \
Token library_name; \
String collection_name; \
- Array<String> fullpaths; \
+ Slice<String> fullpaths; \
Array<Ast *> attributes; \
CommentGroup *docs; \
CommentGroup *comment; \
@@ -497,11 +501,11 @@ AST_KIND(_DeclBegin, "", bool) \
AST_KIND(_DeclEnd, "", bool) \
AST_KIND(Attribute, "attribute", struct { \
Token token; \
- Array<Ast *> elems; \
+ Slice<Ast *> elems; \
Token open, close; \
}) \
AST_KIND(Field, "field", struct { \
- Array<Ast *> names; \
+ Slice<Ast *> names; \
Ast * type; \
Ast * default_value; \
Token tag; \
@@ -511,7 +515,7 @@ AST_KIND(_DeclEnd, "", bool) \
}) \
AST_KIND(FieldList, "field list", struct { \
Token token; \
- Array<Ast *> list; \
+ Slice<Ast *> list; \
}) \
AST_KIND(_TypeBegin, "", bool) \
AST_KIND(TypeidType, "typeid", struct { \
@@ -565,34 +569,34 @@ AST_KIND(_TypeBegin, "", bool) \
}) \
AST_KIND(StructType, "struct type", struct { \
Token token; \
- Array<Ast *> fields; \
+ Slice<Ast *> fields; \
isize field_count; \
Ast *polymorphic_params; \
Ast *align; \
Token where_token; \
- Array<Ast *> where_clauses; \
+ Slice<Ast *> where_clauses; \
bool is_packed; \
bool is_raw_union; \
}) \
AST_KIND(UnionType, "union type", struct { \
Token token; \
- Array<Ast *> variants; \
+ Slice<Ast *> variants; \
Ast *polymorphic_params; \
Ast * align; \
bool maybe; \
bool no_nil; \
Token where_token; \
- Array<Ast *> where_clauses; \
+ Slice<Ast *> where_clauses; \
}) \
AST_KIND(EnumType, "enum type", struct { \
Token token; \
Ast * base_type; \
- Array<Ast *> fields; /* FieldValue */ \
+ Slice<Ast *> fields; /* FieldValue */ \
bool is_using; \
}) \
AST_KIND(BitFieldType, "bit field type", struct { \
Token token; \
- Array<Ast *> fields; /* FieldValue with : */ \
+ Slice<Ast *> fields; /* FieldValue with : */ \
Ast * align; \
}) \
AST_KIND(BitSetType, "bit set type", struct { \
@@ -638,23 +642,22 @@ isize const ast_variant_sizes[] = {
struct AstCommonStuff {
AstKind kind;
- u32 state_flags;
- u32 viral_state_flags;
- bool been_handled;
+ u16 state_flags;
+ u16 viral_state_flags;
AstFile * file;
Scope * scope;
- TypeAndValue tav;
+ TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
};
struct Ast {
AstKind kind;
- u32 state_flags;
- u32 viral_state_flags;
- bool been_handled;
+ u16 state_flags;
+ u16 viral_state_flags;
AstFile * file;
Scope * scope;
- TypeAndValue tav;
+ TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
+ // IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant
union {
#define AST_KIND(_kind_name_, name, ...) GB_JOIN2(Ast, _kind_name_) _kind_name_;
AST_KINDS
diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp
index e343628af..5432fa094 100644
--- a/src/ptr_set.cpp
+++ b/src/ptr_set.cpp
@@ -1,25 +1,30 @@
+typedef u32 PtrSetIndex;
+
struct PtrSetFindResult {
- isize hash_index;
- isize entry_prev;
- isize entry_index;
+ PtrSetIndex hash_index;
+ PtrSetIndex entry_prev;
+ PtrSetIndex entry_index;
};
+enum : PtrSetIndex { PTR_SET_SENTINEL = ~(PtrSetIndex)0 };
+
template <typename T>
struct PtrSetEntry {
- T ptr;
- isize next;
+ T ptr;
+ PtrSetIndex next;
};
template <typename T>
struct PtrSet {
- Array<isize> hashes;
+ Array<PtrSetIndex> hashes;
Array<PtrSetEntry<T>> entries;
};
template <typename T> void ptr_set_init (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
template <typename T> void ptr_set_destroy(PtrSet<T> *s);
template <typename T> T ptr_set_add (PtrSet<T> *s, T ptr);
+template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existsed
template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
template <typename T> void ptr_set_clear (PtrSet<T> *s);
@@ -27,12 +32,31 @@ template <typename T> void ptr_set_grow (PtrSet<T> *s);
template <typename T> void ptr_set_rehash (PtrSet<T> *s, isize new_count);
+isize next_pow2_isize(isize n) {
+ if (n <= 0) {
+ return 0;
+ }
+ n--;
+ n |= n >> 1;
+ n |= n >> 2;
+ n |= n >> 4;
+ n |= n >> 8;
+ n |= n >> 16;
+ if (gb_size_of(isize) == 8) {
+ n |= n >> 32;
+ }
+ n++;
+ return n;
+}
+
template <typename T>
void ptr_set_init(PtrSet<T> *s, gbAllocator a, isize capacity) {
+ capacity = next_pow2_isize(gb_max(16, capacity));
+
array_init(&s->hashes, a, capacity);
array_init(&s->entries, a, 0, capacity);
for (isize i = 0; i < capacity; i++) {
- s->hashes.data[i] = -1;
+ s->hashes.data[i] = PTR_SET_SENTINEL;
}
}
@@ -43,72 +67,69 @@ void ptr_set_destroy(PtrSet<T> *s) {
}
template <typename T>
-gb_internal isize ptr_set__add_entry(PtrSet<T> *s, T ptr) {
+gb_internal PtrSetIndex ptr_set__add_entry(PtrSet<T> *s, T ptr) {
PtrSetEntry<T> e = {};
e.ptr = ptr;
- e.next = -1;
+ e.next = PTR_SET_SENTINEL;
array_add(&s->entries, e);
- return s->entries.count-1;
+ return cast(PtrSetIndex)(s->entries.count-1);
}
template <typename T>
gb_internal PtrSetFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
- PtrSetFindResult fr = {-1, -1, -1};
- if (s->hashes.count > 0) {
+ PtrSetFindResult fr = {PTR_SET_SENTINEL, PTR_SET_SENTINEL, PTR_SET_SENTINEL};
+ if (s->hashes.count != 0) {
u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr;
u64 n = cast(u64)s->hashes.count;
- fr.hash_index = cast(isize)(hash % n);
- fr.entry_index = s->hashes[fr.hash_index];
- while (fr.entry_index >= 0) {
- if (s->entries[fr.entry_index].ptr == ptr) {
+ fr.hash_index = cast(PtrSetIndex)(hash & (n-1));
+ fr.entry_index = s->hashes.data[fr.hash_index];
+ while (fr.entry_index != PTR_SET_SENTINEL) {
+ if (s->entries.data[fr.entry_index].ptr == ptr) {
return fr;
}
fr.entry_prev = fr.entry_index;
- fr.entry_index = s->entries[fr.entry_index].next;
+ fr.entry_index = s->entries.data[fr.entry_index].next;
}
}
return fr;
}
template <typename T>
-gb_internal b32 ptr_set__full(PtrSet<T> *s) {
+gb_internal bool ptr_set__full(PtrSet<T> *s) {
return 0.75f * s->hashes.count <= s->entries.count;
}
-#define PTR_ARRAY_GROW_FORMULA(x) (4*(x) + 7)
-GB_STATIC_ASSERT(PTR_ARRAY_GROW_FORMULA(0) > 0);
-
template <typename T>
gb_inline void ptr_set_grow(PtrSet<T> *s) {
- isize new_count = PTR_ARRAY_GROW_FORMULA(s->entries.count);
+ isize new_count = s->hashes.count*2;
ptr_set_rehash(s, new_count);
}
template <typename T>
void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
- isize i, j;
+ PtrSetIndex i, j;
PtrSet<T> ns = {};
ptr_set_init(&ns, s->hashes.allocator);
array_resize(&ns.hashes, new_count);
array_reserve(&ns.entries, s->entries.count);
for (i = 0; i < new_count; i++) {
- ns.hashes[i] = -1;
+ ns.hashes.data[i] = PTR_SET_SENTINEL;
}
for (i = 0; i < s->entries.count; i++) {
- PtrSetEntry<T> *e = &s->entries[i];
+ PtrSetEntry<T> *e = &s->entries.data[i];
PtrSetFindResult fr;
if (ns.hashes.count == 0) {
ptr_set_grow(&ns);
}
fr = ptr_set__find(&ns, e->ptr);
j = ptr_set__add_entry(&ns, e->ptr);
- if (fr.entry_prev < 0) {
- ns.hashes[fr.hash_index] = j;
+ if (fr.entry_prev == PTR_SET_SENTINEL) {
+ ns.hashes.data[fr.hash_index] = j;
} else {
- ns.entries[fr.entry_prev].next = j;
+ ns.entries.data[fr.entry_prev].next = j;
}
- ns.entries[j].next = fr.entry_index;
+ ns.entries.data[j].next = fr.entry_index;
if (ptr_set__full(&ns)) {
ptr_set_grow(&ns);
}
@@ -120,26 +141,24 @@ void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
template <typename T>
gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
isize index = ptr_set__find(s, ptr).entry_index;
- return index >= 0;
+ return index != PTR_SET_SENTINEL;
}
// Returns true if it already exists
template <typename T>
T ptr_set_add(PtrSet<T> *s, T ptr) {
- isize index;
+ PtrSetIndex index;
PtrSetFindResult fr;
if (s->hashes.count == 0) {
ptr_set_grow(s);
}
fr = ptr_set__find(s, ptr);
- if (fr.entry_index >= 0) {
- index = fr.entry_index;
- } else {
+ if (fr.entry_index == PTR_SET_SENTINEL) {
index = ptr_set__add_entry(s, ptr);
- if (fr.entry_prev >= 0) {
- s->entries[fr.entry_prev].next = index;
+ if (fr.entry_prev != PTR_SET_SENTINEL) {
+ s->entries.data[fr.entry_prev].next = index;
} else {
- s->hashes[fr.hash_index] = index;
+ s->hashes.data[fr.hash_index] = index;
}
}
if (ptr_set__full(s)) {
@@ -148,32 +167,58 @@ T ptr_set_add(PtrSet<T> *s, T ptr) {
return ptr;
}
+template <typename T>
+bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it previously existsed
+ bool exists = false;
+ PtrSetIndex index;
+ PtrSetFindResult fr;
+ if (s->hashes.count == 0) {
+ ptr_set_grow(s);
+ }
+ fr = ptr_set__find(s, ptr);
+ if (fr.entry_index != PTR_SET_SENTINEL) {
+ exists = true;
+ } else {
+ index = ptr_set__add_entry(s, ptr);
+ if (fr.entry_prev != PTR_SET_SENTINEL) {
+ s->entries.data[fr.entry_prev].next = index;
+ } else {
+ s->hashes.data[fr.hash_index] = index;
+ }
+ }
+ if (ptr_set__full(s)) {
+ ptr_set_grow(s);
+ }
+ return exists;
+}
+
+
template <typename T>
void ptr_set__erase(PtrSet<T> *s, PtrSetFindResult fr) {
PtrSetFindResult last;
- if (fr.entry_prev < 0) {
- s->hashes[fr.hash_index] = s->entries[fr.entry_index].next;
+ if (fr.entry_prev == PTR_SET_SENTINEL) {
+ s->hashes.data[fr.hash_index] = s->entries.data[fr.entry_index].next;
} else {
- s->entries[fr.entry_prev].next = s->entries[fr.entry_index].next;
+ s->entries.data[fr.entry_prev].next = s->entries.data[fr.entry_index].next;
}
if (fr.entry_index == s->entries.count-1) {
array_pop(&s->entries);
return;
}
- s->entries[fr.entry_index] = s->entries[s->entries.count-1];
- last = ptr_set__find(s, s->entries[fr.entry_index].ptr);
- if (last.entry_prev >= 0) {
- s->entries[last.entry_prev].next = fr.entry_index;
+ s->entries.data[fr.entry_index] = s->entries.data[s->entries.count-1];
+ last = ptr_set__find(s, s->entries.data[fr.entry_index].ptr);
+ if (last.entry_prev != PTR_SET_SENTINEL) {
+ s->entries.data[last.entry_prev].next = fr.entry_index;
} else {
- s->hashes[last.hash_index] = fr.entry_index;
+ s->hashes.data[last.hash_index] = fr.entry_index;
}
}
template <typename T>
void ptr_set_remove(PtrSet<T> *s, T ptr) {
PtrSetFindResult fr = ptr_set__find(s, ptr);
- if (fr.entry_index >= 0) {
+ if (fr.entry_index != PTR_SET_SENTINEL) {
ptr_set__erase(s, fr);
}
}
diff --git a/src/string.cpp b/src/string.cpp
index 6a0192507..408f53d72 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -164,6 +164,7 @@ int string_compare(String const &x, String const &y) {
return cast(int)x[offset] - cast(int)y[offset];
}
}
+ return cast(int)(x.len - y.len);
}
return 0;
}
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 72448b869..bef82633f 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -118,8 +118,6 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
TOKEN_KIND(Token_no_inline, "no_inline"), \
TOKEN_KIND(Token_context, "context"), \
TOKEN_KIND(Token_asm, "asm"), \
- TOKEN_KIND(Token_macro, "macro"), \
- TOKEN_KIND(Token_const, "const"), \
TOKEN_KIND(Token__KeywordEnd, ""), \
TOKEN_KIND(Token_Count, "")
diff --git a/src/types.cpp b/src/types.cpp
index fc4544385..df87cb645 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -323,6 +323,8 @@ String const type_strings[] = {
enum TypeFlag : u32 {
TypeFlag_Polymorphic = 1<<1,
TypeFlag_PolySpecialized = 1<<2,
+ TypeFlag_InProcessOfCheckingPolymorphic = 1<<3,
+ TypeFlag_InProcessOfCheckingABI = 1<<4,
};
struct Type {
@@ -371,7 +373,28 @@ enum Typeid_Kind : u8 {
Typeid_Relative_Slice,
};
+// IMPORTANT NOTE(bill): This must match the same as the in core.odin
+enum TypeInfoFlag : u32 {
+ TypeInfoFlag_Comparable = 1<<0,
+ TypeInfoFlag_Simple_Compare = 1<<1,
+};
+bool is_type_comparable(Type *t);
+bool is_type_simple_compare(Type *t);
+
+u32 type_info_flags_of_type(Type *type) {
+ if (type == nullptr) {
+ return 0;
+ }
+ u32 flags = 0;
+ if (is_type_comparable(type)) {
+ flags |= TypeInfoFlag_Comparable;
+ }
+ if (is_type_simple_compare(type)) {
+ flags |= TypeInfoFlag_Comparable;
+ }
+ return flags;
+}
// TODO(bill): Should I add extra information here specifying the kind of selection?
@@ -661,12 +684,15 @@ gb_global Type *t_context_ptr = nullptr;
gb_global Type *t_source_code_location = nullptr;
gb_global Type *t_source_code_location_ptr = nullptr;
-gb_global Type *t_map_key = nullptr;
+gb_global Type *t_map_hash = nullptr;
gb_global Type *t_map_header = nullptr;
gb_global Type *t_vector_x86_mmx = nullptr;
+gb_global Type *t_equal_proc = nullptr;
+gb_global Type *t_hasher_proc = nullptr;
+
i64 type_size_of (Type *t);
i64 type_align_of (Type *t);
@@ -769,7 +795,8 @@ void set_base_type(Type *t, Type *base) {
Type *alloc_type(TypeKind kind) {
- gbAllocator a = heap_allocator();
+ // gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
Type *t = gb_alloc_item(a, Type);
zero_item(t);
t->kind = kind;
@@ -884,6 +911,25 @@ Type *alloc_type_named(String name, Type *base, Entity *type_name) {
return t;
}
+bool is_calling_convention_none(ProcCallingConvention calling_convention) {
+ switch (calling_convention) {
+ case ProcCC_None:
+ case ProcCC_PureNone:
+ case ProcCC_InlineAsm:
+ return true;
+ }
+ return false;
+}
+
+bool is_calling_convention_odin(ProcCallingConvention calling_convention) {
+ switch (calling_convention) {
+ case ProcCC_Odin:
+ case ProcCC_Contextless:
+ return true;
+ }
+ return false;
+}
+
Type *alloc_type_tuple() {
Type *t = alloc_type(Type_Tuple);
return t;
@@ -918,7 +964,6 @@ bool is_type_valid_for_keys(Type *t);
Type *alloc_type_map(i64 count, Type *key, Type *value) {
if (key != nullptr) {
- GB_ASSERT(is_type_valid_for_keys(key));
GB_ASSERT(value != nullptr);
}
Type *t = alloc_type(Type_Map);
@@ -1192,20 +1237,6 @@ bool is_type_slice(Type *t) {
t = base_type(t);
return t->kind == Type_Slice;
}
-bool is_type_u8_slice(Type *t) {
- t = base_type(t);
- if (t->kind == Type_Slice) {
- return is_type_u8(t->Slice.elem);
- }
- return false;
-}
-bool is_type_u8_ptr(Type *t) {
- t = base_type(t);
- if (t->kind == Type_Pointer) {
- return is_type_u8(t->Slice.elem);
- }
- return false;
-}
bool is_type_proc(Type *t) {
t = base_type(t);
return t->kind == Type_Proc;
@@ -1249,6 +1280,37 @@ bool is_type_relative_slice(Type *t) {
return t->kind == Type_RelativeSlice;
}
+bool is_type_u8_slice(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Slice) {
+ return is_type_u8(t->Slice.elem);
+ }
+ return false;
+}
+bool is_type_u8_array(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Array) {
+ return is_type_u8(t->Array.elem);
+ }
+ return false;
+}
+bool is_type_u8_ptr(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Pointer) {
+ return is_type_u8(t->Slice.elem);
+ }
+ return false;
+}
+bool is_type_rune_array(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Array) {
+ return is_type_rune(t->Array.elem);
+ }
+ return false;
+}
+
+
+
Type *core_array_type(Type *t) {
for (;;) {
@@ -1261,53 +1323,7 @@ Type *core_array_type(Type *t) {
return t;
}
-// NOTE(bill): type can be easily compared using memcmp
-bool is_type_simple_compare(Type *t) {
- t = core_type(t);
- switch (t->kind) {
- case Type_Array:
- return is_type_simple_compare(t->Array.elem);
-
- case Type_EnumeratedArray:
- return is_type_simple_compare(t->EnumeratedArray.elem);
-
- case Type_Basic:
- if (t->Basic.flags & BasicFlag_SimpleCompare) {
- return true;
- }
- return false;
-
- case Type_Pointer:
- case Type_Proc:
- case Type_BitSet:
- case Type_BitField:
- return true;
-
- case Type_Struct:
- for_array(i, t->Struct.fields) {
- Entity *f = t->Struct.fields[i];
- if (!is_type_simple_compare(f->type)) {
- return false;
- }
- }
- return true;
-
- case Type_Union:
- for_array(i, t->Union.variants) {
- Type *v = t->Union.variants[i];
- if (!is_type_simple_compare(v)) {
- return false;
- }
- }
- return true;
-
- case Type_SimdVector:
- return is_type_simple_compare(t->SimdVector.elem);
-
- }
- return false;
-}
Type *base_complex_elem_type(Type *t) {
t = core_type(t);
@@ -1526,6 +1542,8 @@ bool is_type_valid_for_keys(Type *t) {
if (is_type_untyped(t)) {
return false;
}
+ return is_type_comparable(t);
+#if 0
if (is_type_integer(t)) {
return true;
}
@@ -1541,8 +1559,15 @@ bool is_type_valid_for_keys(Type *t) {
if (is_type_typeid(t)) {
return true;
}
+ if (is_type_simple_compare(t)) {
+ return true;
+ }
+ if (is_type_comparable(t)) {
+ return true;
+ }
return false;
+#endif
}
bool is_type_valid_bit_set_elem(Type *t) {
@@ -1695,12 +1720,23 @@ TypeTuple *get_record_polymorphic_params(Type *t) {
bool is_type_polymorphic(Type *t, bool or_specialized=false) {
+ if (t->flags & TypeFlag_InProcessOfCheckingPolymorphic) {
+ return false;
+ }
+
switch (t->kind) {
case Type_Generic:
return true;
case Type_Named:
- return is_type_polymorphic(t->Named.base, or_specialized);
+ {
+ u32 flags = t->flags;
+ t->flags |= TypeFlag_InProcessOfCheckingPolymorphic;
+ bool ok = is_type_polymorphic(t->Named.base, or_specialized);
+ t->flags = flags;
+ return ok;
+ }
+
case Type_Opaque:
return is_type_polymorphic(t->Opaque.elem, or_specialized);
case Type_Pointer:
@@ -1892,10 +1928,77 @@ bool is_type_comparable(Type *t) {
case Type_Opaque:
return is_type_comparable(t->Opaque.elem);
+
+ case Type_Struct:
+ if (type_size_of(t) == 0) {
+ return false;
+ }
+ if (t->Struct.is_raw_union) {
+ return is_type_simple_compare(t);
+ }
+ for_array(i, t->Struct.fields) {
+ Entity *f = t->Struct.fields[i];
+ if (!is_type_comparable(f->type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+// NOTE(bill): type can be easily compared using memcmp
+bool is_type_simple_compare(Type *t) {
+ t = core_type(t);
+ switch (t->kind) {
+ case Type_Array:
+ return is_type_simple_compare(t->Array.elem);
+
+ case Type_EnumeratedArray:
+ return is_type_simple_compare(t->EnumeratedArray.elem);
+
+ case Type_Basic:
+ if (t->Basic.flags & BasicFlag_SimpleCompare) {
+ return true;
+ }
+ if (t->Basic.kind == Basic_typeid) {
+ return true;
+ }
+ return false;
+
+ case Type_Pointer:
+ case Type_Proc:
+ case Type_BitSet:
+ case Type_BitField:
+ return true;
+
+ case Type_Struct:
+ for_array(i, t->Struct.fields) {
+ Entity *f = t->Struct.fields[i];
+ if (!is_type_simple_compare(f->type)) {
+ return false;
+ }
+ }
+ return true;
+
+ case Type_Union:
+ for_array(i, t->Union.variants) {
+ Type *v = t->Union.variants[i];
+ if (!is_type_simple_compare(v)) {
+ return false;
+ }
+ }
+ return true;
+
+ case Type_SimdVector:
+ return is_type_simple_compare(t->SimdVector.elem);
+
}
+
return false;
}
+
Type *strip_type_aliasing(Type *x) {
if (x == nullptr) {
return x;
@@ -2317,7 +2420,7 @@ Selection lookup_field_from_index(Type *type, i64 index) {
GB_ASSERT(is_type_struct(type) || is_type_union(type) || is_type_tuple(type));
type = base_type(type);
- gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
isize max_count = 0;
switch (type->kind) {
case Type_Struct: max_count = type->Struct.fields.count; break;
@@ -2365,7 +2468,6 @@ Selection lookup_field_from_index(Type *type, i64 index) {
return empty_selection;
}
-
Entity *scope_lookup_current(Scope *s, String const &name);
Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) {
@@ -2375,7 +2477,6 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
return empty_selection;
}
- gbAllocator a = heap_allocator();
Type *type = type_deref(type_);
bool is_ptr = type != type_;
sel.indirect = sel.indirect || is_ptr;
@@ -2964,7 +3065,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
}
Array<i64> type_set_offsets_of(Array<Entity *> const &fields, bool is_packed, bool is_raw_union) {
- gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
auto offsets = array_make<i64>(a, fields.count);
i64 curr_offset = 0;
if (is_raw_union) {
@@ -3353,6 +3454,58 @@ Type *reduce_tuple_to_single_type(Type *original_type) {
}
+Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) {
+ Type *t = alloc_type_struct();
+ t->Struct.fields = array_make<Entity *>(heap_allocator(), field_count);
+
+ Scope *scope = nullptr;
+ for_array(i, t->Struct.fields) {
+ t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved);
+ }
+ t->Struct.is_packed = is_packed;
+
+ return t;
+}
+
+Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) {
+ if (field_count == 0) {
+ return nullptr;
+ }
+ if (!must_be_tuple && field_count == 1) {
+ return field_types[0];
+ }
+
+ Type *t = alloc_type_tuple();
+ t->Tuple.variables = array_make<Entity *>(heap_allocator(), field_count);
+
+ Scope *scope = nullptr;
+ for_array(i, t->Tuple.variables) {
+ t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false);
+ }
+ t->Tuple.is_packed = is_packed;
+
+ return t;
+}
+
+Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg, ProcCallingConvention calling_convention) {
+
+ Type *params = alloc_type_tuple_from_field_types(param_types, param_count, false, true);
+ isize results_count = 0;
+ if (results != nullptr) {
+ if (results->kind != Type_Tuple) {
+ results = alloc_type_tuple_from_field_types(&results, 1, false, true);
+ }
+ results_count = results->Tuple.variables.count;
+ }
+
+ Scope *scope = nullptr;
+ Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, calling_convention);
+ t->Proc.c_vararg = is_c_vararg;
+ return t;
+}
+
+
+
gbString write_type_to_string(gbString str, Type *type) {
if (type == nullptr) {
return gb_string_appendc(str, "<no type>");
@@ -3671,3 +3824,6 @@ gbString type_to_string(Type *type) {
return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
}
+
+
+