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 ""; + } + 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..= 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, ""); + io.write_string(fi.writer, ""); } else { fmt_arg(&fi, args[index], 'v'); } } - strings.write_string(b, ")"); + io.write_string(fi.writer, ")"); } - strings.flush_builder(b); - return strings.to_string(b^); + io.flush(auto_cast 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, ""); + io.write_string(writer, ""); } - strings.write_byte(buf, ')'); + io.write_byte(writer, ')'); } fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { switch verb { case 't', 'v': - strings.write_string(buf, b ? "true" : "false"); + io.write_string(writer, b ? "true" : "false"); case: fmt_bad_verb(fi, verb); } @@ -633,7 +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.. 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, ""); + io.write_string(fi.writer, ""); 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.."); + io.write_string(fi.writer, ""); 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.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && index > 0 { io.write_string(fi.writer, ", "); } field_count := -1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } - strings.write_string(fi.buf, base_type_name); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_string(fi.writer, base_type_name); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); for name, i in b.names { field_count += 1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.."); + io.write_string(fi.writer, ""); return; } if fi.record_level < 1 { fi.record_level += 1; defer fi.record_level -= 1; - strings.write_byte(fi.buf, '&'); + io.write_byte(fi.writer, '&'); fmt_value(fi, a, verb); return; } @@ -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, ""); + io.write_string(fi.writer, ""); return; } if fi.record_level < 1 { fi.record_level += 1; defer fi.record_level -= 1; - strings.write_byte(fi.buf, '&'); + io.write_byte(fi.writer, '&'); fmt_value(fi, a, verb); return; } @@ -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.. 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.. 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.. 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.. 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.. 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.. 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.. 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.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && index > 0 { io.write_string(fi.writer, ", "); } field_count := -1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } - strings.write_string(fi.buf, base_type_name); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_string(fi.writer, base_type_name); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0..= 0); if v.data == nil { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else if info.no_nil { id := info.variants[tag].id; fmt_arg(fi, any{v.data, id}, verb); } else if tag == 0 { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else { id := info.variants[tag-1].id; fmt_arg(fi, any{v.data, id}, verb); @@ -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.. 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, ""); + io.write_string(fi.writer, ""); 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.. 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.. 0 { - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return; - } - - data := (^rawptr)(array)^; - - len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); - - - soa_offset := 0; - item_offset := 0; - - arg_copy := arg; - arg_ptr := &arg_copy; - - max_align := 0; - for i in 0.. 0 { - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return; - } - - data := (^rawptr)(array)^; - - len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); - - - soa_offset := 0; - item_offset := 0; - - args_ptr := &args[0]; - - max_align := 0; - for i in 0.. (ok: bool) #no_bounds_check { - if array == nil { - return; - } - n := len(array); - m :: 1; - resize(array, n+m, loc); - if n+m <= len(array) { - when size_of(E) != 0 { - copy(array[index+m:], array[index:]); - array[index] = arg; - } - ok = true; - } - return; -} - -@builtin -insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { - if array == nil { - return; - } - if len(args) == 0 { - ok = true; - return; - } - - n := len(array); - m := len(args); - resize(array, n+m, loc); - if n+m <= len(array) { - when size_of(E) != 0 { - copy(array[index+m:], array[index:]); - copy(array[index:], args); - } - ok = true; - } - return; -} - -@builtin -insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { - if array == nil { - return; - } - if len(args) == 0 { - ok = true; - return; - } - - n := len(array); - m := len(args); - resize(array, n+m, loc); - if n+m <= len(array) { - copy(array[index+m:], array[index:]); - copy(array[index:], args); - ok = true; - } - return; -} - -@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}; - - - - -@builtin -clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) { - if array != nil { - (^Raw_Dynamic_Array)(array).len = 0; - } -} - -@builtin -reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - a := (^Raw_Dynamic_Array)(array); - - if capacity <= a.cap { - return true; - } - - if a.allocator.procedure == nil { - a.allocator = context.allocator; - } - assert(a.allocator.procedure != nil); - - old_size := a.cap * size_of(E); - new_size := capacity * size_of(E); - allocator := a.allocator; - - new_data := allocator.procedure( - allocator.data, .Resize, new_size, align_of(E), - a.data, old_size, 0, loc, - ); - if new_data == nil { - return false; - } - - a.data = new_data; - a.cap = capacity; - return true; -} - -@builtin -resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - a := (^Raw_Dynamic_Array)(array); - - if length <= a.cap { - a.len = max(length, 0); - return true; - } - - if a.allocator.procedure == nil { - a.allocator = context.allocator; - } - assert(a.allocator.procedure != nil); - - old_size := a.cap * size_of(E); - new_size := length * size_of(E); - allocator := a.allocator; - - new_data := allocator.procedure( - allocator.data, .Resize, new_size, align_of(E), - a.data, old_size, 0, loc, - ); - if new_data == nil { - return false; - } - - a.data = new_data; - a.len = length; - a.cap = length; - return true; -} - - - -@builtin -incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { - s^ |= {elem}; - return s^; -} -@builtin -incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { - for elem in elems { - s^ |= {elem}; - } - return s^; -} -@builtin -incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { - s^ |= other; - return s^; -} -@builtin -excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { - s^ &~= {elem}; - return s^; -} -@builtin -excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { - for elem in elems { - s^ &~= {elem}; - } - return s^; -} -@builtin -excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { - s^ &~= other; - return s^; -} - -@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}; -@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}; - - -@builtin -card :: proc(s: $S/bit_set[$E; $U]) -> int { - when size_of(S) == 1 { - foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- } - return int(count_ones(transmute(u8)s)); - } else when size_of(S) == 2 { - foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- } - return int(count_ones(transmute(u16)s)); - } else when size_of(S) == 4 { - foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- } - return int(count_ones(transmute(u32)s)); - } else when size_of(S) == 8 { - foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- } - return int(count_ones(transmute(u64)s)); - } else when size_of(S) == 16 { - foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- } - return int(count_ones(transmute(u128)s)); - } else { - #panic("Unhandled card bit_set size"); - } -} - - - -@builtin -raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { - return (^E)(a); -} -@builtin -raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E { - ptr := (transmute(Raw_Slice)s).data; - return (^E)(ptr); -} -@builtin -raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E { - ptr := (transmute(Raw_Dynamic_Array)s).data; - return (^E)(ptr); -} -@builtin -raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 { - return (transmute(Raw_String)s).data; -} - -@builtin -raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}; - - - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -assert :: proc(condition: bool, message := "", loc := #caller_location) { - if !condition { - proc(message: string, loc: Source_Code_Location) { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("runtime assertion", message, loc); - }(message, loc); - } -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -panic :: proc(message: string, loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("panic", message, loc); -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -unimplemented :: proc(message := "", loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("not yet implemented", message, loc); -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -unreachable :: proc(message := "", loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - if message != "" { - p("internal error", message, loc); - } else { - p("internal error", "entered unreachable code", loc); - } -} - - -// Dynamic Array - - -__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) { - array := (^Raw_Dynamic_Array)(array_); - array.allocator = context.allocator; - assert(array.allocator.procedure != nil); - - if cap > 0 { - __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc); - array.len = len; - } -} - -__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool { - array := (^Raw_Dynamic_Array)(array_); - - // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written - // assuming that appending/reserving will set the allocator, if it is not already set. - if array.allocator.procedure == nil { - array.allocator = context.allocator; - } - assert(array.allocator.procedure != nil); - - if cap <= array.cap { - return true; - } - - old_size := array.cap * elem_size; - new_size := cap * elem_size; - allocator := array.allocator; - - new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc); - if new_data != nil || elem_size == 0 { - array.data = new_data; - array.cap = cap; - return true; - } - return false; -} - -__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { - array := (^Raw_Dynamic_Array)(array_); - - ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc); - if ok { - array.len = len; - } - return ok; -} - - -__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, - items: rawptr, item_count: int, loc := #caller_location) -> int { - array := (^Raw_Dynamic_Array)(array_); - - if items == nil { - return 0; - } - if item_count <= 0 { - return 0; - } - - - ok := true; - if array.cap <= array.len+item_count { - cap := 2 * array.cap + max(8, item_count); - ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); - } - // TODO(bill): Better error handling for failed reservation - if !ok { - return array.len; - } - - assert(array.data != nil); - data := uintptr(array.data) + uintptr(elem_size*array.len); - - mem_copy(rawptr(data), items, elem_size * item_count); - array.len += item_count; - return array.len; -} - -__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int { - array := (^Raw_Dynamic_Array)(array_); - - ok := true; - if array.cap <= array.len+1 { - cap := 2 * array.cap + max(8, 1); - ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); - } - // TODO(bill): Better error handling for failed reservation - if !ok { - return array.len; - } - - assert(array.data != nil); - data := uintptr(array.data) + uintptr(elem_size*array.len); - mem_zero(rawptr(data), elem_size); - array.len += 1; - return array.len; -} - - - - -// Map - -__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { - header := Map_Header{m = (^Raw_Map)(m)}; - Entry :: struct { - key: Map_Key, - next: int, - value: V, - }; - - header.is_key_string = intrinsics.type_is_string(K); - header.entry_size = int(size_of(Entry)); - header.entry_align = int(align_of(Entry)); - header.value_offset = uintptr(offset_of(Entry, value)); - header.value_size = int(size_of(V)); - return header; -} - -__get_map_key :: proc "contextless" (k: $K) -> Map_Key { - key := k; - map_key: Map_Key; - - T :: intrinsics.type_core_type(K); - - when intrinsics.type_is_integer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); } - else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); } - else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled integer size"); } - } else when intrinsics.type_is_rune(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64((^rune)(&key)^); - } else when intrinsics.type_is_pointer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64(uintptr((^rawptr)(&key)^)); - } else when intrinsics.type_is_float(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled float size"); } - } else when intrinsics.type_is_string(T) { - #assert(T == string); - str := (^string)(&key)^; - map_key.hash = default_hash_string(str); - map_key.key.str = str; - } else { - #panic("Unhandled map key type"); - } - - return map_key; -} - -_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { - h: u64 = seed; - for b in data { - h = (h ~ u64(b)) * 0x100000001b3; - } - return h; -} - - -default_hash :: inline proc "contextless" (data: []byte) -> u64 { - return _fnv64a(data); -} -default_hash_string :: inline proc "contextless" (s: string) -> u64 { - return default_hash(transmute([]byte)(s)); -} -default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 { - s := Raw_Slice{data, size}; - return default_hash(transmute([]byte)(s)); -} - - -source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { - hash := _fnv64a(transmute([]byte)s.file_path); - hash = hash ~ (u64(s.line) * 0x100000001b3); - hash = hash ~ (u64(s.column) * 0x100000001b3); - return hash; -} - - - -__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { - array := (^Raw_Slice)(array_); - - if new_count < array.len { - return true; - } - - assert(allocator.procedure != nil); - - old_size := array.len*size_of(T); - new_size := new_count*size_of(T); - - new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc); - if new_data == nil { - return false; - } - array.data = new_data; - array.len = new_count; - return true; -} - -__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) { - __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc); - - old_len := len(m.hashes); - __slice_resize(&m.hashes, cap, m.entries.allocator, loc); - for i in old_len.. rawptr { - index := __dynamic_map_find(h, key).entry_index; - if index >= 0 { - data := uintptr(__dynamic_map_get_entry(h, index)); - return rawptr(data + h.value_offset); - } - return nil; -} - -__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check { - index: int; - assert(value != nil); - - if len(h.m.hashes) == 0 { - __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc); - __dynamic_map_grow(h, loc); - } - - fr := __dynamic_map_find(h, key); - if fr.entry_index >= 0 { - index = fr.entry_index; - } else { - index = __dynamic_map_add_entry(h, key, loc); - if fr.entry_prev >= 0 { - entry := __dynamic_map_get_entry(h, fr.entry_prev); - entry.next = index; - } else { - h.m.hashes[fr.hash_index] = index; - } - } - { - e := __dynamic_map_get_entry(h, index); - e.key = key; - val := (^byte)(uintptr(e) + h.value_offset); - mem_copy(val, value, h.value_size); - } - - if __dynamic_map_full(h) { - __dynamic_map_grow(h, loc); - } -} - - -__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { - // TODO(bill): Determine an efficient growing rate - new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP); - __dynamic_map_rehash(h, new_count, loc); -} - -__dynamic_map_full :: inline proc(using h: Map_Header) -> bool { - return int(0.75 * f64(len(m.hashes))) <= m.entries.cap; -} - - -__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool { - if a.hash == b.hash { - if h.is_key_string { - return a.key.str == b.key.str; - } else { - return a.key.val == b.key.val; - } - return true; - } - return false; -} - -__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check { - fr := Map_Find_Result{-1, -1, -1}; - if n := u64(len(m.hashes)); n > 0 { - fr.hash_index = int(key.hash % n); - fr.entry_index = m.hashes[fr.hash_index]; - for fr.entry_index >= 0 { - entry := __dynamic_map_get_entry(h, fr.entry_index); - if __dynamic_map_hash_equal(h, entry.key, key) { - return fr; - } - fr.entry_prev = fr.entry_index; - fr.entry_index = entry.next; - } - } - return fr; -} - -__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int { - prev := m.entries.len; - c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); - if c != prev { - end := __dynamic_map_get_entry(h, c-1); - end.key = key; - end.next = -1; - } - return prev; -} - -__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) { - fr := __dynamic_map_find(h, key); - if fr.entry_index >= 0 { - __dynamic_map_erase(h, fr); - } -} - -__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { - assert(0 <= index && index < m.entries.len); - return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)); -} - -__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { - if fr.entry_prev < 0 { - m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next; - } else { - prev := __dynamic_map_get_entry(h, fr.entry_prev); - curr := __dynamic_map_get_entry(h, fr.entry_index); - prev.next = curr.next; - } - if (fr.entry_index == m.entries.len-1) { - // NOTE(bill): No need to do anything else, just pop - } else { - old := __dynamic_map_get_entry(h, fr.entry_index); - end := __dynamic_map_get_entry(h, m.entries.len-1); - mem_copy(old, end, entry_size); - - if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 { - last_entry := __dynamic_map_get_entry(h, last.entry_prev); - last_entry.next = fr.entry_index; - } else { - m.hashes[last.hash_index] = fr.entry_index; - } - } - - // TODO(bill): Is this correct behaviour? - m.entries.len -= 1; -} diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin new file mode 100644 index 000000000..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.. 0 { + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return; + } + + data := (^rawptr)(array)^; + + len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); + + + soa_offset := 0; + item_offset := 0; + + arg_copy := arg; + arg_ptr := &arg_copy; + + max_align := 0; + for i in 0.. 0 { + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return; + } + + data := (^rawptr)(array)^; + + len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); + + + soa_offset := 0; + item_offset := 0; + + args_ptr := &args[0]; + + max_align := 0; + for i in 0.. (ok: bool) #no_bounds_check { + if array == nil { + return; + } + n := len(array); + m :: 1; + resize(array, n+m, loc); + if n+m <= len(array) { + when size_of(E) != 0 { + copy(array[index+m:], array[index:]); + array[index] = arg; + } + ok = true; + } + return; +} + +@builtin +insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if array == nil { + return; + } + if len(args) == 0 { + ok = true; + return; + } + + n := len(array); + m := len(args); + resize(array, n+m, loc); + if n+m <= len(array) { + when size_of(E) != 0 { + copy(array[index+m:], array[index:]); + copy(array[index:], args); + } + ok = true; + } + return; +} + +@builtin +insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if array == nil { + return; + } + if len(args) == 0 { + ok = true; + return; + } + + n := len(array); + m := len(args); + resize(array, n+m, loc); + if n+m <= len(array) { + copy(array[index+m:], array[index:]); + copy(array[index:], args); + ok = true; + } + return; +} + +@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}; + + + + +@builtin +clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) { + if array != nil { + (^Raw_Dynamic_Array)(array).len = 0; + } +} + +@builtin +reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + a := (^Raw_Dynamic_Array)(array); + + if capacity <= a.cap { + return true; + } + + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } + assert(a.allocator.procedure != nil); + + old_size := a.cap * size_of(E); + new_size := capacity * size_of(E); + allocator := a.allocator; + + new_data := allocator.procedure( + allocator.data, .Resize, new_size, align_of(E), + a.data, old_size, 0, loc, + ); + if new_data == nil { + return false; + } + + a.data = new_data; + a.cap = capacity; + return true; +} + +@builtin +resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + a := (^Raw_Dynamic_Array)(array); + + if length <= a.cap { + a.len = max(length, 0); + return true; + } + + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } + assert(a.allocator.procedure != nil); + + old_size := a.cap * size_of(E); + new_size := length * size_of(E); + allocator := a.allocator; + + new_data := allocator.procedure( + allocator.data, .Resize, new_size, align_of(E), + a.data, old_size, 0, loc, + ); + if new_data == nil { + return false; + } + + a.data = new_data; + a.len = length; + a.cap = length; + return true; +} + + + +@builtin +incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { + s^ |= {elem}; + return s^; +} +@builtin +incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems { + s^ |= {elem}; + } + return s^; +} +@builtin +incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ |= other; + return s^; +} +@builtin +excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { + s^ &~= {elem}; + return s^; +} +@builtin +excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems { + s^ &~= {elem}; + } + return s^; +} +@builtin +excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ &~= other; + return s^; +} + +@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}; +@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}; + + +@builtin +card :: proc(s: $S/bit_set[$E; $U]) -> int { + when size_of(S) == 1 { + foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- } + return int(count_ones(transmute(u8)s)); + } else when size_of(S) == 2 { + foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- } + return int(count_ones(transmute(u16)s)); + } else when size_of(S) == 4 { + foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- } + return int(count_ones(transmute(u32)s)); + } else when size_of(S) == 8 { + foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- } + return int(count_ones(transmute(u64)s)); + } else when size_of(S) == 16 { + foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- } + return int(count_ones(transmute(u128)s)); + } else { + #panic("Unhandled card bit_set size"); + } +} + + + +@builtin +raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { + return (^E)(a); +} +@builtin +raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E { + ptr := (transmute(Raw_Slice)s).data; + return (^E)(ptr); +} +@builtin +raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E { + ptr := (transmute(Raw_Dynamic_Array)s).data; + return (^E)(ptr); +} +@builtin +raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 { + return (transmute(Raw_String)s).data; +} + +@builtin +raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}; + + + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +assert :: proc(condition: bool, message := "", loc := #caller_location) { + if !condition { + proc(message: string, loc: Source_Code_Location) { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("runtime assertion", message, loc); + }(message, loc); + } +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +panic :: proc(message: string, loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("panic", message, loc); +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +unimplemented :: proc(message := "", loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("not yet implemented", message, loc); +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +unreachable :: proc(message := "", loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + if message != "" { + p("internal error", message, loc); + } else { + p("internal error", "entered unreachable code", loc); + } +} diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin new file mode 100644 index 000000000..55289bbe4 --- /dev/null +++ b/core/runtime/dynamic_array_internal.odin @@ -0,0 +1,100 @@ +package runtime + +__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) { + array := (^Raw_Dynamic_Array)(array_); + array.allocator = context.allocator; + assert(array.allocator.procedure != nil); + + if cap > 0 { + __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc); + array.len = len; + } +} + +__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool { + array := (^Raw_Dynamic_Array)(array_); + + // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written + // assuming that appending/reserving will set the allocator, if it is not already set. + if array.allocator.procedure == nil { + array.allocator = context.allocator; + } + assert(array.allocator.procedure != nil); + + if cap <= array.cap { + return true; + } + + old_size := array.cap * elem_size; + new_size := cap * elem_size; + allocator := array.allocator; + + new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc); + if new_data != nil || elem_size == 0 { + array.data = new_data; + array.cap = cap; + return true; + } + return false; +} + +__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { + array := (^Raw_Dynamic_Array)(array_); + + ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc); + if ok { + array.len = len; + } + return ok; +} + + +__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, + items: rawptr, item_count: int, loc := #caller_location) -> int { + array := (^Raw_Dynamic_Array)(array_); + + if items == nil { + return 0; + } + if item_count <= 0 { + return 0; + } + + + ok := true; + if array.cap <= array.len+item_count { + cap := 2 * array.cap + max(8, item_count); + ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); + } + // TODO(bill): Better error handling for failed reservation + if !ok { + return array.len; + } + + assert(array.data != nil); + data := uintptr(array.data) + uintptr(elem_size*array.len); + + mem_copy(rawptr(data), items, elem_size * item_count); + array.len += item_count; + return array.len; +} + +__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int { + array := (^Raw_Dynamic_Array)(array_); + + ok := true; + if array.cap <= array.len+1 { + cap := 2 * array.cap + max(8, 1); + ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); + } + // TODO(bill): Better error handling for failed reservation + if !ok { + return array.len; + } + + assert(array.data != nil); + data := uintptr(array.data) + uintptr(elem_size*array.len); + mem_zero(rawptr(data), elem_size); + array.len += 1; + return array.len; +} diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin new file mode 100644 index 000000000..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.. uintptr { + h := u64(seed) + 0xcbf29ce484222325; + p := uintptr(data); + for _ in 0.. uintptr { return inline _default_hasher_const(data, seed, 1); } +default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); } +default_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.. 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; + 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; + 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); + 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); + 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); + // 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 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 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)); - } + // 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); - }; + 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; + 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; + 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); } - - s, n := utf8.encode_rune(r); - write_bytes(b, s[:n]); - return n; + n += _write_byte(w, quote); + return; } -write_string :: proc(b: ^Builder, s: string) -> (n: int) { - return write_bytes(b, transmute([]byte)s); + +write_string :: proc{ + write_string_builder, + write_string_writer, +}; + +write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) { + return write_string_writer(to_writer(b), s); } +write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) { + n, _ = io.write(w, transmute([]byte)s); + return; +} + + + + pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { return 0; @@ -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 ""; - } - - b := make_builder_len_cap(0, 0, allocator); - - s := s; - for c, i in s { - if c != utf8.RUNE_ERROR { - continue; - } - - _, w := utf8.decode_rune_in_string(s[i:]); - if w == 1 { - grow_builder(&b, len(s) + len(replacement)); - write_string(&b, s[:i]); - s = s[i:]; - break; - } - } - - if builder_cap(b) == 0 { - return clone(s, allocator); - } - - invalid := false; - - for i := 0; i < len(s); /**/ { - c := s[i]; - if c < utf8.RUNE_SELF { - i += 1; - invalid = false; - write_byte(&b, c); - continue; - } - - _, w := utf8.decode_rune_in_string(s[i:]); - if w == 1 { - i += 1; - if !invalid { - invalid = true; - write_string(&b, replacement); - } - continue; - } - invalid = false; - write_string(&b, s[i:][:w]); - i += w; - } - return to_string(b); -} - - @private -write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) { +write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { repeats := remains / pad_len; for i := 0; i < repeats; i += 1 { - write_string(b, pad); + io.write_string(w, pad); } n := remains % pad_len; p := pad; for i := 0; i < n; i += 1 { - r, w := utf8.decode_rune_in_string(p); - write_rune(b, r); - p = p[w:]; + r, width := utf8.decode_rune_in_string(p); + io.write_rune(w, r); + p = p[width:]; } } + + +// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space +// returning a slice of substrings of s or an empty slice if s only contains white space +fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check { + n := 0; + was_space := 1; + set_bits := u8(0); + + // check to see + for i in 0..= utf8.RUNE_SELF { + return fields_proc(s, unicode.is_space, allocator); + } + + if n == 0 { + return nil; + } + + a := make([]string, n, allocator); + na := 0; + field_start := 0; + i := 0; + for i < len(s) && _ascii_space[s[i]] != 0 { + i += 1; + } + field_start = i; + for i < len(s) { + if _ascii_space[s[i]] == 0 { + i += 1; + continue; + } + a[na] = s[field_start : i]; + na += 1; + i += 1; + for i < len(s) && _ascii_space[s[i]] != 0 { + i += 1; + } + field_start = i; + } + if field_start < len(s) { + a[na] = s[field_start:]; + } + return a; +} + + +// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch) +// returns a slice of substrings of s +// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned +// +// fields_proc makes no guarantee about the order in which it calls f(ch) +// it assumes that `f` always returns the same value for a given ch +fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { + substrings := make([dynamic]string, 0, 32, allocator); + + 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; + } + } + } + + 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_len :: proc(ch: $C/Channel($T)) -> int { - return ch._internal.len; +channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) { + res._internal = ch._internal; + return; } -channel_cap :: proc(ch: $C/Channel($T)) -> int { - return ch._internal.cap; + +channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) { + res._internal = ch._internal; + return; } -channel_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) { +channel_len :: proc(ch: $C/Channel($T, $D)) -> int { + return ch._internal.len if ch._internal != nil else 0; +} +channel_cap :: proc(ch: $C/Channel($T, $D)) -> int { + return ch._internal.cap if ch._internal != nil else 0; +} + + +channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both { msg := msg; _ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc); } -channel_try_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) -> bool { +channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both { msg := msg; return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc); } -channel_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T) { +channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both { c := ch._internal; + if c == nil { + panic(message="cannot recv message; channel is nil", loc=loc); + } mutex_lock(&c.mutex); raw_channel_recv_impl(c, &msg, loc); mutex_unlock(&c.mutex); return; } -channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T, ok: bool) { +channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both { c := ch._internal; - if mutex_try_lock(&c.mutex) { + if c != nil && mutex_try_lock(&c.mutex) { if c.len > 0 { raw_channel_recv_impl(c, &msg, loc); ok = true; @@ -64,7 +93,7 @@ channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T } return; } -channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_location) -> (ok: bool) { +channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both { res: T; res, ok = channel_try_recv(ch, loc); if ok && msg != nil { @@ -74,32 +103,32 @@ channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_locatio } -channel_is_nil :: proc(ch: $C/Channel($T)) -> bool { +channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool { return ch._internal == nil; } -channel_is_open :: proc(ch: $C/Channel($T)) -> bool { +channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool { c := ch._internal; return c != nil && !c.closed; } -channel_eq :: proc(a, b: $C/Channel($T)) -> bool { +channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool { return a._internal == b._internal; } -channel_ne :: proc(a, b: $C/Channel($T)) -> bool { +channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool { return a._internal != b._internal; } -channel_can_send :: proc(ch: $C/Channel($T)) -> (ok: bool) { +channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both { return raw_channel_can_send(ch._internal); } -channel_can_recv :: proc(ch: $C/Channel($T)) -> (ok: bool) { +channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both { return raw_channel_can_recv(ch._internal); } -channel_peek :: proc(ch: $C/Channel($T)) -> int { +channel_peek :: proc(ch: $C/Channel($T, $D)) -> int { c := ch._internal; if c == nil { return -1; @@ -111,12 +140,12 @@ channel_peek :: proc(ch: $C/Channel($T)) -> int { } -channel_close :: proc(ch: $C/Channel($T), loc := #caller_location) { +channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) { raw_channel_close(ch._internal, loc); } -channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) { +channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both { c := ch._internal; if c == nil { return; @@ -127,12 +156,12 @@ channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) { } return; } -channel_drain :: proc(ch: $C/Channel($T)) { +channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both { raw_channel_drain(ch._internal); } -channel_move :: proc(dst, src: $C/Channel($T)) { +channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both { for msg in channel_iterator(src) { channel_send(dst, msg); } @@ -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 = ""; + } + + context.allocator = allocator; + if position_is_valid(pos) { + return fmt.aprintf("%s(%d:%d)", s, pos.line, pos.column); + } else { + return strings.clone(s); + } +} + +EOF :: -1; +Ident :: -2; +Int :: -3; +Float :: -4; +Char :: -5; +String :: -6; +Raw_String :: -7; +Comment :: -8; + +Scan_Flag :: enum u32 { + Scan_Idents, + Scan_Ints, + Scan_C_Int_Prefixes, + Scan_Floats, + Scan_Chars, + Scan_Strings, + Scan_Raw_Strings, + Scan_Comments, + Skip_Comments, +} +Scan_Flags :: bit_set[Scan_Flag; u32]; + +Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; +C_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; + +Odin_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '; +C_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<'\v' | 1<<'\f' | 1<<' '; + + +Scanner :: struct { + src: string, + + src_pos: int, + src_end: int, + + tok_pos: int, + tok_end: int, + + ch: rune, + + line: int, + column: int, + prev_line_len: int, + prev_char_len: int, + + error: proc(s: ^Scanner, msg: string), + error_count: int, + + flags: Scan_Flags, + whitespace: u64, + + is_ident_rune: proc(ch: rune, i: int) -> bool, + + pos: Position, +} + +init :: proc(s: ^Scanner, src: string, filename := "") -> ^Scanner { + s^ = {}; + + s.src = src; + s.pos.filename = filename; + + s.tok_pos = -1; + + s.ch = -2; // no char read yet, not an EOF + + s.line = 1; + + s.flags = Odin_Like_Tokens; + s.whitespace = Odin_Whitespace; + + return s; +} + + +@(private) +advance :: proc(s: ^Scanner) -> rune { + if s.src_pos >= len(s.src) { + s.prev_char_len = 0; + return EOF; + } + ch, width := rune(s.src[s.src_pos]), 1; + + if ch >= utf8.RUNE_SELF { + ch, width = utf8.decode_rune_in_string(s.src[s.src_pos:]); + if ch == utf8.RUNE_ERROR && width == 1 { + s.src_pos += width; + s.prev_char_len = width; + s.column += 1; + error(s, "invalid UTF-8 encoding"); + return ch; + } + } + + s.src_pos += width; + s.prev_char_len = width; + s.column += 1; + + switch ch { + case 0: + error(s, "invalid character NUL"); + case '\n': + s.line += 1; + s.prev_line_len = s.column; + s.column = 0; + } + + return ch; +} + +next :: proc(s: ^Scanner) -> rune { + s.tok_pos = -1; + s.pos.line = 0; + ch := peek(s); + if ch != EOF { + s.ch = advance(s); + } + return ch; +} + +peek :: proc(s: ^Scanner) -> rune { + if s.ch == -2 { + s.ch = advance(s); + if s.ch == '\ufeff' { // Ignore BOM + s.ch = advance(s); + } + } + return s.ch; +} + + +error :: proc(s: ^Scanner, msg: string) { + s.error_count += 1; + if s.error != nil { + s.error(s, msg); + return; + } + p := s.pos; + if !position_is_valid(p) { + p = position(s); + } + + s := p.filename; + if s == "" { + s = ""; + } + + if position_is_valid(p) { + fmt.eprintf("%s(%d:%d): %s\n", s, p.line, p.column, msg); + } else { + fmt.eprintf("%s: %s\n", s, msg); + } +} + +errorf :: proc(s: ^Scanner, format: string, args: ..any) { + error(s, fmt.tprintf(format, ..args)); +} + +@(private) +is_ident_rune :: proc(s: ^Scanner, ch: rune, i: int) -> bool { + if s.is_ident_rune != nil { + return s.is_ident_rune(ch, i); + } + return ch == '_' || unicode.is_letter(ch) || unicode.is_digit(ch) && i > 0; +} + +@(private) +scan_identifier :: proc(s: ^Scanner) -> rune { + ch := advance(s); + for i := 1; is_ident_rune(s, ch, i); i += 1 { + ch = advance(s); + } + return ch; +} + +@(private) lower :: proc(ch: rune) -> rune { return ('a' - 'A') | ch; } +@(private) is_decimal :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9'; } +@(private) is_hex :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f'; } + + + +@(private) +scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) { + lit_name :: proc(prefix: rune) -> string { + switch prefix { + case 'b': return "binary literal"; + case 'o': return "octal literal"; + case 'z': return "dozenal literal"; + case 'x': return "hexadecimal literal"; + } + return "decimal literal"; + } + + digits :: proc(s: ^Scanner, ch0: rune, base: int, invalid: ^rune) -> (ch: rune, digsep: int) { + ch = ch0; + if base <= 10 { + max := rune('0' + base); + for is_decimal(ch) || ch == '_' { + ds := 1; + if ch == '_' { + ds = 2; + } else if ch >= max && invalid^ == 0 { + invalid^ = ch; + } + digsep |= ds; + ch = advance(s); + } + } else { + for is_hex(ch) || ch == '_' { + ds := 1; + if ch == '_' { + ds = 2; + } + digsep |= ds; + ch = advance(s); + } + } + return; + } + + ch, seen_dot := ch, seen_dot; + + base := 10; + prefix := rune(0); + digsep := 0; + invalid := rune(0); + + tok: rune; + ds: int; + + if !seen_dot { + tok = Int; + 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< 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, -}; +is_graphic :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pg != 0; + } + return false; +} -@(static) -space_ranges := [?]i32{ - 0x0009, 0x000d, // tab and newline - 0x0020, 0x0020, // space - 0x0085, 0x0085, // next line - 0x00a0, 0x00a0, - 0x1680, 0x1680, // Ogham space mark - 0x2000, 0x200b, // en dash .. zero-width space - 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) - 0x2028, 0x2029, 0x3000, 0x3000, - 0x202f, 0x202f, // narrow no-break space - 0x205f, 0x205f, // medium mathematical space - 0x3000, 0x3000, // ideographic space - 0xfeff, 0xfeff, -}; +is_print :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pp != 0; + } + return false; +} -@(static) -unicode_spaces := [?]i32{ - 0x0009, // tab - 0x000a, // LF - 0x000d, // CR - 0x0020, // space - 0x0085, // next line - 0x00a0, // unknown - 0x1680, // Ogham space mark - 0x2000, // en dash .. zero-width space - 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) - 0x2028, 0x2029, 0x3000, 0x3000, - 0x202f, // narrow no-break space - 0x205f, // medium mathematical space - 0x3000, // ideographic space - 0xfeff, // unknown -}; +is_control :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pC != 0; + } + return false; +} -@(static) -to_upper_ranges := [?]i32{ - 0x0061, 0x007a, 468, // a-z A-Z - 0x00e0, 0x00f6, 468, - 0x00f8, 0x00fe, 468, - 0x0256, 0x0257, 295, - 0x0258, 0x0259, 298, - 0x028a, 0x028b, 283, - 0x03ad, 0x03af, 463, - 0x03b1, 0x03c1, 468, - 0x03c3, 0x03cb, 468, - 0x03cd, 0x03ce, 437, - 0x0430, 0x044f, 468, - 0x0451, 0x045c, 420, - 0x045e, 0x045f, 420, - 0x0561, 0x0586, 452, - 0x1f00, 0x1f07, 508, - 0x1f10, 0x1f15, 508, - 0x1f20, 0x1f27, 508, - 0x1f30, 0x1f37, 508, - 0x1f40, 0x1f45, 508, - 0x1f60, 0x1f67, 508, - 0x1f70, 0x1f71, 574, - 0x1f72, 0x1f75, 586, - 0x1f76, 0x1f77, 600, - 0x1f78, 0x1f79, 628, - 0x1f7a, 0x1f7b, 612, - 0x1f7c, 0x1f7d, 626, - 0x1f80, 0x1f87, 508, - 0x1f90, 0x1f97, 508, - 0x1fa0, 0x1fa7, 508, - 0x1fb0, 0x1fb1, 508, - 0x1fd0, 0x1fd1, 508, - 0x1fe0, 0x1fe1, 508, - 0x2170, 0x217f, 484, - 0x24d0, 0x24e9, 474, - 0xff41, 0xff5a, 468, -}; +is_number :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pN != 0; + } + return false; +} -@(static) -to_upper_singlets := [?]i32{ - 0x00ff, 621, - 0x0101, 499, - 0x0103, 499, - 0x0105, 499, - 0x0107, 499, - 0x0109, 499, - 0x010b, 499, - 0x010d, 499, - 0x010f, 499, - 0x0111, 499, - 0x0113, 499, - 0x0115, 499, - 0x0117, 499, - 0x0119, 499, - 0x011b, 499, - 0x011d, 499, - 0x011f, 499, - 0x0121, 499, - 0x0123, 499, - 0x0125, 499, - 0x0127, 499, - 0x0129, 499, - 0x012b, 499, - 0x012d, 499, - 0x012f, 499, - 0x0131, 268, // I - 0x0133, 499, - 0x0135, 499, - 0x0137, 499, - 0x013a, 499, - 0x013c, 499, - 0x013e, 499, - 0x0140, 499, - 0x0142, 499, - 0x0144, 499, - 0x0146, 499, - 0x0148, 499, - 0x014b, 499, - 0x014d, 499, - 0x014f, 499, - 0x0151, 499, - 0x0153, 499, - 0x0155, 499, - 0x0157, 499, - 0x0159, 499, - 0x015b, 499, - 0x015d, 499, - 0x015f, 499, - 0x0161, 499, - 0x0163, 499, - 0x0165, 499, - 0x0167, 499, - 0x0169, 499, - 0x016b, 499, - 0x016d, 499, - 0x016f, 499, - 0x0171, 499, - 0x0173, 499, - 0x0175, 499, - 0x0177, 499, - 0x017a, 499, - 0x017c, 499, - 0x017e, 499, - 0x017f, 200, // S - 0x0183, 499, - 0x0185, 499, - 0x0188, 499, - 0x018c, 499, - 0x0192, 499, - 0x0199, 499, - 0x01a1, 499, - 0x01a3, 499, - 0x01a5, 499, - 0x01a8, 499, - 0x01ad, 499, - 0x01b0, 499, - 0x01b4, 499, - 0x01b6, 499, - 0x01b9, 499, - 0x01bd, 499, - 0x01c5, 499, - 0x01c6, 498, - 0x01c8, 499, - 0x01c9, 498, - 0x01cb, 499, - 0x01cc, 498, - 0x01ce, 499, - 0x01d0, 499, - 0x01d2, 499, - 0x01d4, 499, - 0x01d6, 499, - 0x01d8, 499, - 0x01da, 499, - 0x01dc, 499, - 0x01df, 499, - 0x01e1, 499, - 0x01e3, 499, - 0x01e5, 499, - 0x01e7, 499, - 0x01e9, 499, - 0x01eb, 499, - 0x01ed, 499, - 0x01ef, 499, - 0x01f2, 499, - 0x01f3, 498, - 0x01f5, 499, - 0x01fb, 499, - 0x01fd, 499, - 0x01ff, 499, - 0x0201, 499, - 0x0203, 499, - 0x0205, 499, - 0x0207, 499, - 0x0209, 499, - 0x020b, 499, - 0x020d, 499, - 0x020f, 499, - 0x0211, 499, - 0x0213, 499, - 0x0215, 499, - 0x0217, 499, - 0x0253, 290, - 0x0254, 294, - 0x025b, 297, - 0x0260, 295, - 0x0263, 293, - 0x0268, 291, - 0x0269, 289, - 0x026f, 289, - 0x0272, 287, - 0x0283, 282, - 0x0288, 282, - 0x0292, 281, - 0x03ac, 462, - 0x03cc, 436, - 0x03d0, 438, - 0x03d1, 443, - 0x03d5, 453, - 0x03d6, 446, - 0x03e3, 499, - 0x03e5, 499, - 0x03e7, 499, - 0x03e9, 499, - 0x03eb, 499, - 0x03ed, 499, - 0x03ef, 499, - 0x03f0, 414, - 0x03f1, 420, - 0x0461, 499, - 0x0463, 499, - 0x0465, 499, - 0x0467, 499, - 0x0469, 499, - 0x046b, 499, - 0x046d, 499, - 0x046f, 499, - 0x0471, 499, - 0x0473, 499, - 0x0475, 499, - 0x0477, 499, - 0x0479, 499, - 0x047b, 499, - 0x047d, 499, - 0x047f, 499, - 0x0481, 499, - 0x0491, 499, - 0x0493, 499, - 0x0495, 499, - 0x0497, 499, - 0x0499, 499, - 0x049b, 499, - 0x049d, 499, - 0x049f, 499, - 0x04a1, 499, - 0x04a3, 499, - 0x04a5, 499, - 0x04a7, 499, - 0x04a9, 499, - 0x04ab, 499, - 0x04ad, 499, - 0x04af, 499, - 0x04b1, 499, - 0x04b3, 499, - 0x04b5, 499, - 0x04b7, 499, - 0x04b9, 499, - 0x04bb, 499, - 0x04bd, 499, - 0x04bf, 499, - 0x04c2, 499, - 0x04c4, 499, - 0x04c8, 499, - 0x04cc, 499, - 0x04d1, 499, - 0x04d3, 499, - 0x04d5, 499, - 0x04d7, 499, - 0x04d9, 499, - 0x04db, 499, - 0x04dd, 499, - 0x04df, 499, - 0x04e1, 499, - 0x04e3, 499, - 0x04e5, 499, - 0x04e7, 499, - 0x04e9, 499, - 0x04eb, 499, - 0x04ef, 499, - 0x04f1, 499, - 0x04f3, 499, - 0x04f5, 499, - 0x04f9, 499, - 0x1e01, 499, - 0x1e03, 499, - 0x1e05, 499, - 0x1e07, 499, - 0x1e09, 499, - 0x1e0b, 499, - 0x1e0d, 499, - 0x1e0f, 499, - 0x1e11, 499, - 0x1e13, 499, - 0x1e15, 499, - 0x1e17, 499, - 0x1e19, 499, - 0x1e1b, 499, - 0x1e1d, 499, - 0x1e1f, 499, - 0x1e21, 499, - 0x1e23, 499, - 0x1e25, 499, - 0x1e27, 499, - 0x1e29, 499, - 0x1e2b, 499, - 0x1e2d, 499, - 0x1e2f, 499, - 0x1e31, 499, - 0x1e33, 499, - 0x1e35, 499, - 0x1e37, 499, - 0x1e39, 499, - 0x1e3b, 499, - 0x1e3d, 499, - 0x1e3f, 499, - 0x1e41, 499, - 0x1e43, 499, - 0x1e45, 499, - 0x1e47, 499, - 0x1e49, 499, - 0x1e4b, 499, - 0x1e4d, 499, - 0x1e4f, 499, - 0x1e51, 499, - 0x1e53, 499, - 0x1e55, 499, - 0x1e57, 499, - 0x1e59, 499, - 0x1e5b, 499, - 0x1e5d, 499, - 0x1e5f, 499, - 0x1e61, 499, - 0x1e63, 499, - 0x1e65, 499, - 0x1e67, 499, - 0x1e69, 499, - 0x1e6b, 499, - 0x1e6d, 499, - 0x1e6f, 499, - 0x1e71, 499, - 0x1e73, 499, - 0x1e75, 499, - 0x1e77, 499, - 0x1e79, 499, - 0x1e7b, 499, - 0x1e7d, 499, - 0x1e7f, 499, - 0x1e81, 499, - 0x1e83, 499, - 0x1e85, 499, - 0x1e87, 499, - 0x1e89, 499, - 0x1e8b, 499, - 0x1e8d, 499, - 0x1e8f, 499, - 0x1e91, 499, - 0x1e93, 499, - 0x1e95, 499, - 0x1ea1, 499, - 0x1ea3, 499, - 0x1ea5, 499, - 0x1ea7, 499, - 0x1ea9, 499, - 0x1eab, 499, - 0x1ead, 499, - 0x1eaf, 499, - 0x1eb1, 499, - 0x1eb3, 499, - 0x1eb5, 499, - 0x1eb7, 499, - 0x1eb9, 499, - 0x1ebb, 499, - 0x1ebd, 499, - 0x1ebf, 499, - 0x1ec1, 499, - 0x1ec3, 499, - 0x1ec5, 499, - 0x1ec7, 499, - 0x1ec9, 499, - 0x1ecb, 499, - 0x1ecd, 499, - 0x1ecf, 499, - 0x1ed1, 499, - 0x1ed3, 499, - 0x1ed5, 499, - 0x1ed7, 499, - 0x1ed9, 499, - 0x1edb, 499, - 0x1edd, 499, - 0x1edf, 499, - 0x1ee1, 499, - 0x1ee3, 499, - 0x1ee5, 499, - 0x1ee7, 499, - 0x1ee9, 499, - 0x1eeb, 499, - 0x1eed, 499, - 0x1eef, 499, - 0x1ef1, 499, - 0x1ef3, 499, - 0x1ef5, 499, - 0x1ef7, 499, - 0x1ef9, 499, - 0x1f51, 508, - 0x1f53, 508, - 0x1f55, 508, - 0x1f57, 508, - 0x1fb3, 509, - 0x1fc3, 509, - 0x1fe5, 507, - 0x1ff3, 509, -}; +is_punct :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pP != 0; + } + return false; +} -@(static) -to_lower_ranges := [?]i32{ - 0x0041, 0x005a, 532, // A-Z a-z - 0x00c0, 0x00d6, 532, // - - - 0x00d8, 0x00de, 532, // - - - 0x0189, 0x018a, 705, // - - - 0x018e, 0x018f, 702, // - - - 0x01b1, 0x01b2, 717, // - - - 0x0388, 0x038a, 537, // - - - 0x038e, 0x038f, 563, // - - - 0x0391, 0x03a1, 532, // - - - 0x03a3, 0x03ab, 532, // - - - 0x0401, 0x040c, 580, // - - - 0x040e, 0x040f, 580, // - - - 0x0410, 0x042f, 532, // - - - 0x0531, 0x0556, 548, // - - - 0x10a0, 0x10c5, 548, // - - - 0x1f08, 0x1f0f, 492, // - - - 0x1f18, 0x1f1d, 492, // - - - 0x1f28, 0x1f2f, 492, // - - - 0x1f38, 0x1f3f, 492, // - - - 0x1f48, 0x1f4d, 492, // - - - 0x1f68, 0x1f6f, 492, // - - - 0x1f88, 0x1f8f, 492, // - - - 0x1f98, 0x1f9f, 492, // - - - 0x1fa8, 0x1faf, 492, // - - - 0x1fb8, 0x1fb9, 492, // - - - 0x1fba, 0x1fbb, 426, // - - - 0x1fc8, 0x1fcb, 414, // - - - 0x1fd8, 0x1fd9, 492, // - - - 0x1fda, 0x1fdb, 400, // - - - 0x1fe8, 0x1fe9, 492, // - - - 0x1fea, 0x1feb, 388, // - - - 0x1ff8, 0x1ff9, 372, // - - - 0x1ffa, 0x1ffb, 374, // - - - 0x2160, 0x216f, 516, // - - - 0x24b6, 0x24cf, 526, // - - - 0xff21, 0xff3a, 532, // - - -}; - -@(static) -to_lower_singlets := [?]i32{ - 0x0100, 501, - 0x0102, 501, - 0x0104, 501, - 0x0106, 501, - 0x0108, 501, - 0x010a, 501, - 0x010c, 501, - 0x010e, 501, - 0x0110, 501, - 0x0112, 501, - 0x0114, 501, - 0x0116, 501, - 0x0118, 501, - 0x011a, 501, - 0x011c, 501, - 0x011e, 501, - 0x0120, 501, - 0x0122, 501, - 0x0124, 501, - 0x0126, 501, - 0x0128, 501, - 0x012a, 501, - 0x012c, 501, - 0x012e, 501, - 0x0130, 301, // i - 0x0132, 501, - 0x0134, 501, - 0x0136, 501, - 0x0139, 501, - 0x013b, 501, - 0x013d, 501, - 0x013f, 501, - 0x0141, 501, - 0x0143, 501, - 0x0145, 501, - 0x0147, 501, - 0x014a, 501, - 0x014c, 501, - 0x014e, 501, - 0x0150, 501, - 0x0152, 501, - 0x0154, 501, - 0x0156, 501, - 0x0158, 501, - 0x015a, 501, - 0x015c, 501, - 0x015e, 501, - 0x0160, 501, - 0x0162, 501, - 0x0164, 501, - 0x0166, 501, - 0x0168, 501, - 0x016a, 501, - 0x016c, 501, - 0x016e, 501, - 0x0170, 501, - 0x0172, 501, - 0x0174, 501, - 0x0176, 501, - 0x0178, 379, - 0x0179, 501, - 0x017b, 501, - 0x017d, 501, - 0x0181, 710, - 0x0182, 501, - 0x0184, 501, - 0x0186, 706, - 0x0187, 501, - 0x018b, 501, - 0x0190, 703, - 0x0191, 501, - 0x0193, 705, - 0x0194, 707, - 0x0196, 711, - 0x0197, 709, - 0x0198, 501, - 0x019c, 711, - 0x019d, 713, - 0x01a0, 501, - 0x01a2, 501, - 0x01a4, 501, - 0x01a7, 501, - 0x01a9, 718, - 0x01ac, 501, - 0x01ae, 718, - 0x01af, 501, - 0x01b3, 501, - 0x01b5, 501, - 0x01b7, 719, - 0x01b8, 501, - 0x01bc, 501, - 0x01c4, 502, - 0x01c5, 501, - 0x01c7, 502, - 0x01c8, 501, - 0x01ca, 502, - 0x01cb, 501, - 0x01cd, 501, - 0x01cf, 501, - 0x01d1, 501, - 0x01d3, 501, - 0x01d5, 501, - 0x01d7, 501, - 0x01d9, 501, - 0x01db, 501, - 0x01de, 501, - 0x01e0, 501, - 0x01e2, 501, - 0x01e4, 501, - 0x01e6, 501, - 0x01e8, 501, - 0x01ea, 501, - 0x01ec, 501, - 0x01ee, 501, - 0x01f1, 502, - 0x01f2, 501, - 0x01f4, 501, - 0x01fa, 501, - 0x01fc, 501, - 0x01fe, 501, - 0x0200, 501, - 0x0202, 501, - 0x0204, 501, - 0x0206, 501, - 0x0208, 501, - 0x020a, 501, - 0x020c, 501, - 0x020e, 501, - 0x0210, 501, - 0x0212, 501, - 0x0214, 501, - 0x0216, 501, - 0x0386, 538, - 0x038c, 564, - 0x03e2, 501, - 0x03e4, 501, - 0x03e6, 501, - 0x03e8, 501, - 0x03ea, 501, - 0x03ec, 501, - 0x03ee, 501, - 0x0460, 501, - 0x0462, 501, - 0x0464, 501, - 0x0466, 501, - 0x0468, 501, - 0x046a, 501, - 0x046c, 501, - 0x046e, 501, - 0x0470, 501, - 0x0472, 501, - 0x0474, 501, - 0x0476, 501, - 0x0478, 501, - 0x047a, 501, - 0x047c, 501, - 0x047e, 501, - 0x0480, 501, - 0x0490, 501, - 0x0492, 501, - 0x0494, 501, - 0x0496, 501, - 0x0498, 501, - 0x049a, 501, - 0x049c, 501, - 0x049e, 501, - 0x04a0, 501, - 0x04a2, 501, - 0x04a4, 501, - 0x04a6, 501, - 0x04a8, 501, - 0x04aa, 501, - 0x04ac, 501, - 0x04ae, 501, - 0x04b0, 501, - 0x04b2, 501, - 0x04b4, 501, - 0x04b6, 501, - 0x04b8, 501, - 0x04ba, 501, - 0x04bc, 501, - 0x04be, 501, - 0x04c1, 501, - 0x04c3, 501, - 0x04c7, 501, - 0x04cb, 501, - 0x04d0, 501, - 0x04d2, 501, - 0x04d4, 501, - 0x04d6, 501, - 0x04d8, 501, - 0x04da, 501, - 0x04dc, 501, - 0x04de, 501, - 0x04e0, 501, - 0x04e2, 501, - 0x04e4, 501, - 0x04e6, 501, - 0x04e8, 501, - 0x04ea, 501, - 0x04ee, 501, - 0x04f0, 501, - 0x04f2, 501, - 0x04f4, 501, - 0x04f8, 501, - 0x1e00, 501, - 0x1e02, 501, - 0x1e04, 501, - 0x1e06, 501, - 0x1e08, 501, - 0x1e0a, 501, - 0x1e0c, 501, - 0x1e0e, 501, - 0x1e10, 501, - 0x1e12, 501, - 0x1e14, 501, - 0x1e16, 501, - 0x1e18, 501, - 0x1e1a, 501, - 0x1e1c, 501, - 0x1e1e, 501, - 0x1e20, 501, - 0x1e22, 501, - 0x1e24, 501, - 0x1e26, 501, - 0x1e28, 501, - 0x1e2a, 501, - 0x1e2c, 501, - 0x1e2e, 501, - 0x1e30, 501, - 0x1e32, 501, - 0x1e34, 501, - 0x1e36, 501, - 0x1e38, 501, - 0x1e3a, 501, - 0x1e3c, 501, - 0x1e3e, 501, - 0x1e40, 501, - 0x1e42, 501, - 0x1e44, 501, - 0x1e46, 501, - 0x1e48, 501, - 0x1e4a, 501, - 0x1e4c, 501, - 0x1e4e, 501, - 0x1e50, 501, - 0x1e52, 501, - 0x1e54, 501, - 0x1e56, 501, - 0x1e58, 501, - 0x1e5a, 501, - 0x1e5c, 501, - 0x1e5e, 501, - 0x1e60, 501, - 0x1e62, 501, - 0x1e64, 501, - 0x1e66, 501, - 0x1e68, 501, - 0x1e6a, 501, - 0x1e6c, 501, - 0x1e6e, 501, - 0x1e70, 501, - 0x1e72, 501, - 0x1e74, 501, - 0x1e76, 501, - 0x1e78, 501, - 0x1e7a, 501, - 0x1e7c, 501, - 0x1e7e, 501, - 0x1e80, 501, - 0x1e82, 501, - 0x1e84, 501, - 0x1e86, 501, - 0x1e88, 501, - 0x1e8a, 501, - 0x1e8c, 501, - 0x1e8e, 501, - 0x1e90, 501, - 0x1e92, 501, - 0x1e94, 501, - 0x1ea0, 501, - 0x1ea2, 501, - 0x1ea4, 501, - 0x1ea6, 501, - 0x1ea8, 501, - 0x1eaa, 501, - 0x1eac, 501, - 0x1eae, 501, - 0x1eb0, 501, - 0x1eb2, 501, - 0x1eb4, 501, - 0x1eb6, 501, - 0x1eb8, 501, - 0x1eba, 501, - 0x1ebc, 501, - 0x1ebe, 501, - 0x1ec0, 501, - 0x1ec2, 501, - 0x1ec4, 501, - 0x1ec6, 501, - 0x1ec8, 501, - 0x1eca, 501, - 0x1ecc, 501, - 0x1ece, 501, - 0x1ed0, 501, - 0x1ed2, 501, - 0x1ed4, 501, - 0x1ed6, 501, - 0x1ed8, 501, - 0x1eda, 501, - 0x1edc, 501, - 0x1ede, 501, - 0x1ee0, 501, - 0x1ee2, 501, - 0x1ee4, 501, - 0x1ee6, 501, - 0x1ee8, 501, - 0x1eea, 501, - 0x1eec, 501, - 0x1eee, 501, - 0x1ef0, 501, - 0x1ef2, 501, - 0x1ef4, 501, - 0x1ef6, 501, - 0x1ef8, 501, - 0x1f59, 492, - 0x1f5b, 492, - 0x1f5d, 492, - 0x1f5f, 492, - 0x1fbc, 491, - 0x1fcc, 491, - 0x1fec, 493, - 0x1ffc, 491, -}; - -@(static) -to_title_singlets := [?]i32{ - 0x01c4, 501, - 0x01c6, 499, - 0x01c7, 501, - 0x01c9, 499, - 0x01ca, 501, - 0x01cc, 499, - 0x01f1, 501, - 0x01f3, 499, -}; +is_symbol :: proc(r: rune) -> bool { + if u32(r) <= MAX_LATIN1 { + return char_properties[u8(r)]&pS != 0; + } + return false; +} diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin new file mode 100644 index 000000000..bb858fd04 --- /dev/null +++ b/core/unicode/tables.odin @@ -0,0 +1,1272 @@ +package unicode + +@(private) pC :: 1<<0; // a control character. +@(private) pP :: 1<<1; // a punctuation character. +@(private) pN :: 1<<2; // a numeral. +@(private) pS :: 1<<3; // a symbolic character. +@(private) pZ :: 1<<4; // a spacing character. +@(private) pLu :: 1<<5; // an upper-case letter. +@(private) pLl :: 1<<6; // a lower-case letter. +@(private) pp :: 1<<7; // a printable character according to Go's definition. +@(private) pg :: pp | pZ; // a graphical character according to the Unicode definition. +@(private) pLo :: pLl | pLu; // a letter that is neither upper nor lower case. +@(private) pLmask :: pLo; + +@(static) +char_properties := [MAX_LATIN1+1]u8{ + 0x00 = pC, // '\x00' + 0x01 = pC, // '\x01' + 0x02 = pC, // '\x02' + 0x03 = pC, // '\x03' + 0x04 = pC, // '\x04' + 0x05 = pC, // '\x05' + 0x06 = pC, // '\x06' + 0x07 = pC, // '\a' + 0x08 = pC, // '\b' + 0x09 = pC, // '\t' + 0x0A = pC, // '\n' + 0x0B = pC, // '\v' + 0x0C = pC, // '\f' + 0x0D = pC, // '\r' + 0x0E = pC, // '\x0e' + 0x0F = pC, // '\x0f' + 0x10 = pC, // '\x10' + 0x11 = pC, // '\x11' + 0x12 = pC, // '\x12' + 0x13 = pC, // '\x13' + 0x14 = pC, // '\x14' + 0x15 = pC, // '\x15' + 0x16 = pC, // '\x16' + 0x17 = pC, // '\x17' + 0x18 = pC, // '\x18' + 0x19 = pC, // '\x19' + 0x1A = pC, // '\x1a' + 0x1B = pC, // '\x1b' + 0x1C = pC, // '\x1c' + 0x1D = pC, // '\x1d' + 0x1E = pC, // '\x1e' + 0x1F = pC, // '\x1f' + 0x20 = pZ | pp, // ' ' + 0x21 = pP | pp, // '!' + 0x22 = pP | pp, // '"' + 0x23 = pP | pp, // '#' + 0x24 = pS | pp, // '$' + 0x25 = pP | pp, // '%' + 0x26 = pP | pp, // '&' + 0x27 = pP | pp, // '\'' + 0x28 = pP | pp, // '(' + 0x29 = pP | pp, // ')' + 0x2A = pP | pp, // '*' + 0x2B = pS | pp, // '+' + 0x2C = pP | pp, // ',' + 0x2D = pP | pp, // '-' + 0x2E = pP | pp, // '.' + 0x2F = pP | pp, // '/' + 0x30 = pN | pp, // '0' + 0x31 = pN | pp, // '1' + 0x32 = pN | pp, // '2' + 0x33 = pN | pp, // '3' + 0x34 = pN | pp, // '4' + 0x35 = pN | pp, // '5' + 0x36 = pN | pp, // '6' + 0x37 = pN | pp, // '7' + 0x38 = pN | pp, // '8' + 0x39 = pN | pp, // '9' + 0x3A = pP | pp, // ':' + 0x3B = pP | pp, // ';' + 0x3C = pS | pp, // '<' + 0x3D = pS | pp, // '=' + 0x3E = pS | pp, // '>' + 0x3F = pP | pp, // '?' + 0x40 = pP | pp, // '@' + 0x41 = pLu | pp, // 'A' + 0x42 = pLu | pp, // 'B' + 0x43 = pLu | pp, // 'C' + 0x44 = pLu | pp, // 'D' + 0x45 = pLu | pp, // 'E' + 0x46 = pLu | pp, // 'F' + 0x47 = pLu | pp, // 'G' + 0x48 = pLu | pp, // 'H' + 0x49 = pLu | pp, // 'I' + 0x4A = pLu | pp, // 'J' + 0x4B = pLu | pp, // 'K' + 0x4C = pLu | pp, // 'L' + 0x4D = pLu | pp, // 'M' + 0x4E = pLu | pp, // 'N' + 0x4F = pLu | pp, // 'O' + 0x50 = pLu | pp, // 'P' + 0x51 = pLu | pp, // 'Q' + 0x52 = pLu | pp, // 'R' + 0x53 = pLu | pp, // 'S' + 0x54 = pLu | pp, // 'T' + 0x55 = pLu | pp, // 'U' + 0x56 = pLu | pp, // 'V' + 0x57 = pLu | pp, // 'W' + 0x58 = pLu | pp, // 'X' + 0x59 = pLu | pp, // 'Y' + 0x5A = pLu | pp, // 'Z' + 0x5B = pP | pp, // '[' + 0x5C = pP | pp, // '\\' + 0x5D = pP | pp, // ']' + 0x5E = pS | pp, // '^' + 0x5F = pP | pp, // '_' + 0x60 = pS | pp, // '`' + 0x61 = pLl | pp, // 'a' + 0x62 = pLl | pp, // 'b' + 0x63 = pLl | pp, // 'c' + 0x64 = pLl | pp, // 'd' + 0x65 = pLl | pp, // 'e' + 0x66 = pLl | pp, // 'f' + 0x67 = pLl | pp, // 'g' + 0x68 = pLl | pp, // 'h' + 0x69 = pLl | pp, // 'i' + 0x6A = pLl | pp, // 'j' + 0x6B = pLl | pp, // 'k' + 0x6C = pLl | pp, // 'l' + 0x6D = pLl | pp, // 'm' + 0x6E = pLl | pp, // 'n' + 0x6F = pLl | pp, // 'o' + 0x70 = pLl | pp, // 'p' + 0x71 = pLl | pp, // 'q' + 0x72 = pLl | pp, // 'r' + 0x73 = pLl | pp, // 's' + 0x74 = pLl | pp, // 't' + 0x75 = pLl | pp, // 'u' + 0x76 = pLl | pp, // 'v' + 0x77 = pLl | pp, // 'w' + 0x78 = pLl | pp, // 'x' + 0x79 = pLl | pp, // 'y' + 0x7A = pLl | pp, // 'z' + 0x7B = pP | pp, // '{' + 0x7C = pS | pp, // '|' + 0x7D = pP | pp, // '}' + 0x7E = pS | pp, // '~' + 0x7F = pC, // '\u007f' + 0x80 = pC, // '\u0080' + 0x81 = pC, // '\u0081' + 0x82 = pC, // '\u0082' + 0x83 = pC, // '\u0083' + 0x84 = pC, // '\u0084' + 0x85 = pC, // '\u0085' + 0x86 = pC, // '\u0086' + 0x87 = pC, // '\u0087' + 0x88 = pC, // '\u0088' + 0x89 = pC, // '\u0089' + 0x8A = pC, // '\u008a' + 0x8B = pC, // '\u008b' + 0x8C = pC, // '\u008c' + 0x8D = pC, // '\u008d' + 0x8E = pC, // '\u008e' + 0x8F = pC, // '\u008f' + 0x90 = pC, // '\u0090' + 0x91 = pC, // '\u0091' + 0x92 = pC, // '\u0092' + 0x93 = pC, // '\u0093' + 0x94 = pC, // '\u0094' + 0x95 = pC, // '\u0095' + 0x96 = pC, // '\u0096' + 0x97 = pC, // '\u0097' + 0x98 = pC, // '\u0098' + 0x99 = pC, // '\u0099' + 0x9A = pC, // '\u009a' + 0x9B = pC, // '\u009b' + 0x9C = pC, // '\u009c' + 0x9D = pC, // '\u009d' + 0x9E = pC, // '\u009e' + 0x9F = pC, // '\u009f' + 0xA0 = pZ, // '\u00a0' + 0xA1 = pP | pp, // '¡' + 0xA2 = pS | pp, // '¢' + 0xA3 = pS | pp, // '£' + 0xA4 = pS | pp, // '¤' + 0xA5 = pS | pp, // '¥' + 0xA6 = pS | pp, // '¦' + 0xA7 = pP | pp, // '§' + 0xA8 = pS | pp, // '¨' + 0xA9 = pS | pp, // '©' + 0xAA = pLo | pp, // 'ª' + 0xAB = pP | pp, // '«' + 0xAC = pS | pp, // '¬' + 0xAD = 0, // '\u00ad' + 0xAE = pS | pp, // '®' + 0xAF = pS | pp, // '¯' + 0xB0 = pS | pp, // '°' + 0xB1 = pS | pp, // '±' + 0xB2 = pN | pp, // '²' + 0xB3 = pN | pp, // '³' + 0xB4 = pS | pp, // '´' + 0xB5 = pLl | pp, // 'µ' + 0xB6 = pP | pp, // '¶' + 0xB7 = pP | pp, // '·' + 0xB8 = pS | pp, // '¸' + 0xB9 = pN | pp, // '¹' + 0xBA = pLo | pp, // 'º' + 0xBB = pP | pp, // '»' + 0xBC = pN | pp, // '¼' + 0xBD = pN | pp, // '½' + 0xBE = pN | pp, // '¾' + 0xBF = pP | pp, // '¿' + 0xC0 = pLu | pp, // 'À' + 0xC1 = pLu | pp, // 'Á' + 0xC2 = pLu | pp, // 'Â' + 0xC3 = pLu | pp, // 'Ã' + 0xC4 = pLu | pp, // 'Ä' + 0xC5 = pLu | pp, // 'Å' + 0xC6 = pLu | pp, // 'Æ' + 0xC7 = pLu | pp, // 'Ç' + 0xC8 = pLu | pp, // 'È' + 0xC9 = pLu | pp, // 'É' + 0xCA = pLu | pp, // 'Ê' + 0xCB = pLu | pp, // 'Ë' + 0xCC = pLu | pp, // 'Ì' + 0xCD = pLu | pp, // 'Í' + 0xCE = pLu | pp, // 'Î' + 0xCF = pLu | pp, // 'Ï' + 0xD0 = pLu | pp, // 'Ð' + 0xD1 = pLu | pp, // 'Ñ' + 0xD2 = pLu | pp, // 'Ò' + 0xD3 = pLu | pp, // 'Ó' + 0xD4 = pLu | pp, // 'Ô' + 0xD5 = pLu | pp, // 'Õ' + 0xD6 = pLu | pp, // 'Ö' + 0xD7 = pS | pp, // '×' + 0xD8 = pLu | pp, // 'Ø' + 0xD9 = pLu | pp, // 'Ù' + 0xDA = pLu | pp, // 'Ú' + 0xDB = pLu | pp, // 'Û' + 0xDC = pLu | pp, // 'Ü' + 0xDD = pLu | pp, // 'Ý' + 0xDE = pLu | pp, // 'Þ' + 0xDF = pLl | pp, // 'ß' + 0xE0 = pLl | pp, // 'à' + 0xE1 = pLl | pp, // 'á' + 0xE2 = pLl | pp, // 'â' + 0xE3 = pLl | pp, // 'ã' + 0xE4 = pLl | pp, // 'ä' + 0xE5 = pLl | pp, // 'å' + 0xE6 = pLl | pp, // 'æ' + 0xE7 = pLl | pp, // 'ç' + 0xE8 = pLl | pp, // 'è' + 0xE9 = pLl | pp, // 'é' + 0xEA = pLl | pp, // 'ê' + 0xEB = pLl | pp, // 'ë' + 0xEC = pLl | pp, // 'ì' + 0xED = pLl | pp, // 'í' + 0xEE = pLl | pp, // 'î' + 0xEF = pLl | pp, // 'ï' + 0xF0 = pLl | pp, // 'ð' + 0xF1 = pLl | pp, // 'ñ' + 0xF2 = pLl | pp, // 'ò' + 0xF3 = pLl | pp, // 'ó' + 0xF4 = pLl | pp, // 'ô' + 0xF5 = pLl | pp, // 'õ' + 0xF6 = pLl | pp, // 'ö' + 0xF7 = pS | pp, // '÷' + 0xF8 = pLl | pp, // 'ø' + 0xF9 = pLl | pp, // 'ù' + 0xFA = pLl | pp, // 'ú' + 0xFB = pLl | pp, // 'û' + 0xFC = pLl | pp, // 'ü' + 0xFD = pLl | pp, // 'ý' + 0xFE = pLl | pp, // 'þ' + 0xFF = pLl | pp, // 'ÿ' +}; + + +@(static) +alpha_ranges := [?]i32{ + 0x00d8, 0x00f6, + 0x00f8, 0x01f5, + 0x0250, 0x02a8, + 0x038e, 0x03a1, + 0x03a3, 0x03ce, + 0x03d0, 0x03d6, + 0x03e2, 0x03f3, + 0x0490, 0x04c4, + 0x0561, 0x0587, + 0x05d0, 0x05ea, + 0x05f0, 0x05f2, + 0x0621, 0x063a, + 0x0640, 0x064a, + 0x0671, 0x06b7, + 0x06ba, 0x06be, + 0x06c0, 0x06ce, + 0x06d0, 0x06d3, + 0x0905, 0x0939, + 0x0958, 0x0961, + 0x0985, 0x098c, + 0x098f, 0x0990, + 0x0993, 0x09a8, + 0x09aa, 0x09b0, + 0x09b6, 0x09b9, + 0x09dc, 0x09dd, + 0x09df, 0x09e1, + 0x09f0, 0x09f1, + 0x0a05, 0x0a0a, + 0x0a0f, 0x0a10, + 0x0a13, 0x0a28, + 0x0a2a, 0x0a30, + 0x0a32, 0x0a33, + 0x0a35, 0x0a36, + 0x0a38, 0x0a39, + 0x0a59, 0x0a5c, + 0x0a85, 0x0a8b, + 0x0a8f, 0x0a91, + 0x0a93, 0x0aa8, + 0x0aaa, 0x0ab0, + 0x0ab2, 0x0ab3, + 0x0ab5, 0x0ab9, + 0x0b05, 0x0b0c, + 0x0b0f, 0x0b10, + 0x0b13, 0x0b28, + 0x0b2a, 0x0b30, + 0x0b32, 0x0b33, + 0x0b36, 0x0b39, + 0x0b5c, 0x0b5d, + 0x0b5f, 0x0b61, + 0x0b85, 0x0b8a, + 0x0b8e, 0x0b90, + 0x0b92, 0x0b95, + 0x0b99, 0x0b9a, + 0x0b9e, 0x0b9f, + 0x0ba3, 0x0ba4, + 0x0ba8, 0x0baa, + 0x0bae, 0x0bb5, + 0x0bb7, 0x0bb9, + 0x0c05, 0x0c0c, + 0x0c0e, 0x0c10, + 0x0c12, 0x0c28, + 0x0c2a, 0x0c33, + 0x0c35, 0x0c39, + 0x0c60, 0x0c61, + 0x0c85, 0x0c8c, + 0x0c8e, 0x0c90, + 0x0c92, 0x0ca8, + 0x0caa, 0x0cb3, + 0x0cb5, 0x0cb9, + 0x0ce0, 0x0ce1, + 0x0d05, 0x0d0c, + 0x0d0e, 0x0d10, + 0x0d12, 0x0d28, + 0x0d2a, 0x0d39, + 0x0d60, 0x0d61, + 0x0e01, 0x0e30, + 0x0e32, 0x0e33, + 0x0e40, 0x0e46, + 0x0e5a, 0x0e5b, + 0x0e81, 0x0e82, + 0x0e87, 0x0e88, + 0x0e94, 0x0e97, + 0x0e99, 0x0e9f, + 0x0ea1, 0x0ea3, + 0x0eaa, 0x0eab, + 0x0ead, 0x0eae, + 0x0eb2, 0x0eb3, + 0x0ec0, 0x0ec4, + 0x0edc, 0x0edd, + 0x0f18, 0x0f19, + 0x0f40, 0x0f47, + 0x0f49, 0x0f69, + 0x10d0, 0x10f6, + 0x1100, 0x1159, + 0x115f, 0x11a2, + 0x11a8, 0x11f9, + 0x1e00, 0x1e9b, + 0x1f50, 0x1f57, + 0x1f80, 0x1fb4, + 0x1fb6, 0x1fbc, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fcc, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fdb, + 0x1fe0, 0x1fec, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ffc, + 0x210a, 0x2113, + 0x2115, 0x211d, + 0x2120, 0x2122, + 0x212a, 0x2131, + 0x2133, 0x2138, + 0x3041, 0x3094, + 0x30a1, 0x30fa, + 0x3105, 0x312c, + 0x3131, 0x318e, + 0x3192, 0x319f, + 0x3260, 0x327b, + 0x328a, 0x32b0, + 0x32d0, 0x32fe, + 0x3300, 0x3357, + 0x3371, 0x3376, + 0x337b, 0x3394, + 0x3399, 0x339e, + 0x33a9, 0x33ad, + 0x33b0, 0x33c1, + 0x33c3, 0x33c5, + 0x33c7, 0x33d7, + 0x33d9, 0x33dd, + 0x4e00, 0x9fff, + 0xac00, 0xd7a3, + 0xf900, 0xfb06, + 0xfb13, 0xfb17, + 0xfb1f, 0xfb28, + 0xfb2a, 0xfb36, + 0xfb38, 0xfb3c, + 0xfb40, 0xfb41, + 0xfb43, 0xfb44, + 0xfb46, 0xfbb1, + 0xfbd3, 0xfd3d, + 0xfd50, 0xfd8f, + 0xfd92, 0xfdc7, + 0xfdf0, 0xfdf9, + 0xfe70, 0xfe72, + 0xfe76, 0xfefc, + 0xff66, 0xff6f, + 0xff71, 0xff9d, + 0xffa0, 0xffbe, + 0xffc2, 0xffc7, + 0xffca, 0xffcf, + 0xffd2, 0xffd7, + 0xffda, 0xffdc, +}; + +@(static) +alpha_singlets := [?]i32{ + 0x00aa, + 0x00b5, + 0x00ba, + 0x03da, + 0x03dc, + 0x03de, + 0x03e0, + 0x06d5, + 0x09b2, + 0x0a5e, + 0x0a8d, + 0x0ae0, + 0x0b9c, + 0x0cde, + 0x0e4f, + 0x0e84, + 0x0e8a, + 0x0e8d, + 0x0ea5, + 0x0ea7, + 0x0eb0, + 0x0ebd, + 0x1fbe, + 0x207f, + 0x20a8, + 0x2102, + 0x2107, + 0x2124, + 0x2126, + 0x2128, + 0xfb3e, + 0xfe74, +}; + +@(static) +space_ranges := [?]i32{ + 0x0009, 0x000d, // tab and newline + 0x0020, 0x0020, // space + 0x0085, 0x0085, // next line + 0x00a0, 0x00a0, + 0x1680, 0x1680, // Ogham space mark + 0x2000, 0x200b, // en dash .. zero-width space + 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) + 0x2028, 0x2029, 0x3000, 0x3000, + 0x202f, 0x202f, // narrow no-break space + 0x205f, 0x205f, // medium mathematical space + 0x3000, 0x3000, // ideographic space + 0xfeff, 0xfeff, +}; + +@(static) +unicode_spaces := [?]i32{ + 0x0009, // tab + 0x000a, // LF + 0x000d, // CR + 0x0020, // space + 0x0085, // next line + 0x00a0, // unknown + 0x1680, // Ogham space mark + 0x2000, // en dash .. zero-width space + 0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace) + 0x2028, 0x2029, 0x3000, 0x3000, + 0x202f, // narrow no-break space + 0x205f, // medium mathematical space + 0x3000, // ideographic space + 0xfeff, // unknown +}; + +@(static) +to_upper_ranges := [?]i32{ + 0x0061, 0x007a, 468, // a-z A-Z + 0x00e0, 0x00f6, 468, + 0x00f8, 0x00fe, 468, + 0x0256, 0x0257, 295, + 0x0258, 0x0259, 298, + 0x028a, 0x028b, 283, + 0x03ad, 0x03af, 463, + 0x03b1, 0x03c1, 468, + 0x03c3, 0x03cb, 468, + 0x03cd, 0x03ce, 437, + 0x0430, 0x044f, 468, + 0x0451, 0x045c, 420, + 0x045e, 0x045f, 420, + 0x0561, 0x0586, 452, + 0x1f00, 0x1f07, 508, + 0x1f10, 0x1f15, 508, + 0x1f20, 0x1f27, 508, + 0x1f30, 0x1f37, 508, + 0x1f40, 0x1f45, 508, + 0x1f60, 0x1f67, 508, + 0x1f70, 0x1f71, 574, + 0x1f72, 0x1f75, 586, + 0x1f76, 0x1f77, 600, + 0x1f78, 0x1f79, 628, + 0x1f7a, 0x1f7b, 612, + 0x1f7c, 0x1f7d, 626, + 0x1f80, 0x1f87, 508, + 0x1f90, 0x1f97, 508, + 0x1fa0, 0x1fa7, 508, + 0x1fb0, 0x1fb1, 508, + 0x1fd0, 0x1fd1, 508, + 0x1fe0, 0x1fe1, 508, + 0x2170, 0x217f, 484, + 0x24d0, 0x24e9, 474, + 0xff41, 0xff5a, 468, +}; + +@(static) +to_upper_singlets := [?]i32{ + 0x00ff, 621, + 0x0101, 499, + 0x0103, 499, + 0x0105, 499, + 0x0107, 499, + 0x0109, 499, + 0x010b, 499, + 0x010d, 499, + 0x010f, 499, + 0x0111, 499, + 0x0113, 499, + 0x0115, 499, + 0x0117, 499, + 0x0119, 499, + 0x011b, 499, + 0x011d, 499, + 0x011f, 499, + 0x0121, 499, + 0x0123, 499, + 0x0125, 499, + 0x0127, 499, + 0x0129, 499, + 0x012b, 499, + 0x012d, 499, + 0x012f, 499, + 0x0131, 268, // I + 0x0133, 499, + 0x0135, 499, + 0x0137, 499, + 0x013a, 499, + 0x013c, 499, + 0x013e, 499, + 0x0140, 499, + 0x0142, 499, + 0x0144, 499, + 0x0146, 499, + 0x0148, 499, + 0x014b, 499, + 0x014d, 499, + 0x014f, 499, + 0x0151, 499, + 0x0153, 499, + 0x0155, 499, + 0x0157, 499, + 0x0159, 499, + 0x015b, 499, + 0x015d, 499, + 0x015f, 499, + 0x0161, 499, + 0x0163, 499, + 0x0165, 499, + 0x0167, 499, + 0x0169, 499, + 0x016b, 499, + 0x016d, 499, + 0x016f, 499, + 0x0171, 499, + 0x0173, 499, + 0x0175, 499, + 0x0177, 499, + 0x017a, 499, + 0x017c, 499, + 0x017e, 499, + 0x017f, 200, // S + 0x0183, 499, + 0x0185, 499, + 0x0188, 499, + 0x018c, 499, + 0x0192, 499, + 0x0199, 499, + 0x01a1, 499, + 0x01a3, 499, + 0x01a5, 499, + 0x01a8, 499, + 0x01ad, 499, + 0x01b0, 499, + 0x01b4, 499, + 0x01b6, 499, + 0x01b9, 499, + 0x01bd, 499, + 0x01c5, 499, + 0x01c6, 498, + 0x01c8, 499, + 0x01c9, 498, + 0x01cb, 499, + 0x01cc, 498, + 0x01ce, 499, + 0x01d0, 499, + 0x01d2, 499, + 0x01d4, 499, + 0x01d6, 499, + 0x01d8, 499, + 0x01da, 499, + 0x01dc, 499, + 0x01df, 499, + 0x01e1, 499, + 0x01e3, 499, + 0x01e5, 499, + 0x01e7, 499, + 0x01e9, 499, + 0x01eb, 499, + 0x01ed, 499, + 0x01ef, 499, + 0x01f2, 499, + 0x01f3, 498, + 0x01f5, 499, + 0x01fb, 499, + 0x01fd, 499, + 0x01ff, 499, + 0x0201, 499, + 0x0203, 499, + 0x0205, 499, + 0x0207, 499, + 0x0209, 499, + 0x020b, 499, + 0x020d, 499, + 0x020f, 499, + 0x0211, 499, + 0x0213, 499, + 0x0215, 499, + 0x0217, 499, + 0x0253, 290, + 0x0254, 294, + 0x025b, 297, + 0x0260, 295, + 0x0263, 293, + 0x0268, 291, + 0x0269, 289, + 0x026f, 289, + 0x0272, 287, + 0x0283, 282, + 0x0288, 282, + 0x0292, 281, + 0x03ac, 462, + 0x03cc, 436, + 0x03d0, 438, + 0x03d1, 443, + 0x03d5, 453, + 0x03d6, 446, + 0x03e3, 499, + 0x03e5, 499, + 0x03e7, 499, + 0x03e9, 499, + 0x03eb, 499, + 0x03ed, 499, + 0x03ef, 499, + 0x03f0, 414, + 0x03f1, 420, + 0x0461, 499, + 0x0463, 499, + 0x0465, 499, + 0x0467, 499, + 0x0469, 499, + 0x046b, 499, + 0x046d, 499, + 0x046f, 499, + 0x0471, 499, + 0x0473, 499, + 0x0475, 499, + 0x0477, 499, + 0x0479, 499, + 0x047b, 499, + 0x047d, 499, + 0x047f, 499, + 0x0481, 499, + 0x0491, 499, + 0x0493, 499, + 0x0495, 499, + 0x0497, 499, + 0x0499, 499, + 0x049b, 499, + 0x049d, 499, + 0x049f, 499, + 0x04a1, 499, + 0x04a3, 499, + 0x04a5, 499, + 0x04a7, 499, + 0x04a9, 499, + 0x04ab, 499, + 0x04ad, 499, + 0x04af, 499, + 0x04b1, 499, + 0x04b3, 499, + 0x04b5, 499, + 0x04b7, 499, + 0x04b9, 499, + 0x04bb, 499, + 0x04bd, 499, + 0x04bf, 499, + 0x04c2, 499, + 0x04c4, 499, + 0x04c8, 499, + 0x04cc, 499, + 0x04d1, 499, + 0x04d3, 499, + 0x04d5, 499, + 0x04d7, 499, + 0x04d9, 499, + 0x04db, 499, + 0x04dd, 499, + 0x04df, 499, + 0x04e1, 499, + 0x04e3, 499, + 0x04e5, 499, + 0x04e7, 499, + 0x04e9, 499, + 0x04eb, 499, + 0x04ef, 499, + 0x04f1, 499, + 0x04f3, 499, + 0x04f5, 499, + 0x04f9, 499, + 0x1e01, 499, + 0x1e03, 499, + 0x1e05, 499, + 0x1e07, 499, + 0x1e09, 499, + 0x1e0b, 499, + 0x1e0d, 499, + 0x1e0f, 499, + 0x1e11, 499, + 0x1e13, 499, + 0x1e15, 499, + 0x1e17, 499, + 0x1e19, 499, + 0x1e1b, 499, + 0x1e1d, 499, + 0x1e1f, 499, + 0x1e21, 499, + 0x1e23, 499, + 0x1e25, 499, + 0x1e27, 499, + 0x1e29, 499, + 0x1e2b, 499, + 0x1e2d, 499, + 0x1e2f, 499, + 0x1e31, 499, + 0x1e33, 499, + 0x1e35, 499, + 0x1e37, 499, + 0x1e39, 499, + 0x1e3b, 499, + 0x1e3d, 499, + 0x1e3f, 499, + 0x1e41, 499, + 0x1e43, 499, + 0x1e45, 499, + 0x1e47, 499, + 0x1e49, 499, + 0x1e4b, 499, + 0x1e4d, 499, + 0x1e4f, 499, + 0x1e51, 499, + 0x1e53, 499, + 0x1e55, 499, + 0x1e57, 499, + 0x1e59, 499, + 0x1e5b, 499, + 0x1e5d, 499, + 0x1e5f, 499, + 0x1e61, 499, + 0x1e63, 499, + 0x1e65, 499, + 0x1e67, 499, + 0x1e69, 499, + 0x1e6b, 499, + 0x1e6d, 499, + 0x1e6f, 499, + 0x1e71, 499, + 0x1e73, 499, + 0x1e75, 499, + 0x1e77, 499, + 0x1e79, 499, + 0x1e7b, 499, + 0x1e7d, 499, + 0x1e7f, 499, + 0x1e81, 499, + 0x1e83, 499, + 0x1e85, 499, + 0x1e87, 499, + 0x1e89, 499, + 0x1e8b, 499, + 0x1e8d, 499, + 0x1e8f, 499, + 0x1e91, 499, + 0x1e93, 499, + 0x1e95, 499, + 0x1ea1, 499, + 0x1ea3, 499, + 0x1ea5, 499, + 0x1ea7, 499, + 0x1ea9, 499, + 0x1eab, 499, + 0x1ead, 499, + 0x1eaf, 499, + 0x1eb1, 499, + 0x1eb3, 499, + 0x1eb5, 499, + 0x1eb7, 499, + 0x1eb9, 499, + 0x1ebb, 499, + 0x1ebd, 499, + 0x1ebf, 499, + 0x1ec1, 499, + 0x1ec3, 499, + 0x1ec5, 499, + 0x1ec7, 499, + 0x1ec9, 499, + 0x1ecb, 499, + 0x1ecd, 499, + 0x1ecf, 499, + 0x1ed1, 499, + 0x1ed3, 499, + 0x1ed5, 499, + 0x1ed7, 499, + 0x1ed9, 499, + 0x1edb, 499, + 0x1edd, 499, + 0x1edf, 499, + 0x1ee1, 499, + 0x1ee3, 499, + 0x1ee5, 499, + 0x1ee7, 499, + 0x1ee9, 499, + 0x1eeb, 499, + 0x1eed, 499, + 0x1eef, 499, + 0x1ef1, 499, + 0x1ef3, 499, + 0x1ef5, 499, + 0x1ef7, 499, + 0x1ef9, 499, + 0x1f51, 508, + 0x1f53, 508, + 0x1f55, 508, + 0x1f57, 508, + 0x1fb3, 509, + 0x1fc3, 509, + 0x1fe5, 507, + 0x1ff3, 509, +}; + +@(static) +to_lower_ranges := [?]i32{ + 0x0041, 0x005a, 532, // A-Z a-z + 0x00c0, 0x00d6, 532, // - - + 0x00d8, 0x00de, 532, // - - + 0x0189, 0x018a, 705, // - - + 0x018e, 0x018f, 702, // - - + 0x01b1, 0x01b2, 717, // - - + 0x0388, 0x038a, 537, // - - + 0x038e, 0x038f, 563, // - - + 0x0391, 0x03a1, 532, // - - + 0x03a3, 0x03ab, 532, // - - + 0x0401, 0x040c, 580, // - - + 0x040e, 0x040f, 580, // - - + 0x0410, 0x042f, 532, // - - + 0x0531, 0x0556, 548, // - - + 0x10a0, 0x10c5, 548, // - - + 0x1f08, 0x1f0f, 492, // - - + 0x1f18, 0x1f1d, 492, // - - + 0x1f28, 0x1f2f, 492, // - - + 0x1f38, 0x1f3f, 492, // - - + 0x1f48, 0x1f4d, 492, // - - + 0x1f68, 0x1f6f, 492, // - - + 0x1f88, 0x1f8f, 492, // - - + 0x1f98, 0x1f9f, 492, // - - + 0x1fa8, 0x1faf, 492, // - - + 0x1fb8, 0x1fb9, 492, // - - + 0x1fba, 0x1fbb, 426, // - - + 0x1fc8, 0x1fcb, 414, // - - + 0x1fd8, 0x1fd9, 492, // - - + 0x1fda, 0x1fdb, 400, // - - + 0x1fe8, 0x1fe9, 492, // - - + 0x1fea, 0x1feb, 388, // - - + 0x1ff8, 0x1ff9, 372, // - - + 0x1ffa, 0x1ffb, 374, // - - + 0x2160, 0x216f, 516, // - - + 0x24b6, 0x24cf, 526, // - - + 0xff21, 0xff3a, 532, // - - +}; + +@(static) +to_lower_singlets := [?]i32{ + 0x0100, 501, + 0x0102, 501, + 0x0104, 501, + 0x0106, 501, + 0x0108, 501, + 0x010a, 501, + 0x010c, 501, + 0x010e, 501, + 0x0110, 501, + 0x0112, 501, + 0x0114, 501, + 0x0116, 501, + 0x0118, 501, + 0x011a, 501, + 0x011c, 501, + 0x011e, 501, + 0x0120, 501, + 0x0122, 501, + 0x0124, 501, + 0x0126, 501, + 0x0128, 501, + 0x012a, 501, + 0x012c, 501, + 0x012e, 501, + 0x0130, 301, // i + 0x0132, 501, + 0x0134, 501, + 0x0136, 501, + 0x0139, 501, + 0x013b, 501, + 0x013d, 501, + 0x013f, 501, + 0x0141, 501, + 0x0143, 501, + 0x0145, 501, + 0x0147, 501, + 0x014a, 501, + 0x014c, 501, + 0x014e, 501, + 0x0150, 501, + 0x0152, 501, + 0x0154, 501, + 0x0156, 501, + 0x0158, 501, + 0x015a, 501, + 0x015c, 501, + 0x015e, 501, + 0x0160, 501, + 0x0162, 501, + 0x0164, 501, + 0x0166, 501, + 0x0168, 501, + 0x016a, 501, + 0x016c, 501, + 0x016e, 501, + 0x0170, 501, + 0x0172, 501, + 0x0174, 501, + 0x0176, 501, + 0x0178, 379, + 0x0179, 501, + 0x017b, 501, + 0x017d, 501, + 0x0181, 710, + 0x0182, 501, + 0x0184, 501, + 0x0186, 706, + 0x0187, 501, + 0x018b, 501, + 0x0190, 703, + 0x0191, 501, + 0x0193, 705, + 0x0194, 707, + 0x0196, 711, + 0x0197, 709, + 0x0198, 501, + 0x019c, 711, + 0x019d, 713, + 0x01a0, 501, + 0x01a2, 501, + 0x01a4, 501, + 0x01a7, 501, + 0x01a9, 718, + 0x01ac, 501, + 0x01ae, 718, + 0x01af, 501, + 0x01b3, 501, + 0x01b5, 501, + 0x01b7, 719, + 0x01b8, 501, + 0x01bc, 501, + 0x01c4, 502, + 0x01c5, 501, + 0x01c7, 502, + 0x01c8, 501, + 0x01ca, 502, + 0x01cb, 501, + 0x01cd, 501, + 0x01cf, 501, + 0x01d1, 501, + 0x01d3, 501, + 0x01d5, 501, + 0x01d7, 501, + 0x01d9, 501, + 0x01db, 501, + 0x01de, 501, + 0x01e0, 501, + 0x01e2, 501, + 0x01e4, 501, + 0x01e6, 501, + 0x01e8, 501, + 0x01ea, 501, + 0x01ec, 501, + 0x01ee, 501, + 0x01f1, 502, + 0x01f2, 501, + 0x01f4, 501, + 0x01fa, 501, + 0x01fc, 501, + 0x01fe, 501, + 0x0200, 501, + 0x0202, 501, + 0x0204, 501, + 0x0206, 501, + 0x0208, 501, + 0x020a, 501, + 0x020c, 501, + 0x020e, 501, + 0x0210, 501, + 0x0212, 501, + 0x0214, 501, + 0x0216, 501, + 0x0386, 538, + 0x038c, 564, + 0x03e2, 501, + 0x03e4, 501, + 0x03e6, 501, + 0x03e8, 501, + 0x03ea, 501, + 0x03ec, 501, + 0x03ee, 501, + 0x0460, 501, + 0x0462, 501, + 0x0464, 501, + 0x0466, 501, + 0x0468, 501, + 0x046a, 501, + 0x046c, 501, + 0x046e, 501, + 0x0470, 501, + 0x0472, 501, + 0x0474, 501, + 0x0476, 501, + 0x0478, 501, + 0x047a, 501, + 0x047c, 501, + 0x047e, 501, + 0x0480, 501, + 0x0490, 501, + 0x0492, 501, + 0x0494, 501, + 0x0496, 501, + 0x0498, 501, + 0x049a, 501, + 0x049c, 501, + 0x049e, 501, + 0x04a0, 501, + 0x04a2, 501, + 0x04a4, 501, + 0x04a6, 501, + 0x04a8, 501, + 0x04aa, 501, + 0x04ac, 501, + 0x04ae, 501, + 0x04b0, 501, + 0x04b2, 501, + 0x04b4, 501, + 0x04b6, 501, + 0x04b8, 501, + 0x04ba, 501, + 0x04bc, 501, + 0x04be, 501, + 0x04c1, 501, + 0x04c3, 501, + 0x04c7, 501, + 0x04cb, 501, + 0x04d0, 501, + 0x04d2, 501, + 0x04d4, 501, + 0x04d6, 501, + 0x04d8, 501, + 0x04da, 501, + 0x04dc, 501, + 0x04de, 501, + 0x04e0, 501, + 0x04e2, 501, + 0x04e4, 501, + 0x04e6, 501, + 0x04e8, 501, + 0x04ea, 501, + 0x04ee, 501, + 0x04f0, 501, + 0x04f2, 501, + 0x04f4, 501, + 0x04f8, 501, + 0x1e00, 501, + 0x1e02, 501, + 0x1e04, 501, + 0x1e06, 501, + 0x1e08, 501, + 0x1e0a, 501, + 0x1e0c, 501, + 0x1e0e, 501, + 0x1e10, 501, + 0x1e12, 501, + 0x1e14, 501, + 0x1e16, 501, + 0x1e18, 501, + 0x1e1a, 501, + 0x1e1c, 501, + 0x1e1e, 501, + 0x1e20, 501, + 0x1e22, 501, + 0x1e24, 501, + 0x1e26, 501, + 0x1e28, 501, + 0x1e2a, 501, + 0x1e2c, 501, + 0x1e2e, 501, + 0x1e30, 501, + 0x1e32, 501, + 0x1e34, 501, + 0x1e36, 501, + 0x1e38, 501, + 0x1e3a, 501, + 0x1e3c, 501, + 0x1e3e, 501, + 0x1e40, 501, + 0x1e42, 501, + 0x1e44, 501, + 0x1e46, 501, + 0x1e48, 501, + 0x1e4a, 501, + 0x1e4c, 501, + 0x1e4e, 501, + 0x1e50, 501, + 0x1e52, 501, + 0x1e54, 501, + 0x1e56, 501, + 0x1e58, 501, + 0x1e5a, 501, + 0x1e5c, 501, + 0x1e5e, 501, + 0x1e60, 501, + 0x1e62, 501, + 0x1e64, 501, + 0x1e66, 501, + 0x1e68, 501, + 0x1e6a, 501, + 0x1e6c, 501, + 0x1e6e, 501, + 0x1e70, 501, + 0x1e72, 501, + 0x1e74, 501, + 0x1e76, 501, + 0x1e78, 501, + 0x1e7a, 501, + 0x1e7c, 501, + 0x1e7e, 501, + 0x1e80, 501, + 0x1e82, 501, + 0x1e84, 501, + 0x1e86, 501, + 0x1e88, 501, + 0x1e8a, 501, + 0x1e8c, 501, + 0x1e8e, 501, + 0x1e90, 501, + 0x1e92, 501, + 0x1e94, 501, + 0x1ea0, 501, + 0x1ea2, 501, + 0x1ea4, 501, + 0x1ea6, 501, + 0x1ea8, 501, + 0x1eaa, 501, + 0x1eac, 501, + 0x1eae, 501, + 0x1eb0, 501, + 0x1eb2, 501, + 0x1eb4, 501, + 0x1eb6, 501, + 0x1eb8, 501, + 0x1eba, 501, + 0x1ebc, 501, + 0x1ebe, 501, + 0x1ec0, 501, + 0x1ec2, 501, + 0x1ec4, 501, + 0x1ec6, 501, + 0x1ec8, 501, + 0x1eca, 501, + 0x1ecc, 501, + 0x1ece, 501, + 0x1ed0, 501, + 0x1ed2, 501, + 0x1ed4, 501, + 0x1ed6, 501, + 0x1ed8, 501, + 0x1eda, 501, + 0x1edc, 501, + 0x1ede, 501, + 0x1ee0, 501, + 0x1ee2, 501, + 0x1ee4, 501, + 0x1ee6, 501, + 0x1ee8, 501, + 0x1eea, 501, + 0x1eec, 501, + 0x1eee, 501, + 0x1ef0, 501, + 0x1ef2, 501, + 0x1ef4, 501, + 0x1ef6, 501, + 0x1ef8, 501, + 0x1f59, 492, + 0x1f5b, 492, + 0x1f5d, 492, + 0x1f5f, 492, + 0x1fbc, 491, + 0x1fcc, 491, + 0x1fec, 493, + 0x1ffc, 491, +}; + +@(static) +to_title_singlets := [?]i32{ + 0x01c4, 501, + 0x01c6, 499, + 0x01c7, 501, + 0x01c9, 499, + 0x01ca, 501, + 0x01cc, 499, + 0x01f1, 501, + 0x01f3, 499, +}; 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 void array_set_capacity (Array *array, isize capac template Array array_slice (Array const &array, isize lo, isize hi); template Array array_clone (gbAllocator const &a, Array const &array); - - template void array_ordered_remove (Array *array, isize index); template void array_unordered_remove(Array *array, isize index); +template void array_copy(Array *array, Array const &data, isize offset); +template void array_copy(Array *array, Array const &data, isize offset, isize count); + +template T *array_end_ptr(Array *array); + + +template +struct Slice { + T *data; + isize count; + + T &operator[](isize index) { + #if !defined(NO_ARRAY_BOUNDS_CHECK) + GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #endif + return data[index]; + } + + T const &operator[](isize index) const { + #if !defined(NO_ARRAY_BOUNDS_CHECK) + GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #endif + return data[index]; + } +}; + +template Slice slice_from_array(Array const &a); + + + +template +Slice slice_make(gbAllocator const &allocator, isize count) { + Slice s = {}; + s.data = gb_alloc_array(allocator, T, count); + s.count = count; + return s; +} + + +template +Slice slice_from_array(Array const &a) { + return {a.data, a.count}; +} +template +Slice slice_clone(gbAllocator const &allocator, Slice const &a) { + T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T)); + return {data, a.count}; +} + +template +Slice slice_clone_from_array(gbAllocator const &allocator, Array const &a) { + auto c = array_clone(allocator, a); + return {c.data, c.count}; +} + + +template +void slice_copy(Slice *slice, Slice const &data, isize offset) { + gb_memmove(slice->data+offset, data.data, gb_size_of(T)*data.count); +} +template +void slice_copy(Slice *slice, Slice const &data, isize offset, isize count) { + gb_memmove(slice->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count)); +} + + + +template +void slice_ordered_remove(Slice *array, isize index) { + GB_ASSERT(0 <= index && index < array->count); + + isize bytes = gb_size_of(T) * (array->count-(index+1)); + gb_memmove(array->data+index, array->data+index+1, bytes); + array->count -= 1; +} + +template +void slice_unordered_remove(Slice *array, isize index) { + GB_ASSERT(0 <= index && index < array->count); + + isize n = array->count-1; + if (index != n) { + gb_memmove(array->data+index, array->data+n, gb_size_of(T)); + } + array->count -= 1; +} + template void array_copy(Array *array, Array const &data, isize offset) { diff --git a/src/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 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 const &inits, String context_name) { +void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Slice const &inits, String context_name) { if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) { return; } @@ -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(ctx->allocator, 0, 2*lhs_count); - defer (array_free(&operands)); + auto operands = array_make(temporary_allocator(), 0, 2*lhs_count); check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false); isize rhs_count = operands.count; @@ -317,7 +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(ctx->allocator, 0, pg->args.count); + pge->entities = array_make(permanent_allocator(), 0, pg->args.count); // NOTE(bill): This must be set here to prevent cycles in checking if someone // places the entity within itself @@ -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 const &stmts, u32 flags); +void check_stmt_list (CheckerContext *c, Slice const &stmts, u32 flags); void check_init_constant (CheckerContext *c, Entity *e, Operand *operand); bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value); bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array *operands = nullptr); @@ -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 const &nodes, isize reserve_size) { +void check_scope_decls(CheckerContext *c, Slice const &nodes, isize reserve_size) { Scope *s = c->scope; check_collect_entities(c, nodes); @@ -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 const &lhs, Array *operands, Array const &rhs) { +bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, Array *operands, Slice const &rhs) { bool optional_ok = false; isize tuple_index = 0; for_array(i, rhs) { @@ -6145,7 +6216,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, -bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Array const &rhs, bool allow_ok, bool is_variadic) { +bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Slice const &rhs, bool allow_ok, bool is_variadic) { bool optional_ok = false; isize tuple_index = 0; for_array(i, rhs) { @@ -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(c->allocator, param_count); - defer (array_free(&ordered_operands)); + bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); + auto ordered_operands = array_make(temporary_allocator(), param_count); defer ({ for_array(i, ordered_operands) { Operand const &o = ordered_operands[i]; @@ -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 *clauses, bool print_err) { +bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Slice *clauses, bool print_err) { if (clauses != nullptr) { for_array(i, *clauses) { Ast *clause = (*clauses)[i]; @@ -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 const &args) { +CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice const &args) { ast_node(ce, CallExpr, call); CallArgumentCheckerType *call_checker = check_call_arguments_internal; @@ -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 ordered_operands = operands; - if (named_fields) { - bool *visited = gb_alloc_array(c->allocator, bool, param_count); + if (!named_fields) { + ordered_operands = array_make(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(c->allocator, param_count); + ordered_operands = array_make(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 const &args, ProcInlining inlining, Type *type_hint) { +ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice const &args, ProcInlining inlining, Type *type_hint) { if (proc != nullptr && proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, proc); @@ -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(heap_allocator(), ce->args.count+1); + auto modified_args = slice_make(heap_allocator(), ce->args.count+1); modified_args[0] = first_arg; - array_copy(&modified_args, ce->args, 1); + slice_copy(&modified_args, ce->args, 1); ce->args = modified_args; se->modified_call = true; + 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 const ¶ms) { +gbString write_struct_fields_to_string(gbString str, Slice const ¶ms) { for_array(i, params) { if (i > 0) { str = gb_string_appendc(str, ", "); } - 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 const &stmts, u32 flags) { +void check_stmt_list(CheckerContext *ctx, Slice const &stmts, u32 flags) { if (stmts.count == 0) { return; } @@ -78,7 +78,7 @@ void check_stmt_list(CheckerContext *ctx, Array const &stmts, u32 flags) } } -bool check_is_terminating_list(Array const &stmts, String const &label) { +bool check_is_terminating_list(Slice const &stmts, String const &label) { // Iterate backwards for (isize n = stmts.count-1; n >= 0; n--) { Ast *stmt = stmts[n]; @@ -96,7 +96,7 @@ bool check_is_terminating_list(Array const &stmts, String const &label) { return false; } -bool check_has_break_list(Array const &stmts, String const &label, bool implicit) { +bool check_has_break_list(Slice const &stmts, String const &label, bool implicit) { for_array(i, stmts) { Ast *stmt = stmts[i]; if (check_has_break(stmt, label, implicit)) { @@ -641,8 +641,7 @@ void add_constant_switch_case(CheckerContext *ctx, Map *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(ctx->allocator, 0, fields.count); - defer (array_free(&unhandled)); + auto unhandled = array_make(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(ctx->allocator, 0, variants.count); - defer (array_free(&unhandled)); + auto unhandled = array_make(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(ctx->allocator, lhs_count); - auto rhs_operands = array_make(ctx->allocator, 0, 2*lhs_count); - defer (array_free(&lhs_operands)); - defer (array_free(&rhs_operands)); + auto lhs_operands = array_make(temporary_allocator(), lhs_count); + auto rhs_operands = array_make(temporary_allocator(), 0, 2*lhs_count); for_array(i, as->lhs) { if (is_blank_ident(as->lhs[i])) { @@ -1462,8 +1457,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } - auto lhs_to_ignore = array_make(ctx->allocator, lhs_count); - defer (array_free(&lhs_to_ignore)); + auto lhs_to_ignore = array_make(temporary_allocator(), lhs_count); isize max = gb_min(lhs_count, rhs_count); // NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working @@ -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 *fields, Array *tags, Array const ¶ms, +void check_struct_fields(CheckerContext *ctx, Ast *node, Array *fields, Array *tags, Slice const ¶ms, isize init_field_capacity, Type *struct_type, String context) { *fields = array_make(heap_allocator(), 0, init_field_capacity); *tags = array_make(heap_allocator(), 0, init_field_capacity); @@ -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 params = field_list->list; + Slice 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(ctx->allocator, 0, variable_count); + auto entities = array_make(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->kind == Ast_TypeidType) { - is_type_param = true; - Type *specialization = nullptr; - if (type_expr->TypeidType.specialization != nullptr) { - Ast *s = type_expr->TypeidType.specialization; - specialization = check_type(ctx, s); + + if (type_expr != nullptr) { + if (type_expr->kind == Ast_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); } - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); - } else { - type = check_type(ctx, type_expr); - if (is_type_polymorphic(type)) { - is_type_polymorphic_type = true; + if (type_expr->kind == Ast_TypeidType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeidType.specialization != nullptr) { + Ast *s = type_expr->TypeidType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else { + type = check_type(ctx, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } } } + ParameterValue param_value = {}; + if (default_value != nullptr) { + Type *out_type = nullptr; + param_value = handle_parameter_value(ctx, type, &out_type, default_value, false); + if (type == nullptr && out_type != nullptr) { + type = out_type; + } + if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) { + error(default_value, "Invalid parameter value"); + param_value = {}; + } + } + + if (type == nullptr) { error(params[i], "Invalid parameter type"); type = t_invalid; @@ -471,7 +490,14 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< Token token = name->Ident.token; if (poly_operands != nullptr) { - Operand operand = (*poly_operands)[entities.count]; + Operand operand = {}; + operand.type = t_invalid; + if (entities.count < poly_operands->count) { + operand = (*poly_operands)[entities.count]; + } else if (param_value.kind != ParameterValue_Invalid) { + operand.mode = Addressing_Constant; + operand.value = param_value.value; + } if (is_type_param) { if (is_type_polymorphic(base_type(operand.type))) { is_polymorphic = true; @@ -486,6 +512,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< } if (e == nullptr) { e = alloc_entity_constant(scope, token, operand.type, operand.value); + e->Constant.param_value = param_value; } } } else { @@ -493,7 +520,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< e = alloc_entity_type_name(scope, token, type); e->TypeName.is_type_alias = true; } else { - e = alloc_entity_constant(scope, token, type, empty_exact_value); + e = alloc_entity_constant(scope, token, type, param_value.value); + e->Constant.param_value = param_value; } } @@ -568,7 +596,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array(ctx->allocator, 0, variant_count); + auto variants = array_make(permanent_allocator(), 0, variant_count); union_type->Union.scope = ctx->scope; @@ -579,7 +607,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraypolymorphic_params != nullptr) { ast_node(field_list, FieldList, ut->polymorphic_params); - Array params = field_list->list; + Slice params = field_list->list; if (params.count != 0) { isize variable_count = 0; for_array(i, params) { @@ -590,7 +618,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array(ctx->allocator, 0, variable_count); + auto entities = array_make(permanent_allocator(), 0, variable_count); for_array(i, params) { Ast *param = params[i]; @@ -599,29 +627,45 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraytype; + Ast *default_value = unparen_expr(p->default_value); Type *type = nullptr; bool is_type_param = false; bool is_type_polymorphic_type = false; - if (type_expr == nullptr) { + if (type_expr == nullptr && default_value == nullptr) { error(param, "Expected a type for this parameter"); continue; } - if (type_expr->kind == Ast_Ellipsis) { - type_expr = type_expr->Ellipsis.expr; - error(param, "A polymorphic parameter cannot be variadic"); - } - if (type_expr->kind == Ast_TypeidType) { - is_type_param = true; - Type *specialization = nullptr; - if (type_expr->TypeidType.specialization != nullptr) { - Ast *s = type_expr->TypeidType.specialization; - specialization = check_type(ctx, s); + if (type_expr != nullptr) { + if (type_expr->kind == Ast_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); } - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); - } else { - type = check_type(ctx, type_expr); - if (is_type_polymorphic(type)) { - is_type_polymorphic_type = true; + if (type_expr->kind == Ast_TypeidType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeidType.specialization != nullptr) { + Ast *s = type_expr->TypeidType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else { + type = check_type(ctx, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } + } + } + + ParameterValue param_value = {}; + if (default_value != nullptr) { + Type *out_type = nullptr; + param_value = handle_parameter_value(ctx, type, &out_type, default_value, false); + if (type == nullptr && out_type != nullptr) { + type = out_type; + } + if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) { + error(default_value, "Invalid parameter value"); + param_value = {}; } } @@ -662,7 +706,14 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayIdent.token; if (poly_operands != nullptr) { - Operand operand = (*poly_operands)[entities.count]; + Operand operand = {}; + operand.type = t_invalid; + if (entities.count < poly_operands->count) { + operand = (*poly_operands)[entities.count]; + } else if (param_value.kind != ParameterValue_Invalid) { + operand.mode = Addressing_Constant; + operand.value = param_value.value; + } if (is_type_param) { GB_ASSERT(operand.mode == Addressing_Type || operand.mode == Addressing_Invalid); @@ -675,6 +726,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayConstant.param_value = param_value; } } else { if (is_type_param) { @@ -682,6 +734,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayTypeName.is_type_alias = true; } else { e = alloc_entity_constant(scope, token, type, empty_exact_value); + e->Constant.param_value = param_value; } } @@ -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(ctx->allocator, 0, et->fields.count); + auto fields = array_make(permanent_allocator(), 0, et->fields.count); Type *constant_type = enum_type; if (named_type != nullptr) { @@ -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(ctx->allocator, 0, bft->fields.count); - auto sizes = array_make (ctx->allocator, 0, bft->fields.count); - auto offsets = array_make (ctx->allocator, 0, bft->fields.count); + auto fields = array_make(permanent_allocator(), 0, bft->fields.count); + auto sizes = array_make (permanent_allocator(), 0, bft->fields.count); + auto offsets = array_make (permanent_allocator(), 0, bft->fields.count); scope_reserve(ctx->scope, bft->fields.count); @@ -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 params = field_list->list; + Slice 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(ctx->allocator, 0, variable_count); + auto variables = array_make(permanent_allocator(), 0, variable_count); for_array(i, params) { Ast *param = params[i]; if (param->kind != Ast_Field) { @@ -1822,7 +1875,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { return nullptr; } ast_node(field_list, FieldList, _results); - Array results = field_list->list; + Slice results = field_list->list; if (results.count == 0) { return nullptr; @@ -1838,7 +1891,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { } } - auto variables = array_make(ctx->allocator, 0, variable_count); + auto variables = array_make(permanent_allocator(), 0, variable_count); for_array(i, results) { ast_node(field, Field, results[i]); Ast *default_value = unparen_expr(field->default_value); @@ -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(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(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(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(a, 0, 2); + auto fields = array_make(permanent_allocator(), 0, 2); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved)); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved)); @@ -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 *M, } -Array generate_entity_dependency_graph(CheckerInfo *info) { +Array generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) - gbAllocator a = heap_allocator(); - Map M = {}; // Key: Entity * - map_init(&M, a, info->entities.count); + map_init(&M, allocator, info->entities.count); defer (map_destroy(&M)); for_array(i, info->entities) { Entity *e = info->entities[i]; DeclInfo *d = e->decl_info; if (is_entity_a_dependency(e)) { - EntityGraphNode *n = gb_alloc_item(a, EntityGraphNode); + EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode); n->entity = e; map_set(&M, hash_pointer(e), n); } @@ -1928,7 +2005,7 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { // This means that the entity graph node set will have to be thread safe TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2"); - auto G = array_make(a, 0, M.entries.count); + auto G = array_make(allocator, 0, M.entries.count); for_array(i, M.entries) { auto *entry = &M.entries[i]; @@ -1949,17 +2026,27 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *s = n->succ.entries[k].ptr; // Ignore self-cycles if (s != n) { + if (p->entity->kind == Entity_Procedure && + s->entity->kind == Entity_Procedure) { + // NOTE(bill, 2020-11-15): Only care about variable initialization ordering + // TODO(bill): This is probably wrong!!!! + continue; + } + // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the + // the time to process + entity_graph_node_set_add(&p->succ, s); entity_graph_node_set_add(&s->pred, p); // Remove edge to 'n' entity_graph_node_set_remove(&s->pred, n); } } + // Remove edge to 'n' entity_graph_node_set_remove(&p->succ, n); } } - } else { + } else if (e->kind == Entity_Variable) { array_add(&G, n); } } @@ -1972,6 +2059,28 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { GB_ASSERT(n->dep_count >= 0); } + // f64 succ_count = 0.0; + // f64 pred_count = 0.0; + // f64 succ_capacity = 0.0; + // f64 pred_capacity = 0.0; + // f64 succ_max = 0.0; + // f64 pred_max = 0.0; + // for_array(i, G) { + // EntityGraphNode *n = G[i]; + // succ_count += n->succ.entries.count; + // pred_count += n->pred.entries.count; + // succ_capacity += n->succ.entries.capacity; + // pred_capacity += n->pred.entries.capacity; + + // succ_max = gb_max(succ_max, n->succ.entries.capacity); + // pred_max = gb_max(pred_max, n->pred.entries.capacity); + + // } + // f64 count = cast(f64)G.count; + // gb_printf_err(">>>count pred: %f succ: %f\n", pred_count/count, succ_count/count); + // gb_printf_err(">>>capacity pred: %f succ: %f\n", pred_capacity/count, succ_capacity/count); + // gb_printf_err(">>>max pred: %f succ: %f\n", pred_max, succ_max); + return G; #undef TIME_SECTION @@ -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 const &attributes, De } -isize get_total_value_count(Array const &values) { +isize get_total_value_count(Slice const &values) { isize count = 0; for_array(i, values) { Type *t = type_of_expr(values[i]); @@ -2967,8 +3076,8 @@ void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array *attr } void check_collect_value_decl(CheckerContext *c, Ast *decl) { - if (decl->been_handled) return; - decl->been_handled = true; + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; ast_node(vd, ValueDecl, decl); @@ -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 const &nodes) { +void check_collect_entities(CheckerContext *c, Slice const &nodes) { for_array(decl_index, nodes) { Ast *decl = nodes[decl_index]; if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) { @@ -3522,11 +3637,9 @@ struct ImportPathItem { Array find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet *visited) { Array empty_path = {}; - if (ptr_set_exists(visited, start)) { + if (ptr_set_update(visited, start)) { return empty_path; } - ptr_set_add(visited, start); - String path = start->fullpath; AstPackage **found = string_map_get(&c->info.packages, path); @@ -3571,8 +3684,8 @@ Array find_import_path(Checker *c, AstPackage *start, AstPackage } #endif void check_add_import_decl(CheckerContext *ctx, Ast *decl) { - if (decl->been_handled) return; - decl->been_handled = true; + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; ast_node(id, ImportDecl, decl); Token token = id->relpath; @@ -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 const &decls) { +bool collect_checked_packages_from_decl_list(Checker *c, Slice const &decls) { bool new_files = false; for_array(i, decls) { Ast *decl = decls[i]; @@ -3760,7 +3871,7 @@ bool collect_checked_packages_from_decl_list(Checker *c, Array const &dec } // Returns true if a new package is present -bool collect_file_decls(CheckerContext *ctx, Array const &decls); +bool collect_file_decls(CheckerContext *ctx, Slice const &decls); bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws); bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) { @@ -3835,7 +3946,7 @@ bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) { return false; } -bool collect_file_decls(CheckerContext *ctx, Array const &decls) { +bool collect_file_decls(CheckerContext *ctx, Slice const &decls) { GB_ASSERT(ctx->scope->flags&ScopeFlag_File); if (collect_checked_packages_from_decl_list(ctx->checker, decls)) { @@ -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 dep_graph = generate_entity_dependency_graph(info); + Array dep_graph = generate_entity_dependency_graph(info, heap_allocator()); defer ({ for_array(i, dep_graph) { entity_graph_node_destroy(dep_graph[i], heap_allocator()); @@ -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 attributes; @@ -140,6 +141,9 @@ struct DeclInfo { bool is_using; bool where_clauses_evaluated; + CommentGroup *comment; + CommentGroup *docs; + PtrSet deps; PtrSet type_info_deps; Array 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 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 minimum_dependency_set; @@ -278,6 +288,7 @@ struct CheckerInfo { Map atom_op_map; // Key: Ast * + Array testing_procedures; bool allow_identifier_uses; Array 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 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 const &nodes); +void check_collect_entities(CheckerContext *c, Slice const &nodes); void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); diff --git a/src/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 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; - } - if (name[0] == '_') { - return; - } - gb_printf("type %.*s\n", LIT(name)); -} + // Ignore the first space + if (comment.len > 0 && comment[0] == ' ') { + comment.text += 1; + comment.len -= 1; + } -void print_proc_decl(AstProcDecl *pd) { - GB_ASSERT(pd->name->kind == Ast_Ident); - String name = pd->name->Ident.string; - if (name.len == 0) { - return; - } - if (name[0] == '_') { - return; - } - - String docs = alloc_comment_group_string(heap_allocator(), pd->docs); - defer (gb_free(heap_allocator(), docs.text)); - - if (docs.len > 0) { - gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len); - } else { - return; - } - - ast_node(proc_type, ProcType, pd->type); - - gbString params = expr_to_string(proc_type->params); - defer (gb_string_free(params)); - gb_printf("proc %.*s(%s)", LIT(name), params); - if (proc_type->results != nullptr) { - ast_node(fl, FieldList, proc_type->results); - isize count = fl->list.count; - if (count > 0) { - gbString results = expr_to_string(proc_type->results); - defer (gb_string_free(results)); - gb_printf(" -> "); - if (count != 1) { - gb_printf("("); + if (slash_slash) { + if (string_starts_with(comment, str_lit("+"))) { + continue; } - gb_printf("%s", results); - if (count != 1) { - gb_printf(")"); + 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; } } } - gb_printf("\n\n"); -} -#endif -void print_declaration(Ast *decl) { + + if (count > 0) { + print_doc_line(0, ""); + return true; + } + return false; } -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 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); + } + 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; + } + + print_doc_line(0, "package %.*s", LIT(pkg->name)); + + + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + if (f->pkg_decl) { + GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl); + print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs); + } + } + + if (pkg->scope != nullptr) { + auto entities = array_make(heap_allocator(), 0, pkg->scope->elements.entries.count); + defer (array_free(&entities)); + 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_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, ""); + } + + 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(Checker *c) { + CheckerInfo *info = &c->info; + + auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); + for_array(i, info->packages.entries) { + AstPackage *pkg = info->packages.entries[i].value; + if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) { + array_add(&pkgs, pkg); + } else { + if (pkg->kind == Package_Init) { + array_add(&pkgs, pkg); + } else if (pkg->is_extra) { + array_add(&pkgs, pkg); + } + } + } + + gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + + for_array(i, pkgs) { + print_doc_package(info, pkgs[i]); + } } 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 paths; + Slice 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 paths, String name) { + Slice paths, String name) { Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type); entity->LibraryName.paths = paths; entity->LibraryName.name = name; diff --git a/src/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 debug_info; // Key: Unique pointer Map anonymous_proc_lits; // Key: Ast * + Map equal_procs; // Key: Type * + Map hasher_procs; // Key: Type * + irDebugInfo * debug_compile_unit; Array debug_location_stack; @@ -161,6 +164,7 @@ struct irProcedure { Ast * return_ptr_hint_ast; bool return_ptr_hint_used; + bool ignore_dead_instr; Array 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 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 const &ar GB_ASSERT_MSG(param_count == args.count, "%.*s %td == %td", LIT(p->entity->token.string), param_count, args.count); } - auto processed_args = array_make(heap_allocator(), 0, args.count); + auto processed_args = array_make(permanent_allocator(), 0, args.count); for (isize i = 0; i < param_count; i++) { Entity *e = pt->Proc.params->Tuple.variables[i]; @@ -3416,7 +3424,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array const &ar case DeferredProcedure_in_out: { auto out_args = ir_value_to_array(p, result); - array_init(&result_as_args, heap_allocator(), in_args.count + out_args.count); + array_init(&result_as_args, permanent_allocator(), in_args.count + out_args.count); array_copy(&result_as_args, in_args, 0); array_copy(&result_as_args, out_args, in_args.count); } @@ -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) { +irValue *ir_const_hash(irModule *m, irValue *key, Type *key_type) { + irValue *hashed_key = nullptr; + + if (key->kind == irValue_Constant) { + u64 hash = 0xcbf29ce484222325; + if (is_type_string(key_type)) { + GB_ASSERT(key->Constant.value.kind == ExactValue_String); + String s = key->Constant.value.value_string; + hash = fnv64a(s.text, s.len); + } else { + return nullptr; + } + // TODO(bill): other const hash types + + if (build_context.word_size == 4) { + hash &= 0xffffffffull; + } + hashed_key = ir_const_uintptr(hash); + } + + return hashed_key; +} + +irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) { Type *hash_type = t_u64; - irValue *v = ir_add_local_generated(proc, t_map_key, true); + irValue *v = ir_add_local_generated(proc, t_map_hash, true); Type *t = base_type(ir_type(key)); key = ir_emit_conv(proc, key, key_type); - if (is_type_string(t)) { - irValue *str = ir_emit_conv(proc, key, t_string); - irValue *hashed_str = nullptr; + irValue *key_ptr = ir_address_from_load_or_generate_local(proc, key); + key_ptr = ir_emit_conv(proc, key_ptr, t_rawptr); - 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)); - } else { - auto args = array_make(ir_allocator(), 1); - args[0] = str; - hashed_str = ir_emit_runtime_call(proc, "default_hash_string", args); - } - ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_str); - - 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(ir_allocator(), 2); - args[0] = ir_address_from_load_or_generate_local(proc, key); - args[1] = ir_const_int(sz); - irValue *hash = ir_emit_runtime_call(proc, "default_hash_ptr", args); - - - irValue *hash_ptr = ir_emit_struct_ep(proc, v, 0); - irValue *key_data = ir_emit_struct_ep(proc, v, 1); - key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type)); - - ir_emit_store(proc, hash_ptr, hash); - ir_emit_store(proc, key_data, key); - } + irValue *hashed_key = ir_const_hash(proc->module, key, key_type); + if (hashed_key == nullptr) { + irValue *hasher = ir_get_hasher_proc_for_type(proc->module, key_type); + auto args = array_make(permanent_allocator(), 2); + args[0] = key_ptr; + args[1] = ir_value_constant(t_uintptr, exact_value_i64(0)); + hashed_key = ir_emit_call(proc, hasher, args); } + ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_key); + ir_emit_store(proc, ir_emit_struct_ep(proc, v, 1), key_ptr); + 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(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(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); irValue *lhs = ir_address_from_load_or_generate_local(proc, x); args[0] = ir_emit_conv(proc, lhs, t_rawptr); args[1] = ir_const_int(type_size_of(t)); @@ -4848,7 +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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); irValue *lhs = ir_address_from_load_or_generate_local(proc, x); args[0] = ir_emit_conv(proc, lhs, t_rawptr); args[1] = ir_const_int(type_size_of(t)); @@ -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(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + return ir_emit_runtime_call(p, name, args); + } + + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = ir_const_int(type_size_of(type)); + return ir_emit_runtime_call(p, "default_hasher_n", args); +} + +irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) { + Type *original_type = type; + type = core_type(type); + 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(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + irValue *res = ir_emit_runtime_call(proc, "default_hasher_cstring", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else if (is_type_string(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args); + ir_emit(proc, ir_instr_return(proc, res)); + } else if (type->kind == Type_Struct) { + type_set_offsets(type); + data = ir_emit_conv(proc, data, t_u8_ptr); + + auto args = array_make(permanent_allocator(), 2); + for_array(i, type->Struct.fields) { + i64 offset = type->Struct.offsets[i]; + Entity *field = type->Struct.fields[i]; + irValue *field_hasher = ir_get_hasher_proc_for_type(m, field->type); + irValue *ptr = ir_emit_ptr_offset(proc, data, ir_const_uintptr(offset)); + + args[0] = ptr; + args[1] = seed; + seed = ir_emit_call(proc, field_hasher, args); + } + ir_emit(proc, ir_instr_return(proc, seed)); + } else if (type->kind == Type_Array) { + irValue *pres = ir_add_local_generated(proc, t_uintptr, false); + ir_emit_store(proc, pres, seed); + + auto args = array_make(permanent_allocator(), 2); + irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); + + auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); + + data = ir_emit_conv(proc, data, pt); + + irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); + args[0] = ptr; + args[1] = ir_emit_load(proc, pres); + irValue *new_seed = ir_emit_call(proc, elem_hasher, args); + ir_emit_store(proc, pres, new_seed); + + ir_loop_end(proc, loop_data); + + irValue *res = ir_emit_load(proc, pres); + ir_emit(proc, ir_instr_return(proc, res)); + } else if (type->kind == Type_EnumeratedArray) { + irValue *pres = ir_add_local_generated(proc, t_uintptr, false); + ir_emit_store(proc, pres, seed); + + auto args = array_make(permanent_allocator(), 2); + irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem); + + auto loop_data = ir_loop_start(proc, type->Array.count, t_i32); + + data = ir_emit_conv(proc, data, pt); + + irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx); + args[0] = ptr; + args[1] = ir_emit_load(proc, pres); + irValue *new_seed = ir_emit_call(proc, elem_hasher, args); + ir_emit_store(proc, pres, new_seed); + + ir_loop_end(proc, loop_data); + + irValue *res = ir_emit_load(proc, pres); + ir_emit(proc, ir_instr_return(proc, res)); + } else { + 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(heap_allocator(), 3); + auto args = array_make(permanent_allocator(), 3); args[0] = ir_emit_conv(proc, lhs, t_rawptr); args[1] = ir_emit_conv(proc, rhs, t_rawptr); args[2] = ir_const_int(type_size_of(tl)); @@ -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(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(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 stmts); +void ir_build_stmt_list(irProcedure *proc, Slice stmts); void ir_build_assign_op(irProcedure *proc, irAddr const &lhs, irValue *value, TokenKind op); bool is_double_pointer(Type *t) { @@ -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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = ir_emit_conv(proc, ir_build_expr(proc, ce->args[0]), t_i32); args[1] = ir_build_expr(proc, ce->args[1]); return ir_emit(proc, ir_instr_inline_code(proc, id, args, t_u8_ptr)); @@ -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(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(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(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -9123,8 +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(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -9232,8 +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(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -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 stmts) { +void ir_build_stmt_list(irProcedure *proc, Slice 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 default_stmts = {}; + Slice 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 empty_args = {}; + if (build_context.command_kind == Command_test) { + for_array(i, m->info->testing_procedures) { + Entity *e = m->info->testing_procedures[i]; + irValue **found = map_get(&proc->module->values, hash_entity(e)); + GB_ASSERT(found != nullptr); + ir_emit_call(proc, *found, empty_args); + } + } else { irValue **found = map_get(&proc->module->values, hash_entity(entry_point)); if (found != nullptr) { - Array args = {}; - ir_emit_call(proc, *found, args); + ir_emit_call(proc, *found, empty_args); } } @@ -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 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 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 compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + 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 compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + switch (sz) { + case 1: + case 2: + case 4: + case 8: + args[i] = lb_arg_type_direct(t, LLVMIntTypeInContext(c, 8*cast(unsigned)sz), nullptr, nullptr); + break; + default: + args[i] = lb_arg_type_indirect(t, nullptr); + break; + } + } else { + args[i] = lbAbi386::non_struct(c, t, 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 *cs) { + for_array(i, *cs) { + (*cs)[i] = RegClass_Memory; + } + } + + enum Amd64TypeAttributeKind { + Amd64TypeAttribute_None, + Amd64TypeAttribute_ByVal, + Amd64TypeAttribute_StructRect, + }; + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off); + void fixup(LLVMTypeRef t, Array *cls); + lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind); + Array classify(LLVMTypeRef t); + LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes); + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->calling_convention = calling_convention; + + ft->args = array_make(heap_allocator(), arg_count); + for (unsigned i = 0; i < arg_count; i++) { + ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal); + } + + if (return_is_defined) { + ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect); + } else { + ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } + + return ft; + } + + bool is_mem_cls(Array const &cls, Amd64TypeAttributeKind attribute_kind) { + if (attribute_kind == Amd64TypeAttribute_ByVal) { + if (cls.count == 0) { + return false; + } + auto first = cls[0]; + return first == RegClass_Memory || first == RegClass_X87 || first == RegClass_ComplexX87; + } else if (attribute_kind == Amd64TypeAttribute_StructRect) { + if (cls.count == 0) { + return false; + } + return cls[0] == RegClass_Memory; + } + return false; + } + + bool is_register(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMIntegerTypeKind: + case LLVMFloatTypeKind: + case LLVMDoubleTypeKind: + case LLVMPointerTypeKind: + return true; + } + return false; + } + + lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind) { + if (is_register(type)) { + LLVMAttributeRef attribute = nullptr; + if (type == LLVMInt1TypeInContext(c)) { + attribute = lb_create_enum_attribute(c, "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 classify(LLVMTypeRef t) { + i64 sz = lb_sizeof(t); + i64 words = (sz + 7)/8; + auto reg_classes = array_make(heap_allocator(), cast(isize)words); + if (words > 4) { + all_mem(®_classes); + } else { + classify_with(t, ®_classes, 0, 0); + fixup(t, ®_classes); + } + return reg_classes; + } + + void unify(Array *cls, i64 i, RegClass newv) { + RegClass &oldv = (*cls)[i]; + if (oldv == newv) { + return; + } else if (oldv == RegClass_NoClass) { + oldv = newv; + } else if (newv == RegClass_NoClass) { + return; + } else if (oldv == RegClass_Memory || newv == RegClass_Memory) { + return; + } else if (oldv == RegClass_Int || newv == RegClass_Int) { + return; + } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 || + newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { + oldv = RegClass_Memory; + } else { + oldv = newv; + } + } + + void fixup(LLVMTypeRef t, Array *cls) { + i64 i = 0; + i64 e = cls->count; + if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) { + RegClass &oldv = (*cls)[i]; + if (is_sse(oldv)) { + for (i++; i < e; i++) { + if (oldv != RegClass_SSEUp) { + all_mem(cls); + return; + } + } + } else { + all_mem(cls); + return; + } + } else { + while (i < e) { + RegClass &oldv = (*cls)[i]; + if (oldv == RegClass_Memory) { + all_mem(cls); + return; + } else if (oldv == RegClass_X87Up) { + // NOTE(bill): Darwin + all_mem(cls); + return; + } else if (oldv == RegClass_SSEUp) { + oldv = RegClass_SSEDv; + } else if (is_sse(oldv)) { + i++; + while (i != e && oldv == RegClass_SSEUp) { + i++; + } + } else if (oldv == RegClass_X87) { + i++; + while (i != e && oldv == RegClass_X87Up) { + i++; + } + } else { + i++; + } + } + } + } + + unsigned llvec_len(Array const ®_classes, 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 const ®_classes) { + auto types = array_make(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 *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 compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + if (sz == 0) { + args[i] = lb_arg_type_ignore(t); + } else { + args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true)); + } + } else { + args[i] = non_struct(c, t); + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) { + i64 sz = lb_sizeof(return_type); + switch (sz) { + case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr); + case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr); + case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); + } + return lb_arg_type_indirect(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(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 5); + auto args = array_make(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(heap_allocator(), 5); + auto args = array_make(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(heap_allocator(), 6); + auto args = array_make(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(heap_allocator(), gb_max(arg_count, param_count)); + auto args = array_make(permanent_allocator(), gb_max(arg_count, param_count)); args[0] = ptr; args[1] = index; args[2] = value; @@ -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(heap_allocator(), 2); + auto args = array_make(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); - 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; - } + LLVMTypeRef ret = nullptr; + LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count); + if (type->Proc.result_count != 0) { + Type *single_ret = reduce_tuple_to_single_type(type->Proc.results); + ret = lb_type(m, single_ret); + if (ret != nullptr) { + if (is_type_boolean(single_ret) && + is_calling_convention_none(type->Proc.calling_convention) && + type_size_of(single_ret) <= 1) { + ret = LLVMInt1TypeInContext(m->ctx); + } + } + } - isize param_count = type->Proc.abi_compat_params.count + extra_param_count; - auto param_types = array_make(heap_allocator(), 0, param_count); - defer (array_free(¶m_types)); + 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; + } - if (type->Proc.return_by_pointer) { - array_add(¶m_types, LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0)); - } + Type *e_type = reduce_tuple_to_single_type(e->type); - for_array(i, type->Proc.abi_compat_params) { - Type *param = type->Proc.abi_compat_params[i]; - if (param == nullptr) { - continue; + 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); + } + } + + 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); + + + 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 (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 + } else { + LLVMTypeRef old_abi_fn_type = nullptr; + + set_procedure_abi_types(type); + LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx); + if (type->Proc.return_by_pointer) { + // Void + } else if (type->Proc.abi_compat_result_type != nullptr) { + return_type = lb_type(m, type->Proc.abi_compat_result_type); + } + + isize extra_param_count = 0; + if (type->Proc.return_by_pointer) { + extra_param_count += 1; + } + if (type->Proc.calling_convention == ProcCC_Odin) { + extra_param_count += 1; + } + + isize param_count = type->Proc.abi_compat_params.count + extra_param_count; + auto param_types = array_make(temporary_allocator(), 0, param_count); + + if (type->Proc.return_by_pointer) { + array_add(¶m_types, LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0)); + } + + for_array(i, type->Proc.abi_compat_params) { + Type *param = type->Proc.abi_compat_params[i]; + if (param == nullptr) { continue; } - array_add(¶m_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(¶m_types, t); + } + } else { + array_add(¶m_types, lb_type(m, param)); + } + } + if (type->Proc.calling_convention == ProcCC_Odin) { + array_add(¶m_types, lb_type(m, t_rawptr)); + // array_add(¶m_types, lb_type(m, t_context_ptr)); } - } else { - array_add(¶m_types, lb_type(m, param)); - } - } - if (type->Proc.calling_convention == ProcCC_Odin) { - array_add(¶m_types, lb_type(m, t_context_ptr)); - } - LLVMTypeRef t = LLVMFunctionType(return_type, param_types.data, cast(unsigned)param_types.count, type->Proc.c_vararg); - return LLVMPointerType(t, 0); + old_abi_fn_type = LLVMFunctionType(return_type, param_types.data, cast(unsigned)param_types.count, type->Proc.c_vararg); + return LLVMPointerType(old_abi_fn_type, 0); + } } + break; case Type_BitFieldValue: - return LLVMIntType(type->BitFieldValue.bits); + return LLVMIntTypeInContext(m->ctx, type->BitFieldValue.bits); case Type_BitField: { @@ -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; - } - - if (p->type->Proc.params != nullptr) { - TypeTuple *params = &p->type->Proc.params->Tuple; - auto abi_types = p->type->Proc.abi_compat_params; - - for_array(i, params->variables) { - Entity *e = params->variables[i]; - if (e->kind != Entity_Variable) { - continue; - } - Type *abi_type = e->type; - if (abi_types.count > 0) { - abi_type = abi_types[i]; - } - if (e->token.string != "") { - lb_add_param(p, e, nullptr, abi_type, parameter_index); - } - if (is_type_tuple(abi_type)) { - parameter_index += cast(i32)abi_type->Tuple.variables.count; - } else { - parameter_index += 1; - } + param_offset += 1; } - } + if (p->type->Proc.params != nullptr) { + TypeTuple *params = &p->type->Proc.params->Tuple; - if (p->type->Proc.has_named_results) { - GB_ASSERT(p->type->Proc.result_count > 0); - TypeTuple *results = &p->type->Proc.results->Tuple; + unsigned param_index = 0; + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } - for_array(i, results->variables) { - Entity *e = results->variables[i]; - GB_ASSERT(e->kind == Entity_Variable); + lbArgType *arg_type = &ft->args[param_index]; + if (arg_type->kind == lbArg_Ignore) { + continue; + } else if (arg_type->kind == lbArg_Direct) { + lbParamPasskind kind = lbParamPass_Value; + LLVMTypeRef param_type = lb_type(p->module, e->type); + if (param_type != arg_type->type) { + kind = lbParamPass_BitCast; + } + LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index); - if (e->token.string != "") { - GB_ASSERT(!is_blank_ident(e->token)); + value = OdinLLVMBuildTransmute(p, value, param_type); - 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); + 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); } - res = lb_addr(ptr); - lb_add_entity(p->module, e, ptr); - } else { - res = lb_add_local(p, e->type, e); - } + 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, ""); - 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); + lbValue param = {}; + param.value = value; + param.type = e->type; + array_add(&p->params, param); + + lbValue ptr = {}; + ptr.value = value_ptr; + ptr.type = alloc_type_pointer(e->type); + + lb_add_entity(p->module, e, ptr); + param_index += 1; + } + } + } + + if (p->type->Proc.has_named_results) { + GB_ASSERT(p->type->Proc.result_count > 0); + TypeTuple *results = &p->type->Proc.results->Tuple; + + for_array(i, results->variables) { + Entity *e = results->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + + if (e->token.string != "") { + GB_ASSERT(!is_blank_ident(e->token)); + + lbAddr res = {}; + if (return_ptr_value.value) { + lbValue ptr = return_ptr_value; + if (results->variables.count != 1) { + ptr = lb_emit_struct_ep(p, ptr, cast(i32)i); + } + + res = lb_addr(ptr); + lb_add_entity(p->module, e, ptr); + } else { + res = lb_add_local(p, e->type, e); + } + + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); + lb_addr_store(p, res, c); + } + } + } + } + } else { + i32 parameter_index = 0; + lbValue return_ptr_value = {}; + if (p->type->Proc.return_by_pointer) { + // NOTE(bill): this must be parameter 0 + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); + Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; + + return_ptr_value.value = LLVMGetParam(p->value, 0); + return_ptr_value.type = ptr_type; + p->return_ptr = lb_addr(return_ptr_value); + + lb_add_entity(p->module, e, return_ptr_value); + + parameter_index += 1; + } + + if (p->type->Proc.params != nullptr) { + TypeTuple *params = &p->type->Proc.params->Tuple; + auto abi_types = p->type->Proc.abi_compat_params; + + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + Type *abi_type = e->type; + if (abi_types.count > 0) { + abi_type = abi_types[i]; + } + if (e->token.string != "") { + lb_add_param(p, e, nullptr, abi_type, parameter_index); + } + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; + } + } + } + + + if (p->type->Proc.has_named_results) { + GB_ASSERT(p->type->Proc.result_count > 0); + TypeTuple *results = &p->type->Proc.results->Tuple; + + for_array(i, results->variables) { + Entity *e = results->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + + if (e->token.string != "") { + GB_ASSERT(!is_blank_ident(e->token)); + + lbAddr res = {}; + if (p->type->Proc.return_by_pointer) { + lbValue ptr = return_ptr_value; + if (results->variables.count != 1) { + ptr = lb_emit_struct_ep(p, ptr, cast(i32)i); + } + + res = lb_addr(ptr); + lb_add_entity(p->module, e, ptr); + } else { + res = lb_add_local(p, e->type, e); + } + + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); + lb_addr_store(p, res, c); + } } } } } - if (p->type->Proc.calling_convention == ProcCC_Odin) { Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; @@ -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 const &stmts) { +void lb_build_stmt_list(lbProcedure *p, Slice 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(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = lb_emit_string(p, str_elem, str_len); lbValue rune_and_len = lb_emit_runtime_call(p, "string_decode_rune", args); lbValue len = lb_emit_struct_ev(p, rune_and_len, 1); @@ -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 default_stmts = {}; + Slice 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(heap_allocator(), 0, vd->names.count); - auto inits = array_make(heap_allocator(), 0, vd->names.count); + auto lvals = array_make(permanent_allocator(), 0, vd->names.count); + auto inits = array_make(permanent_allocator(), 0, vd->names.count); for_array(i, vd->names) { Ast *name = vd->names[i]; @@ -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(heap_allocator(), 0, as->lhs.count); + auto lvals = array_make(permanent_allocator(), 0, as->lhs.count); for_array(i, as->lhs) { Ast *lhs = as->lhs[i]; @@ -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(heap_allocator(), 0, lvals.count); + auto inits = array_make(permanent_allocator(), 0, lvals.count); for_array(i, as->rhs) { lbValue init = lb_build_expr(p, as->rhs[i]); @@ -4125,7 +4445,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } } else { - auto inits = array_make(heap_allocator(), 0, lvals.count); + auto inits = array_make(permanent_allocator(), 0, lvals.count); for_array(i, as->rhs) { lbValue init = lb_build_expr(p, as->rhs[i]); @@ -4217,7 +4537,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } else { - auto results = array_make(heap_allocator(), 0, return_count); + auto results = array_make(permanent_allocator(), 0, return_count); if (res_count != 0) { for (isize res_index = 0; res_index < res_count; res_index++) { @@ -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); } - - lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - - LLVMBuildRetVoid(p->builder); } else { - GB_ASSERT_MSG(res.value != nullptr, "%.*s", LIT(p->name)); - Type *abi_rt = p->type->Proc.abi_compat_result_type; - if (!are_types_identical(res.type, abi_rt)) { - res = lb_emit_transmute(p, res, abi_rt); + GB_ASSERT(!USE_LLVM_ABI); + + if (p->type->Proc.return_by_pointer) { + if (res.value != nullptr) { + lb_addr_store(p, p->return_ptr, res); + } else { + lb_addr_store(p, p->return_ptr, lb_const_nil(p->module, p->type->Proc.abi_compat_result_type)); + } + + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + + LLVMBuildRetVoid(p->builder); + } else { + GB_ASSERT_MSG(res.value != nullptr, "%.*s", LIT(p->name)); + + Type *abi_rt = p->type->Proc.abi_compat_result_type; + if (!are_types_identical(res.type, abi_rt)) { + res = lb_emit_transmute(p, res, abi_rt); + } + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + LLVMBuildRet(p->builder, res.value); } - - lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - - LLVMBuildRet(p->builder, res.value); } + + case_end; case_ast_node(is, IfStmt, node); @@ -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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 2); + auto args = array_make(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(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = c; lbValue s = lb_emit_runtime_call(p, "cstring_to_string", args); return lb_emit_conv(p, s, dst); @@ -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(a, 1); + auto args = array_make(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 lb_value_to_array(lbProcedure *p, lbValue value) { GB_ASSERT(t->kind == Type_Tuple); auto *rt = &t->Tuple; if (rt->variables.count > 0) { - array = array_make(heap_allocator(), rt->variables.count); + array = array_make(permanent_allocator(), rt->variables.count); for_array(i, rt->variables) { lbValue elem = lb_emit_struct_ev(p, value, cast(i32)i); array[i] = elem; } } } else { - array = array_make(heap_allocator(), 1); + array = array_make(permanent_allocator(), 1); array[0] = value; } return array; @@ -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 const &args) { @@ -7048,7 +7454,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, LLVMBuildUnreachable(p->builder); }); - set_procedure_abi_types(heap_allocator(), pt); + set_procedure_abi_types(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 const &args, GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count); } - auto processed_args = array_make(heap_allocator(), 0, args.count); - - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->Proc.params->Tuple.variables[i]; - if (e->kind != Entity_Variable) { - // array_add(&processed_args, args[i]); - continue; - } - GB_ASSERT(e->flags & EntityFlag_Param); - - Type *original_type = e->type; - Type *new_type = pt->Proc.abi_compat_params[i]; - Type *arg_type = args[i].type; - - if (are_types_identical(arg_type, new_type)) { - // NOTE(bill): Done - array_add(&processed_args, args[i]); - } else if (!are_types_identical(original_type, new_type)) { - if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { - Type *av = core_type(type_deref(new_type)); - if (are_types_identical(av, core_type(original_type))) { - if (e->flags&EntityFlag_ImplicitReference) { - array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i])); - } else if (!is_type_pointer(arg_type)) { - array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16)); - } - } else { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } - } else if (new_type == t_llvm_bool) { - array_add(&processed_args, lb_emit_conv(p, args[i], new_type)); - } else if (is_type_integer(new_type) || is_type_float(new_type) || is_type_boolean(new_type)) { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } else if (is_type_simd_vector(new_type)) { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } else if (is_type_tuple(new_type)) { - Type *abi_type = pt->Proc.abi_compat_params[i]; - Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); - lbValue x = {}; - i64 st_sz = type_size_of(st); - i64 arg_sz = type_size_of(args[i].type); - if (st_sz == arg_sz) { - x = lb_emit_transmute(p, args[i], st); - } else { - // NOTE(bill): struct{f32, f32, f32} != struct{#simd[2]f32, f32} - GB_ASSERT(st_sz > arg_sz); - lbAddr xx = lb_add_local_generated(p, st, false); - lbValue pp = lb_emit_conv(p, xx.addr, alloc_type_pointer(args[i].type)); - lb_emit_store(p, pp, args[i]); - x = lb_addr_load(p, xx); - } - for (isize j = 0; j < new_type->Tuple.variables.count; j++) { - lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j); - array_add(&processed_args, xx); - } - } - } else { - lbValue x = lb_emit_conv(p, args[i], new_type); - array_add(&processed_args, x); - } - } - - if (inlining == ProcInlining_none) { - inlining = p->inlining; - } - lbValue result = {}; - Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type); - Type *rt = reduce_tuple_to_single_type(results); - if (pt->Proc.return_by_pointer) { - lbValue return_ptr = {}; - if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { - if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { - return_ptr = p->return_ptr_hint_value; - p->return_ptr_hint_used = true; + auto processed_args = array_make(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(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 { + x.value = OdinLLVMBuildTransmute(p, x.value, abi_type); + array_add(&processed_args, x); + } + + } 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 { + ptr = lb_copy_value_to_ptr(p, x, original_type, 16); + } + 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 { + lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining); + } + + } 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 *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 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(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = lb_emit_conv(p, value, t_cstring); return lb_emit_runtime_call(p, "cstring_len", args); } @@ -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(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = lb_build_expr(p, arg); return lb_emit_runtime_call(p, "__type_info_of", args); } @@ -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(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(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = x; switch (sz) { case 128: return lb_emit_runtime_call(p, "abs_quaternion128", args); @@ -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(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = x; switch (sz) { case 64: return lb_emit_runtime_call(p, "abs_complex64", args); @@ -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(heap_allocator(), 1); + auto args = array_make(permanent_allocator(), 1); args[0] = x; switch (sz) { case 32: return lb_emit_runtime_call(p, "abs_f32", args); @@ -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(heap_allocator(), pt->param_count); + auto args = array_make(permanent_allocator(), pt->param_count); for_array(arg_index, ce->args) { Ast *arg = ce->args[arg_index]; @@ -8241,7 +8740,7 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { param_count = pt->params->Tuple.variables.count; } - auto args = array_make(heap_allocator(), cast(isize)gb_max(param_count, arg_count)); + auto args = array_make(permanent_allocator(), cast(isize)gb_max(param_count, arg_count)); isize variadic_index = pt->variadic_index; bool variadic = pt->variadic && variadic_index >= 0; bool vari_expand = ce->ellipsis.pos.line != 0; @@ -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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); lbValue lhs = lb_address_from_load_or_generate_local(p, x); args[0] = lb_emit_conv(p, lhs, t_rawptr); args[1] = lb_const_int(p->module, t_int, type_size_of(t)); @@ -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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); lbValue lhs = lb_address_from_load_or_generate_local(p, x); args[0] = lb_emit_conv(p, lhs, t_rawptr); args[1] = lb_const_int(p->module, t_int, type_size_of(t)); @@ -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(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + return lb_emit_runtime_call(p, name, args); + } + + auto args = array_make(permanent_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = lb_const_int(p->module, t_int, type_size_of(type)); + return lb_emit_runtime_call(p, "default_hasher_n", args); +} + +lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { + Type *original_type = type; + type = core_type(type); + 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(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(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(permanent_allocator(), 2); + lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem); + + auto loop_data = lb_loop_start(p, type->EnumeratedArray.count, t_i32); + + data = lb_emit_conv(p, data, pt); + + lbValue ptr = lb_emit_array_ep(p, data, loop_data.idx); + args[0] = ptr; + args[1] = lb_addr_load(p, res); + lbValue new_seed = lb_emit_call(p, elem_hasher, args); + lb_addr_store(p, res, new_seed); + + lb_loop_end(p, loop_data); + + lbValue vres = lb_addr_load(p, res); + LLVMBuildRet(p->builder, vres.value); + } else if (is_type_cstring(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args); + LLVMBuildRet(p->builder, res.value); + } else if (is_type_string(type)) { + auto args = array_make(permanent_allocator(), 2); + args[0] = data; + 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(heap_allocator(), 3); + auto args = array_make(permanent_allocator(), 3); args[0] = lb_emit_conv(p, lhs, t_rawptr); args[1] = lb_emit_conv(p, rhs, t_rawptr); args[2] = lb_const_int(p->module, t_int, type_size_of(tl)); @@ -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(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(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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; return lb_emit_runtime_call(p, runtime_procedure, args); @@ -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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; return lb_emit_runtime_call(p, runtime_procedure, args); @@ -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(heap_allocator(), 2); + auto args = array_make(permanent_allocator(), 2); args[0] = left; args[1] = right; return lb_emit_runtime_call(p, runtime_procedure, args); @@ -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(heap_allocator(), 6); + auto args = array_make(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(heap_allocator(), 6); + auto args = array_make(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(heap_allocator(), 6); + auto args = array_make(permanent_allocator(), 6); args[0] = ok; args[1] = lb_find_or_add_entity_string(p->module, pos.file); @@ -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(heap_allocator(), 6); + auto args = array_make(permanent_allocator(), 6); args[0] = ok; args[1] = lb_find_or_add_entity_string(p->module, pos.file); @@ -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) { +lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { + if (true) { + return {}; + } + + lbValue hashed_key = {}; + + + if (lb_is_const(key)) { + u64 hash = 0xcbf29ce484222325; + if (is_type_cstring(key_type)) { + size_t length = 0; + char const *text = LLVMGetAsString(key.value, &length); + hash = fnv64a(text, cast(isize)length); + } else if (is_type_string(key_type)) { + unsigned data_indices[] = {0}; + unsigned len_indices[] = {1}; + LLVMValueRef data = LLVMConstExtractValue(key.value, data_indices, gb_count_of(data_indices)); + LLVMValueRef len = LLVMConstExtractValue(key.value, len_indices, gb_count_of(len_indices)); + isize length = LLVMConstIntGetSExtValue(len); + char const *text = nullptr; + if (length != 0) { + if (LLVMGetConstOpcode(data) != LLVMGetElementPtr) { + return {}; + } + // TODO(bill): THIS IS BROKEN! THIS NEEDS FIXING :P + + size_t ulength = 0; + text = LLVMGetAsString(data, &ulength); + gb_printf_err("%td %td %s\n", length, ulength, text); + length = gb_min(length, cast(isize)ulength); + } + hash = fnv64a(text, cast(isize)length); + } else { + return {}; + } + // TODO(bill): other const hash types + + if (build_context.word_size == 4) { + hash &= 0xffffffffull; + } + hashed_key = lb_const_int(m, t_uintptr, hash); + } + + return hashed_key; +} + +lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type) { Type *hash_type = t_u64; - lbAddr v = lb_add_local_generated(p, t_map_key, true); + lbAddr v = lb_add_local_generated(p, t_map_hash, true); lbValue vp = lb_addr_get_ptr(p, v); Type *t = base_type(key.type); key = lb_emit_conv(p, key, key_type); - if (is_type_string(t)) { - lbValue str = lb_emit_conv(p, key, t_string); - lbValue hashed_str = {}; + lbValue key_ptr = lb_address_from_load_or_generate_local(p, key); + key_ptr = lb_emit_conv(p, key_ptr, t_rawptr); - 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); - } else { - auto args = array_make(heap_allocator(), 1); - args[0] = str; - hashed_str = lb_emit_runtime_call(p, "default_hash_string", args); - } - lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_str); + 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); - 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(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); - - - lbValue hash_ptr = lb_emit_struct_ep(p, vp, 0); - lbValue key_data = lb_emit_struct_ep(p, vp, 1); - key_data = lb_emit_conv(p, key_data, alloc_type_pointer(key_type)); - - lb_emit_store(p, hash_ptr, hash); - lb_emit_store(p, key_data, key); - } + auto args = array_make(permanent_allocator(), 2); + args[0] = key_ptr; + args[1] = lb_const_int(p->module, t_uintptr, 0); + hashed_key = lb_emit_call(p, hasher, args); } + lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_key); + lb_emit_store(p, lb_emit_struct_ep(p, vp, 1), key_ptr); + 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(heap_allocator(), 4); + auto args = array_make(permanent_allocator(), 4); args[0] = h; args[1] = key; args[2] = lb_emit_conv(p, value_addr.addr, t_rawptr); @@ -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(a, 3); + auto args = array_make(permanent_allocator(), 3); args[0] = lb_gen_map_header(p, v.addr, type); args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count); args[2] = lb_emit_source_code_location(p, proc_name, pos); @@ -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(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -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(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { @@ -10672,8 +11442,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { lbValue data = lb_slice_elem(p, slice); - auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); - defer (array_free(&temp_data)); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -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(a, 5); + auto args = array_make(permanent_allocator(), 5); args[0] = lb_emit_conv(p, lb_addr_get_ptr(p, v), t_rawptr); args[1] = size; args[2] = align; @@ -10827,7 +11595,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { } { - auto args = array_make(a, 6); + auto args = array_make(permanent_allocator(), 6); args[0] = lb_emit_conv(p, v.addr, t_rawptr); args[1] = size; args[2] = align; @@ -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(heap_allocator(), 0, global_variable_max_count); + auto global_variables = array_make(permanent_allocator(), 0, global_variable_max_count); for_array(i, info->variable_init_order) { DeclInfo *d = info->variable_init_order[i]; @@ -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); + { + auto dfpm = 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); + 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); - 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); + // LLVMAddInstructionCombiningPass(dfpm); + LLVMAddSLPVectorizePass(dfpm); + LLVMAddLoopVectorizePass(dfpm); + LLVMAddScalarizerPass(dfpm); + LLVMAddLoopIdiomPass(dfpm); + } else { + bool do_extra_passes = true; + + int repeat_count = cast(int)build_context.optimization_level; + do { + LLVMAddMemCpyOptPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddMergedLoadStoreMotionPass(dfpm); + LLVMAddAlignmentFromAssumptionsPass(dfpm); + LLVMAddEarlyCSEPass(dfpm); + LLVMAddEarlyCSEMemSSAPass(dfpm); + LLVMAddConstantPropagationPass(dfpm); + if (do_extra_passes) { + LLVMAddAggressiveInstCombinerPass(dfpm); + LLVMAddAggressiveDCEPass(dfpm); + } + LLVMAddMergedLoadStoreMotionPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddCFGSimplificationPass(dfpm); + LLVMAddTailCallEliminationPass(dfpm); + + if (do_extra_passes) { + LLVMAddSLPVectorizePass(dfpm); + LLVMAddLoopVectorizePass(dfpm); + + LLVMAddConstantPropagationPass(dfpm); + LLVMAddScalarizerPass(dfpm); + LLVMAddLoopIdiomPass(dfpm); + + LLVMAddAggressiveInstCombinerPass(dfpm); + LLVMAddLowerExpectIntrinsicPass(dfpm); + + LLVMAddDeadStoreEliminationPass(dfpm); + LLVMAddReassociatePass(dfpm); + LLVMAddAddDiscriminatorsPass(dfpm); + LLVMAddPromoteMemoryToRegisterPass(dfpm); + LLVMAddCorrelatedValuePropagationPass(dfpm); + } + } while (repeat_count --> 0); } - }*/ - 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); - - - // LLVMAddInstructionCombiningPass(dfpm); - LLVMAddSLPVectorizePass(dfpm); - LLVMAddLoopVectorizePass(dfpm); - LLVMAddEarlyCSEPass(dfpm); - LLVMAddEarlyCSEMemSSAPass(dfpm); - - LLVMAddScalarizerPass(dfpm); - LLVMAddLoopIdiomPass(dfpm); - - // LLVMAddAggressiveInstCombinerPass(dfpm); - // LLVMAddLowerExpectIntrinsicPass(dfpm); - - // LLVMAddPartiallyInlineLibCallsPass(dfpm); - - // LLVMAddAlignmentFromAssumptionsPass(dfpm); - // LLVMAddDeadStoreEliminationPass(dfpm); - // LLVMAddReassociatePass(dfpm); - // LLVMAddAddDiscriminatorsPass(dfpm); - // LLVMAddPromoteMemoryToRegisterPass(dfpm); - // LLVMAddCorrelatedValuePropagationPass(dfpm); - // LLVMAddMemCpyOptPass(dfpm); } LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod); defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); { - LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy); - LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy); - LLVMAddAggressiveInstCombinerPass(default_function_pass_manager_without_memcpy); - LLVMAddConstantPropagationPass(default_function_pass_manager_without_memcpy); - LLVMAddAggressiveDCEPass(default_function_pass_manager_without_memcpy); - LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy); - LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy); - LLVMAddCFGSimplificationPass(default_function_pass_manager_without_memcpy); - // LLVMAddUnifyFunctionExitNodesPass(default_function_pass_manager_without_memcpy); + 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(¶ms->Tuple.variables, heap_allocator(), 2); + array_init(¶ms->Tuple.variables, permanent_allocator(), 2); params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true); params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), alloc_type_pointer(t_cstring), false, true); } - array_init(&results->Tuple.variables, heap_allocator(), 1); + array_init(&results->Tuple.variables, permanent_allocator(), 1); results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"), t_i32, false, true); Type *proc_type = alloc_type_proc(nullptr, @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#endif struct lbProcedure; @@ -74,6 +93,8 @@ struct lbModule { gbMutex mutex; Map types; // Key: Type * + Map llvm_types; // Key: LLVMTypeRef + i32 internal_type_level; Map values; // Key: Entity * StringMap members; @@ -83,6 +104,10 @@ struct lbModule { StringMap const_strings; Map anonymous_proc_lits; // Key: Ast * + Map function_type_map; // Key: Type * + + Map equal_procs; // Key: Type * + Map 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 ¶m_value, TokenPos const &pos); +lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type); +lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type); +lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #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 +#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 *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) { - BuildFlag flag = {kind, name, param_kind}; + +void add_flag(Array *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_mulitple=false) { + BuildFlag flag = {kind, name, param_kind, command_support, allow_mulitple}; array_add(build_flags, flag); } @@ -662,44 +669,50 @@ ExactValue build_param_to_exact_value(String name, String param) { bool parse_build_flags(Array args) { auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT); - add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); - add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_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_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); - 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_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); - 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); #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 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 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 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 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 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 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 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 args) { } } - - switch (bf.kind) { - case BuildFlag_Define: - // Allow for multiple - break; - default: + if (!bf.allow_mulitple) { set_flags[bf.kind] = ok; - break; } } break; } } - if (!found) { + if (found && !is_supported) { + gb_printf_err("Unknown flag for 'odin %.*s': '%.*s'\n", LIT(build_context.command), LIT(name)); + gb_printf_err("'%.*s' is supported with the following commands:\n", LIT(name)); + gb_printf_err("\t"); + i32 count = 0; + for (u32 i = 0; i < 32; i++) { + if (found_bf.command_support & (1< 0) { + gb_printf_err(", "); + } + gb_printf_err("%s", odin_command_strings[i]); + count += 1; + } + } + gb_printf_err("\n"); + bad_flags = true; + } else if (!found) { gb_printf_err("Unknown flag: '%.*s'\n", LIT(name)); bad_flags = true; } } - 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:"); 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:"); print_usage_line(2, "Adds extra linker specific flags in a string"); print_usage_line(0, ""); + + print_usage_line(1, "-microarch:"); + print_usage_line(2, "Specifies the specific micro-architecture for the build in a string"); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-microarch:sandybridge"); + print_usage_line(3, "-microarch:native"); + print_usage_line(0, ""); + } + + if (check) { + print_usage_line(1, "-disallow-do"); + print_usage_line(2, "Disallows the 'do' keyword in the project"); + print_usage_line(0, ""); + + print_usage_line(1, "-default-to-nil-allocator"); + print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing"); + print_usage_line(0, ""); } if (run_or_build) { @@ -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(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 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 run_args = array_make(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 clone_ast_array(Array array) { +Array clone_ast_array(Array const &array) { Array result = {}; if (array.count > 0) { result = array_make(ast_allocator(nullptr), array.count); @@ -119,13 +136,23 @@ Array clone_ast_array(Array array) { } return result; } +Slice clone_ast_array(Slice const &array) { + Slice result = {}; + if (array.count > 0) { + result = slice_clone(permanent_allocator(), array); + for_array(i, array) { + result[i] = clone_ast(array[i]); + } + } + return result; +} Ast *clone_ast(Ast *node) { if (node == nullptr) { 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 args, Token open, Token close, Token ellipsis) { +Ast *ast_call_expr(AstFile *f, Ast *proc, Array const &args, Token open, Token close, Token ellipsis) { Ast *result = alloc_ast_node(f, Ast_CallExpr); result->CallExpr.proc = proc; - result->CallExpr.args = args; + result->CallExpr.args = slice_from_array(args); result->CallExpr.open = open; result->CallExpr.close = close; result->CallExpr.ellipsis = ellipsis; @@ -624,7 +634,8 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; - result->BasicLit.value = exact_value_from_basic_literal(basic_lit); + result->tav.mode = Addressing_Constant; + result->tav.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -643,12 +654,12 @@ Ast *ast_ellipsis(AstFile *f, Token token, Ast *expr) { } -Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array args) { +Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array const &args) { Ast *result = alloc_ast_node(f, Ast_ProcGroup); result->ProcGroup.token = token; result->ProcGroup.open = open; result->ProcGroup.close = close; - result->ProcGroup.args = args; + result->ProcGroup.args = slice_from_array(args); return result; } @@ -658,7 +669,7 @@ Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token, result->ProcLit.body = body; result->ProcLit.tags = tags; result->ProcLit.where_token = where_token; - result->ProcLit.where_clauses = where_clauses; + result->ProcLit.where_clauses = slice_from_array(where_clauses); return result; } @@ -670,10 +681,10 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) { return result; } -Ast *ast_compound_lit(AstFile *f, Ast *type, Array elems, Token open, Token close) { +Ast *ast_compound_lit(AstFile *f, Ast *type, Array const &elems, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_CompoundLit); result->CompoundLit.type = type; - result->CompoundLit.elems = elems; + result->CompoundLit.elems = slice_from_array(elems); result->CompoundLit.open = open; result->CompoundLit.close = close; return result; @@ -736,7 +747,7 @@ Ast *ast_inline_asm_expr(AstFile *f, Token token, Token open, Token close, result->InlineAsmExpr.token = token; result->InlineAsmExpr.open = open; result->InlineAsmExpr.close = close; - result->InlineAsmExpr.param_types = param_types; + result->InlineAsmExpr.param_types = slice_from_array(param_types); result->InlineAsmExpr.return_type = return_type; result->InlineAsmExpr.asm_string = asm_string; result->InlineAsmExpr.constraints_string = constraints_string; @@ -768,18 +779,18 @@ Ast *ast_expr_stmt(AstFile *f, Ast *expr) { return result; } -Ast *ast_assign_stmt(AstFile *f, Token op, Array lhs, Array rhs) { +Ast *ast_assign_stmt(AstFile *f, Token op, Array const &lhs, Array const &rhs) { Ast *result = alloc_ast_node(f, Ast_AssignStmt); result->AssignStmt.op = op; - result->AssignStmt.lhs = lhs; - result->AssignStmt.rhs = rhs; + result->AssignStmt.lhs = slice_from_array(lhs); + result->AssignStmt.rhs = slice_from_array(rhs); return result; } -Ast *ast_block_stmt(AstFile *f, Array stmts, Token open, Token close) { +Ast *ast_block_stmt(AstFile *f, Array const &stmts, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_BlockStmt); - result->BlockStmt.stmts = stmts; + result->BlockStmt.stmts = slice_from_array(stmts); result->BlockStmt.open = open; result->BlockStmt.close = close; return result; @@ -805,10 +816,10 @@ Ast *ast_when_stmt(AstFile *f, Token token, Ast *cond, Ast *body, Ast *else_stmt } -Ast *ast_return_stmt(AstFile *f, Token token, Array results) { +Ast *ast_return_stmt(AstFile *f, Token token, Array const &results) { Ast *result = alloc_ast_node(f, Ast_ReturnStmt); result->ReturnStmt.token = token; - result->ReturnStmt.results = results; + result->ReturnStmt.results = slice_from_array(results); return result; } @@ -866,11 +877,11 @@ Ast *ast_type_switch_stmt(AstFile *f, Token token, Ast *tag, Ast *body) { return result; } -Ast *ast_case_clause(AstFile *f, Token token, Array list, Array stmts) { +Ast *ast_case_clause(AstFile *f, Token token, Array const &list, Array const &stmts) { Ast *result = alloc_ast_node(f, Ast_CaseClause); result->CaseClause.token = token; - result->CaseClause.list = list; - result->CaseClause.stmts = stmts; + result->CaseClause.list = slice_from_array(list); + result->CaseClause.stmts = slice_from_array(stmts); return result; } @@ -889,10 +900,10 @@ Ast *ast_branch_stmt(AstFile *f, Token token, Ast *label) { return result; } -Ast *ast_using_stmt(AstFile *f, Token token, Array list) { +Ast *ast_using_stmt(AstFile *f, Token token, Array const &list) { Ast *result = alloc_ast_node(f, Ast_UsingStmt); result->UsingStmt.token = token; - result->UsingStmt.list = list; + result->UsingStmt.list = slice_from_array(list); return result; } @@ -905,10 +916,10 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) { return result; } -Ast *ast_field(AstFile *f, Array names, Ast *type, Ast *default_value, u32 flags, Token tag, +Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast *default_value, u32 flags, Token tag, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_Field); - result->Field.names = names; + result->Field.names = slice_from_array(names); result->Field.type = type; result->Field.default_value = default_value; result->Field.flags = flags; @@ -918,10 +929,10 @@ Ast *ast_field(AstFile *f, Array names, Ast *type, Ast *default_value, u3 return result; } -Ast *ast_field_list(AstFile *f, Token token, Array list) { +Ast *ast_field_list(AstFile *f, Token token, Array const &list) { Ast *result = alloc_ast_node(f, Ast_FieldList); result->FieldList.token = token; - result->FieldList.list = list; + result->FieldList.list = slice_from_array(list); return result; } @@ -1002,7 +1013,7 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { return result; } -Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_count, +Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, Ast *align, Token where_token, Array const &where_clauses) { @@ -1015,38 +1026,38 @@ Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_c result->StructType.is_raw_union = is_raw_union; result->StructType.align = align; result->StructType.where_token = where_token; - result->StructType.where_clauses = where_clauses; + result->StructType.where_clauses = slice_from_array(where_clauses); return result; } -Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, +Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; - result->UnionType.variants = variants; + result->UnionType.variants = slice_from_array(variants); result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; result->UnionType.no_nil = no_nil; - result->UnionType.maybe = maybe; + result->UnionType.maybe = maybe; result->UnionType.where_token = where_token; - result->UnionType.where_clauses = where_clauses; + result->UnionType.where_clauses = slice_from_array(where_clauses); return result; } -Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array fields) { +Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array const &fields) { Ast *result = alloc_ast_node(f, Ast_EnumType); result->EnumType.token = token; result->EnumType.base_type = base_type; - result->EnumType.fields = fields; + result->EnumType.fields = slice_from_array(fields); return result; } -Ast *ast_bit_field_type(AstFile *f, Token token, Array fields, Ast *align) { +Ast *ast_bit_field_type(AstFile *f, Token token, Array const &fields, Ast *align) { Ast *result = alloc_ast_node(f, Ast_BitFieldType); result->BitFieldType.token = token; - result->BitFieldType.fields = fields; + result->BitFieldType.fields = slice_from_array(fields); result->BitFieldType.align = align; return result; } @@ -1069,7 +1080,7 @@ Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) { Ast *ast_foreign_block_decl(AstFile *f, Token token, Ast *foreign_library, Ast *body, - CommentGroup *docs) { + CommentGroup *docs) { Ast *result = alloc_ast_node(f, Ast_ForeignBlockDecl); result->ForeignBlockDecl.token = token; result->ForeignBlockDecl.foreign_library = foreign_library; @@ -1087,12 +1098,12 @@ Ast *ast_label_decl(AstFile *f, Token token, Ast *name) { return result; } -Ast *ast_value_decl(AstFile *f, Array names, Ast *type, Array values, bool is_mutable, - CommentGroup *docs, CommentGroup *comment) { +Ast *ast_value_decl(AstFile *f, Array const &names, Ast *type, Array const &values, bool is_mutable, + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ValueDecl); - result->ValueDecl.names = names; + result->ValueDecl.names = slice_from_array(names); result->ValueDecl.type = type; - result->ValueDecl.values = values; + result->ValueDecl.values = slice_from_array(values); result->ValueDecl.is_mutable = is_mutable; result->ValueDecl.docs = docs; result->ValueDecl.comment = comment; @@ -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 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 filepaths, To } -Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array elems) { +Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array const &elems) { Ast *result = alloc_ast_node(f, Ast_Attribute); result->Attribute.token = token; result->Attribute.open = open; - result->Attribute.elems = elems; + result->Attribute.elems = slice_from_array(elems); result->Attribute.close = close; return result; } @@ -1182,6 +1193,12 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) { Array list = {}; list.allocator = heap_allocator(); isize end_line = f->curr_token.pos.line; + if (f->curr_token_index == 1 && + f->prev_token.kind == Token_Comment && + f->prev_token.pos.line+1 == f->curr_token.pos.line) { + // NOTE(bill): Special logic for the first comment in the file + array_add(&list, f->prev_token); + } while (f->curr_token.kind == Token_Comment && f->curr_token.pos.line <= end_line+n) { array_add(&list, consume_comment(f, &end_line)); @@ -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, ¶m_count, 0, Token_CloseParen, false, true); + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true); if (param_count == 0) { syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter"); polymorphic_params = nullptr; @@ -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 decls = {}; + Slice 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, ¶m_count, 0, Token_CloseParen, false, true); + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true); if (param_count == 0) { syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric"); polymorphic_params = nullptr; @@ -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 &decls); +void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls); void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) { if (ws->body != nullptr) { @@ -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 &decls) { +void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls) { for_array(i, decls) { Ast *node = decls[i]; if (!is_ast_decl(node) && @@ -5034,8 +5098,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array } else if (node->kind == Ast_ForeignImportDecl) { ast_node(fl, ForeignImportDecl, node); - fl->fullpaths.allocator = heap_allocator(); - array_reserve(&fl->fullpaths, fl->filepaths.count); + auto fullpaths = array_make(permanent_allocator(), 0, fl->filepaths.count); for_array(fp_idx, fl->filepaths) { String file_str = fl->filepaths[fp_idx].string; @@ -5049,14 +5112,17 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array } fullpath = foreign_path; } - array_add(&fl->fullpaths, fullpath); + array_add(&fullpaths, fullpath); } - if (fl->fullpaths.count == 0) { + if (fullpaths.count == 0) { syntax_error(decls[i], "No foreign paths found"); decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]); goto end; } + fl->fullpaths = slice_from_array(fullpaths); + + } else if (node->kind == Ast_WhenStmt) { ast_node(ws, WhenStmt, node); parse_setup_file_when_stmt(p, f, base_dir, ws); @@ -5218,12 +5284,12 @@ bool parse_file(Parser *p, AstFile *f) { f->pkg_decl = pd; if (f->error_count == 0) { - f->decls = array_make(heap_allocator()); + auto decls = array_make(heap_allocator()); while (f->curr_token.kind != Token_EOF) { Ast *stmt = parse_stmt(f); if (stmt && stmt->kind != Ast_EmptyStmt) { - array_add(&f->decls, stmt); + array_add(&decls, stmt); if (stmt->kind == Ast_ExprStmt && stmt->ExprStmt.expr != nullptr && stmt->ExprStmt.expr->kind == Ast_ProcLit) { @@ -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 list; // Token_Comment + Slice list; // Token_Comment }; @@ -98,8 +98,8 @@ struct AstFile { bool in_foreign_block; bool allow_type; - Array decls; - Array imports; // 'import' 'using import' + Slice decls; + Array imports; // 'import' isize directive_count; Ast * curr_proc; @@ -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 args; \ + Slice 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 where_clauses; \ + Slice where_clauses; \ DeclInfo *decl; \ }) \ AST_KIND(CompoundLit, "compound literal", struct { \ Ast *type; \ - Array elems; \ + Slice elems; \ Token open, close; \ i64 max_count; \ }) \ @@ -325,7 +329,7 @@ AST_KIND(_ExprBegin, "", bool) \ }) \ AST_KIND(CallExpr, "call expression", struct { \ Ast * proc; \ - Array args; \ + Slice 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 param_types; \ + Slice 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 lhs, rhs; \ + Slice lhs, rhs; \ }) \ AST_KIND(_ComplexStmtBegin, "", bool) \ AST_KIND(BlockStmt, "block statement", struct { \ - Array stmts; \ + Slice stmts; \ Ast *label; \ Token open, close; \ }) \ @@ -388,7 +392,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ }) \ AST_KIND(ReturnStmt, "return statement", struct { \ Token token; \ - Array results; \ + Slice results; \ }) \ AST_KIND(ForStmt, "for statement", struct { \ Token token; \ @@ -418,8 +422,8 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ }) \ AST_KIND(CaseClause, "case clause", struct { \ Token token; \ - Array list; \ - Array stmts; \ + Slice list; \ + Slice stmts; \ Entity *implicit_entity; \ }) \ AST_KIND(SwitchStmt, "switch statement", struct { \ @@ -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 list; \ + Slice 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 names; \ + Slice names; \ Ast * type; \ - Array values; \ + Slice values; \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ @@ -486,10 +490,10 @@ AST_KIND(_DeclBegin, "", bool) \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ - Array filepaths; \ + Slice filepaths; \ Token library_name; \ String collection_name; \ - Array fullpaths; \ + Slice fullpaths; \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ @@ -497,11 +501,11 @@ AST_KIND(_DeclBegin, "", bool) \ AST_KIND(_DeclEnd, "", bool) \ AST_KIND(Attribute, "attribute", struct { \ Token token; \ - Array elems; \ + Slice elems; \ Token open, close; \ }) \ AST_KIND(Field, "field", struct { \ - Array names; \ + Slice names; \ Ast * type; \ Ast * default_value; \ Token tag; \ @@ -511,7 +515,7 @@ AST_KIND(_DeclEnd, "", bool) \ }) \ AST_KIND(FieldList, "field list", struct { \ Token token; \ - Array list; \ + Slice list; \ }) \ AST_KIND(_TypeBegin, "", bool) \ AST_KIND(TypeidType, "typeid", struct { \ @@ -565,34 +569,34 @@ AST_KIND(_TypeBegin, "", bool) \ }) \ AST_KIND(StructType, "struct type", struct { \ Token token; \ - Array fields; \ + Slice fields; \ isize field_count; \ Ast *polymorphic_params; \ Ast *align; \ Token where_token; \ - Array where_clauses; \ + Slice where_clauses; \ bool is_packed; \ bool is_raw_union; \ }) \ AST_KIND(UnionType, "union type", struct { \ Token token; \ - Array variants; \ + Slice variants; \ Ast *polymorphic_params; \ Ast * align; \ bool maybe; \ bool no_nil; \ Token where_token; \ - Array where_clauses; \ + Slice where_clauses; \ }) \ AST_KIND(EnumType, "enum type", struct { \ Token token; \ Ast * base_type; \ - Array fields; /* FieldValue */ \ + Slice fields; /* FieldValue */ \ bool is_using; \ }) \ AST_KIND(BitFieldType, "bit field type", struct { \ Token token; \ - Array fields; /* FieldValue with : */ \ + Slice fields; /* FieldValue with : */ \ Ast * align; \ }) \ AST_KIND(BitSetType, "bit set type", struct { \ @@ -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 struct PtrSetEntry { - T ptr; - isize next; + T ptr; + PtrSetIndex next; }; template struct PtrSet { - Array hashes; + Array hashes; Array> entries; }; template void ptr_set_init (PtrSet *s, gbAllocator a, isize capacity = 16); template void ptr_set_destroy(PtrSet *s); template T ptr_set_add (PtrSet *s, T ptr); +template bool ptr_set_update (PtrSet *s, T ptr); // returns true if it previously existsed template bool ptr_set_exists (PtrSet *s, T ptr); template void ptr_set_remove (PtrSet *s, T ptr); template void ptr_set_clear (PtrSet *s); @@ -27,12 +32,31 @@ template void ptr_set_grow (PtrSet *s); template void ptr_set_rehash (PtrSet *s, isize new_count); +isize next_pow2_isize(isize n) { + if (n <= 0) { + return 0; + } + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + if (gb_size_of(isize) == 8) { + n |= n >> 32; + } + n++; + return n; +} + template void ptr_set_init(PtrSet *s, gbAllocator a, isize capacity) { + capacity = next_pow2_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 *s) { } template -gb_internal isize ptr_set__add_entry(PtrSet *s, T ptr) { +gb_internal PtrSetIndex ptr_set__add_entry(PtrSet *s, T ptr) { PtrSetEntry e = {}; e.ptr = ptr; - e.next = -1; + e.next = PTR_SET_SENTINEL; array_add(&s->entries, e); - return s->entries.count-1; + return cast(PtrSetIndex)(s->entries.count-1); } template gb_internal PtrSetFindResult ptr_set__find(PtrSet *s, T ptr) { - PtrSetFindResult fr = {-1, -1, -1}; - if (s->hashes.count > 0) { + PtrSetFindResult fr = {PTR_SET_SENTINEL, PTR_SET_SENTINEL, PTR_SET_SENTINEL}; + if (s->hashes.count != 0) { u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr; u64 n = cast(u64)s->hashes.count; - fr.hash_index = cast(isize)(hash % n); - fr.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 -gb_internal b32 ptr_set__full(PtrSet *s) { +gb_internal bool ptr_set__full(PtrSet *s) { return 0.75f * s->hashes.count <= s->entries.count; } -#define PTR_ARRAY_GROW_FORMULA(x) (4*(x) + 7) -GB_STATIC_ASSERT(PTR_ARRAY_GROW_FORMULA(0) > 0); - template gb_inline void ptr_set_grow(PtrSet *s) { - isize new_count = PTR_ARRAY_GROW_FORMULA(s->entries.count); + isize new_count = s->hashes.count*2; ptr_set_rehash(s, new_count); } template void ptr_set_rehash(PtrSet *s, isize new_count) { - isize i, j; + PtrSetIndex i, j; PtrSet ns = {}; ptr_set_init(&ns, s->hashes.allocator); array_resize(&ns.hashes, new_count); array_reserve(&ns.entries, s->entries.count); for (i = 0; i < new_count; i++) { - ns.hashes[i] = -1; + ns.hashes.data[i] = PTR_SET_SENTINEL; } for (i = 0; i < s->entries.count; i++) { - PtrSetEntry *e = &s->entries[i]; + PtrSetEntry *e = &s->entries.data[i]; PtrSetFindResult fr; if (ns.hashes.count == 0) { ptr_set_grow(&ns); } 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 *s, isize new_count) { template gb_inline bool ptr_set_exists(PtrSet *s, T ptr) { isize index = ptr_set__find(s, ptr).entry_index; - return index >= 0; + return index != PTR_SET_SENTINEL; } // Returns true if it already exists template T ptr_set_add(PtrSet *s, T ptr) { - isize index; + PtrSetIndex index; PtrSetFindResult fr; if (s->hashes.count == 0) { ptr_set_grow(s); } fr = ptr_set__find(s, ptr); - if (fr.entry_index >= 0) { - 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 *s, T ptr) { return ptr; } +template +bool ptr_set_update(PtrSet *s, T ptr) { // returns true if it previously existsed + bool exists = false; + PtrSetIndex index; + PtrSetFindResult fr; + if (s->hashes.count == 0) { + ptr_set_grow(s); + } + fr = ptr_set__find(s, ptr); + if (fr.entry_index != PTR_SET_SENTINEL) { + exists = true; + } else { + index = ptr_set__add_entry(s, ptr); + if (fr.entry_prev != PTR_SET_SENTINEL) { + s->entries.data[fr.entry_prev].next = index; + } else { + s->hashes.data[fr.hash_index] = index; + } + } + if (ptr_set__full(s)) { + ptr_set_grow(s); + } + return exists; +} + + template void ptr_set__erase(PtrSet *s, PtrSetFindResult fr) { 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 void ptr_set_remove(PtrSet *s, T ptr) { PtrSetFindResult fr = ptr_set__find(s, ptr); - if (fr.entry_index >= 0) { + if (fr.entry_index != PTR_SET_SENTINEL) { ptr_set__erase(s, fr); } } diff --git a/src/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 type_set_offsets_of(Array const &fields, bool is_packed, bool is_raw_union) { - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); auto offsets = array_make(a, fields.count); i64 curr_offset = 0; if (is_raw_union) { @@ -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(heap_allocator(), field_count); + + Scope *scope = nullptr; + for_array(i, t->Struct.fields) { + t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved); + } + t->Struct.is_packed = is_packed; + + return t; +} + +Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) { + if (field_count == 0) { + return nullptr; + } + if (!must_be_tuple && field_count == 1) { + return field_types[0]; + } + + Type *t = alloc_type_tuple(); + t->Tuple.variables = array_make(heap_allocator(), field_count); + + Scope *scope = nullptr; + for_array(i, t->Tuple.variables) { + t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false); + } + t->Tuple.is_packed = is_packed; + + return t; +} + +Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg, ProcCallingConvention calling_convention) { + + Type *params = alloc_type_tuple_from_field_types(param_types, param_count, false, true); + isize results_count = 0; + if (results != nullptr) { + if (results->kind != Type_Tuple) { + results = alloc_type_tuple_from_field_types(&results, 1, false, true); + } + results_count = results->Tuple.variables.count; + } + + Scope *scope = nullptr; + Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, calling_convention); + t->Proc.c_vararg = is_c_vararg; + return t; +} + + + gbString write_type_to_string(gbString str, Type *type) { if (type == nullptr) { return gb_string_appendc(str, ""); @@ -3671,3 +3824,6 @@ gbString type_to_string(Type *type) { return write_type_to_string(gb_string_make(heap_allocator(), ""), type); } + + +