diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 412587b3c..2bd3ca9ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,9 +7,9 @@ jobs: steps: - uses: actions/checkout@v1 - name: Download LLVM, botan - run: sudo apt-get install llvm-11 clang-11 llvm libbotan-2-dev botan + run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan - name: build odin - run: make release + run: ./build_odin.sh release - name: Odin version run: ./odin version timeout-minutes: 1 @@ -41,6 +41,9 @@ jobs: - name: Odin check examples/all for Linux i386 run: ./odin check examples/all -vet -strict-style -target:linux_i386 timeout-minutes: 10 + - name: Odin check examples/all for FreeBSD amd64 + run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64 + timeout-minutes: 10 - name: Odin check examples/all for OpenBSD amd64 run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64 timeout-minutes: 10 @@ -55,7 +58,7 @@ jobs: TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - name: build odin - run: make release + run: ./build_odin.sh release - name: Odin version run: ./odin version timeout-minutes: 1 @@ -87,6 +90,9 @@ jobs: - name: Odin check examples/all for Darwin arm64 run: ./odin check examples/all -vet -strict-style -target:darwin_arm64 timeout-minutes: 10 + - name: Odin check examples/all for Linux arm64 + run: ./odin check examples/all -vet -strict-style -target:linux_arm64 + timeout-minutes: 10 build_windows: runs-on: windows-2019 steps: diff --git a/Makefile b/Makefile index ceff3c207..bea0569a9 100644 --- a/Makefile +++ b/Makefile @@ -30,10 +30,10 @@ ifeq ($(OS), Darwin) ifeq ($(shell $(LLVM_CONFIG) --version | grep -E $(LLVM_VERSION_PATTERN)),) ifeq ($(ARCH), arm64) $(error "Requirement: llvm-config must be base version 13 for arm64") - else + else $(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86") - endif - endif + endif + endif LDFLAGS:=$(LDFLAGS) -liconv -ldl CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) @@ -62,6 +62,12 @@ ifeq ($(OS), OpenBSD) CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) endif +ifeq ($(OS), FreeBSD) + LLVM_CONFIG=/usr/local/bin/llvm-config11 + + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) + LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) +endif all: debug demo diff --git a/build_odin.sh b/build_odin.sh new file mode 100755 index 000000000..a323782a1 --- /dev/null +++ b/build_odin.sh @@ -0,0 +1,140 @@ +#!/bin/bash +set -eu + +GIT_SHA=$(git rev-parse --short HEAD) +DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value" +LDFLAGS="-pthread -lm -lstdc++" +CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\"" +CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\"" +CC=clang +OS=$(uname) + +panic() { + printf "%s\n" "$1" + exit 1 +} + +version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; } + +config_darwin() { + ARCH=$(uname -m) + LLVM_CONFIG=llvm-config + + # allow for arm only llvm's with version 13 + if [ ARCH == arm64 ]; then + MIN_LLVM_VERSION=("13.0.0") + else + # allow for x86 / amd64 all llvm versions begining from 11 + MIN_LLVM_VERSION=("11.1.0") + fi + + if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then + if [ ARCH == arm64 ]; then + panic "Requirement: llvm-config must be base version 13 for arm64" + else + panic "Requirement: llvm-config must be base version greater than 11 for amd64/x86" + fi + fi + + LDFLAGS="$LDFLAGS -liconv -ldl" + CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS -lLLVM-C" +} + +config_openbsd() { + LLVM_CONFIG=/usr/local/bin/llvm-config + + LDFLAGS="$LDFLAGS -liconv" + CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" +} + +config_linux() { + if which llvm-config > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config + elif which llvm-config-11 > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config-11 + elif which llvm-config-11-64 > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config-11-64 + else + panic "Unable to find LLVM-config" + fi + + MIN_LLVM_VERSION=("11.0.0") + if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then + echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version) + panic "Requirement: llvm-config must be base version greater than 11" + fi + + LDFLAGS="$LDFLAGS -ldl" + CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" +} + +build_odin() { + case $1 in + debug) + EXTRAFLAGS="-g" + ;; + release) + EXTRAFLAGS="-O3" + ;; + release-native) + EXTRAFLAGS="-O3 -march=native" + ;; + nightly) + EXTRAFLAGS="-DNIGHTLY -O3" + ;; + *) + panic "Build mode unsupported!" + esac + + set -x + $CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin + set +x +} + +run_demo() { + ./odin run examples/demo/demo.odin +} + +case $OS in +Linux) + config_linux + ;; +Darwin) + config_darwin + ;; +OpenBSD) + config_openbsd + ;; +*) + panic "Platform unsupported!" +esac + +if [[ $# -eq 0 ]]; then + build_odin debug + run_demo + exit 0 +fi + +if [[ $# -eq 1 ]]; then + case $1 in + report) + if [[ ! -f "./odin" ]]; then + build_odin debug + fi + + ./odin report + exit 0 + ;; + *) + build_odin $1 + ;; + esac + + run_demo + exit 0 +else + panic "Too many arguments!" +fi diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin index fc65b954a..01d0cd427 100644 --- a/core/c/libc/stdio.odin +++ b/core/c/libc/stdio.odin @@ -79,7 +79,32 @@ when ODIN_OS == .Linux { } when ODIN_OS == .OpenBSD { - fpos_t :: i64 + fpos_t :: distinct i64 + + _IOFBF :: 0 + _IOLBF :: 1 + _IONBF :: 1 + + BUFSIZ :: 1024 + + EOF :: int(-1) + + FOPEN_MAX :: 20 + FILENAME_MAX :: 1024 + + SEEK_SET :: 0 + SEEK_CUR :: 1 + SEEK_END :: 2 + + foreign libc { + stderr: ^FILE + stdin: ^FILE + stdout: ^FILE + } +} + +when ODIN_OS == .FreeBSD { + fpos_t :: distinct i64 _IOFBF :: 0 _IOLBF :: 1 diff --git a/core/c/libc/wctype.odin b/core/c/libc/wctype.odin index f833af51f..43aee9dc6 100644 --- a/core/c/libc/wctype.odin +++ b/core/c/libc/wctype.odin @@ -13,21 +13,23 @@ when ODIN_OS == .Windows { when ODIN_OS == .Windows { wctrans_t :: distinct wchar_t wctype_t :: distinct ushort -} -when ODIN_OS == .Linux { +} else when ODIN_OS == .Linux { wctrans_t :: distinct intptr_t wctype_t :: distinct ulong -} -when ODIN_OS == .Darwin { +} else when ODIN_OS == .Darwin { wctrans_t :: distinct int wctype_t :: distinct u32 -} -when ODIN_OS == .OpenBSD { +} else when ODIN_OS == .OpenBSD { wctrans_t :: distinct rawptr wctype_t :: distinct rawptr + +} else when ODIN_OS == .FreeBSD { + wctrans_t :: distinct int + wctype_t :: distinct ulong + } @(default_calling_convention="c") diff --git a/core/hash/hash.odin b/core/hash/hash.odin index 5044d567a..f2152f1b6 100644 --- a/core/hash/hash.odin +++ b/core/hash/hash.odin @@ -151,7 +151,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 { k1 ~= u32(tail[2]) << 16 fallthrough case 2: - k1 ~= u32(tail[2]) << 8 + k1 ~= u32(tail[1]) << 8 fallthrough case 1: k1 ~= u32(tail[0]) diff --git a/core/math/math.odin b/core/math/math.odin index b81598da9..afbda051d 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -396,7 +396,7 @@ trunc_f16 :: proc "contextless" (x: f16) -> f16 { e := (x >> shift) & mask - bias if e < shift { - x &= ~(1 << (shift-e)) - 1 + x &~= 1 << (shift-e) - 1 } return transmute(f16)x } @@ -428,7 +428,7 @@ trunc_f32 :: proc "contextless" (x: f32) -> f32 { e := (x >> shift) & mask - bias if e < shift { - x &= ~(1 << (shift-e)) - 1 + x &~= 1 << (shift-e) - 1 } return transmute(f32)x } @@ -460,7 +460,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 { e := (x >> shift) & mask - bias if e < shift { - x &= ~(1 << (shift-e)) - 1 + x &~= 1 << (shift-e) - 1 } return transmute(f64)x } @@ -473,6 +473,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 { } trunc_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(trunc_f64(f64(x))) } trunc_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(trunc_f64(f64(x))) } +// Removes the fractional part of the value, i.e. rounds towards zero. trunc :: proc{ trunc_f16, trunc_f16le, trunc_f16be, trunc_f32, trunc_f32le, trunc_f32be, @@ -958,7 +959,7 @@ classify_f16 :: proc "contextless" (x: f16) -> Float_Class { return .Neg_Zero } return .Zero - case x*0.5 == x: + case x*0.25 == x: if x < 0 { return .Neg_Inf } @@ -1027,6 +1028,8 @@ classify_f64 :: proc "contextless" (x: f64) -> Float_Class { } classify_f64le :: proc "contextless" (x: f64le) -> Float_Class { return #force_inline classify_f64(f64(x)) } classify_f64be :: proc "contextless" (x: f64be) -> Float_Class { return #force_inline classify_f64(f64(x)) } +// Returns the `Float_Class` of the value, i.e. whether normal, subnormal, zero, negative zero, NaN, infinity or +// negative infinity. classify :: proc{ classify_f16, classify_f16le, classify_f16be, classify_f32, classify_f32le, classify_f32be, @@ -1715,4 +1718,22 @@ F32_BIAS :: 0x7f F64_MASK :: 0x7ff F64_SHIFT :: 64 - 12 -F64_BIAS :: 0x3ff \ No newline at end of file +F64_BIAS :: 0x3ff + +INF_F16 :f16: 0h7C00 +NEG_INF_F16 :f16: 0hFC00 + +SNAN_F16 :f16: 0h7C01 +QNAN_F16 :f16: 0h7E01 + +INF_F32 :f32: 0h7F80_0000 +NEG_INF_F32 :f32: 0hFF80_0000 + +SNAN_F32 :f32: 0hFF80_0001 +QNAN_F32 :f32: 0hFFC0_0001 + +INF_F64 :f64: 0h7FF0_0000_0000_0000 +NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000 + +SNAN_F64 :f64: 0h7FF0_0000_0000_0001 +QNAN_F64 :f64: 0h7FF8_0000_0000_0001 \ No newline at end of file diff --git a/core/os/dir_freebsd.odin b/core/os/dir_freebsd.odin new file mode 100644 index 000000000..74c410a51 --- /dev/null +++ b/core/os/dir_freebsd.odin @@ -0,0 +1,70 @@ +package os + +import "core:strings" +import "core:mem" + +read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { + dirp: Dir + dirp, err = _fdopendir(fd) + if err != ERROR_NONE { + return + } + + defer _closedir(dirp) + + dirpath: string + dirpath, err = absolute_path_from_handle(fd) + + if err != ERROR_NONE { + return + } + + defer delete(dirpath) + + n := n + size := n + if n <= 0 { + n = -1 + size = 100 + } + + dfi := make([dynamic]File_Info, 0, size, allocator) + + for { + entry: Dirent + end_of_stream: bool + entry, err, end_of_stream = _readdir(dirp) + if err != ERROR_NONE { + for fi_ in dfi { + file_info_delete(fi_, allocator) + } + delete(dfi) + return + } else if end_of_stream { + break + } + + fi_: File_Info + filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] }) + + if filename == "." || filename == ".." { + continue + } + + fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator) + defer delete(fullpath, context.temp_allocator) + + fi_, err = stat(fullpath, allocator) + if err != ERROR_NONE { + for fi__ in dfi { + file_info_delete(fi__, allocator) + } + delete(dfi) + return + } + + append(&dfi, fi_) + } + + return dfi[:], ERROR_NONE +} diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin index 0626272d6..4c8d385f2 100644 --- a/core/os/file_windows.odin +++ b/core/os/file_windows.odin @@ -113,7 +113,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { if max_read == 0 { break } - + single_read_length: u32 ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) if !ok { diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index ace622582..ae5336849 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -260,13 +260,13 @@ S_ISUID :: 0o4000 // Set user id on execution S_ISGID :: 0o2000 // Set group id on execution S_ISVTX :: 0o1000 // Directory restrcted delete -S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } -S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } -S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } -S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } -S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } -S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } -S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } +S_ISLNK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFSOCK } R_OK :: 4 // Test for read permission W_OK :: 2 // Test for write permission @@ -313,7 +313,7 @@ foreign libc { @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- - @(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u32) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u16) -> c.int --- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- @(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring --- @@ -433,7 +433,7 @@ is_file_handle :: proc(fd: Handle) -> bool { if err != ERROR_NONE { return false } - return S_ISREG(cast(u32)s.mode) + return S_ISREG(s.mode) } is_file_path :: proc(path: string, follow_links: bool = true) -> bool { @@ -447,7 +447,7 @@ is_file_path :: proc(path: string, follow_links: bool = true) -> bool { if err != ERROR_NONE { return false } - return S_ISREG(cast(u32)s.mode) + return S_ISREG(s.mode) } @@ -456,7 +456,7 @@ is_dir_handle :: proc(fd: Handle) -> bool { if err != ERROR_NONE { return false } - return S_ISDIR(cast(u32)s.mode) + return S_ISDIR(s.mode) } is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { @@ -470,7 +470,7 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { if err != ERROR_NONE { return false } - return S_ISDIR(cast(u32)s.mode) + return S_ISDIR(s.mode) } is_file :: proc {is_file_path, is_file_handle} @@ -670,7 +670,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) { return ERROR_NONE } -make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno { +make_directory :: proc(path: string, mode: u16 = 0o775) -> Errno { path_cstr := strings.clone_to_cstring(path, context.temp_allocator) res := _unix_mkdir(path_cstr, mode) if res == -1 { diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index 7fb9dd26f..4a95028c1 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -10,7 +10,6 @@ import "core:c" Handle :: distinct i32 File_Time :: distinct u64 Errno :: distinct i32 -Syscall :: distinct i32 INVALID_HANDLE :: ~Handle(0) @@ -142,40 +141,74 @@ RTLD_TRACE :: 0x200 RTLD_NODELETE :: 0x01000 RTLD_NOLOAD :: 0x02000 +MAX_PATH :: 1024 + args := _alloc_command_line_arguments() Unix_File_Time :: struct { - seconds: i64, + seconds: time_t, nanoseconds: c.long, } +dev_t :: u64 +ino_t :: u64 +nlink_t :: u64 +off_t :: i64 +mode_t :: u16 pid_t :: u32 +uid_t :: u32 +gid_t :: u32 +blkcnt_t :: i64 +blksize_t :: i32 +fflags_t :: u32 + +when ODIN_ARCH == .amd64 /* LP64 */ { + time_t :: i64 +} else { + time_t :: i32 +} + OS_Stat :: struct { - device_id: u64, - serial: u64, - nlink: u64, - mode: u32, + device_id: dev_t, + serial: ino_t, + nlink: nlink_t, + mode: mode_t, _padding0: i16, - uid: u32, - gid: u32, + uid: uid_t, + gid: gid_t, _padding1: i32, - rdev: u64, + rdev: dev_t, last_access: Unix_File_Time, modified: Unix_File_Time, status_change: Unix_File_Time, birthtime: Unix_File_Time, - size: i64, - blocks: i64, - block_size: i32, + size: off_t, + blocks: blkcnt_t, + block_size: blksize_t, - flags: u32, + flags: fflags_t, gen: u64, - lspare: i64, + lspare: [10]u64, } + +// since FreeBSD v12 +Dirent :: struct { + ino: ino_t, + off: off_t, + reclen: u16, + type: u8, + _pad0: u8, + namlen: u16, + _pad1: u16, + name: [256]byte, +} + +Dir :: distinct rawptr // DIR* + // File type S_IFMT :: 0o170000 // Type of file mask S_IFIFO :: 0o010000 // Named pipe (fifo) @@ -211,13 +244,13 @@ S_ISGID :: 0o2000 // Set group id on execution S_ISVTX :: 0o1000 // Directory restrcted delete -S_ISLNK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK -S_ISREG :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG -S_ISDIR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR -S_ISCHR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR -S_ISBLK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK -S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO -S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK +S_ISLNK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFLNK +S_ISREG :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFREG +S_ISDIR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFDIR +S_ISCHR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFCHR +S_ISBLK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFBLK +S_ISFIFO :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFIFO +S_ISSOCK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFSOCK F_OK :: 0 // Test for file existance X_OK :: 1 // Test for execute permission @@ -225,27 +258,38 @@ W_OK :: 2 // Test for write permission R_OK :: 4 // Test for read permission foreign libc { - @(link_name="__error") __errno_location :: proc() -> ^int --- - @(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int --- + @(link_name="__error") __errno_location :: proc() -> ^int --- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- - @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- - @(link_name="gettid") _unix_gettid :: proc() -> u64 --- + @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- - @(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- + @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- + @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- + @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- + @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- + @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- + @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- + @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- + @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + + @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- + @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- + @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- + @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- @(link_name="free") _unix_free :: proc(ptr: rawptr) --- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- + @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- - @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- } @@ -318,12 +362,102 @@ file_size :: proc(fd: Handle) -> (i64, Errno) { return s.size, ERROR_NONE } -stdin: Handle = 0 +rename :: proc(old_path, new_path: string) -> Errno { + old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) + new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) + res := _unix_rename(old_path_cstr, new_path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove :: proc(path: string) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_unlink(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_mkdir(path_cstr, mode) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove_directory :: proc(path: string) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_rmdir(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +is_file_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_file_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_dir_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_file :: proc {is_file_path, is_file_handle} +is_dir :: proc {is_dir_path, is_dir_handle} + +// NOTE(bill): Uses startup to initialize it + +stdin: Handle = 0 stdout: Handle = 1 stderr: Handle = 2 +/* TODO(zangent): Implement these! +last_write_time :: proc(fd: Handle) -> File_Time {} +last_write_time_by_name :: proc(name: string) -> File_Time {} +*/ last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { - s, err := fstat(fd) + s, err := _fstat(fd) if err != ERROR_NONE { return 0, err } @@ -332,7 +466,7 @@ last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { } last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { - s, err := stat(name) + s, err := _stat(name) if err != ERROR_NONE { return 0, err } @@ -340,18 +474,33 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { return File_Time(modified), ERROR_NONE } -stat :: proc(path: string) -> (OS_Stat, Errno) { +@private +_stat :: proc(path: string) -> (OS_Stat, Errno) { cstr := strings.clone_to_cstring(path, context.temp_allocator) - s: OS_Stat - result := _unix_stat(cstr, &s) + s: OS_Stat = --- + result := _unix_lstat(cstr, &s) if result == -1 { return s, Errno(get_last_error()) } return s, ERROR_NONE } -fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { - s: OS_Stat +@private +_lstat :: proc(path: string) -> (OS_Stat, Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_lstat(cstr, &s) + if res == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { + s: OS_Stat = --- result := _unix_fstat(fd, &s) if result == -1 { return s, Errno(get_last_error()) @@ -359,6 +508,95 @@ fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { return s, ERROR_NONE } +@private +_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { + dirp := _unix_fdopendir(fd) + if dirp == cast(Dir)nil { + return nil, Errno(get_last_error()) + } + return dirp, ERROR_NONE +} + +@private +_closedir :: proc(dirp: Dir) -> Errno { + rc := _unix_closedir(dirp) + if rc != 0 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +@private +_rewinddir :: proc(dirp: Dir) { + _unix_rewinddir(dirp) +} + +@private +_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { + result: ^Dirent + rc := _unix_readdir_r(dirp, &entry, &result) + + if rc != 0 { + err = Errno(get_last_error()) + return + } + err = ERROR_NONE + + if result == nil { + end_of_stream = true + return + } + + return +} + +@private +_readlink :: proc(path: string) -> (string, Errno) { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + + bufsz : uint = MAX_PATH + buf := make([]byte, MAX_PATH) + for { + rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) + if rc == -1 { + delete(buf) + return "", Errno(get_last_error()) + } else if rc == int(bufsz) { + bufsz += MAX_PATH + delete(buf) + buf = make([]byte, bufsz) + } else { + return strings.string_from_ptr(&buf[0], rc), ERROR_NONE + } + } + unreachable() +} + +// XXX FreeBSD +absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { + return "", Errno(ENOSYS) +} + +absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { + rel := rel + if rel == "" { + rel = "." + } + + rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) + + path_ptr := _unix_realpath(rel_cstr, nil) + if path_ptr == nil { + return "", Errno(get_last_error()) + } + defer _unix_free(path_ptr) + + path_cstr := transmute(cstring)path_ptr + path = strings.clone( string(path_cstr) ) + + return path, ERROR_NONE +} + access :: proc(path: string, mask: int) -> (bool, Errno) { cstr := strings.clone_to_cstring(path, context.temp_allocator) result := _unix_access(cstr, c.int(mask)) @@ -464,4 +702,3 @@ _alloc_command_line_arguments :: proc() -> []string { } return res } - diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 9716e2925..ed73341c0 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -269,7 +269,7 @@ X_OK :: 1 // Test for execute permission W_OK :: 2 // Test for write permission R_OK :: 4 // Test for read permission -AT_FDCWD :: -100 +AT_FDCWD :: ~uintptr(99) /* -100 */ AT_REMOVEDIR :: uintptr(0x200) AT_SYMLINK_NOFOLLOW :: uintptr(0x100) @@ -278,7 +278,11 @@ _unix_personality :: proc(persona: u64) -> int { } _unix_fork :: proc() -> Pid { - res := int(intrinsics.syscall(unix.SYS_fork)) + when ODIN_ARCH != .arm64 { + res := int(intrinsics.syscall(unix.SYS_fork)) + } else { + res := int(intrinsics.syscall(unix.SYS_clone, unix.SIGCHLD)) + } return -1 if res < 0 else Pid(res) } @@ -286,7 +290,7 @@ _unix_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> Handle { when ODIN_ARCH != .arm64 { res := int(intrinsics.syscall(unix.SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } else { // NOTE: arm64 does not have open - res := int(intrinsics.syscall(unix.SYS_openat, uintptr(AT_FDCWD), uintptr(rawptr(path), uintptr(flags), uintptr(mode)))) + res := int(intrinsics.syscall(unix.SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } return -1 if res < 0 else Handle(res) } @@ -321,7 +325,7 @@ _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int { } else when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_stat64, uintptr(rawptr(path)), uintptr(stat))) } else { // NOTE: arm64 does not have stat - return int(intrinsics.syscall(unix.SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), 0)) + return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0)) } } @@ -339,7 +343,7 @@ _unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int { } else when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_lstat64, uintptr(rawptr(path)), uintptr(stat))) } else { // NOTE: arm64 does not have any lstat - return int(intrinsics.syscall(unix.SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW)) + return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW)) } } @@ -347,7 +351,7 @@ _unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) } else { // NOTE: arm64 does not have readlink - return int(intrinsics.syscall(unix.SYS_readlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) + return int(intrinsics.syscall(unix.SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) } } @@ -355,7 +359,7 @@ _unix_access :: proc(path: cstring, mask: int) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_access, uintptr(rawptr(path)), uintptr(mask))) } else { // NOTE: arm64 does not have access - return int(intrinsics.syscall(unix.SYS_faccessat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mask))) + return int(intrinsics.syscall(unix.SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask))) } } @@ -371,7 +375,7 @@ _unix_rename :: proc(old, new: cstring) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new)))) } else { // NOTE: arm64 does not have rename - return int(intrinsics.syscall(unix.SYS_renameat, uintptr(AT_FDCWD), uintptr(rawptr(old)), uintptr(rawptr(new)))) + return int(intrinsics.syscall(unix.SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new)))) } } @@ -379,7 +383,7 @@ _unix_unlink :: proc(path: cstring) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_unlink, uintptr(rawptr(path)))) } else { // NOTE: arm64 does not have unlink - return int(intrinsics.syscall(unix.SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path), 0))) + return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0)) } } @@ -387,7 +391,7 @@ _unix_rmdir :: proc(path: cstring) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_rmdir, uintptr(rawptr(path)))) } else { // NOTE: arm64 does not have rmdir - return int(intrinsics.syscall(unix.SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), AT_REMOVEDIR)) + return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR)) } } @@ -395,7 +399,7 @@ _unix_mkdir :: proc(path: cstring, mode: u32) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_mkdir, uintptr(rawptr(path)), uintptr(mode))) } else { // NOTE: arm64 does not have mkdir - return int(intrinsics.syscall(unix.SYS_mkdirat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode))) + return int(intrinsics.syscall(unix.SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode))) } } diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin index 2aa9fc283..395d2e73e 100644 --- a/core/os/stat_unix.odin +++ b/core/os/stat_unix.odin @@ -61,7 +61,7 @@ _make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time { _fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) { fi.size = s.size fi.mode = cast(File_Mode)s.mode - fi.is_dir = S_ISDIR(u32(s.mode)) + fi.is_dir = S_ISDIR(s.mode) // NOTE(laleksic, 2021-01-21): Not really creation time, but closest we can get (maybe better to leave it 0?) fi.creation_time = _make_time_from_unix_file_time(s.status_change) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 08ca4c049..8c95a234f 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -451,7 +451,7 @@ Odin_Endian_Type :: type_of(ODIN_ENDIAN) // This is probably only useful for freestanding targets foreign { @(link_name="__$startup_runtime") - _startup_runtime :: proc() --- + _startup_runtime :: proc "odin" () --- } @(link_name="__$cleanup_runtime") @@ -513,16 +513,18 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check return &type_table[n] } -typeid_base :: proc "contextless" (id: typeid) -> typeid { - ti := type_info_of(id) - ti = type_info_base(ti) - return ti.id +when !ODIN_DISALLOW_RTTI { + typeid_base :: proc "contextless" (id: typeid) -> typeid { + ti := type_info_of(id) + ti = type_info_base(ti) + return ti.id + } + typeid_core :: proc "contextless" (id: typeid) -> typeid { + ti := type_info_core(type_info_of(id)) + return ti.id + } + typeid_base_without_enum :: typeid_core } -typeid_core :: proc "contextless" (id: typeid) -> typeid { - ti := type_info_core(type_info_of(id)) - return ti.id -} -typeid_base_without_enum :: typeid_core diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index 14d55c64c..0d0b39072 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -21,6 +21,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index if 0 <= index && index < count { return } + @(cold) handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Index ") @@ -81,6 +82,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, if 0 <= low && low <= high && high <= max { return } + @(cold) handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid dynamic array indices ") @@ -97,10 +99,11 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) { - if 0 <= row_index && row_index < row_count && + if 0 <= row_index && row_index < row_count && 0 <= column_index && column_index < column_count { return } + @(cold) handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Matrix indices [") @@ -119,71 +122,101 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 } -type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) { - if ok { - return - } - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) { - print_caller_location(Source_Code_Location{file, line, column, ""}) - print_string(" Invalid type assertion from ") - print_typeid(from) - print_string(" to ") - print_typeid(to) - print_byte('\n') - type_assertion_trap() - } - handle_error(file, line, column, from, to) -} - -type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) { - if ok { - return +when ODIN_DISALLOW_RTTI { + type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) { + if ok { + return + } + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32) { + print_caller_location(Source_Code_Location{file, line, column, ""}) + print_string(" Invalid type assertion\n") + type_assertion_trap() + } + handle_error(file, line, column) } - variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid { - if id == nil || data == nil { + type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) { + if ok { + return + } + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32) { + print_caller_location(Source_Code_Location{file, line, column, ""}) + print_string(" Invalid type assertion\n") + type_assertion_trap() + } + handle_error(file, line, column) + } +} else { + type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) { + if ok { + return + } + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) { + print_caller_location(Source_Code_Location{file, line, column, ""}) + print_string(" Invalid type assertion from ") + print_typeid(from) + print_string(" to ") + print_typeid(to) + print_byte('\n') + type_assertion_trap() + } + handle_error(file, line, column, from, to) + } + + type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, 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 } - 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 + + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) { + + 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() } - return id + handle_error(file, line, column, from, to, from_data) } - - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) { - - 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) } @@ -191,6 +224,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio if 0 <= len { return } + @(cold) handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) { print_caller_location(loc) print_string(" Invalid slice length for make: ") @@ -205,6 +239,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := # if 0 <= len && len <= cap { return } + @(cold) handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) { print_caller_location(loc) print_string(" Invalid dynamic array parameters for make: ") @@ -221,6 +256,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca if 0 <= cap { return } + @(cold) handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) { print_caller_location(loc) print_string(" Invalid map capacity for make: ") diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 06740bc75..89c196fc2 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -160,11 +160,19 @@ print_caller_location :: proc "contextless" (using loc: Source_Code_Location) { } } print_typeid :: proc "contextless" (id: typeid) { - if id == nil { - print_string("nil") + when ODIN_DISALLOW_RTTI { + if id == nil { + print_string("nil") + } else { + print_string("") + } } else { - ti := type_info_of(id) - print_type(ti) + if id == nil { + print_string("nil") + } else { + ti := type_info_of(id) + print_type(ti) + } } } print_type :: proc "contextless" (ti: ^Type_Info) { diff --git a/core/sync/sync2/futex_freebsd.odin b/core/sync/sync2/futex_freebsd.odin new file mode 100644 index 000000000..2cbdb4aaa --- /dev/null +++ b/core/sync/sync2/futex_freebsd.odin @@ -0,0 +1,75 @@ +//+private +//+build freebsd +package sync2 + +import "core:c" +import "core:os" +import "core:time" + +UMTX_OP_WAIT :: 2 +UMTX_OP_WAKE :: 3 + +foreign import libc "system:c" + +foreign libc { + _umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int --- +} + +_futex_wait :: proc(f: ^Futex, expected: u32) -> bool { + timeout := os.Unix_File_Time{ + seconds = 5, + nanoseconds = 0, + } + + for { + res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout) + + if res != -1 { + return true + } + + if os.Errno(os.get_last_error()) == os.ETIMEDOUT { + continue + } + + panic("_futex_wait failure") + } + unreachable() +} + +_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool { + if duration <= 0 { + return false + } + + res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &os.Unix_File_Time{ + seconds = (os.time_t)(duration/1e9), + nanoseconds = (c.long)(duration%1e9), + }) + + if res != -1 { + return true + } + + if os.Errno(os.get_last_error()) == os.ETIMEDOUT { + return false + } + + panic("_futex_wait_with_timeout failure") +} + +_futex_signal :: proc(f: ^Futex) { + res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil) + + if res == -1 { + panic("_futex_signal failure") + } +} + +_futex_broadcast :: proc(f: ^Futex) { + res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil) + + if res == -1 { + panic("_futex_broadcast failure") + } +} diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index 046ecbc35..6d056d439 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -11,7 +11,7 @@ current_thread_id :: proc "contextless" () -> int { // // A Mutex must not be copied after first use Mutex :: struct { - impl: _Mutex `This is a tag`, + impl: _Mutex, } // mutex_lock locks m diff --git a/core/sync/sync2/primitives_freebsd.odin b/core/sync/sync2/primitives_freebsd.odin new file mode 100644 index 000000000..2a25a18a4 --- /dev/null +++ b/core/sync/sync2/primitives_freebsd.odin @@ -0,0 +1,9 @@ +//+build freebsd +//+private +package sync2 + +import "core:os" + +_current_thread_id :: proc "contextless" () -> int { + return os.current_thread_id() +} diff --git a/core/sync/sync_freebsd.odin b/core/sync/sync_freebsd.odin index 240308b7d..20e6bfceb 100644 --- a/core/sync/sync_freebsd.odin +++ b/core/sync/sync_freebsd.odin @@ -5,8 +5,8 @@ import "core:intrinsics" current_thread_id :: proc "contextless" () -> int { - SYS_GETTID :: 186; - return int(intrinsics.syscall(SYS_GETTID)); + SYS_GETTID :: 186 + return int(intrinsics.syscall(SYS_GETTID)) } @@ -19,22 +19,22 @@ Semaphore :: struct #align 16 { } semaphore_init :: proc(s: ^Semaphore, initial_count := 0) { - assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0); + assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0) } semaphore_destroy :: proc(s: ^Semaphore) { - assert(unix.sem_destroy(&s.handle) == 0); - s.handle = {}; + assert(unix.sem_destroy(&s.handle) == 0) + s.handle = {} } semaphore_post :: proc(s: ^Semaphore, count := 1) { // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop. for in 0.. ^sem_t ---; + sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- - sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---; - sem_destroy :: proc(sem: ^sem_t) -> c.int ---; - sem_post :: proc(sem: ^sem_t) -> c.int ---; - sem_wait :: proc(sem: ^sem_t) -> c.int ---; - sem_trywait :: proc(sem: ^sem_t) -> c.int ---; - // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---; + sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- + sem_destroy :: proc(sem: ^sem_t) -> c.int --- + sem_post :: proc(sem: ^sem_t) -> c.int --- + sem_wait :: proc(sem: ^sem_t) -> c.int --- + sem_trywait :: proc(sem: ^sem_t) -> c.int --- + // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int --- // NOTE: unclear whether pthread_yield is well-supported on Linux systems, // see https://linux.die.net/man/3/pthread_yield - pthread_yield :: proc() ---; + pthread_yield :: proc() --- } diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 926e69691..8cfb97076 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -675,6 +675,8 @@ when ODIN_ARCH == .amd64 { SYS_landlock_create_ruleset : uintptr : 444 SYS_landlock_add_rule : uintptr : 445 SYS_landlock_restrict_self : uintptr : 446 + + SIGCHLD :: 17 } else when ODIN_ARCH == .i386 { SYS_restart_syscall : uintptr : 0 SYS_exit : uintptr : 1 diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index e694060d2..7d2082250 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -54,6 +54,7 @@ import base64 "core:encoding/base64" import csv "core:encoding/csv" import hxa "core:encoding/hxa" import json "core:encoding/json" +import varint "core:encoding/varint" import fmt "core:fmt" import hash "core:hash" @@ -153,6 +154,7 @@ _ :: base64 _ :: csv _ :: hxa _ :: json +_ :: varint _ :: fmt _ :: hash _ :: image diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 044860ff7..8c2321dae 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -94,6 +94,7 @@ the_basics :: proc() { z: f64 // `z` is typed of type `f64` (64-bit floating point number) 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 + // (with the exception of negative zero, which must be given as `-0.0`) // CONSTANTS JUST WORK!!! @@ -1714,7 +1715,6 @@ deprecated_attribute :: proc() { } range_statements_with_multiple_return_values :: proc() { - // IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed fmt.println("\n#range statements with multiple return values") My_Iterator :: struct { index: int, @@ -2004,7 +2004,6 @@ relative_data_types :: proc() { or_else_operator :: proc() { fmt.println("\n#'or_else'") - // IMPORTANT NOTE: 'or_else' is an experimental feature and subject to change/removal { m: map[string]int i: int @@ -2035,8 +2034,6 @@ or_else_operator :: proc() { or_return_operator :: proc() { fmt.println("\n#'or_return'") - // IMPORTANT NOTE: 'or_return' is an experimental feature and subject to change/removal - // // The concept of 'or_return' will work by popping off the end value in a multiple // valued expression and checking whether it was not 'nil' or 'false', and if so, // set the end return value to value if possible. If the procedure only has one diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 9cb281656..b9875e43f 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -8,7 +8,7 @@ // #define DEFAULT_TO_THREADED_CHECKER // #endif -enum TargetOsKind { +enum TargetOsKind : u16 { TargetOs_Invalid, TargetOs_windows, @@ -26,7 +26,7 @@ enum TargetOsKind { TargetOs_COUNT, }; -enum TargetArchKind { +enum TargetArchKind : u16 { TargetArch_Invalid, TargetArch_amd64, @@ -38,7 +38,7 @@ enum TargetArchKind { TargetArch_COUNT, }; -enum TargetEndianKind { +enum TargetEndianKind : u8 { TargetEndian_Invalid, TargetEndian_Little, @@ -47,6 +47,16 @@ enum TargetEndianKind { TargetEndian_COUNT, }; +enum TargetABIKind : u16 { + TargetABI_Default, + + TargetABI_Win64, + TargetABI_SysV, + + TargetABI_COUNT, +}; + + String target_os_names[TargetOs_COUNT] = { str_lit(""), str_lit("windows"), @@ -77,6 +87,12 @@ String target_endian_names[TargetEndian_COUNT] = { str_lit("big"), }; +String target_abi_names[TargetABI_COUNT] = { + str_lit(""), + str_lit("win64"), + str_lit("sysv"), +}; + TargetEndianKind target_endians[TargetArch_COUNT] = { TargetEndian_Invalid, TargetEndian_Little, @@ -100,6 +116,7 @@ struct TargetMetrics { isize max_align; String target_triplet; String target_data_layout; + TargetABIKind abi; }; @@ -174,6 +191,13 @@ enum ErrorPosStyle { ErrorPosStyle_COUNT }; +enum RelocMode : u8 { + RelocMode_Default, + RelocMode_Static, + RelocMode_PIC, + RelocMode_DynamicNoPIC, +}; + // This stores the information for the specify architecture of this build struct BuildContext { // Constants @@ -185,6 +209,7 @@ struct BuildContext { bool ODIN_DEBUG; // Odin in debug mode bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing) + bool ODIN_FOREIGN_ERROR_PROCEDURES; ErrorPosStyle ODIN_ERROR_POS_STYLE; @@ -209,6 +234,7 @@ struct BuildContext { String extra_linker_flags; String extra_assembler_flags; String microarch; + String target_features; BuildModeKind build_mode; bool generate_docs; i32 optimization_level; @@ -254,6 +280,12 @@ struct BuildContext { bool copy_file_contents; + bool disallow_rtti; + + RelocMode reloc_mode; + bool disable_red_zone; + + u32 cmd_doc_flags; Array extra_packages; @@ -410,6 +442,16 @@ gb_global TargetMetrics target_wasi_wasm32 = { // str_lit(""), // }; +gb_global TargetMetrics target_freestanding_amd64_sysv = { + TargetOs_freestanding, + TargetArch_amd64, + 8, + 16, + str_lit("x86_64-pc-none-gnu"), + str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), + TargetABI_SysV, +}; + struct NamedTargetMetrics { @@ -432,7 +474,8 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("js_wasm32"), &target_js_wasm32 }, - // { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 }, + + { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, }; NamedTargetMetrics *selected_target_metrics; @@ -946,7 +989,6 @@ void init_build_context(TargetMetrics *cross_target) { } } - bc->copy_file_contents = true; TargetMetrics *metrics = nullptr; @@ -1005,6 +1047,21 @@ void init_build_context(TargetMetrics *cross_target) { bc->threaded_checker = true; #endif + if (bc->disable_red_zone) { + if (!!is_arch_wasm() && bc->metrics.os == TargetOs_freestanding) { + gb_printf_err("-disable-red-zone is not support for this target"); + gb_exit(1); + } + } + + if (bc->metrics.os == TargetOs_freestanding) { + bc->no_entry_point = true; + } else { + if (bc->disallow_rtti) { + gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n"); + gb_exit(1); + } + } // NOTE(zangent): The linker flags to set the build architecture are different // across OSs. It doesn't make sense to allocate extra data on the heap @@ -1059,14 +1116,14 @@ void init_build_context(TargetMetrics *cross_target) { if (bc->metrics.arch == TargetArch_wasm64) { link_flags = gb_string_appendc(link_flags, "-mwas64 "); } - if (bc->metrics.os == TargetOs_freestanding) { + if (bc->no_entry_point) { link_flags = gb_string_appendc(link_flags, "--no-entry "); } bc->link_flags = make_string_c(link_flags); // Disallow on wasm - build_context.use_separate_modules = false; + bc->use_separate_modules = false; } else { gb_printf_err("Compiler Error: Unsupported architecture\n"); gb_exit(1); @@ -1074,6 +1131,8 @@ void init_build_context(TargetMetrics *cross_target) { bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3); + + #undef LINK_FLAG_X64 #undef LINK_FLAG_386 } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 365d3434f..4cd09b8e9 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1241,6 +1241,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (c->scope->flags&ScopeFlag_Global) { compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } + if (build_context.disallow_rtti) { + error(call, "'%.*s' has been disallowed", LIT(builtin_name)); + return false; + } // NOTE(bill): The type information may not be setup yet init_core_type_info(c->checker); @@ -1253,9 +1257,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *t = o.type; if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) { if (is_type_polymorphic(t)) { - error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type"); + error(ce->args[0], "Invalid argument for '%.*s', unspecialized polymorphic type", LIT(builtin_name)); } else { - error(ce->args[0], "Invalid argument for 'type_info_of'"); + error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name)); } return false; } @@ -1266,7 +1270,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_operand_value(o) && is_type_typeid(t)) { add_package_dependency(c, "runtime", "__type_info_of"); } else if (o.mode != Addressing_Type) { - error(expr, "Expected a type or typeid for 'type_info_of'"); + error(expr, "Expected a type or typeid for '%.*s'", LIT(builtin_name)); return false; } @@ -1280,6 +1284,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (c->scope->flags&ScopeFlag_Global) { compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } + if (build_context.disallow_rtti) { + error(call, "'%.*s' has been disallowed", LIT(builtin_name)); + return false; + } // NOTE(bill): The type information may not be setup yet init_core_type_info(c->checker); @@ -1291,7 +1299,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } Type *t = o.type; if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) { - error(ce->args[0], "Invalid argument for 'typeid_of'"); + error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name)); return false; } t = default_type(t); @@ -1299,7 +1307,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 add_type_info_type(c, t); if (o.mode != Addressing_Type) { - error(expr, "Expected a type for 'typeid_of'"); + error(expr, "Expected a type for '%.*s'", LIT(builtin_name)); return false; } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3fdd944f9..d4a320f03 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -440,6 +440,12 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, if (type_expr) { e->type = check_type(ctx, type_expr); + if (are_types_identical(e->type, t_typeid)) { + e->type = nullptr; + e->kind = Entity_TypeName; + check_type_decl(ctx, e, init, named_type); + return; + } } Operand operand = {}; @@ -1130,6 +1136,10 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { + error(e->token, "@(thread_local) is not supported for this target platform"); + } + String context_name = str_lit("variable declaration"); if (type_expr != nullptr) { @@ -1205,6 +1215,8 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Operand o = {}; check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_init_variable(ctx, e, &o, str_lit("variable declaration")); + + check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 614da2368..577f3b07c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9352,6 +9352,8 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi if (o->type != nullptr && is_type_untyped(o->type)) { add_untyped(c, node, o->mode, o->type, o->value); } + check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed"); + add_type_and_value(c->info, node, o->mode, o->type, o->value); return kind; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 7cae1893f..f2c830c1b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2152,7 +2152,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { e->state = EntityState_Resolved; } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); - e->Variable.thread_local_model = ac.thread_local_model; if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; @@ -2182,6 +2181,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } e->Variable.thread_local_model = ac.thread_local_model; } + + if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { + error(e->token, "@(thread_local) is not supported for this target platform"); + } if (ac.is_static && ac.thread_local_model != "") { diff --git a/src/check_type.cpp b/src/check_type.cpp index 64fb67723..ecb2c26ea 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2031,10 +2031,14 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, if (param_count > 0) { Entity *end = params->Tuple.variables[param_count-1]; if (end->flags&EntityFlag_CVarArg) { - if (cc == ProcCC_StdCall || cc == ProcCC_CDecl) { + switch (cc) { + default: type->Proc.c_vararg = true; - } else { + break; + case ProcCC_Odin: + case ProcCC_Contextless: error(end->token, "Calling convention does not support #c_vararg"); + break; } } } @@ -2170,7 +2174,7 @@ void init_map_entry_type(Type *type) { /* struct { - hash: runtime.Map_Hash, + hash: uintptr, next: int, key: Key, value: Value, @@ -3027,5 +3031,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) { } set_base_type(named_type, type); + check_rtti_type_disallowed(e, type, "Use of a type, %s, which has been disallowed"); + return type; } diff --git a/src/checker.cpp b/src/checker.cpp index 5a7ece263..ea300afd9 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -29,6 +29,23 @@ bool is_operand_undef(Operand o) { return o.mode == Addressing_Value && o.type == t_untyped_undef; } +bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) { + if (build_context.disallow_rtti && type) { + if (is_type_any(type)) { + gbString t = type_to_string(type); + error(token, format, t); + gb_string_free(t); + return true; + } + } + return false; +} + +bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *format) { + GB_ASSERT(expr != nullptr); + return check_rtti_type_disallowed(ast_token(expr), type, format); +} + void scope_reset(Scope *scope) { if (scope == nullptr) return; @@ -875,7 +892,8 @@ void init_universal(void) { // Types for (isize i = 0; i < gb_count_of(basic_types); i++) { - add_global_type_entity(basic_types[i].Basic.name, &basic_types[i]); + String const &name = basic_types[i].Basic.name; + add_global_type_entity(name, &basic_types[i]); } add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]); @@ -977,6 +995,8 @@ void init_universal(void) { add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules); add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test); add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point); + add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES); + add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti); // Builtin Procedures @@ -1669,6 +1689,10 @@ void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) { void add_type_info_type(CheckerContext *c, Type *t) { void add_type_info_type_internal(CheckerContext *c, Type *t); + if (build_context.disallow_rtti) { + return; + } + mutex_lock(&c->info->type_info_mutex); add_type_info_type_internal(c, t); mutex_unlock(&c->info->type_info_mutex); @@ -2181,21 +2205,25 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { 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[] = { +#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \ + if (condition) { \ + String entities[] = {__VA_ARGS__}; \ + for (isize i = 0; i < gb_count_of(entities); i++) { \ + force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \ + } \ + } \ +} while (0) + + // required runtime entities + FORCE_ADD_RUNTIME_ENTITIES(true, // Odin types - str_lit("Type_Info"), str_lit("Source_Code_Location"), str_lit("Context"), str_lit("Allocator"), str_lit("Logger"), - // Global variables - str_lit("args__"), - str_lit("type_table"), - // Odin internal procedures str_lit("__init_context"), - str_lit("__type_info_of"), str_lit("cstring_to_string"), str_lit("_cleanup_runtime"), @@ -2228,35 +2256,36 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { // WASM Specific str_lit("__ashlti3"), str_lit("__multi3"), - }; - for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) { - force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]); - } + ); - if (build_context.no_crt) { - String required_no_crt_entities[] = { - // NOTE(bill): Only if these exist - str_lit("_tls_index"), - str_lit("_fltused"), - }; - for (isize i = 0; i < gb_count_of(required_no_crt_entities); i++) { - force_add_dependency_entity(c, c->info.runtime_package->scope, required_no_crt_entities[i]); - } - } + FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti, + // Odin types + str_lit("Type_Info"), - if (!build_context.no_bounds_check) { - String bounds_check_entities[] = { - // Bounds checking related procedures - str_lit("bounds_check_error"), - str_lit("matrix_bounds_check_error"), - str_lit("slice_expr_error_hi"), - str_lit("slice_expr_error_lo_hi"), - str_lit("multi_pointer_slice_expr_error"), - }; - for (isize i = 0; i < gb_count_of(bounds_check_entities); i++) { - force_add_dependency_entity(c, c->info.runtime_package->scope, bounds_check_entities[i]); - } - } + // Global variables + str_lit("type_table"), + str_lit("__type_info_of"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point, + // Global variables + str_lit("args__"), + ); + + FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()), + // NOTE(bill): Only if these exist + str_lit("_tls_index"), + str_lit("_fltused"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check, + // Bounds checking related procedures + str_lit("bounds_check_error"), + str_lit("matrix_bounds_check_error"), + str_lit("slice_expr_error_hi"), + str_lit("slice_expr_error_lo_hi"), + str_lit("multi_pointer_slice_expr_error"), + ); for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; @@ -2378,6 +2407,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { start->flags |= EntityFlag_Used; add_dependency_to_set(c, start); } + +#undef FORCE_ADD_RUNTIME_ENTITIES } bool is_entity_a_dependency(Entity *e) { diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 0244b73d6..07d2dd6e3 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1194,8 +1194,10 @@ LB_ABI_INFO(lb_get_abi_info) { switch (build_context.metrics.arch) { case TargetArch_amd64: - if (build_context.metrics.os == TargetOs_windows) { + if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) { return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } else if (build_context.metrics.abi == TargetABI_SysV) { + return lbAbiAmd64SysV::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); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 3b11f95a2..f5cb84785 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -624,6 +624,9 @@ struct lbGlobalVariable { }; lbProcedure *lb_create_startup_type_info(lbModule *m) { + if (build_context.disallow_rtti) { + return nullptr; + } LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -711,7 +714,9 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start lb_begin_procedure_body(p); - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + if (startup_type_info) { + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + } if (objc_names) { LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, ""); @@ -996,6 +1001,19 @@ String lb_filepath_obj_for_module(lbModule *m) { case TargetOs_essence: ext = STR_LIT(".o"); break; + + case TargetOs_freestanding: + switch (build_context.metrics.abi) { + default: + case TargetABI_Default: + case TargetABI_SysV: + ext = STR_LIT(".o"); + break; + case TargetABI_Win64: + ext = STR_LIT(".obj"); + break; + } + break; } } } @@ -1248,6 +1266,8 @@ void lb_generate_code(lbGenerator *gen) { LLVMCodeModel code_mode = LLVMCodeModelDefault; if (is_arch_wasm()) { code_mode = LLVMCodeModelJITDefault; + } else if (build_context.metrics.os == TargetOs_freestanding) { + code_mode = LLVMCodeModelKernel; } char const *host_cpu_name = LLVMGetHostCPUName(); @@ -1270,10 +1290,18 @@ void lb_generate_code(lbGenerator *gen) { // x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL if (ODIN_LLVM_MINIMUM_VERSION_12) { - llvm_cpu = "x86-64-v2"; + if (build_context.metrics.os == TargetOs_freestanding) { + llvm_cpu = "x86-64"; + } else { + llvm_cpu = "x86-64-v2"; + } } } + if (build_context.target_features.len != 0) { + llvm_features = alloc_cstring(permanent_allocator(), build_context.target_features); + } + // GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target)); LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone; @@ -1295,9 +1323,22 @@ void lb_generate_code(lbGenerator *gen) { reloc_mode = LLVMRelocPIC; } - if (build_context.metrics.os == TargetOs_openbsd) { - // Always use PIC for OpenBSD: it defaults to PIE + switch (build_context.reloc_mode) { + case RelocMode_Default: + if (build_context.metrics.os == TargetOs_openbsd) { + // Always use PIC for OpenBSD: it defaults to PIE + reloc_mode = LLVMRelocPIC; + } + break; + case RelocMode_Static: + reloc_mode = LLVMRelocStatic; + break; + case RelocMode_PIC: reloc_mode = LLVMRelocPIC; + break; + case RelocMode_DynamicNoPIC: + reloc_mode = LLVMRelocDynamicNoPic; + break; } for_array(i, gen->modules.entries) { @@ -1366,7 +1407,7 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Global Variables"); - { + if (!build_context.disallow_rtti) { lbModule *m = default_module; { // Add type info data @@ -1473,9 +1514,8 @@ void lb_generate_code(lbGenerator *gen) { if ((e->scope->flags&ScopeFlag_Init) && name == "main") { GB_ASSERT(e == info->entry_point); } - if (e->Procedure.is_export || - (e->Procedure.link_name.len > 0) || - ((e->scope->flags&ScopeFlag_File) && e->Procedure.link_name.len > 0)) { + if (build_context.command_kind == Command_test && + (e->Procedure.is_export || e->Procedure.link_name.len > 0)) { String link_name = e->Procedure.link_name; if (e->pkg->kind == Package_Runtime) { if (link_name == "main" || @@ -1699,6 +1739,11 @@ void lb_generate_code(lbGenerator *gen) { } } + if (build_context.command_kind == Command_test && !already_has_entry_point) { + TIME_SECTION("LLVM main"); + lb_create_main_procedure(default_module, startup_runtime); + } + for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; for_array(i, m->missing_procedures_to_check) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 026350440..4294747b9 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2824,16 +2824,25 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); dst_tag = lb_const_union_tag(p->module, src_type, dst_type); } + + + isize arg_count = 6; + if (build_context.disallow_rtti) { + arg_count = 4; + } + lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); - auto args = array_make(permanent_allocator(), 6); + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); args[2] = lb_const_int(p->module, t_i32, pos.line); args[3] = lb_const_int(p->module, t_i32, pos.column); - args[4] = lb_typeid(p->module, src_type); - args[5] = lb_typeid(p->module, dst_type); + if (!build_context.disallow_rtti) { + args[4] = lb_typeid(p->module, src_type); + args[5] = lb_typeid(p->module, dst_type); + } lb_emit_runtime_call(p, "type_assertion_check", args); } @@ -2846,6 +2855,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { } lbValue data_ptr = lb_emit_struct_ev(p, v, 0); if ((p->state_flags & StateFlag_no_type_assert) == 0) { + GB_ASSERT(!build_context.disallow_rtti); + lbValue any_id = lb_emit_struct_ev(p, v, 1); lbValue id = lb_typeid(p->module, type); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index eb6c89b85..27a6ec209 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -135,6 +135,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) lb_add_attribute_to_proc(m, p->value, "naked"); } + if (!entity->Procedure.is_foreign && build_context.disable_red_zone) { + lb_add_attribute_to_proc(m, p->value, "noredzone"); + } + switch (p->inlining) { case ProcInlining_inline: lb_add_attribute_to_proc(m, p->value, "alwaysinline"); @@ -1402,14 +1406,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_read_cycle_counter: { - char const *name = "llvm.readcyclecounter"; - unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); - GB_ASSERT_MSG(id != 0, "Unable to find %s", name); - LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); - lbValue res = {}; - res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, ""); res.type = tv.type; + + if (build_context.metrics.arch == TargetArch_arm64) { + LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false); + bool has_side_effects = false; + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs x9, cntvct_el0"), str_lit("=r"), has_side_effects); + GB_ASSERT(the_asm != nullptr); + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + } else { + char const *name = "llvm.readcyclecounter"; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); + + res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, ""); + } return res; } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 1d6297164..e245a8b40 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -14,6 +14,8 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr } lbValue lb_typeid(lbModule *m, Type *type) { + GB_ASSERT(!build_context.disallow_rtti); + type = default_type(type); u64 id = cast(u64)lb_type_info_index(m->info, type); @@ -88,6 +90,8 @@ lbValue lb_typeid(lbModule *m, Type *type) { } lbValue lb_type_info(lbModule *m, Type *type) { + GB_ASSERT(!build_context.disallow_rtti); + type = default_type(type); isize index = lb_type_info_index(m->info, type); @@ -106,6 +110,8 @@ lbValue lb_type_info(lbModule *m, Type *type) { } lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { + GB_ASSERT(!build_context.disallow_rtti); + i32 index = cast(i32)lb_type_info_index(m->info, type); GB_ASSERT(index >= 0); // gb_printf_err("%d %s\n", index, type_to_string(type)); @@ -155,6 +161,10 @@ lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) { void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data + if (build_context.disallow_rtti) { + return; + } + lbModule *m = p->module; CheckerInfo *info = m->info; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 431cfea9b..399d1632d 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -676,17 +676,24 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p // NOTE(bill): Panic on invalid conversion Type *dst_type = tuple->Tuple.variables[0]->type; + isize arg_count = 7; + if (build_context.disallow_rtti) { + arg_count = 4; + } + lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); args[2] = lb_const_int(m, t_i32, pos.line); args[3] = lb_const_int(m, t_i32, pos.column); - args[4] = lb_typeid(m, src_type); - args[5] = lb_typeid(m, dst_type); - args[6] = lb_emit_conv(p, value_, t_rawptr); + if (!build_context.disallow_rtti) { + args[4] = lb_typeid(m, src_type); + args[5] = lb_typeid(m, dst_type); + 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)); @@ -744,16 +751,23 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos if (!is_tuple) { // NOTE(bill): Panic on invalid conversion lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); + + isize arg_count = 7; + if (build_context.disallow_rtti) { + arg_count = 4; + } + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); args[2] = lb_const_int(m, t_i32, pos.line); args[3] = lb_const_int(m, t_i32, pos.column); - args[4] = any_typeid; - args[5] = dst_typeid; - args[6] = lb_emit_struct_ev(p, value, 0);; + if (!build_context.disallow_rtti) { + args[4] = any_typeid; + args[5] = dst_typeid; + 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)); @@ -1779,7 +1793,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin return LLVMGetInlineAsm(func_type, cast(char *)str.text, cast(size_t)str.len, cast(char *)clobbers.text, cast(size_t)clobbers.len, - /*HasSideEffects*/true, /*IsAlignStack*/false, + has_side_effects, is_align_stack, dialect #if LLVM_VERSION_MAJOR >= 13 , /*CanThrow*/false diff --git a/src/main.cpp b/src/main.cpp index aab695de2..63b2c8386 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -632,6 +632,10 @@ enum BuildFlagKind { BuildFlag_ExtraLinkerFlags, BuildFlag_ExtraAssemblerFlags, BuildFlag_Microarch, + BuildFlag_TargetFeatures, + + BuildFlag_RelocMode, + BuildFlag_DisableRedZone, BuildFlag_TestName, @@ -640,6 +644,8 @@ enum BuildFlagKind { BuildFlag_InsertSemicolon, BuildFlag_StrictStyle, BuildFlag_StrictStyleInitOnly, + BuildFlag_ForeignErrorProcedures, + BuildFlag_DisallowRTTI, BuildFlag_Compact, BuildFlag_GlobalDefinitions, @@ -784,7 +790,11 @@ bool parse_build_flags(Array args) { 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_ExtraAssemblerFlags, str_lit("extra-assembler-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_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build); + + add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test); @@ -793,10 +803,16 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check); + + add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check); + + 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_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc); @@ -1343,6 +1359,37 @@ bool parse_build_flags(Array args) { string_to_lower(&build_context.microarch); break; } + case BuildFlag_TargetFeatures: { + GB_ASSERT(value.kind == ExactValue_String); + build_context.target_features = value.value_string; + string_to_lower(&build_context.target_features); + break; + } + case BuildFlag_RelocMode: { + GB_ASSERT(value.kind == ExactValue_String); + String v = value.value_string; + if (v == "default") { + build_context.reloc_mode = RelocMode_Default; + } else if (v == "static") { + build_context.reloc_mode = RelocMode_Static; + } else if (v == "pic") { + build_context.reloc_mode = RelocMode_PIC; + } else if (v == "dynamic-no-pic") { + build_context.reloc_mode = RelocMode_DynamicNoPIC; + } else { + gb_printf_err("-reloc-mode flag expected one of the following\n"); + gb_printf_err("\tdefault\n"); + gb_printf_err("\tstatic\n"); + gb_printf_err("\tpic\n"); + gb_printf_err("\tdynamic-no-pic\n"); + bad_flags = true; + } + + break; + } + case BuildFlag_DisableRedZone: + build_context.disable_red_zone = true; + break; case BuildFlag_TestName: { GB_ASSERT(value.kind == ExactValue_String); { @@ -1361,9 +1408,15 @@ bool parse_build_flags(Array args) { case BuildFlag_DisallowDo: build_context.disallow_do = true; break; + case BuildFlag_DisallowRTTI: + build_context.disallow_rtti = true; + break; case BuildFlag_DefaultToNilAllocator: build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; break; + case BuildFlag_ForeignErrorProcedures: + build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true; + break; case BuildFlag_InsertSemicolon: { gb_printf_err("-insert-semicolon flag is not required any more\n"); bad_flags = true; @@ -2062,6 +2115,18 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(3, "-microarch:sandybridge"); print_usage_line(3, "-microarch:native"); print_usage_line(0, ""); + + print_usage_line(1, "-reloc-mode:"); + print_usage_line(2, "Specifies the reloc mode"); + print_usage_line(2, "Options:"); + print_usage_line(3, "default"); + print_usage_line(3, "static"); + print_usage_line(3, "pic"); + print_usage_line(3, "dynamic-no-pic"); + print_usage_line(0, ""); + + print_usage_line(1, "-disable-red-zone"); + print_usage_line(2, "Disable red zone on a supported freestanding target"); } if (check) { @@ -2092,6 +2157,11 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-verbose-errors"); print_usage_line(2, "Prints verbose error messages showing the code on that line and the location in that line"); print_usage_line(0, ""); + + print_usage_line(1, "-foreign-error-procedures"); + print_usage_line(2, "States that the error procedues used in the runtime are defined in a separate translation unit"); + print_usage_line(0, ""); + } if (run_or_build) { diff --git a/src/parser.cpp b/src/parser.cpp index 94a585f35..a435d1317 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1577,7 +1577,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) { } } -void expect_semicolon(AstFile *f, Ast *s) { +void expect_semicolon(AstFile *f) { Token prev_token = {}; if (allow_token(f, Token_Semicolon)) { @@ -1602,17 +1602,17 @@ void expect_semicolon(AstFile *f, Ast *s) { if (f->curr_token.kind == Token_EOF) { return; } - - if (s != nullptr) { - return; - } switch (f->curr_token.kind) { case Token_EOF: return; } - String p = token_to_string(f->curr_token); - syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); - fix_advance_to_next_stmt(f); + + if (f->curr_token.pos.line == f->prev_token.pos.line) { + String p = token_to_string(f->curr_token); + prev_token.pos = token_pos_end(prev_token); + syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); + fix_advance_to_next_stmt(f); + } } @@ -3181,7 +3181,7 @@ Ast *parse_foreign_block(AstFile *f, Token token) { Ast *body = ast_block_stmt(f, decls, open, close); Ast *decl = ast_foreign_block_decl(f, token, foreign_library, body, docs); - expect_semicolon(f, decl); + expect_semicolon(f); return decl; } @@ -3227,15 +3227,11 @@ Ast *parse_value_decl(AstFile *f, Array names, CommentGroup *docs) { } if (f->expr_level >= 0) { - Ast *end = nullptr; - if (!is_mutable && values.count > 0) { - end = values[values.count-1]; - } if (f->curr_token.kind == Token_CloseBrace && f->curr_token.pos.line == f->prev_token.pos.line) { } else { - expect_semicolon(f, end); + expect_semicolon(f); } } @@ -4143,11 +4139,7 @@ Ast *parse_return_stmt(AstFile *f) { advance_token(f); } - Ast *end = nullptr; - if (results.count > 0) { - end = results[results.count-1]; - } - expect_semicolon(f, end); + expect_semicolon(f); return ast_return_stmt(f, token, results); } @@ -4398,7 +4390,7 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly"); } - expect_semicolon(f, s); + expect_semicolon(f); return s; } @@ -4456,7 +4448,7 @@ Ast *parse_foreign_decl(AstFile *f) { } else { s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment); } - expect_semicolon(f, s); + expect_semicolon(f); return s; } } @@ -4595,7 +4587,7 @@ Ast *parse_stmt(AstFile *f) { case Token_Not: case Token_And: s = parse_simple_stmt(f, StmtAllowFlag_Label); - expect_semicolon(f, s); + expect_semicolon(f); return s; @@ -4623,7 +4615,7 @@ Ast *parse_stmt(AstFile *f) { label = parse_ident(f); } s = ast_branch_stmt(f, token, label); - expect_semicolon(f, s); + expect_semicolon(f); return s; } @@ -4638,12 +4630,12 @@ Ast *parse_stmt(AstFile *f) { Array list = parse_lhs_expr_list(f); if (list.count == 0) { syntax_error(token, "Illegal use of 'using' statement"); - expect_semicolon(f, nullptr); + expect_semicolon(f); return ast_bad_stmt(f, token, f->curr_token); } if (f->curr_token.kind != Token_Colon) { - expect_semicolon(f, list[list.count-1]); + expect_semicolon(f); return ast_using_stmt(f, token, list); } expect_token_after(f, Token_Colon, "identifier list"); @@ -4700,13 +4692,13 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "assert" || tag == "panic") { Ast *t = ast_basic_directive(f, hash_token, name); Ast *stmt = ast_expr_stmt(f, parse_call_expr(f, t)); - expect_semicolon(f, stmt); + expect_semicolon(f); return stmt; } else if (name.string == "force_inline" || name.string == "force_no_inline") { Ast *expr = parse_force_inlining_operand(f, name); Ast *stmt = ast_expr_stmt(f, expr); - expect_semicolon(f, stmt); + expect_semicolon(f); return stmt; } else if (tag == "unroll") { return parse_unrolled_for_loop(f, name); @@ -4728,7 +4720,7 @@ Ast *parse_stmt(AstFile *f) { case Token_Semicolon: s = ast_empty_stmt(f, token); - expect_semicolon(f, nullptr); + expect_semicolon(f); return s; } @@ -5586,7 +5578,7 @@ bool parse_file(Parser *p, AstFile *f) { } Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment); - expect_semicolon(f, pd); + expect_semicolon(f); f->pkg_decl = pd; if (f->error_count == 0) { diff --git a/src/string.cpp b/src/string.cpp index bcaf23b9b..d3dbc6904 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -10,10 +10,6 @@ struct String { u8 * text; isize len; - // u8 &operator[](isize i) { - // GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); - // return text[i]; - // } u8 const &operator[](isize i) const { GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); return text[i]; @@ -33,10 +29,6 @@ struct String { struct String16 { wchar_t *text; isize len; - wchar_t &operator[](isize i) { - GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); - return text[i]; - } wchar_t const &operator[](isize i) const { GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); return text[i]; diff --git a/tests/common/common.odin b/tests/common/common.odin new file mode 100644 index 000000000..9a715d38e --- /dev/null +++ b/tests/common/common.odin @@ -0,0 +1,40 @@ +// Boilerplate for tests +package common + +import "core:testing" +import "core:fmt" +import "core:os" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] FAIL %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +report :: proc(t: ^testing.T) { + if TEST_fail > 0 { + if TEST_fail > 1 { + fmt.printf("%v/%v tests successful, %v tests failed.\n", TEST_count - TEST_fail, TEST_count, TEST_fail) + } else { + fmt.printf("%v/%v tests successful, 1 test failed.\n", TEST_count - TEST_fail, TEST_count) + } + os.exit(1) + } else { + fmt.printf("%v/%v tests successful.\n", TEST_count, TEST_count) + } +} diff --git a/tests/core/Makefile b/tests/core/Makefile index 82bcae068..449efbb25 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -1,7 +1,12 @@ ODIN=../../odin PYTHON=$(shell which python3) +<<<<<<< HEAD all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test os2_test +======= +all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \ + math_test linalg_glsl_math_test +>>>>>>> upstream/master download_test_assets: $(PYTHON) download_assets.py @@ -30,3 +35,12 @@ os2_test: encoding_test: $(ODIN) run encoding/json -out=test_json $(ODIN) run encoding/varint -out=test_varint +<<<<<<< HEAD +======= + +math_test: + $(ODIN) run math/test_core_math.odin -out=test_core_math -collection:tests=.. + +linalg_glsl_math_test: + $(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -out=test_linalg_glsl_math -collection:tests=.. +>>>>>>> upstream/master diff --git a/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin new file mode 100644 index 000000000..e0b4f5145 --- /dev/null +++ b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin @@ -0,0 +1,85 @@ +// Tests "linalg_glsl_math.odin" in "core:math/linalg/glsl". +// Must be run with `-collection:tests=` flag, e.g. +// ./odin run tests/core/math/linalg/glsl/test_linalg_glsl_math.odin -collection:tests=./tests +package test_core_math_linalg_glsl_math + +import glsl "core:math/linalg/glsl" + +import "core:fmt" +import "core:math" +import "core:testing" +import tc "tests:common" + +main :: proc() { + + t := testing.T{} + + test_fract_f32(&t) + test_fract_f64(&t) + + tc.report(&t) +} + +@test +test_fract_f32 :: proc(t: ^testing.T) { + + using math + + r: f32 + + Datum :: struct { + i: int, + v: f32, + e: f32, + } + @static data := []Datum{ + { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -0.5 }, + { 2, F32_MIN, F32_MIN }, // 0x1p-126 + { 3, -F32_MIN, -F32_MIN }, + { 4, 0.0, 0.0 }, + { 5, -0.0, -0.0 }, + { 6, 1, 0.0 }, + { 7, -1, -0.0 }, + { 8, 0h3F80_0001, 0h3400_0000 }, // 0x1.000002p+0, 0x1p-23 + { 9, -0h3F80_0001, -0h3400_0000 }, + } + + for d, i in data { + assert(i == d.i) + r = glsl.fract(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e)) + } +} + +@test +test_fract_f64 :: proc(t: ^testing.T) { + + using math + + r: f64 + + Datum :: struct { + i: int, + v: f64, + e: f64, + } + @static data := []Datum{ + { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -0.5 }, + { 2, F64_MIN, F64_MIN }, // 0x1p-1022 + { 3, -F64_MIN, -F64_MIN }, + { 4, 0.0, 0.0 }, + { 5, -0.0, -0.0 }, + { 6, 1, 0.0 }, + { 7, -1, -0.0 }, + { 8, 0h3FF0_0000_0000_0001, 0h3CB0_0000_0000_0000 }, // 0x1.0000000000001p+0, 0x1p-52 + { 9, -0h3FF0_0000_0000_0001, -0h3CB0_0000_0000_0000 }, + } + + for d, i in data { + assert(i == d.i) + r = glsl.fract(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e)) + } +} diff --git a/tests/core/math/test_core_math.odin b/tests/core/math/test_core_math.odin new file mode 100644 index 000000000..57da27002 --- /dev/null +++ b/tests/core/math/test_core_math.odin @@ -0,0 +1,310 @@ +// Tests "math.odin" in "core:math". +// Must be run with `-collection:tests=` flag, e.g. +// ./odin run tests/core/math/test_core_math.odin -collection:tests=./tests +package test_core_math + +import "core:fmt" +import "core:math" +import "core:testing" +import tc "tests:common" + +main :: proc() { + t := testing.T{} + + test_classify_f16(&t) + test_classify_f32(&t) + test_classify_f64(&t) + + test_trunc_f16(&t) + test_trunc_f32(&t) + test_trunc_f64(&t) + + tc.report(&t) +} + +@test +test_classify_f16 :: proc(t: ^testing.T) { + + using math + using Float_Class + + r: Float_Class + + Datum :: struct { + i: int, + v: f16, + e: math.Float_Class, + } + @static data := []Datum{ + { 0, 1.2, Normal }, + { 1, 0h0001, Subnormal }, + { 2, 0.0, Zero }, + { 3, -0.0, Neg_Zero }, + { 4, SNAN_F16, NaN }, + { 5, QNAN_F16, NaN }, + { 6, INF_F16, Inf }, + { 7, NEG_INF_F16, Neg_Inf }, + } + + for d, i in data { + assert(i == d.i) + r = classify_f16(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) + } + + /* Check all subnormals (exponent 0, 10-bit significand non-zero) */ + for i :u16 = 1; i < 0x400; i += 1 { + v :f16 = transmute(f16)i + r = classify_f16(v) + e :Float_Class: Subnormal + tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e)) + } +} + +@test +test_classify_f32 :: proc(t: ^testing.T) { + + using math + using Float_Class + + r: Float_Class + + Datum :: struct { + i: int, + v: f32, + e: math.Float_Class, + } + @static data := []Datum{ + { 0, 1.2, Normal }, + { 1, 0h0000_0001, Subnormal }, + { 2, 0.0, Zero }, + { 3, -0.0, Neg_Zero }, + { 4, SNAN_F32, NaN }, + { 5, QNAN_F32, NaN }, + { 6, INF_F32, Inf }, + { 7, NEG_INF_F32, Neg_Inf }, + } + + for d, i in data { + assert(i == d.i) + r = classify_f32(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) + } +} + +@test +test_classify_f64 :: proc(t: ^testing.T) { + + using math + using Float_Class + + r: Float_Class + + Datum :: struct { + i: int, + v: f64, + e: math.Float_Class, + } + @static data := []Datum{ + { 0, 1.2, Normal }, + { 1, 0h0000_0000_0000_0001, Subnormal }, + { 2, 0.0, Zero }, + { 3, -0.0, Neg_Zero }, + { 4, SNAN_F64, NaN }, + { 5, QNAN_F64, NaN }, + { 6, INF_F64, Inf }, + { 7, NEG_INF_F64, Neg_Inf }, + } + + for d, i in data { + assert(i == d.i) + r = classify_f64(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) + } +} + +@test +test_trunc_f16 :: proc(t: ^testing.T) { + + using math + + r, v: f16 + + Datum :: struct { + i: int, + v: f16, + e: f16, + } + @static data := []Datum{ + { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -10 }, + + { 2, F16_MAX, F16_MAX }, + { 3, -F16_MAX, -F16_MAX }, + { 4, F16_MIN, 0.0 }, + { 5, -F16_MIN, -0.0 }, + { 6, 0.0, 0.0 }, + { 7, -0.0, -0.0 }, + { 8, 1, 1 }, + { 9, -1, -1 }, + { 10, INF_F16, INF_F16 }, + { 11, NEG_INF_F16, NEG_INF_F16 }, + + /* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */ + { 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1) + { 13, -0h3C01, -1 }, + { 14, 0h3BFF, 0.0 }, // 0x1.ffcp-1 (largest < 1) + { 15, -0h3BFF, -0.0 }, + { 16, 0h0001, 0.0 }, // 0x0.004p-14 (smallest subnormal) + { 17, -0h0001, -0.0 }, + { 18, 0h03FF, 0.0 }, // 0x0.ffcp-14 (largest subnormal) + { 19, -0h03FF, -0.0 }, + + { 20, 0hC809, -8 }, // -0x1.024p+3 + { 21, 0h4458, 4 }, // 0x1.16p+2 + } + + for d, i in data { + assert(i == d.i) + r = trunc_f16(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) + } + + v = SNAN_F16 + r = trunc_f16(v) + tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) + + v = QNAN_F16 + r = trunc_f16(v) + tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) +} + +@test +test_trunc_f32 :: proc(t: ^testing.T) { + + using math + + r, v: f32 + + Datum :: struct { + i: int, + v: f32, + e: f32, + } + @static data := []Datum{ + { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -10 }, + + { 2, F32_MAX, F32_MAX }, + { 3, -F32_MAX, -F32_MAX }, + { 4, F32_MIN, 0.0 }, + { 5, -F32_MIN, -0.0 }, + { 6, 0.0, 0.0 }, + { 7, -0.0, -0.0 }, + { 8, 1, 1 }, + { 9, -1, -1 }, + { 10, INF_F32, INF_F32 }, + { 11, NEG_INF_F32, NEG_INF_F32 }, + + /* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */ + { 12, 0h3F80_0001, 1 }, // 0x1.000002p+0 (smallest > 1) + { 13, -0h3F80_0001, -1 }, + { 14, 0h3F7F_FFFF, 0.0 }, // 0x1.fffffep-1 (largest < 1) + { 15, -0h3F7F_FFFF, -0.0 }, + { 16, 0h0000_0001, 0.0 }, // 0x0.000002p-126 (smallest subnormal) + { 17, -0h0000_0001, -0.0 }, + { 18, 0h007F_FFFF, 0.0 }, // 0x0.fffffep-126 (largest subnormal) + { 19, -0h007F_FFFF, -0.0 }, + + /* From libc-test src/math/sanity/truncf.h */ + { 20, 0hC101_11D0, -8 }, // -0x1.0223ap+3 + { 21, 0h408B_0C34, 4 }, // 0x1.161868p+2 + { 22, 0hC106_1A5A, -8 }, // -0x1.0c34b4p+3 + { 23, 0hC0D1_0378, -6 }, // -0x1.a206fp+2 + { 24, 0h4114_45DE, 9 }, // 0x1.288bbcp+3 + { 25, 0h3F29_77E8, 0.0 }, // 0x1.52efdp-1 + { 26, 0hBED0_2E64, -0.0 }, // -0x1.a05cc8p-2 + { 27, 0h3F0F_CF7D, 0.0 }, // 0x1.1f9efap-1 + { 28, 0h3F46_2ED8, 0.0 }, // 0x1.8c5dbp-1 + { 29, 0hBF2D_C375, -0.0 }, // -0x1.5b86eap-1 + } + + for d, i in data { + assert(i == d.i) + r = trunc_f32(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) + } + + v = SNAN_F32 + r = trunc_f32(v) + tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) + + v = QNAN_F32 + r = trunc_f32(v) + tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) +} + +@test +test_trunc_f64 :: proc(t: ^testing.T) { + + using math + + r, v: f64 + + Datum :: struct { + i: int, + v: f64, + e: f64, + } + data := []Datum{ + { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -10 }, + + { 2, F64_MAX, F64_MAX }, + { 3, -F64_MAX, -F64_MAX }, + { 4, F64_MIN, 0.0 }, + { 5, -F64_MIN, -0.0 }, + { 6, 0.0, 0.0 }, + { 7, -0.0, -0.0 }, + { 8, 1, 1 }, + { 9, -1, -1 }, + { 10, INF_F64, INF_F64 }, + { 11, NEG_INF_F64, NEG_INF_F64 }, + + /* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */ + { 12, 0h3FF0_0000_0000_0001, 1 }, // 0x1.0000000000001p+0 (smallest > 1) + { 13, -0h3FF0_0000_0000_0001, -1 }, + { 14, 0h3FEF_FFFF_FFFF_FFFF, 0.0 }, // 0x1.fffffffffffffp-1 (largest < 1) + { 15, -0h3FEF_FFFF_FFFF_FFFF, -0.0 }, + { 16, 0h0000_0000_0000_0001, 0.0 }, // 0x0.0000000000001p-1022 (smallest subnormal) + { 17, -0h0000_0000_0000_0001, -0.0 }, + { 18, 0h000F_FFFF_FFFF_FFFF, 0.0 }, // 0x0.fffffffffffffp-1022 (largest subnormal) + { 19, -0h000F_FFFF_FFFF_FFFF, -0.0 }, + + /* From libc-test src/math/sanity/trunc.h */ + { 20, 0hC020_2239_F3C6_A8F1, -8 }, // -0x1.02239f3c6a8f1p+3 + { 21, 0h4011_6186_8E18_BC67, 4 }, // 0x1.161868e18bc67p+2 + { 22, 0hC020_C34B_3E01_E6E7, -8 }, // -0x1.0c34b3e01e6e7p+3 + { 23, 0hC01A_206F_0A19_DCC4, -6 }, // -0x1.a206f0a19dcc4p+2 + { 24, 0h4022_88BB_B0D6_A1E6, 9 }, // 0x1.288bbb0d6a1e6p+3 + { 25, 0h3FE5_2EFD_0CD8_0497, 0.0 }, // 0x1.52efd0cd80497p-1 + { 26, 0hBFDA_05CC_7544_81D1, -0.0 }, // -0x1.a05cc754481d1p-2 + { 27, 0h3FE1_F9EF_9347_45CB, 0.0 }, // 0x1.1f9ef934745cbp-1 + { 28, 0h3FE8_C5DB_097F_7442, 0.0 }, // 0x1.8c5db097f7442p-1 + { 29, 0hBFE5_B86E_A811_8A0E, -0.0 }, // -0x1.5b86ea8118a0ep-1 + } + + for d, i in data { + assert(i == d.i) + r = trunc_f64(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) + } + + v = SNAN_F64 + r = trunc_f64(v) + tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) + + v = QNAN_F64 + r = trunc_f64(v) + tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) +} diff --git a/vendor/directx/d3d12/d3d12_constants.odin b/vendor/directx/d3d12/d3d12_constants.odin index 3f83dca6b..a30296cc1 100644 --- a/vendor/directx/d3d12/d3d12_constants.odin +++ b/vendor/directx/d3d12/d3d12_constants.odin @@ -292,7 +292,8 @@ MAX_DEPTH :: 1.0 MAX_LIVE_STATIC_SAMPLERS :: 2032 MAX_MAXANISOTROPY :: 16 MAX_MULTISAMPLE_SAMPLE_COUNT :: 32 -MAX_POSITION_VALUE :: 3.402823466e+34D3D12_MAX_ROOT_COST :: 64 +MAX_POSITION_VALUE :: 3.402823466e+34 +MAX_ROOT_COST :: 64 MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1 :: 1000000 MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2 :: 1000000 MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE :: 2048