From c96d8237ba1dfcc0db455bb8652ea7d8a947f817 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 3 May 2025 22:31:01 +0200 Subject: [PATCH 01/10] Clarify error messages for types that aren't simply comparable. Previously, it implied that these are different types: ``` W:/Scratch/scratch.odin(17:5) Error: Cannot compare expression, operator '==' not defined between the types 'Handle_Map($T=u32, $HT=u32, $Max=10000)' and 'Handle_Map($T=u32, $HT=u32, $Max=10000)' if m == {} { ^~~~~~^ ``` Now: ``` W:/Scratch/scratch.odin(20:5) Error: Cannot compare expression. Type 'Handle_Map($T=u32, $HT=u32, $Max=10000)' is not simply comparable, so operator '==' is not defined for it. if m == {} { ^~~~~~^ ``` --- src/check_expr.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6f585fe73..10b37bbf3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2910,9 +2910,20 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper if (!defined) { gbString xs = type_to_string(x->type, temporary_allocator()); gbString ys = type_to_string(y->type, temporary_allocator()); - err_str = gb_string_make(temporary_allocator(), - gb_bprintf("operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys) - ); + + if (!is_type_comparable(x->type)) { + err_str = gb_string_make(temporary_allocator(), + gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", xs, LIT(token_strings[op])) + ); + } else if (!is_type_comparable(y->type)) { + err_str = gb_string_make(temporary_allocator(), + gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", ys, LIT(token_strings[op])) + ); + } else { + err_str = gb_string_make(temporary_allocator(), + gb_bprintf("Operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys) + ); + } } else { Type *comparison_type = x->type; if (x->type == err_type && is_operand_nil(*x)) { @@ -2933,11 +2944,11 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper } else { yt = type_to_string(y->type); } - err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt)); + err_str = gb_string_make(temporary_allocator(), gb_bprintf("Mismatched types '%s' and '%s'", xt, yt)); } if (err_str != nullptr) { - error(node, "Cannot compare expression, %s", err_str); + error(node, "Cannot compare expression. %s.", err_str); x->type = t_untyped_bool; } else { if (x->mode == Addressing_Constant && From 1b8a65c327a5bb010a24773d6f4994f46270037f Mon Sep 17 00:00:00 2001 From: omark96 <43138339+omark96@users.noreply.github.com> Date: Sat, 3 May 2025 23:44:55 +0200 Subject: [PATCH 02/10] win/sys: Add GetWindowThreadProcessId --- core/sys/windows/user32.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 94cd57811..49ebb49cb 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -47,6 +47,8 @@ foreign user32 { lpParam: LPVOID, ) -> HWND --- + GetWindowThreadProcessId :: proc(hwnd: HWND, lpdwProcessId: LPDWORD) -> DWORD --- + DestroyWindow :: proc(hWnd: HWND) -> BOOL --- ShowWindow :: proc(hWnd: HWND, nCmdShow: INT) -> BOOL --- From deededfb0a99742a4e7cca2464c7f4a6b3d07604 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 4 May 2025 00:21:20 +0200 Subject: [PATCH 03/10] Fix `executable_path` info on Linux --- core/os/os2/process_linux.odin | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin index 6d654008b..a0815ca7d 100644 --- a/core/os/os2/process_linux.odin +++ b/core/os/os2/process_linux.odin @@ -200,15 +200,36 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator } if .Executable_Path in selection { - if cmdline[0] == '/' { - info.executable_path = strings.clone(cmdline[:terminator], allocator) or_return - info.fields += {.Executable_Path} - } else if cwd_err == nil { - info.executable_path = join_path({ cwd, cmdline[:terminator] }, allocator) or_return + if cwd_err == nil { + info.executable_path = strings.clone(command_line_exec, allocator) or_return info.fields += {.Executable_Path} } else { break cmdline_if } + /* + NOTE(Jeroen): + + This old version returns the wrong executable path for things like `bash` or `sh`, + for whom `/proc//cmdline` will just report "bash" or "sh", + resulting in misleading paths like `$PWD/sh`, even though that executable doesn't exist there. + + A way to "fix" this would be to invoke `which ` or scour the $PATH variable, but a better way + would be preferred. + + To be fair, `htop` also suffers from this problem and will list `bash`, `tmux`, `xfce4-panel` as just their + executable name in the command line column. So I think we shouldn't prepend the current directory when an executable is + found in the $PATH, which is what seems to be happening here. + + if command_line_exec[0] == '/' { + info.executable_path = strings.clone(command_line_exec, allocator) or_return + info.fields += {.Executable_Path} + } else if cwd_err == nil { + info.executable_path = join_path({cwd, command_line_exec}, allocator) or_return + info.fields += {.Executable_Path} + } else { + break cmdline_if + } + */ } if selection & {.Command_Line, .Command_Args} != {} { From 0f2a4b80efcb8e30a31fe74e10c2fcc503537466 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 4 May 2025 01:05:10 +0200 Subject: [PATCH 04/10] Proper fix for executable name on Linux. --- core/os/os2/process_linux.odin | 63 +++++++++++++++------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin index a0815ca7d..1480e66b5 100644 --- a/core/os/os2/process_linux.odin +++ b/core/os/os2/process_linux.odin @@ -162,7 +162,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator } } - cmdline_if: if selection & {.Working_Dir, .Command_Line, .Command_Args, .Executable_Path} != {} { + cmdline_if: if selection & {.Working_Dir, .Command_Line, .Command_Args} != {} { strings.builder_reset(&path_builder) strings.write_string(&path_builder, "/proc/") strings.write_int(&path_builder, pid) @@ -178,12 +178,12 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator terminator := strings.index_byte(cmdline, 0) assert(terminator > 0) - command_line_exec := cmdline[:terminator] + // command_line_exec := cmdline[:terminator] // Still need cwd if the execution on the command line is relative. cwd: string cwd_err: Error - if .Working_Dir in selection || (.Executable_Path in selection && command_line_exec[0] != '/') { + if .Working_Dir in selection { strings.builder_reset(&path_builder) strings.write_string(&path_builder, "/proc/") strings.write_int(&path_builder, pid) @@ -199,39 +199,6 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator } } - if .Executable_Path in selection { - if cwd_err == nil { - info.executable_path = strings.clone(command_line_exec, allocator) or_return - info.fields += {.Executable_Path} - } else { - break cmdline_if - } - /* - NOTE(Jeroen): - - This old version returns the wrong executable path for things like `bash` or `sh`, - for whom `/proc//cmdline` will just report "bash" or "sh", - resulting in misleading paths like `$PWD/sh`, even though that executable doesn't exist there. - - A way to "fix" this would be to invoke `which ` or scour the $PATH variable, but a better way - would be preferred. - - To be fair, `htop` also suffers from this problem and will list `bash`, `tmux`, `xfce4-panel` as just their - executable name in the command line column. So I think we shouldn't prepend the current directory when an executable is - found in the $PATH, which is what seems to be happening here. - - if command_line_exec[0] == '/' { - info.executable_path = strings.clone(command_line_exec, allocator) or_return - info.fields += {.Executable_Path} - } else if cwd_err == nil { - info.executable_path = join_path({cwd, command_line_exec}, allocator) or_return - info.fields += {.Executable_Path} - } else { - break cmdline_if - } - */ - } - if selection & {.Command_Line, .Command_Args} != {} { // skip to first arg //cmdline = cmdline[terminator + 1:] @@ -344,6 +311,30 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator } } + if .Executable_Path in selection { + /* + NOTE(Jeroen): + + The old version returned the wrong executable path for things like `bash` or `sh`, + for whom `/proc//cmdline` will just report "bash" or "sh", + resulting in misleading paths like `$PWD/sh`, even though that executable doesn't exist there. + + Thanks to Yawning for suggesting `/proc/self/exe`. + */ + + strings.builder_reset(&path_builder) + strings.write_string(&path_builder, "/proc/") + strings.write_int(&path_builder, pid) + strings.write_string(&path_builder, "/exe") + + if exe_bytes, exe_err := _read_link(strings.to_string(path_builder), temp_allocator()); exe_err == nil { + info.executable_path = strings.clone(string(exe_bytes), allocator) or_return + info.fields += {.Executable_Path} + } else { + err = exe_err + } + } + if .Environment in selection { strings.builder_reset(&path_builder) strings.write_string(&path_builder, "/proc/") From 39752faba4f3373ad9bf694262d95f69f5818c18 Mon Sep 17 00:00:00 2001 From: blob1807 <12388588+blob1807@users.noreply.github.com> Date: Sun, 4 May 2025 15:48:26 +1000 Subject: [PATCH 05/10] Fix typo from `b.w-b.w` -> `b.w-b.r` --- core/bufio/reader.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/bufio/reader.odin b/core/bufio/reader.odin index a875c732d..b78cac6e1 100644 --- a/core/bufio/reader.odin +++ b/core/bufio/reader.odin @@ -257,7 +257,7 @@ reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) { for b.r+utf8.UTF_MAX > b.w && !utf8.full_rune(b.buf[b.r:b.w]) && b.err == nil && - b.w-b.w < len(b.buf) { + b.w-b.r < len(b.buf) { _reader_read_new_chunk(b) or_return } From 95923c2059758bac282f2b78a004d8daa3824994 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 4 May 2025 20:03:07 +0200 Subject: [PATCH 06/10] os2: Don't try to translate Windows file attributes to Unix mode flags Also, fix `chmod`. It passed the wrong struct size to `SetFileInformationByHandle`. --- core/os/os2/file_windows.odin | 41 +++++++++++++++++++++++------------ core/os/os2/stat_windows.odin | 15 ++++++++----- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 6caf84a64..068f2979f 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -12,7 +12,30 @@ import win32 "core:sys/windows" INVALID_HANDLE :: ~uintptr(0) -S_IWRITE :: 0o200 +// NOTE(Jeroen): We don't translate mode flags for Linux when given to `chmod`. +// Let's not do so for Windows for `chmod` or `read_directory_iterator` either. +// They're *not* portable between Windows and non-Windows platforms. +// +// It also leads to information loss as flags like Archive, Hidden and System have no equivalent there. +// We can of course parse them so we can set the `.Symlink` and `.Directory` type, but we shouldn't pretend +// that 0o644 is meaningful when returned as a mode. +// `C:\bootmgr` as an example has attributes read only, hidden, system, archive. In no way is it sensible to replace that with 0o444. +FILE_ATTRIBUTE_READONLY :: win32.FILE_ATTRIBUTE_READONLY // 0x00000001 +FILE_ATTRIBUTE_HIDDEN :: win32.FILE_ATTRIBUTE_HIDDEN // 0x00000002 +FILE_ATTRIBUTE_SYSTEM :: win32.FILE_ATTRIBUTE_SYSTEM // 0x00000004 +FILE_ATTRIBUTE_DIRECTORY :: win32.FILE_ATTRIBUTE_DIRECTORY // 0x00000010 +FILE_ATTRIBUTE_ARCHIVE :: win32.FILE_ATTRIBUTE_ARCHIVE // 0x00000020 +FILE_ATTRIBUTE_DEVICE :: win32.FILE_ATTRIBUTE_DEVICE // 0x00000040 +FILE_ATTRIBUTE_NORMAL :: win32.FILE_ATTRIBUTE_NORMAL // 0x00000080 +FILE_ATTRIBUTE_TEMPORARY :: win32.FILE_ATTRIBUTE_TEMPORARY // 0x00000100 +FILE_ATTRIBUTE_SPARSE_FILE :: win32.FILE_ATTRIBUTE_SPARSE_FILE // 0x00000200 +FILE_ATTRIBUTE_REPARSE_Point :: win32.FILE_ATTRIBUTE_REPARSE_Point // 0x00000400 +FILE_ATTRIBUTE_REPARSE_POINT :: win32.FILE_ATTRIBUTE_REPARSE_POINT // 0x00000400 +FILE_ATTRIBUTE_COMPRESSED :: win32.FILE_ATTRIBUTE_COMPRESSED // 0x00000800 +FILE_ATTRIBUTE_OFFLINE :: win32.FILE_ATTRIBUTE_OFFLINE // 0x00001000 +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: win32.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED // 0x00002000 +FILE_ATTRIBUTE_ENCRYPTED :: win32.FILE_ATTRIBUTE_ENCRYPTED // 0x00004000 + _ERROR_BAD_NETPATH :: 53 MAX_RW :: 1<<30 @@ -122,7 +145,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u } attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS - if perm & S_IWRITE == 0 { + if u32(perm) & FILE_ATTRIBUTE_NORMAL == 0 { attrs = win32.FILE_ATTRIBUTE_READONLY if create_mode == win32.CREATE_ALWAYS { // NOTE(bill): Open has just asked to create a file in read-only mode. @@ -748,20 +771,10 @@ _fchmod :: proc(f: ^File, mode: int) -> Error { if f == nil || f.impl == nil { return nil } - d: win32.BY_HANDLE_FILE_INFORMATION - if !win32.GetFileInformationByHandle(_handle(f), &d) { - return _get_platform_error() - } - attrs := d.dwFileAttributes - if mode & S_IWRITE != 0 { - attrs &~= win32.FILE_ATTRIBUTE_READONLY - } else { - attrs |= win32.FILE_ATTRIBUTE_READONLY - } info: win32.FILE_BASIC_INFO - info.FileAttributes = attrs - if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) { + info.FileAttributes = win32.DWORD(mode) + if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(info)) { return _get_platform_error() } return nil diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 7d8dd3843..bf690bcd9 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -212,11 +212,15 @@ _file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes } _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: int) { - if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 { - mode |= 0o444 - } else { - mode |= 0o666 - } + // NOTE(Jeroen): We don't translate mode flags for Linux when given to `chmod`. + // Let's not do so for Windows for `chmod` or `read_directory_iterator` either. + // They're *not* portable between Windows and non-Windows platforms. + // + // It also leads to information loss as flags like Archive, Hidden and System have no equivalent there. + // We can of course parse them so we can set the `.Symlink` and `.Directory` type, but we shouldn't pretend + // that 0o644 is meaningful when returned as a mode. + // `C:\bootmgr` as an example has attributes read only, hidden, system, archive. In no way is it sensible to replace that with 0o444. + mode = int(file_attributes) is_sym := false if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { @@ -229,7 +233,6 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi type = .Symlink } else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { type = .Directory - mode |= 0o111 } else if h != nil { type = file_type(h) } From 32cef4c11b4705eaa22fb08254ab5a0181eee12d Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 4 May 2025 22:55:27 +0200 Subject: [PATCH 07/10] Fix change_times on Windows and simplify time handling in stat --- core/os/os2/file_windows.odin | 16 ++++------------ core/os/os2/stat_windows.odin | 34 +++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 068f2979f..94e51a14c 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -813,19 +813,11 @@ _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { defer close(f) return _fchtimes(f, atime, mtime) } + _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { if f == nil || f.impl == nil { return nil } - d: win32.BY_HANDLE_FILE_INFORMATION - if !win32.GetFileInformationByHandle(_handle(f), &d) { - return _get_platform_error() - } - - to_windows_time :: #force_inline proc(t: time.Time) -> win32.LARGE_INTEGER { - // a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC) - return win32.LARGE_INTEGER(time.time_to_unix_nano(t) * 100 + 116444736000000000) - } atime, mtime := atime, mtime if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) { @@ -833,9 +825,9 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { } info: win32.FILE_BASIC_INFO - info.LastAccessTime = to_windows_time(atime) - info.LastWriteTime = to_windows_time(mtime) - if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) { + info.LastAccessTime = time_as_filetime(atime) + info.LastWriteTime = time_as_filetime(mtime) + if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(info)) { return _get_platform_error() } return nil diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index bf690bcd9..8c3d4a610 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -239,14 +239,30 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi return } +// a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC) +time_as_filetime :: #force_inline proc(t: time.Time) -> (ft: win32.LARGE_INTEGER) { + win := u64(t._nsec / 100) + 116444736000000000 + return win32.LARGE_INTEGER(win) +} + +filetime_as_time_li :: #force_inline proc(ft: win32.LARGE_INTEGER) -> (t: time.Time) { + return {_nsec=(i64(ft) - 116444736000000000) * 100} +} + +filetime_as_time_ft :: #force_inline proc(ft: win32.FILETIME) -> (t: time.Time) { + return filetime_as_time_li(win32.LARGE_INTEGER(ft.dwLowDateTime) + win32.LARGE_INTEGER(ft.dwHighDateTime) << 32) +} + +filetime_as_time :: proc{filetime_as_time_ft, filetime_as_time_li} + _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0) fi.type = type fi.mode |= mode - fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) - fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) - fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) + fi.creation_time = filetime_as_time(d.ftCreationTime) + fi.modification_time = filetime_as_time(d.ftLastWriteTime) + fi.access_time = filetime_as_time(d.ftLastAccessTime) fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) return @@ -257,9 +273,9 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0) fi.type = type fi.mode |= mode - fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) - fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) - fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) + fi.creation_time = filetime_as_time(d.ftCreationTime) + fi.modification_time = filetime_as_time(d.ftLastWriteTime) + fi.access_time = filetime_as_time(d.ftLastAccessTime) fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) return @@ -289,9 +305,9 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0) fi.type = type fi.mode |= mode - fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) - fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) - fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) + fi.creation_time = filetime_as_time(d.ftCreationTime) + fi.modification_time = filetime_as_time(d.ftLastWriteTime) + fi.access_time = filetime_as_time(d.ftLastAccessTime) return fi, nil } From 36945079f8131973abb9e59bf642ab13c1c602be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 5 May 2025 11:41:54 +0100 Subject: [PATCH 08/10] Add `intrinsics.simd_indices` --- src/check_builtin.cpp | 31 +++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 5 +++++ src/llvm_backend_proc.cpp | 17 +++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f66a8605c..9d07de2b6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -760,6 +760,36 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return true; } + case BuiltinProc_simd_indices: + { + Operand x = {}; + check_expr_or_type(c, &x, ce->args[0], nullptr); + if (x.mode == Addressing_Invalid) return false; + if (x.mode != Addressing_Type) { + gbString s = expr_to_string(x.expr); + error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), s); + gb_string_free(s); + return false; + } + if (!is_type_simd_vector(x.type)) { + gbString s = type_to_string(x.type); + error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), s); + gb_string_free(s); + return false; + } + + Type *elem = base_array_type(x.type); + if (!is_type_numeric(elem)) { + gbString s = type_to_string(x.type); + error(x.expr, "'%.*s' expected a simd vector type with a numeric element type, got '%s'", LIT(builtin_name), s); + gb_string_free(s); + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + case BuiltinProc_simd_extract: { Operand x = {}; @@ -2059,6 +2089,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_has_target_feature: case BuiltinProc_procedure_of: + case BuiltinProc_simd_indices: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 40dde8240..d8ac10b11 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -205,6 +205,9 @@ BuiltinProc__simd_begin, BuiltinProc_simd_masked_expand_load, BuiltinProc_simd_masked_compress_store, + BuiltinProc_simd_indices, + + // Platform specific SIMD intrinsics BuiltinProc_simd_x86__MM_SHUFFLE, BuiltinProc__simd_end, @@ -551,6 +554,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_masked_expand_load"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_masked_compress_store"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_indices"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index be51f529d..7bd8dea59 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1293,6 +1293,23 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn lbValue res = {}; res.type = tv.type; + switch (builtin_id) { + case BuiltinProc_simd_indices: { + Type *type = base_type(res.type); + GB_ASSERT(type->kind == Type_SimdVector); + Type *elem = type->SimdVector.elem; + + i64 count = type->SimdVector.count; + LLVMValueRef *scalars = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + scalars[i] = lb_const_value(m, elem, exact_value_i64(i)).value; + } + + res.value = LLVMConstVector(scalars, cast(unsigned)count); + return res; + } + } + lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]); lbValue arg1 = {}; if (ce->args.count > 1) arg1 = lb_build_expr(p, ce->args[1]); lbValue arg2 = {}; if (ce->args.count > 2) arg2 = lb_build_expr(p, ce->args[2]); From c4719e75fd1cc209b46ec3844110e1d87266c5d2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 5 May 2025 11:43:19 +0100 Subject: [PATCH 09/10] Add `simd.indices` and docs --- base/intrinsics/intrinsics.odin | 2 +- core/simd/simd.odin | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index bec452007..c275dedaf 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -298,7 +298,7 @@ simd_masked_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) simd_masked_expand_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) --- simd_masked_compress_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) --- - +simd_indices :: proc($T: typeid/#simd[$N]$E) -> T where type_is_numeric(T) --- simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T --- simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 37cc19ebd..0e69304c3 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -2510,3 +2510,17 @@ Example: recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { return T(1) / v } + + +/* +Create a vector where each lane contains the index of that lane. +Inputs: +- `V`: The type of the vector to create. +Result: +- A vector of the given type, where each lane contains the index of that lane. +**Operation**: + for i in 0 ..< N { + res[i] = i + } +*/ +indices :: intrinsics.simd_indices \ No newline at end of file From 2224911aca77d15cfdb5ae19e16e9c88ed6edea9 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 5 May 2025 18:09:54 +0200 Subject: [PATCH 10/10] Fix `type_union_tag_offset` when all members are zero sized --- src/check_builtin.cpp | 7 ++++--- src/types.cpp | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 9d07de2b6..a315d1880 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -6032,12 +6032,13 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As // NOTE(jakubtomsu): forces calculation of variant_block_size type_size_of(u); - i64 tag_offset = u->Union.variant_block_size; - GB_ASSERT(tag_offset > 0); + // NOTE(Jeroen): A tag offset of zero is perfectly fine if all members of the union are empty structs. + // What matters is that the tag size is > 0. + GB_ASSERT(u->Union.tag_size > 0); operand->mode = Addressing_Constant; operand->type = t_untyped_integer; - operand->value = exact_value_i64(tag_offset); + operand->value = exact_value_i64(u->Union.variant_block_size); } break; diff --git a/src/types.cpp b/src/types.cpp index 9c9472a28..393e35ca1 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4108,10 +4108,10 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { } i64 max = 0; - i64 field_size = 0; for_array(i, t->Union.variants) { Type *variant_type = t->Union.variants[i]; + i64 size = type_size_of_internal(variant_type, path); if (max < size) { max = size; @@ -4130,7 +4130,7 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { size = align_formula(max, tag_size); // NOTE(bill): Calculate the padding between the common fields and the tag t->Union.tag_size = cast(i16)tag_size; - t->Union.variant_block_size = size - field_size; + t->Union.variant_block_size = size; size += tag_size; }