diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 481434a7a..0a344ebf1 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -63,11 +63,13 @@ jobs: cp -r core dist cp -r vendor dist cp -r examples dist + # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 + zip -r dist.zip dist - name: Upload artifact uses: actions/upload-artifact@v1 with: name: ubuntu_artifacts - path: dist + path: dist.zip build_macos: name: MacOS Build if: github.repository == 'odin-lang/Odin' @@ -76,15 +78,13 @@ jobs: - uses: actions/checkout@v1 - name: Download LLVM and setup PATH run: | - brew install llvm@13 + brew install llvm@13 dylibbundler echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - name: build odin run: make nightly - - name: Odin run - run: ./odin run examples/demo - - name: Copy artifacts + - name: Bundle run: | mkdir dist cp odin dist @@ -94,28 +94,31 @@ jobs: cp -r core dist cp -r vendor dist cp -r examples dist + dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs + # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 + zip -r dist.zip dist + - name: Odin run + run: ./dist/odin run examples/demo - name: Upload artifact uses: actions/upload-artifact@v1 with: name: macos_artifacts - path: dist + path: dist.zip build_macos_arm: name: MacOS ARM Build if: github.repository == 'odin-lang/Odin' - runs-on: macos-14 + runs-on: macos-14 # ARM machine steps: - uses: actions/checkout@v1 - name: Download LLVM and setup PATH run: | - brew install llvm@13 + brew install llvm@13 dylibbundler echo "/opt/homebrew/opt/llvm@13/bin" >> $GITHUB_PATH TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - name: build odin run: make nightly - - name: Odin run - run: ./odin run examples/demo - - name: Copy artifacts + - name: Bundle run: | mkdir dist cp odin dist @@ -125,11 +128,16 @@ jobs: cp -r core dist cp -r vendor dist cp -r examples dist + dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs + # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 + zip -r dist.zip dist + - name: Odin run + run: ./dist/odin run examples/demo - name: Upload artifact uses: actions/upload-artifact@v1 with: name: macos_arm_artifacts - path: dist + path: dist.zip upload_b2: runs-on: [ubuntu-latest] needs: [build_windows, build_macos, build_macos_arm, build_ubuntu] @@ -182,9 +190,9 @@ jobs: echo Uploading artifcates to B2 chmod +x ./ci/upload_create_nightly.sh ./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ - ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ - ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/ - ./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/ + ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/dist.zip + ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/dist.zip + ./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/dist.zip echo Deleting old artifacts in B2 python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP" diff --git a/.gitignore b/.gitignore index a5ddfe670..228f006a3 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ tests/internal/test_map tests/internal/test_pow tests/internal/test_rtti tests/core/test_core_compress +tests/core/test_core_container tests/core/test_core_filepath tests/core/test_core_fmt tests/core/test_core_i18n diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 85e64242d..8f27ca674 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -181,6 +181,14 @@ Type_Info_Matrix :: struct { Type_Info_Soa_Pointer :: struct { elem: ^Type_Info, } +Type_Info_Bit_Field :: struct { + backing_type: ^Type_Info, + names: []string, + types: []^Type_Info, + bit_sizes: []uintptr, + bit_offsets: []uintptr, + tags: []string, +} Type_Info_Flag :: enum u8 { Comparable = 0, @@ -223,6 +231,7 @@ Type_Info :: struct { Type_Info_Relative_Multi_Pointer, Type_Info_Matrix, Type_Info_Soa_Pointer, + Type_Info_Bit_Field, }, } @@ -256,6 +265,7 @@ Typeid_Kind :: enum u8 { Relative_Multi_Pointer, Matrix, Soa_Pointer, + Bit_Field, } #assert(len(Typeid_Kind) < 32) @@ -270,7 +280,7 @@ Typeid_Kind :: enum u8 { // NOTE(bill): only the ones that are needed (not all types) // This will be set by the compiler -type_table: []Type_Info +type_table: []^Type_Info args__: []cstring @@ -599,7 +609,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check if n < 0 || n >= len(type_table) { n = 0 } - return &type_table[n] + return type_table[n] } when !ODIN_NO_RTTI { diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 691f76ff1..62bee8620 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1034,3 +1034,25 @@ fixdfti :: proc(a: u64) -> i128 { } } + + + +__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) { + for i in 0.. 0 { print_string(", ") } + print_string(name) + print_string(": ") + print_type(info.types[i]) + print_string(" | ") + print_u64(u64(info.bit_sizes[i])) + } + print_byte('}') + case Type_Info_Simd_Vector: print_string("#simd[") diff --git a/ci/upload_create_nightly.sh b/ci/upload_create_nightly.sh old mode 100644 new mode 100755 index 754b9b87c..065cb13bf --- a/ci/upload_create_nightly.sh +++ b/ci/upload_create_nightly.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + bucket=$1 platform=$2 artifact=$3 @@ -9,5 +11,15 @@ filename="odin-$platform-nightly+$now.zip" echo "Creating archive $filename from $artifact and uploading to $bucket" -7z a -bd "output/$filename" -r "$artifact" -b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename" \ No newline at end of file +# If this is already zipped up (done before artifact upload to keep permissions in tact), just move it. +if [ "${artifact: -4}" == ".zip" ] +then + echo "Artifact already a zip" + mkdir -p "output" + mv "$artifact" "output/$filename" +else + echo "Artifact needs to be zipped" + 7z a -bd "output/$filename" -r "$artifact" +fi + +b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename" diff --git a/core/container/avl/avl.odin b/core/container/avl/avl.odin new file mode 100644 index 000000000..eecc1b756 --- /dev/null +++ b/core/container/avl/avl.odin @@ -0,0 +1,678 @@ +/* +package avl implements an AVL tree. + +The implementation is non-intrusive, and non-recursive. +*/ +package container_avl + +import "base:intrinsics" +import "base:runtime" +import "core:slice" + +_ :: intrinsics +_ :: runtime + +// Originally based on the CC0 implementation by Eric Biggers +// See: https://github.com/ebiggers/avl_tree/ + +// Direction specifies the traversal direction for a tree iterator. +Direction :: enum i8 { + // Backward is the in-order backwards direction. + Backward = -1, + // Forward is the in-order forwards direction. + Forward = 1, +} + +// Ordering specifies order when inserting/finding values into the tree. +Ordering :: slice.Ordering + +// Tree is an AVL tree. +Tree :: struct($Value: typeid) { + // user_data is a parameter that will be passed to the on_remove + // callback. + user_data: rawptr, + // on_remove is an optional callback that can be called immediately + // after a node is removed from the tree. + on_remove: proc(value: Value, user_data: rawptr), + + _root: ^Node(Value), + _node_allocator: runtime.Allocator, + _cmp_fn: proc(a, b: Value) -> Ordering, + _size: int, +} + +// Node is an AVL tree node. +// +// WARNING: It is unsafe to mutate value if the node is part of a tree +// if doing so will alter the Node's sort position relative to other +// elements in the tree. +Node :: struct($Value: typeid) { + value: Value, + + _parent: ^Node(Value), + _left: ^Node(Value), + _right: ^Node(Value), + _balance: i8, +} + +// Iterator is a tree iterator. +// +// WARNING: It is unsafe to modify the tree while iterating, except via +// the iterator_remove method. +Iterator :: struct($Value: typeid) { + _tree: ^Tree(Value), + _cur: ^Node(Value), + _next: ^Node(Value), + _direction: Direction, + _called_next: bool, +} + +// init initializes a tree. +init :: proc { + init_ordered, + init_cmp, +} + +// init_cmp initializes a tree. +init_cmp :: proc( + t: ^$T/Tree($Value), + cmp_fn: proc(a, b: Value) -> Ordering, + node_allocator := context.allocator, +) { + t._root = nil + t._node_allocator = node_allocator + t._cmp_fn = cmp_fn + t._size = 0 +} + +// init_ordered initializes a tree containing ordered items, with +// a comparison function that results in an ascending order sort. +init_ordered :: proc( + t: ^$T/Tree($Value), + node_allocator := context.allocator, +) where intrinsics.type_is_ordered_numeric(Value) { + init_cmp(t, slice.cmp_proc(Value), node_allocator) +} + +// destroy de-initializes a tree. +destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) { + iter := iterator(t, Direction.Forward) + for _ in iterator_next(&iter) { + iterator_remove(&iter, call_on_remove) + } +} + +// len returns the number of elements in the tree. +len :: proc "contextless" (t: ^$T/Tree($Value)) -> int { + return t._size +} + +// first returns the first node in the tree (in-order) or nil iff +// the tree is empty. +first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) { + return tree_first_or_last_in_order(t, Direction.Backward) +} + +// last returns the last element in the tree (in-order) or nil iff +// the tree is empty. +last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) { + return tree_first_or_last_in_order(t, Direction.Forward) +} + +// find finds the value in the tree, and returns the corresponding +// node or nil iff the value is not present. +find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) { + cur := t._root + descend_loop: for cur != nil { + switch t._cmp_fn(value, cur.value) { + case .Less: + cur = cur._left + case .Greater: + cur = cur._right + case .Equal: + break descend_loop + } + } + + return cur +} + +// find_or_insert attempts to insert the value into the tree, and returns +// the node, a boolean indicating if the value was inserted, and the +// node allocator error if relevant. If the value is already +// present, the existing node is returned un-altered. +find_or_insert :: proc( + t: ^$T/Tree($Value), + value: Value, +) -> ( + n: ^Node(Value), + inserted: bool, + err: runtime.Allocator_Error, +) { + n_ptr := &t._root + for n_ptr^ != nil { + n = n_ptr^ + switch t._cmp_fn(value, n.value) { + case .Less: + n_ptr = &n._left + case .Greater: + n_ptr = &n._right + case .Equal: + return + } + } + + parent := n + n = new(Node(Value), t._node_allocator) or_return + n.value = value + n._parent = parent + n_ptr^ = n + tree_rebalance_after_insert(t, n) + + t._size += 1 + inserted = true + + return +} + +// remove removes a node or value from the tree, and returns true iff the +// removal was successful. While the node's value will be left intact, +// the node itself will be freed via the tree's node allocator. +remove :: proc { + remove_value, + remove_node, +} + +// remove_value removes a value from the tree, and returns true iff the +// removal was successful. While the node's value will be left intact, +// the node itself will be freed via the tree's node allocator. +remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool { + n := find(t, value) + if n == nil { + return false + } + return remove_node(t, n, call_on_remove) +} + +// remove_node removes a node from the tree, and returns true iff the +// removal was successful. While the node's value will be left intact, +// the node itself will be freed via the tree's node allocator. +remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool { + if node._parent == node || (node._parent == nil && t._root != node) { + return false + } + defer { + if call_on_remove && t.on_remove != nil { + t.on_remove(node.value, t.user_data) + } + free(node, t._node_allocator) + } + + parent: ^Node(Value) + left_deleted: bool + + t._size -= 1 + if node._left != nil && node._right != nil { + parent, left_deleted = tree_swap_with_successor(t, node) + } else { + child := node._left + if child == nil { + child = node._right + } + parent = node._parent + if parent != nil { + if node == parent._left { + parent._left = child + left_deleted = true + } else { + parent._right = child + left_deleted = false + } + if child != nil { + child._parent = parent + } + } else { + if child != nil { + child._parent = parent + } + t._root = child + node_reset(node) + return true + } + } + + for { + if left_deleted { + parent = tree_handle_subtree_shrink(t, parent, +1, &left_deleted) + } else { + parent = tree_handle_subtree_shrink(t, parent, -1, &left_deleted) + } + if parent == nil { + break + } + } + node_reset(node) + + return true +} + +// iterator returns a tree iterator in the specified direction. +iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) { + it: Iterator(Value) + it._tree = transmute(^Tree(Value))t + it._direction = direction + + iterator_first(&it) + + return it +} + +// iterator_from_pos returns a tree iterator in the specified direction, +// spanning the range [pos, last] (inclusive). +iterator_from_pos :: proc "contextless" ( + t: ^$T/Tree($Value), + pos: ^Node(Value), + direction: Direction, +) -> Iterator(Value) { + it: Iterator(Value) + it._tree = transmute(^Tree(Value))t + it._direction = direction + it._next = nil + it._called_next = false + + if it._cur = pos; pos != nil { + it._next = node_next_or_prev_in_order(it._cur, it._direction) + } + + return it +} + +// iterator_get returns the node currently pointed to by the iterator, +// or nil iff the node has been removed, the tree is empty, or the end +// of the tree has been reached. +iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) { + return it._cur +} + +// iterator_remove removes the node currently pointed to by the iterator, +// and returns true iff the removal was successful. Semantics are the +// same as the Tree remove. +iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool { + if it._cur == nil { + return false + } + + ok := remove_node(it._tree, it._cur, call_on_remove) + if ok { + it._cur = nil + } + + return ok +} + +// iterator_next advances the iterator and returns the (node, true) or +// or (nil, false) iff the end of the tree has been reached. +// +// Note: The first call to iterator_next will return the first node instead +// of advancing the iterator. +iterator_next :: proc "contextless" (it: ^$I/Iterator($Value)) -> (^Node(Value), bool) { + // This check is needed so that the first element gets returned from + // a brand-new iterator, and so that the somewhat contrived case where + // iterator_remove is called before the first call to iterator_next + // returns the correct value. + if !it._called_next { + it._called_next = true + + // There can be the contrived case where iterator_remove is + // called before ever calling iterator_next, which needs to be + // handled as an actual call to next. + // + // If this happens it._cur will be nil, so only return the + // first value, if it._cur is valid. + if it._cur != nil { + return it._cur, true + } + } + + if it._next == nil { + return nil, false + } + + it._cur = it._next + it._next = node_next_or_prev_in_order(it._cur, it._direction) + + return it._cur, true +} + +@(private) +tree_first_or_last_in_order :: proc "contextless" ( + t: ^$T/Tree($Value), + direction: Direction, +) -> ^Node(Value) { + first, sign := t._root, i8(direction) + if first != nil { + for { + tmp := node_get_child(first, +sign) + if tmp == nil { + break + } + first = tmp + } + } + + return first +} + +@(private) +tree_replace_child :: proc "contextless" ( + t: ^$T/Tree($Value), + parent, old_child, new_child: ^Node(Value), +) { + if parent != nil { + if old_child == parent._left { + parent._left = new_child + } else { + parent._right = new_child + } + } else { + t._root = new_child + } +} + +@(private) +tree_rotate :: proc "contextless" (t: ^$T/Tree($Value), a: ^Node(Value), sign: i8) { + b := node_get_child(a, -sign) + e := node_get_child(b, +sign) + p := a._parent + + node_set_child(a, -sign, e) + a._parent = b + + node_set_child(b, +sign, a) + b._parent = p + + if e != nil { + e._parent = a + } + + tree_replace_child(t, p, a, b) +} + +@(private) +tree_double_rotate :: proc "contextless" ( + t: ^$T/Tree($Value), + b, a: ^Node(Value), + sign: i8, +) -> ^Node(Value) { + e := node_get_child(b, +sign) + f := node_get_child(e, -sign) + g := node_get_child(e, +sign) + p := a._parent + e_bal := e._balance + + node_set_child(a, -sign, g) + a_bal := -e_bal + if sign * e_bal >= 0 { + a_bal = 0 + } + node_set_parent_balance(a, e, a_bal) + + node_set_child(b, +sign, f) + b_bal := -e_bal + if sign * e_bal <= 0 { + b_bal = 0 + } + node_set_parent_balance(b, e, b_bal) + + node_set_child(e, +sign, a) + node_set_child(e, -sign, b) + node_set_parent_balance(e, p, 0) + + if g != nil { + g._parent = a + } + + if f != nil { + f._parent = b + } + + tree_replace_child(t, p, a, e) + + return e +} + +@(private) +tree_handle_subtree_growth :: proc "contextless" ( + t: ^$T/Tree($Value), + node, parent: ^Node(Value), + sign: i8, +) -> bool { + old_balance_factor := parent._balance + if old_balance_factor == 0 { + node_adjust_balance_factor(parent, sign) + return false + } + + new_balance_factor := old_balance_factor + sign + if new_balance_factor == 0 { + node_adjust_balance_factor(parent, sign) + return true + } + + if sign * node._balance > 0 { + tree_rotate(t, parent, -sign) + node_adjust_balance_factor(parent, -sign) + node_adjust_balance_factor(node, -sign) + } else { + tree_double_rotate(t, node, parent, -sign) + } + + return true +} + +@(private) +tree_rebalance_after_insert :: proc "contextless" (t: ^$T/Tree($Value), inserted: ^Node(Value)) { + node, parent := inserted, inserted._parent + switch { + case parent == nil: + return + case node == parent._left: + node_adjust_balance_factor(parent, -1) + case: + node_adjust_balance_factor(parent, +1) + } + + if parent._balance == 0 { + return + } + + for done := false; !done; { + node = parent + if parent = node._parent; parent == nil { + return + } + + if node == parent._left { + done = tree_handle_subtree_growth(t, node, parent, -1) + } else { + done = tree_handle_subtree_growth(t, node, parent, +1) + } + } +} + +@(private) +tree_swap_with_successor :: proc "contextless" ( + t: ^$T/Tree($Value), + x: ^Node(Value), +) -> ( + ^Node(Value), + bool, +) { + ret: ^Node(Value) + left_deleted: bool + + y := x._right + if y._left == nil { + ret = y + } else { + q: ^Node(Value) + + for { + q = y + if y = y._left; y._left == nil { + break + } + } + + if q._left = y._right; q._left != nil { + q._left._parent = q + } + y._right = x._right + x._right._parent = y + ret = q + left_deleted = true + } + + y._left = x._left + x._left._parent = y + + y._parent = x._parent + y._balance = x._balance + + tree_replace_child(t, x._parent, x, y) + + return ret, left_deleted +} + +@(private) +tree_handle_subtree_shrink :: proc "contextless" ( + t: ^$T/Tree($Value), + parent: ^Node(Value), + sign: i8, + left_deleted: ^bool, +) -> ^Node(Value) { + old_balance_factor := parent._balance + if old_balance_factor == 0 { + node_adjust_balance_factor(parent, sign) + return nil + } + + node: ^Node(Value) + new_balance_factor := old_balance_factor + sign + if new_balance_factor == 0 { + node_adjust_balance_factor(parent, sign) + node = parent + } else { + node = node_get_child(parent, sign) + if sign * node._balance >= 0 { + tree_rotate(t, parent, -sign) + if node._balance == 0 { + node_adjust_balance_factor(node, -sign) + return nil + } + node_adjust_balance_factor(parent, -sign) + node_adjust_balance_factor(node, -sign) + } else { + node = tree_double_rotate(t, node, parent, -sign) + } + } + + parent := parent + if parent = node._parent; parent != nil { + left_deleted^ = node == parent._left + } + return parent +} + +@(private) +node_reset :: proc "contextless" (n: ^Node($Value)) { + // Mostly pointless as n will be deleted after this is called, but + // attempt to be able to catch cases of n not being in the tree. + n._parent = n + n._left = nil + n._right = nil + n._balance = 0 +} + +@(private) +node_set_parent_balance :: #force_inline proc "contextless" ( + n, parent: ^Node($Value), + balance: i8, +) { + n._parent = parent + n._balance = balance +} + +@(private) +node_get_child :: #force_inline proc "contextless" (n: ^Node($Value), sign: i8) -> ^Node(Value) { + if sign < 0 { + return n._left + } + return n._right +} + +@(private) +node_next_or_prev_in_order :: proc "contextless" ( + n: ^Node($Value), + direction: Direction, +) -> ^Node(Value) { + next, tmp: ^Node(Value) + sign := i8(direction) + + if next = node_get_child(n, +sign); next != nil { + for { + tmp = node_get_child(next, -sign) + if tmp == nil { + break + } + next = tmp + } + } else { + tmp, next = n, n._parent + for next != nil && tmp == node_get_child(next, +sign) { + tmp, next = next, next._parent + } + } + return next +} + +@(private) +node_set_child :: #force_inline proc "contextless" ( + n: ^Node($Value), + sign: i8, + child: ^Node(Value), +) { + if sign < 0 { + n._left = child + } else { + n._right = child + } +} + +@(private) +node_adjust_balance_factor :: #force_inline proc "contextless" (n: ^Node($Value), amount: i8) { + n._balance += amount +} + +@(private) +iterator_first :: proc "contextless" (it: ^Iterator($Value)) { + // This is private because behavior when the user manually calls + // iterator_first followed by iterator_next is unintuitive, since + // the first call to iterator_next MUST return the first node + // instead of advancing so that `for node in iterator_next(&next)` + // works as expected. + + switch it._direction { + case .Forward: + it._cur = tree_first_or_last_in_order(it._tree, .Backward) + case .Backward: + it._cur = tree_first_or_last_in_order(it._tree, .Forward) + } + + it._next = nil + it._called_next = false + + if it._cur != nil { + it._next = node_next_or_prev_in_order(it._cur, it._direction) + } +} diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 78ba4bea4..7b0e74c69 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -231,6 +231,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: case runtime.Type_Info_Matrix: return .Unsupported_Type + case runtime.Type_Info_Bit_Field: + return .Unsupported_Type + case runtime.Type_Info_Array: opt_write_start(w, opt, '[') or_return for i in 0.. str // *Allocates Using Context's Allocator* // // Inputs: +// - fmt: A format string with placeholders for the provided arguments. +// - args: A variadic list of arguments to be formatted. +// - newline: Whether the string should end with a newline. (See `aprintfln`.) +// +// Returns: A formatted string. The returned string must be freed accordingly. +// +aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string { + str: strings.Builder + strings.builder_init(&str, allocator) + sbprintf(&str, fmt, ..args, newline=newline) + return strings.to_string(str) +} +// Creates a formatted string using a format string and arguments, followed by a newline. +// +// *Allocates Using Context's Allocator* +// +// Inputs: // - fmt: A format string with placeholders for the provided arguments. // - args: A variadic list of arguments to be formatted. // // Returns: A formatted string. The returned string must be freed accordingly. // -aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string { - str: strings.Builder - strings.builder_init(&str, allocator) - sbprintf(&str, fmt, ..args) - return strings.to_string(str) +aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string { + return aprintf(fmt, ..args, allocator=allocator, newline=true) } // Creates a formatted string // @@ -195,16 +209,30 @@ tprintln :: proc(args: ..any, sep := " ") -> string { // *Allocates Using Context's Temporary Allocator* // // Inputs: +// - fmt: A format string with placeholders for the provided arguments. +// - args: A variadic list of arguments to be formatted. +// - newline: Whether the string should end with a newline. (See `tprintfln`.) +// +// Returns: A formatted string. +// +tprintf :: proc(fmt: string, args: ..any, newline := false) -> string { + str: strings.Builder + strings.builder_init(&str, context.temp_allocator) + sbprintf(&str, fmt, ..args, newline=newline) + return strings.to_string(str) +} +// Creates a formatted string using a format string and arguments, followed by a newline. +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: // - fmt: A format string with placeholders for the provided arguments. // - args: A variadic list of arguments to be formatted. // // Returns: A formatted string. // -tprintf :: proc(fmt: string, args: ..any) -> string { - str: strings.Builder - strings.builder_init(&str, context.temp_allocator) - sbprintf(&str, fmt, ..args) - return strings.to_string(str) +tprintfln :: proc(fmt: string, args: ..any) -> string { + return tprintf(fmt, ..args, newline=true) } // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. // @@ -238,12 +266,25 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { // - buf: The backing buffer // - fmt: A format string with placeholders for the provided arguments // - args: A variadic list of arguments to be formatted +// - newline: Whether the string should end with a newline. (See `bprintfln`.) // // Returns: A formatted string // -bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string { +bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string { sb := strings.builder_from_bytes(buf) - return sbprintf(&sb, fmt, ..args) + return sbprintf(&sb, fmt, ..args, newline=newline) +} +// Creates a formatted string using a supplied buffer as the backing array, followed by a newline. Writes into the buffer. +// +// Inputs: +// - buf: The backing buffer +// - fmt: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted string +// +bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string { + return bprintf(buf, fmt, ..args, newline=true) } // Runtime assertion with a formatted message // @@ -294,17 +335,31 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { // Inputs: // - format: A format string with placeholders for the provided arguments // - args: A variadic list of arguments to be formatted +// - newline: Whether the string should end with a newline. (See `caprintfln`.) // // Returns: A formatted C string // -caprintf :: proc(format: string, args: ..any) -> cstring { +caprintf :: proc(format: string, args: ..any, newline := false) -> cstring { str: strings.Builder strings.builder_init(&str) - sbprintf(&str, format, ..args) + sbprintf(&str, format, ..args, newline=newline) strings.write_byte(&str, 0) s := strings.to_string(str) return cstring(raw_data(s)) } +// Creates a formatted C string, followed by a newline. +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - format: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted C string +// +caprintfln :: proc(format: string, args: ..any) -> cstring { + return caprintf(format, ..args, newline=true) +} // Creates a formatted C string // // *Allocates Using Context's Temporary Allocator* @@ -312,16 +367,30 @@ caprintf :: proc(format: string, args: ..any) -> cstring { // Inputs: // - format: A format string with placeholders for the provided arguments // - args: A variadic list of arguments to be formatted +// - newline: Whether the string should end with a newline. (See `ctprintfln`.) +// +// Returns: A formatted C string +// +ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring { + str: strings.Builder + strings.builder_init(&str, context.temp_allocator) + sbprintf(&str, format, ..args, newline=newline) + strings.write_byte(&str, 0) + s := strings.to_string(str) + return cstring(raw_data(s)) +} +// Creates a formatted C string, followed by a newline. +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - format: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted // // Returns: A formatted C string // -ctprintf :: proc(format: string, args: ..any) -> cstring { - str: strings.Builder - strings.builder_init(&str, context.temp_allocator) - sbprintf(&str, format, ..args) - strings.write_byte(&str, 0) - s := strings.to_string(str) - return cstring(raw_data(s)) +ctprintfln :: proc(format: string, args: ..any) -> cstring { + return ctprintf(format, ..args, newline=true) } // Formats using the default print settings and writes to the given strings.Builder // @@ -355,13 +424,25 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // - buf: A pointer to a strings.Builder buffer // - fmt: The format string // - args: A variadic list of arguments to be formatted +// - newline: Whether a trailing newline should be written. (See `sbprintfln`.) // // Returns: The resulting formatted string // -sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { - wprintf(strings.to_writer(buf), fmt, ..args, flush=true) +sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string { + wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline) return strings.to_string(buf^) } +// Formats and writes to a strings.Builder buffer according to the specified format string, followed by a newline. +// +// Inputs: +// - buf: A pointer to a strings.Builder to store the formatted string +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted string +// +sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string { + return sbprintf(buf, format, ..args, newline=true) +} // Formats and writes to an io.Writer using the default print settings // // Inputs: @@ -435,10 +516,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { // - w: An io.Writer to write to // - fmt: The format string // - args: A variadic list of arguments to be formatted +// - newline: Whether a trailing newline should be written. (See `wprintfln`.) // // Returns: The number of bytes written // -wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int { +wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int { fi: Info arg_index: int = 0 end := len(fmt) @@ -708,12 +790,27 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int { } io.write_string(fi.writer, ")", &fi.n) } + + if newline { + io.write_byte(w, '\n', &fi.n) + } if flush { io.flush(w) } return fi.n } +// Formats and writes to an io.Writer according to the specified format string, followed by a newline. +// +// Inputs: +// - w: The io.Writer to write to. +// - args: A variadic list of arguments to be formatted. +// +// Returns: The number of bytes written. +// +wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int { + return wprintf(w, format, ..args, flush=flush, newline=true) +} // Writes a ^runtime.Type_Info value to an io.Writer // // Inputs: @@ -1408,34 +1505,9 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) { // // Returns: The string representation of the enum value and a boolean indicating success. // +@(require_results) enum_value_to_string :: proc(val: any) -> (string, bool) { - v := val - v.id = runtime.typeid_base(v.id) - type_info := type_info_of(v.id) - - #partial switch e in type_info.variant { - case: return "", false - case runtime.Type_Info_Enum: - Enum_Value :: runtime.Type_Info_Enum_Value - - ev_, ok := reflect.as_i64(val) - ev := Enum_Value(ev_) - - if ok { - if len(e.values) == 0 { - return "", true - } else { - for val, idx in e.values { - if val == ev { - return e.names[idx], true - } - } - } - return "", false - } - } - - return "", false + return reflect.enum_name_from_value_any(val) } // Returns the enum value of a string representation. // @@ -2198,6 +2270,8 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) #partial switch b in info.base.variant { case runtime.Type_Info_Struct: fmt_struct(fi, v, verb, b, info.name) + case runtime.Type_Info_Bit_Field: + fmt_bit_field(fi, v, verb, b, info.name) case runtime.Type_Info_Bit_Set: fmt_bit_set(fi, v, verb = verb) case: @@ -2308,6 +2382,96 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix fmt_write_indent(fi) } } + +fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit_Field, type_name: string) { + read_bits :: proc(ptr: [^]byte, offset, size: uintptr) -> (res: u64) { + for i in 0.. (do_continue: bool) { + tag := info.tags[idx] + if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok { + value := strings.trim_space(string(vt)) + switch value { + case "": return false + case "-": return true + } + r, w := utf8.decode_rune_in_string(value) + value = value[w:] + if value == "" || value[0] == ',' { + verb^ = r + } + } + return false + } + + io.write_string(fi.writer, type_name if len(type_name) != 0 else "bit_field", &fi.n) + io.write_string(fi.writer, "{", &fi.n) + + hash := fi.hash; defer fi.hash = hash + indent := fi.indent; defer fi.indent -= 1 + do_trailing_comma := hash + + fi.indent += 1 + + if hash { + io.write_byte(fi.writer, '\n', &fi.n) + } + defer { + if hash { + for _ in 0.. 0 { + io.write_string(fi.writer, ", ") + } + if hash { + fmt_write_indent(fi) + } + + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) + + bit_offset := info.bit_offsets[i] + bit_size := info.bit_sizes[i] + + value := read_bits(([^]byte)(v.data), bit_offset, bit_size) + type := info.types[i] + + if !reflect.is_unsigned(runtime.type_info_core(type)) { + // Sign Extension + m := u64(1<<(bit_size-1)) + value = (value ~ m) - m + } + + fmt_value(fi, any{&value, type.id}, field_verb) + if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) } + + } +} + + + // Formats a value based on its type and formatting verb // // Inputs: @@ -2636,6 +2800,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Matrix: fmt_matrix(fi, v, verb, info) + + case runtime.Type_Info_Bit_Field: + fmt_bit_field(fi, v, verb, info, "") } } // Formats a complex number based on the given formatting verb diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index afc28ffff..a403dcd65 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -30,7 +30,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { return wprintln(w, ..args, sep=sep, flush=flush) } // fprintf formats according to the specified format string and writes to fd -fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int { +fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -38,7 +38,11 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int { bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:]) w := bufio.writer_to_writer(&b) - return wprintf(w, fmt, ..args, flush=flush) + return wprintf(w, fmt, ..args, flush=flush, newline=newline) +} +// fprintfln formats according to the specified format string and writes to fd, followed by a newline. +fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int { + return fprintf(fd, fmt, ..args, flush=flush, newline=true) } fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) { buf: [1024]byte @@ -62,15 +66,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err: } // print formats using the default print settings and writes to os.stdout -print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } +print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } // println formats using the default print settings and writes to os.stdout -println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } +println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } // printf formats according to the specified format string and writes to os.stdout -printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } +printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } +// printfln formats according to the specified format string and writes to os.stdout, followed by a newline. +printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) } // eprint formats using the default print settings and writes to os.stderr -eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } +eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } // eprintln formats using the default print settings and writes to os.stderr -eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } +eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } // eprintf formats according to the specified format string and writes to os.stderr -eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } +eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } +// eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline. +eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) } diff --git a/core/net/errors_darwin.odin b/core/net/errors_darwin.odin index c80d2cf56..3116af0ab 100644 --- a/core/net/errors_darwin.odin +++ b/core/net/errors_darwin.odin @@ -34,7 +34,7 @@ Create_Socket_Error :: enum c.int { Dial_Error :: enum c.int { None = 0, - Port_Required = -1, + Port_Required = -1, // Attempted to dial an endpointing without a port being set. Address_In_Use = c.int(os.EADDRINUSE), In_Progress = c.int(os.EINPROGRESS), @@ -54,7 +54,9 @@ Dial_Error :: enum c.int { } Bind_Error :: enum c.int { - None = 0, + None = 0, + Privileged_Port_Without_Root = -1, // Attempted to bind to a port less than 1024 without root access. + Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint. Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine. Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set. diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin index 2585d134b..ba86f1005 100644 --- a/core/net/socket_darwin.odin +++ b/core/net/socket_darwin.odin @@ -92,13 +92,20 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio return } +// On Darwin, any port below 1024 is 'privileged' - which means that you need root access in order to use it. +MAX_PRIVILEGED_PORT :: 1023 + @(private) _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) { sockaddr := _endpoint_to_sockaddr(ep) s := any_socket_to_socket(skt) res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) if res != os.ERROR_NONE { - err = Bind_Error(res) + if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT { + err = .Privileged_Port_Without_Root + } else { + err = Bind_Error(res) + } } return } diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index d22dafd27..5636b1059 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -10,8 +10,8 @@ Array :: struct($T: typeid) { String :: distinct Array(byte) Version_Type_Major :: 0 -Version_Type_Minor :: 2 -Version_Type_Patch :: 4 +Version_Type_Minor :: 3 +Version_Type_Patch :: 0 Version_Type :: struct { major, minor, patch: u8, @@ -110,6 +110,8 @@ Entity_Flag :: enum u32le { Param_No_Alias = 7, // #no_alias Param_Any_Int = 8, // #any_int + Bit_Field_Field = 19, + Type_Alias = 20, Builtin_Pkg_Builtin = 30, @@ -137,6 +139,7 @@ Entity :: struct { // May be used by (Struct fields and procedure fields): // .Variable // .Constant + // This is equal to the negative of the "bit size" it this is a `bit_field`s field field_group_index: i32le, // May used by: @@ -187,6 +190,7 @@ Type_Kind :: enum u32le { Multi_Pointer = 22, Matrix = 23, Soa_Pointer = 24, + Bit_Field = 25, } Type_Elems_Cap :: 4 @@ -247,6 +251,7 @@ Type :: struct { // .Multi_Pointer - 1 type: 0=element // .Matrix - 1 type: 0=element // .Soa_Pointer - 1 type: 0=element + // .Bit_Field - 1 type: 0=backing type types: Array(Type_Index), // Used by: diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 23808cf44..cd8953841 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -137,6 +137,7 @@ Token_Kind :: enum u32 { Union, // union Enum, // enum Bit_Set, // bit_set + Bit_Field, // bit_field Map, // map Dynamic, // dynamic Auto_Cast, // auto_cast @@ -270,6 +271,7 @@ tokens := [Token_Kind.COUNT]string { "union", "enum", "bit_set", + "bit_field", "map", "dynamic", "auto_cast", diff --git a/core/os/os.odin b/core/os/os.odin index c74712d4e..aa460fe01 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -1,7 +1,6 @@ package os import "base:runtime" -import "core:mem" import "core:strconv" import "core:unicode/utf8" @@ -160,13 +159,11 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ } write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { - s := transmute([]byte)mem.Raw_Slice{data, len} - return write(fd, s) + return write(fd, ([^]byte)(data)[:len]) } read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { - s := transmute([]byte)mem.Raw_Slice{data, len} - return read(fd, s) + return read(fd, ([^]byte)(data)[:len]) } heap_allocator_proc :: runtime.heap_allocator_proc diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index e52d53f08..459544fc0 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -1,6 +1,5 @@ package os2 -import "core:mem" import "base:runtime" import "core:strconv" import "core:unicode/utf8" @@ -64,13 +63,11 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { - s := transmute([]byte)mem.Raw_Slice{data, len} - return write(f, s) + return write(f, ([^]byte)(data)[:len]) } read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { - s := transmute([]byte)mem.Raw_Slice{data, len} - return read(f, s) + return read(f, ([^]byte)(data)[:len]) } diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 1ff7df229..de5dec2e3 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -35,6 +35,7 @@ Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer Type_Info_Matrix :: runtime.Type_Info_Matrix Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer +Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value @@ -70,6 +71,7 @@ Type_Kind :: enum { Relative_Multi_Pointer, Matrix, Soa_Pointer, + Bit_Field, } @@ -106,6 +108,7 @@ type_kind :: proc(T: typeid) -> Type_Kind { case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer case Type_Info_Matrix: return .Matrix case Type_Info_Soa_Pointer: return .Soa_Pointer + case Type_Info_Bit_Field: return .Bit_Field } } @@ -627,6 +630,43 @@ enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info return } +@(require_results) +enum_name_from_value :: proc(value: $Enum_Type) -> (name: string, ok: bool) where intrinsics.type_is_enum(Enum_Type) { + ti := type_info_base(type_info_of(Enum_Type)) + e := ti.variant.(runtime.Type_Info_Enum) or_return + if len(e.values) == 0 { + return + } + ev := Type_Info_Enum_Value(value) + for val, idx in e.values { + if val == ev { + return e.names[idx], true + } + } + return +} + +@(require_results) +enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) { + if value.id == nil { + return + } + ti := type_info_base(type_info_of(value.id)) + e := ti.variant.(runtime.Type_Info_Enum) or_return + if len(e.values) == 0 { + return + } + ev := Type_Info_Enum_Value(as_i64(value) or_return) + for val, idx in e.values { + if val == ev { + return e.names[idx], true + } + } + return +} + + + @(require_results) enum_field_names :: proc(Enum_Type: typeid) -> []string { @@ -1567,6 +1607,13 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ } } return true + + case Type_Info_Bit_Field: + x, y := a, b + x.id = v.backing_type.id + y.id = v.backing_type.id + return equal(x, y, including_indirect_array_recursion, recursion_level+0) + } runtime.print_typeid(a.id) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index cbe108d82..2b96dd4fb 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -174,6 +174,23 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { if x.row_count != y.row_count { return false } if x.column_count != y.column_count { return false } return are_types_identical(x.elem, y.elem) + + case Type_Info_Bit_Field: + y := b.variant.(Type_Info_Bit_Field) or_return + if !are_types_identical(x.backing_type, y.backing_type) { return false } + if len(x.names) != len(y.names) { return false } + for _, i in x.names { + if x.names[i] != y.names[i] { + return false + } + if !are_types_identical(x.types[i], y.types[i]) { + return false + } + if x.bit_sizes[i] != y.bit_sizes[i] { + return false + } + } + return true } return false @@ -639,6 +656,20 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - } io.write_byte(w, ']', &n) or_return + case Type_Info_Bit_Field: + io.write_string(w, "bit_field ", &n) or_return + write_type(w, info.backing_type, &n) or_return + io.write_string(w, " {", &n) or_return + for name, i in info.names { + if i > 0 { io.write_string(w, ", ", &n) or_return } + io.write_string(w, name, &n) or_return + io.write_string(w, ": ", &n) or_return + write_type(w, info.types[i], &n) or_return + io.write_string(w, " | ", &n) or_return + io.write_u64(w, u64(info.bit_sizes[i]), 10, &n) or_return + } + io.write_string(w, "}", &n) or_return + case Type_Info_Simd_Vector: io.write_string(w, "#simd[", &n) or_return io.write_i64(w, i64(info.count), 10, &n) or_return diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin new file mode 100644 index 000000000..cbcfdf3bf --- /dev/null +++ b/core/sync/chan/chan.odin @@ -0,0 +1,489 @@ +package sync_chan + +import "base:builtin" +import "base:intrinsics" +import "base:runtime" +import "core:mem" +import "core:sync" +import "core:math/rand" + +Direction :: enum { + Send = -1, + Both = 0, + Recv = +1, +} + +Chan :: struct($T: typeid, $D: Direction = Direction.Both) { + #subtype impl: ^Raw_Chan `fmt:"-"`, +} + +Raw_Chan :: struct { + // Shared + allocator: runtime.Allocator, + allocation_size: int, + msg_size: u16, + closed: b16, // atomic + mutex: sync.Mutex, + r_cond: sync.Cond, + w_cond: sync.Cond, + r_waiting: int, // atomic + w_waiting: int, // atomic + + // Buffered + queue: ^Raw_Queue, + + // Unbuffered + r_mutex: sync.Mutex, + w_mutex: sync.Mutex, + unbuffered_data: rawptr, +} + + +create :: proc{ + create_unbuffered, + create_buffered, +} + +@(require_results) +create_unbuffered :: proc($C: typeid/Chan($T), allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error) + where size_of(T) <= int(max(u16)) { + c.impl, err = create_raw_unbuffered(size_of(T), align_of(T), allocator) + return +} + +@(require_results) +create_buffered :: proc($C: typeid/Chan($T), #any_int cap: int, allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error) + where size_of(T) <= int(max(u16)) { + c.impl, err = create_raw_buffered(size_of(T), align_of(T), cap, allocator) + return +} + +create_raw :: proc{ + create_raw_unbuffered, + create_raw_buffered, +} + +@(require_results) +create_raw_unbuffered :: proc(#any_int msg_size, msg_alignment: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) { + assert(msg_size <= int(max(u16))) + align := max(align_of(Raw_Chan), msg_alignment) + + size := mem.align_forward_int(size_of(Raw_Chan), align) + offset := size + size += msg_size + size = mem.align_forward_int(size, align) + + ptr := mem.alloc(size, align, allocator) or_return + c = (^Raw_Chan)(ptr) + c.allocation_size = size + c.unbuffered_data = ([^]byte)(ptr)[offset:] + c.msg_size = u16(msg_size) + return +} + +@(require_results) +create_raw_buffered :: proc(#any_int msg_size, msg_alignment: int, #any_int cap: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) { + assert(msg_size <= int(max(u16))) + if cap <= 0 { + return create_raw_unbuffered(msg_size, msg_alignment, allocator) + } + + align := max(align_of(Raw_Chan), msg_alignment, align_of(Raw_Queue)) + + size := mem.align_forward_int(size_of(Raw_Chan), align) + q_offset := size + size = mem.align_forward_int(q_offset + size_of(Raw_Queue), msg_alignment) + offset := size + size += msg_size * cap + size = mem.align_forward_int(size, align) + + ptr := mem.alloc(size, align, allocator) or_return + c = (^Raw_Chan)(ptr) + c.allocation_size = size + + bptr := ([^]byte)(ptr) + + c.queue = (^Raw_Queue)(bptr[q_offset:]) + c.msg_size = u16(msg_size) + + raw_queue_init(c.queue, ([^]byte)(bptr[offset:]), cap, msg_size) + return +} + +destroy :: proc(c: ^Raw_Chan) -> (err: runtime.Allocator_Error) { + if c != nil { + allocator := c.allocator + err = mem.free_with_size(c, c.allocation_size, allocator) + } + return +} + +@(require_results) +as_send :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (s: Chan(T, .Send)) where C.D <= .Both { + return transmute(type_of(s))c +} +@(require_results) +as_recv :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (r: Chan(T, .Recv)) where C.D >= .Both { + return transmute(type_of(r))c +} + + +send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both { + data := data + ok = send_raw(c, &data) + return +} + +@(require_results) +try_send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both { + data := data + ok = try_send_raw(c, &data) + return +} + +@(require_results) +recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both { + ok = recv_raw(c, &data) + return +} + + +@(require_results) +try_recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both { + ok = try_recv_raw(c, &data) + return +} + + +@(require_results) +send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) { + if c == nil { + return + } + if c.queue != nil { // buffered + sync.guard(&c.mutex) + for c.queue.len == c.queue.cap { + sync.atomic_add(&c.w_waiting, 1) + sync.wait(&c.w_cond, &c.mutex) + sync.atomic_sub(&c.w_waiting, 1) + } + + ok = raw_queue_push(c.queue, msg_in) + if sync.atomic_load(&c.r_waiting) > 0 { + sync.signal(&c.r_cond) + } + } else if c.unbuffered_data != nil { // unbuffered + sync.guard(&c.w_mutex) + sync.guard(&c.mutex) + + if sync.atomic_load(&c.closed) { + return false + } + + mem.copy(c.unbuffered_data, msg_in, int(c.msg_size)) + sync.atomic_add(&c.w_waiting, 1) + if sync.atomic_load(&c.r_waiting) > 0 { + sync.signal(&c.r_cond) + } + sync.wait(&c.w_cond, &c.mutex) + ok = true + } + return +} + +@(require_results) +recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) { + if c == nil { + return + } + if c.queue != nil { // buffered + sync.guard(&c.mutex) + for c.queue.len == 0 { + if sync.atomic_load(&c.closed) { + return + } + + sync.atomic_add(&c.r_waiting, 1) + sync.wait(&c.r_cond, &c.mutex) + sync.atomic_sub(&c.r_waiting, 1) + } + + msg := raw_queue_pop(c.queue) + if msg != nil { + mem.copy(msg_out, msg, int(c.msg_size)) + } + + if sync.atomic_load(&c.w_waiting) > 0 { + sync.signal(&c.w_cond) + } + ok = true + } else if c.unbuffered_data != nil { // unbuffered + sync.guard(&c.r_mutex) + sync.guard(&c.mutex) + + for !sync.atomic_load(&c.closed) && + sync.atomic_load(&c.w_waiting) == 0 { + sync.atomic_add(&c.r_waiting, 1) + sync.wait(&c.r_cond, &c.mutex) + sync.atomic_sub(&c.r_waiting, 1) + } + + if sync.atomic_load(&c.closed) { + return + } + + mem.copy(msg_out, c.unbuffered_data, int(c.msg_size)) + sync.atomic_sub(&c.w_waiting, 1) + + sync.signal(&c.w_cond) + ok = true + } + return +} + + +@(require_results) +try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) { + if c == nil { + return false + } + if c.queue != nil { // buffered + sync.guard(&c.mutex) + if c.queue.len == c.queue.cap { + return false + } + + ok = raw_queue_push(c.queue, msg_in) + if sync.atomic_load(&c.r_waiting) > 0 { + sync.signal(&c.r_cond) + } + } else if c.unbuffered_data != nil { // unbuffered + sync.guard(&c.w_mutex) + sync.guard(&c.mutex) + + if sync.atomic_load(&c.closed) { + return false + } + + mem.copy(c.unbuffered_data, msg_in, int(c.msg_size)) + sync.atomic_add(&c.w_waiting, 1) + if sync.atomic_load(&c.r_waiting) > 0 { + sync.signal(&c.r_cond) + } + sync.wait(&c.w_cond, &c.mutex) + ok = true + } + return +} + +@(require_results) +try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool { + if c == nil { + return false + } + if c.queue != nil { // buffered + sync.guard(&c.mutex) + if c.queue.len == 0 { + return false + } + + msg := raw_queue_pop(c.queue) + if msg != nil { + mem.copy(msg_out, msg, int(c.msg_size)) + } + + if sync.atomic_load(&c.w_waiting) > 0 { + sync.signal(&c.w_cond) + } + return true + } else if c.unbuffered_data != nil { // unbuffered + sync.guard(&c.r_mutex) + sync.guard(&c.mutex) + + if sync.atomic_load(&c.closed) || + sync.atomic_load(&c.w_waiting) == 0 { + return false + } + + mem.copy(msg_out, c.unbuffered_data, int(c.msg_size)) + sync.atomic_sub(&c.w_waiting, 1) + + sync.signal(&c.w_cond) + return true + } + return false +} + + + +@(require_results) +is_buffered :: proc "contextless" (c: ^Raw_Chan) -> bool { + return c != nil && c.queue != nil +} + +@(require_results) +is_unbuffered :: proc "contextless" (c: ^Raw_Chan) -> bool { + return c != nil && c.unbuffered_data != nil +} + +@(require_results) +len :: proc "contextless" (c: ^Raw_Chan) -> int { + if c != nil && c.queue != nil { + sync.guard(&c.mutex) + return c.queue.len + } + return 0 +} + +@(require_results) +cap :: proc "contextless" (c: ^Raw_Chan) -> int { + if c != nil && c.queue != nil { + sync.guard(&c.mutex) + return c.queue.cap + } + return 0 +} + +close :: proc "contextless" (c: ^Raw_Chan) -> bool { + if c == nil { + return false + } + sync.guard(&c.mutex) + if sync.atomic_load(&c.closed) { + return false + } + sync.atomic_store(&c.closed, true) + sync.broadcast(&c.r_cond) + sync.broadcast(&c.w_cond) + return true +} + +@(require_results) +is_closed :: proc "contextless" (c: ^Raw_Chan) -> bool { + if c == nil { + return true + } + sync.guard(&c.mutex) + return bool(sync.atomic_load(&c.closed)) +} + + + + +Raw_Queue :: struct { + data: [^]byte, + len: int, + cap: int, + next: int, + size: int, // element size +} + +raw_queue_init :: proc "contextless" (q: ^Raw_Queue, data: rawptr, cap: int, size: int) { + q.data = ([^]byte)(data) + q.len = 0 + q.cap = cap + q.next = 0 + q.size = size +} + + +@(require_results) +raw_queue_push :: proc "contextless" (q: ^Raw_Queue, data: rawptr) -> bool { + if q.len == q.cap { + return false + } + pos := q.next + q.len + if pos >= q.cap { + pos -= q.cap + } + + val_ptr := q.data[pos*q.size:] + mem.copy(val_ptr, data, q.size) + q.len += 1 + return true +} + +@(require_results) +raw_queue_pop :: proc "contextless" (q: ^Raw_Queue) -> (data: rawptr) { + if q.len > 0 { + data = q.data[q.next*q.size:] + q.next += 1 + q.len -= 1 + if q.next >= q.cap { + q.next -= q.cap + } + } + return +} + + +@(require_results) +can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool { + if is_buffered(c) { + return len(c) > 0 + } + sync.guard(&c.mutex) + return sync.atomic_load(&c.w_waiting) > 0 +} + + +@(require_results) +can_send :: proc "contextless" (c: ^Raw_Chan) -> bool { + if is_buffered(c) { + sync.guard(&c.mutex) + return len(c) < cap(c) + } + sync.guard(&c.mutex) + return sync.atomic_load(&c.r_waiting) > 0 +} + + + +@(require_results) +select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, ok: bool) #no_bounds_check { + Select_Op :: struct { + idx: int, // local to the slice that was given + is_recv: bool, + } + + candidate_count := builtin.len(recvs)+builtin.len(sends) + candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op))) + count := 0 + + for c, i in recvs { + if can_recv(c) { + candidates[count] = { + is_recv = true, + idx = i, + } + count += 1 + } + } + + for c, i in sends { + if can_send(c) { + candidates[count] = { + is_recv = false, + idx = i, + } + count += 1 + } + } + + if count == 0 { + return + } + + r: ^rand.Rand = nil + + + select_idx = rand.int_max(count, r) if count > 0 else 0 + + sel := candidates[select_idx] + if sel.is_recv { + ok = recv_raw(recvs[sel.idx], recv_out) + } else { + ok = send_raw(sends[sel.idx], send_msgs[sel.idx]) + } + return +} \ No newline at end of file diff --git a/core/sync/extended.odin b/core/sync/extended.odin index c76ab504b..76b7686fe 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -417,4 +417,28 @@ unpark :: proc "contextless" (p: ^Parker) { if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED { futex_signal(&p.state) } +} + + + +// A One_Shot_Event is an associated token which is initially not present: +// * The `one_shot_event_wait` blocks the current thread until the event +// is made available +// * The `one_shot_event_signal` procedure automatically makes the token +// available if its was not already. +One_Shot_Event :: struct #no_copy { + state: Futex, +} + +// Blocks the current thread until the event is made available with `one_shot_event_signal`. +one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) { + for atomic_load_explicit(&e.state, .Acquire) == 0 { + futex_wait(&e.state, 1) + } +} + +// Releases any threads that are currently blocked by this event with `one_shot_event_wait`. +one_shot_event_signal :: proc "contextless" (e: ^One_Shot_Event) { + atomic_store_explicit(&e.state, 1, .Release) + futex_broadcast(&e.state) } \ No newline at end of file diff --git a/core/time/time.odin b/core/time/time.odin index 7911457de..72a09ad94 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -369,6 +369,10 @@ datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: mod = year % divisor return } + _is_leap_year :: proc "contextless" (year: int) -> bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) + } + ok = true @@ -395,6 +399,10 @@ datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: days += int(days_before[_m]) + _d + if _is_leap_year(year) && _m >= 2 { + days += 1 + } + s += i64(days) * SECONDS_PER_DAY s += i64(hour) * SECONDS_PER_HOUR s += i64(minute) * SECONDS_PER_MINUTE diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 8f2eebc8f..fff344b22 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -14,6 +14,7 @@ import shoco "core:compress/shoco" import gzip "core:compress/gzip" import zlib "core:compress/zlib" +import avl "core:container/avl" import bit_array "core:container/bit_array" import priority_queue "core:container/priority_queue" import queue "core:container/queue" @@ -131,6 +132,7 @@ _ :: compress _ :: shoco _ :: gzip _ :: zlib +_ :: avl _ :: bit_array _ :: priority_queue _ :: queue diff --git a/src/bug_report.cpp b/src/bug_report.cpp index ac3805919..b58d14bbf 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -1,1061 +1,1067 @@ -/* - Gather and print platform and version info to help with reporting Odin bugs. -*/ - -#if !defined(GB_COMPILER_MSVC) - #if defined(GB_CPU_X86) - #include - #endif -#endif - -#if defined(GB_SYSTEM_LINUX) - #include - #include -#endif - -#if defined(GB_SYSTEM_OSX) - #include -#endif - -#if defined(GB_SYSTEM_OPENBSD) - #include - #include -#endif - -#if defined(GB_SYSTEM_FREEBSD) - #include -#endif - -/* - NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`. -*/ -#if defined(GB_SYSTEM_WINDOWS) -gb_internal void report_windows_product_type(DWORD ProductType) { - switch (ProductType) { - case PRODUCT_ULTIMATE: - gb_printf("Ultimate"); - break; - - case PRODUCT_HOME_BASIC: - gb_printf("Home Basic"); - break; - - case PRODUCT_HOME_PREMIUM: - gb_printf("Home Premium"); - break; - - case PRODUCT_ENTERPRISE: - gb_printf("Enterprise"); - break; - - case PRODUCT_CORE: - gb_printf("Home Basic"); - break; - - case PRODUCT_HOME_BASIC_N: - gb_printf("Home Basic N"); - break; - - case PRODUCT_EDUCATION: - gb_printf("Education"); - break; - - case PRODUCT_EDUCATION_N: - gb_printf("Education N"); - break; - - case PRODUCT_BUSINESS: - gb_printf("Business"); - break; - - case PRODUCT_STANDARD_SERVER: - gb_printf("Standard Server"); - break; - - case PRODUCT_DATACENTER_SERVER: - gb_printf("Datacenter"); - break; - - case PRODUCT_SMALLBUSINESS_SERVER: - gb_printf("Windows Small Business Server"); - break; - - case PRODUCT_ENTERPRISE_SERVER: - gb_printf("Enterprise Server"); - break; - - case PRODUCT_STARTER: - gb_printf("Starter"); - break; - - case PRODUCT_DATACENTER_SERVER_CORE: - gb_printf("Datacenter Server Core"); - break; - - case PRODUCT_STANDARD_SERVER_CORE: - gb_printf("Server Standard Core"); - break; - - case PRODUCT_ENTERPRISE_SERVER_CORE: - gb_printf("Enterprise Server Core"); - break; - - case PRODUCT_BUSINESS_N: - gb_printf("Business N"); - break; - - case PRODUCT_HOME_SERVER: - gb_printf("Home Server"); - break; - - case PRODUCT_SERVER_FOR_SMALLBUSINESS: - gb_printf("Windows Server 2008 for Windows Essential Server Solutions"); - break; - - case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: - gb_printf("Small Business Server Premium"); - break; - - case PRODUCT_HOME_PREMIUM_N: - gb_printf("Home Premium N"); - break; - - case PRODUCT_ENTERPRISE_N: - gb_printf("Enterprise N"); - break; - - case PRODUCT_ULTIMATE_N: - gb_printf("Ultimate N"); - break; - - case PRODUCT_HYPERV: - gb_printf("HyperV"); - break; - - case PRODUCT_STARTER_N: - gb_printf("Starter N"); - break; - - case PRODUCT_PROFESSIONAL: - gb_printf("Professional"); - break; - - case PRODUCT_PROFESSIONAL_N: - gb_printf("Professional N"); - break; - - case PRODUCT_UNLICENSED: - gb_printf("Unlicensed"); - break; - - default: - gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType); - } -} -#endif - -gb_internal void odin_cpuid(int leaf, int result[]) { - #if defined(GB_CPU_ARM) - return; - - #elif defined(GB_CPU_X86) - - #if defined(GB_COMPILER_MSVC) - __cpuid(result, leaf); - #else - __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]); - #endif - - #endif -} - -gb_internal void report_cpu_info() { - gb_printf("\tCPU: "); - - #if defined(GB_CPU_X86) - - /* - Get extended leaf info - */ - int cpu[4]; - - odin_cpuid(0x80000000, &cpu[0]); - int number_of_extended_ids = cpu[0]; - - int brand[0x12] = {}; - - /* - Read CPU brand if supported. - */ - if (number_of_extended_ids >= 0x80000004) { - odin_cpuid(0x80000002, &brand[0]); - odin_cpuid(0x80000003, &brand[4]); - odin_cpuid(0x80000004, &brand[8]); - - /* - Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them. - */ - char * brand_name = (char *)&brand[0]; - for (; brand_name[0] == ' '; brand_name++) {} - - gb_printf("%s\n", brand_name); - } else { - gb_printf("Unable to retrieve.\n"); - } - - #elif defined(GB_CPU_ARM) - /* - TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`. - */ - #if defined(GB_ARCH_64_BIT) - gb_printf("ARM64\n"); - #else - gb_printf("ARM\n"); - #endif - #else - gb_printf("Unknown\n"); - #endif -} - -/* - Report the amount of installed RAM. -*/ -gb_internal void report_ram_info() { - gb_printf("\tRAM: "); - - #if defined(GB_SYSTEM_WINDOWS) - MEMORYSTATUSEX statex; - statex.dwLength = sizeof(statex); - GlobalMemoryStatusEx (&statex); - - gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1)); - - #elif defined(GB_SYSTEM_LINUX) - /* - Retrieve RAM info using `sysinfo()`, - */ - struct sysinfo info; - int result = sysinfo(&info); - - if (result == 0x0) { - gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1)); - } else { - gb_printf("Unknown.\n"); - } - #elif defined(GB_SYSTEM_OSX) - uint64_t ram_amount; - size_t val_size = sizeof(ram_amount); - - int mibs[] = { CTL_HW, HW_MEMSIZE }; - if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { - gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); - } - #elif defined(GB_SYSTEM_OPENBSD) - uint64_t ram_amount; - size_t val_size = sizeof(ram_amount); - - int mibs[] = { CTL_HW, HW_PHYSMEM64 }; - if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { - gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); - } - #elif defined(GB_SYSTEM_FREEBSD) - uint64_t ram_amount; - size_t val_size = sizeof(ram_amount); - - int mibs[] = { CTL_HW, HW_PHYSMEM }; - if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { - gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1)); - } - #else - gb_printf("Unknown.\n"); - #endif -} - -gb_internal void report_os_info() { - gb_printf("\tOS: "); - - #if defined(GB_SYSTEM_WINDOWS) - /* - NOTE(Jeroen): - `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10. - `RtlGetVersion` will return the true version. - - Rather than include the WinDDK, we ask the kernel directly. - - `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release) - - */ - OSVERSIONINFOEXW osvi; - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); - - typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*); - typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType); - - // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion - RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion"); - // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo - GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); - - NTSTATUS status = {}; - DWORD ProductType = {}; - if (RtlGetVersion != nullptr) { - status = RtlGetVersion((OSVERSIONINFOW*)&osvi); - } - - if (RtlGetVersion == nullptr || status != 0x0) { - gb_printf("Windows (Unknown Version)"); - } else { - if (GetProductInfo != nullptr) { - GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType); - } - - if (false) { - gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion); - gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion); - gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber); - gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId); - gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor); - gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor); - gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask); - gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType); - } - - gb_printf("Windows "); - - switch (osvi.dwMajorVersion) { - case 10: - /* - Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server - */ - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: // Workstation - if (osvi.dwBuildNumber < 22000) { - gb_printf("10 "); - } else { - gb_printf("11 "); - } - - report_windows_product_type(ProductType); - - break; - default: // Server or Domain Controller - switch(osvi.dwBuildNumber) { - case 14393: - gb_printf("2016 Server"); - break; - case 17763: - gb_printf("2019 Server"); - break; - case 20348: - gb_printf("2022 Server"); - break; - default: - gb_printf("Unknown Server"); - break; - } - } - break; - case 6: - switch (osvi.dwMinorVersion) { - case 0: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows Vista "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2008"); - break; - } - break; - - case 1: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows 7 "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2008 R2"); - break; - } - break; - case 2: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows 8 "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2012"); - break; - } - break; - case 3: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows 8.1 "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2012 R2"); - break; - } - break; - } - break; - case 5: - switch (osvi.dwMinorVersion) { - case 0: - gb_printf("Windows 2000"); - break; - case 1: - gb_printf("Windows XP"); - break; - case 2: - gb_printf("Windows Server 2003"); - break; - } - break; - default: - break; - } - - /* - Grab Windows DisplayVersion (like 20H02) - */ - LPDWORD ValueType = {}; - DWORD UBR; - char DisplayVersion[256]; - DWORD ValueSize = 256; - - status = RegGetValue( - HKEY_LOCAL_MACHINE, - TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), - TEXT("DisplayVersion"), - RRF_RT_REG_SZ, - ValueType, - DisplayVersion, - &ValueSize - ); - - if (status == 0x0) { - gb_printf(" (version: %s)", DisplayVersion); - } - - /* - Now print build number. - */ - gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber); - - ValueSize = sizeof(UBR); - status = RegGetValue( - HKEY_LOCAL_MACHINE, - TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), - TEXT("UBR"), - RRF_RT_REG_DWORD, - ValueType, - &UBR, - &ValueSize - ); - - if (status == 0x0) { - gb_printf(".%u", cast(unsigned)UBR); - } - gb_printf("\n"); - } - #elif defined(GB_SYSTEM_LINUX) - /* - Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` - */ - gbAllocator a = heap_allocator(); - - gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release"); - defer (gb_file_free_contents(&release)); - - b32 found = 0; - if (release.size) { - char *start = (char *)release.data; - char *end = (char *)release.data + release.size; - const char *needle = "PRETTY_NAME=\""; - isize needle_len = gb_strlen((needle)); - - char *c = start; - for (; c < end; c++) { - if (gb_strncmp(c, needle, needle_len) == 0) { - found = 1; - start = c + needle_len; - break; - } - } - - if (found) { - for (c = start; c < end; c++) { - if (*c == '"') { - // Found the closing quote. Replace it with \0 - *c = 0; - gb_printf("%s", (char *)start); - break; - } else if (*c == '\n') { - found = 0; - } - } - } - } - - if (!found) { - gb_printf("Unknown Linux Distro"); - } - - /* - Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname - */ - char buffer[1024]; - uname((struct utsname *)&buffer[0]); - - struct utsname *info; - info = (struct utsname *)&buffer[0]; - - gb_printf(", %s %s\n", info->sysname, info->release); - - #elif defined(GB_SYSTEM_OSX) - struct Darwin_To_Release { - const char* build; // 21G83 - int darwin[3]; // Darwin kernel triplet - const char* os_name; // OS X, MacOS - struct { - const char* name; // Monterey, Mojave, etc. - int version[3]; // 12.4, etc. - } release; - }; - - Darwin_To_Release macos_release_map[] = { - {"8A428", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, - {"8A432", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, - {"8B15", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, - {"8B17", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, - {"8C46", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, - {"8C47", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, - {"8E102", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, - {"8E45", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, - {"8E90", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, - {"8F46", { 8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}}, - {"8G32", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, - {"8G1165", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, - {"8H14", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, - {"8G1454", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, - {"8I127", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, - {"8I1119", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, - {"8J135", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, - {"8J2135a", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, - {"8K1079", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, - {"8N5107", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, - {"8L127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, - {"8L2127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, - {"8P135", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, - {"8P2137", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, - {"8R218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, - {"8R2218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, - {"8R2232", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, - {"8S165", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, - {"8S2167", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, - {"9A581", { 9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}}, - {"9B18", { 9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}}, - {"9B2117", { 9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}}, - {"9C31", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, - {"9C7010", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, - {"9D34", { 9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}}, - {"9E17", { 9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}}, - {"9F33", { 9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}}, - {"9G55", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, - {"9G66", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, - {"9G71", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, - {"9J61", { 9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}}, - {"9L30", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, - {"9L34", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, - {"10A432", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, - {"10A433", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, - {"10B504", {10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}}, - {"10C540", {10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}}, - {"10D573", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, - {"10D575", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, - {"10D578", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, - {"10F569", {10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}}, - {"10H574", {10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}}, - {"10J567", {10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}}, - {"10J869", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, - {"10J3250", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, - {"10J4138", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, - {"10K540", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, - {"10K549", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, - {"11A511", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, - {"11A511s", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, - {"11A2061", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, - {"11A2063", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, - {"11B26", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, - {"11B2118", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, - {"11C74", {11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}}, - {"11D50", {11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}}, - {"11E53", {11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}}, - {"11G56", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, - {"11G63", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, - {"12A269", {12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}}, - {"12B19", {12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}}, - {"12C54", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, - {"12C60", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, - {"12C2034", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, - {"12C3104", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, - {"12D78", {12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}}, - {"12E55", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, - {"12E3067", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, - {"12E4022", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, - {"12F37", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, - {"12F45", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, - {"12F2501", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, - {"12F2518", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, - {"12F2542", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, - {"12F2560", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, - {"13A603", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}}, - {"13B42", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}}, - {"13C64", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, - {"13C1021", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, - {"13D65", {13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}}, - {"13E28", {13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}}, - {"13F34", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1066", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1077", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1096", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1112", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1134", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1507", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1603", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1712", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1808", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"13F1911", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, - {"14A389", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}}, - {"14B25", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}}, - {"14C109", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, - {"14C1510", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, - {"14C2043", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, - {"14C1514", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, - {"14C2513", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, - {"14D131", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, - {"14D136", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, - {"14E46", {14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}}, - {"14F27", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1021", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1505", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1509", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1605", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1713", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1808", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1909", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F1912", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F2009", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F2109", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F2315", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F2411", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"14F2511", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, - {"15A284", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}}, - {"15B42", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}}, - {"15C50", {15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}}, - {"15D21", {15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}}, - {"15E65", {15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}}, - {"15F34", {15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}}, - {"15G31", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1004", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1011", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1108", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1212", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1217", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1421", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1510", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G1611", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G17023", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G18013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G19009", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G20015", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G21013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"15G22010", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, - {"16A323", {16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}}, - {"16B2555", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, - {"16B2657", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, - {"16C67", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, - {"16C68", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, - {"16D32", {16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}}, - {"16E195", {16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}}, - {"16F73", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, - {"16F2073", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, - {"16G29", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1036", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1114", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1212", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1314", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1408", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1510", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1618", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1710", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1815", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1917", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G1918", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G2016", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G2127", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G2128", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"16G2136", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, - {"17A365", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, - {"17A405", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, - {"17B48", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, - {"17B1002", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, - {"17B1003", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, - {"17C88", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, - {"17C89", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, - {"17C205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, - {"17C2205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, - {"17D47", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, - {"17D2047", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, - {"17D102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, - {"17D2102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, - {"17E199", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, - {"17E202", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, - {"17F77", {17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}}, - {"17G65", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G2208", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G2307", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G3025", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G4015", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G5019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G6029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G6030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G7024", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G8029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G8030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G8037", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G9016", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G10021", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G11023", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G12034", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G13033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G13035", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G14019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G14033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"17G14042", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, - {"18A391", {18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}}, - {"18B75", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, - {"18B2107", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, - {"18B3094", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, - {"18C54", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}}, - {"18D42", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, - {"18D43", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, - {"18D109", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, - {"18E226", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, - {"18E227", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, - {"18F132", {18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}}, - {"18G84", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G87", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G95", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G103", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G1012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G2022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G3020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G4032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G5033", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G6020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G6032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G6042", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G7016", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G8012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G8022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G9028", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G9216", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"18G9323", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, - {"19A583", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, - {"19A602", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, - {"19A603", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, - {"19B88", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}}, - {"19C57", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, - {"19C58", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, - {"19D76", {19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}}, - {"19E266", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, - {"19E287", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, - {"19F96", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, - {"19F101", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, - {"19G73", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, - {"19G2021", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, - {"19H2", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H4", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H15", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H114", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H512", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H524", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1030", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1217", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1323", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1417", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1419", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1519", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1615", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1713", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1715", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1824", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H1922", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"19H2026", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, - {"20A2411", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}}, - {"20B29", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, - {"20B50", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, - {"20C69", {20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}}, - {"20D64", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}}, - {"20D74", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, - {"20D75", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, - {"20D80", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}}, - {"20D91", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}}, - {"20E232", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}}, - {"20E241", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}}, - {"20F71", {20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}}, - {"20G71", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}}, - {"20G80", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}}, - {"20G95", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}}, - {"20G165", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}}, - {"20G224", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}}, - {"20G314", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}}, - {"20G415", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}}, - {"20G417", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}}, - {"20G527", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}}, - {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}}, - {"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}}, - {"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}}, - {"20G817", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}}, - {"20G918", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}}, - {"20G1020", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}}, - {"20G1116", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}}, - {"20G1120", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}}, - {"20G1225", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}}, - {"20G1231", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}}, - {"20G1345", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}}, - {"20G1351", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}}, - {"20G1426", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}}, - {"20G1427", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}}, - {"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}}, - {"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}}, - {"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}}, - {"21D49", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}}, - {"21D62", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}}, - {"21E230", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}}, - {"21E258", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}}, - {"21F79", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, - {"21F2081", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, - {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, - {"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}}, - {"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}}, - {"21G115", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}}, - {"21G217", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}}, - {"21G320", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}}, - {"21G419", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}}, - {"21G526", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}}, - {"21G531", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}}, - {"21G646", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}}, - {"21G651", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}}, - {"21G725", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}}, - {"21G726", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}}, - {"21G816", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}}, - {"21G920", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}}, - {"21G1974", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}}, - {"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}}, - {"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}}, - {"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}}, - {"22D49", {13, 2, 0}, "macOS", {"Ventura", {22, 3, 0}}}, - {"22D68", {13, 2, 1}, "macOS", {"Ventura", {22, 3, 0}}}, - {"22E252", {13, 3, 0}, "macOS", {"Ventura", {22, 4, 0}}}, - {"22E261", {13, 3, 1}, "macOS", {"Ventura", {22, 4, 0}}}, - {"22F66", {13, 4, 0}, "macOS", {"Ventura", {22, 5, 0}}}, - {"22F82", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}}, - {"22E772610a", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}}, - {"22F770820d", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}}, - {"22G74", {13, 5, 0}, "macOS", {"Ventura", {22, 6, 0}}}, - {"22G90", {13, 5, 1}, "macOS", {"Ventura", {22, 6, 0}}}, - {"22G91", {13, 5, 2}, "macOS", {"Ventura", {22, 6, 0}}}, - {"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}}, - {"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}}, - {"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}}, - {"23A344", {23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}}, - {"23B74", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}}, - {"23B81", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}}, - {"23B92", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}}, - {"23C64", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}}, - {"23C71", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}}, - }; - - - b32 build_found = 1; - b32 darwin_found = 1; - uint32_t major, minor, patch; - - #define MACOS_VERSION_BUFFER_SIZE 100 - char build_buffer[MACOS_VERSION_BUFFER_SIZE]; - char darwin_buffer[MACOS_VERSION_BUFFER_SIZE]; - size_t build_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1; - size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1; - #undef MACOS_VERSION_BUFFER_SIZE - - int build_mibs[] = { CTL_KERN, KERN_OSVERSION }; - if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) { - build_found = 0; - } - - int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE }; - if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) { - gb_printf("macOS Unknown\n"); - return; - } else { - if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) { - darwin_found = 0; - } - } - - // Scan table for match on BUILD - int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]); - Darwin_To_Release build_match = {}; - Darwin_To_Release kernel_match = {}; - - for (int build = 0; build < macos_release_count; build++) { - Darwin_To_Release rel = macos_release_map[build]; - - // Do we have an exact match on the BUILD? - if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) { - build_match = rel; - break; - } - - // Do we have an exact Darwin match? - if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) { - kernel_match = rel; - } - - // Major kernel version needs to match exactly, - if (rel.darwin[0] == major) { - // No major version match yet. - if (!kernel_match.os_name) { - kernel_match = rel; - } - if (minor >= rel.darwin[1]) { - kernel_match = rel; - if (patch >= rel.darwin[2]) { - kernel_match = rel; - } - } - } - } - - Darwin_To_Release match = {}; - if(!build_match.build) { - match = kernel_match; - } else { - match = build_match; - } - - if (match.os_name) { - gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]); - if (match.release.version[1] > 0 || match.release.version[2] > 0) { - gb_printf(".%d", match.release.version[1]); - } - if (match.release.version[2] > 0) { - gb_printf(".%d", match.release.version[2]); - } - if (build_found) { - gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]); - } else { - gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]); - } - return; - } - - if (build_found && darwin_found) { - gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch); - return; - } else if (build_found) { - gb_printf("macOS Unknown (build: %s)\n", build_buffer); - return; - } else if (darwin_found) { - gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch); - return; - } - #elif defined(GB_SYSTEM_OPENBSD) - struct utsname un; - - if (uname(&un) != -1) { - gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine); - } else { - gb_printf("OpenBSD: Unknown\n"); - } - #elif defined(GB_SYSTEM_FREEBSD) - #define freebsd_version_buffer 129 - char buffer[freebsd_version_buffer]; - size_t buffer_size = freebsd_version_buffer - 1; - #undef freebsd_version_buffer - - int mibs[] = { CTL_KERN, KERN_VERSION }; - if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) { - gb_printf("FreeBSD: Unknown\n"); - } else { - // KERN_VERSION can end in a \n, replace it with a \0 - for (int i = 0; i < buffer_size; i += 1) { - if (buffer[i] == '\n') buffer[i] = 0; - } - gb_printf("%s", &buffer[0]); - - // Retrieve kernel revision using `sysctl`, e.g. 199506 - mibs[1] = KERN_OSREV; - uint64_t revision; - size_t revision_size = sizeof(revision); - - if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) { - gb_printf("\n"); - } else { - gb_printf(", revision %ld\n", revision); - } - } - #else - gb_printf("Unknown"); - #endif -} - -// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting. -gb_internal void print_bug_report_help() { - gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n"); - gb_printf("\tWebsite: https://odin-lang.org\n"); - gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n"); - /* - Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site. - gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n"); - */ - gb_printf("\n\n"); - - gb_printf("Useful information to add to a bug report:\n\n"); - - gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION)); - - #ifdef NIGHTLY - gb_printf("-nightly"); - #endif - - #ifdef GIT_SHA - gb_printf(":%s", GIT_SHA); - #endif - - gb_printf("\n"); - - /* - Print OS information. - */ - report_os_info(); - - /* - Now print CPU info. - */ - report_cpu_info(); - - /* - And RAM info. - */ - report_ram_info(); -} \ No newline at end of file +/* + Gather and print platform and version info to help with reporting Odin bugs. +*/ + +#if !defined(GB_COMPILER_MSVC) + #if defined(GB_CPU_X86) + #include + #endif +#endif + +#if defined(GB_SYSTEM_LINUX) + #include + #include +#endif + +#if defined(GB_SYSTEM_OSX) + #include +#endif + +#if defined(GB_SYSTEM_OPENBSD) + #include + #include +#endif + +#if defined(GB_SYSTEM_FREEBSD) + #include +#endif + +/* + NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`. +*/ +#if defined(GB_SYSTEM_WINDOWS) +gb_internal void report_windows_product_type(DWORD ProductType) { + switch (ProductType) { + case PRODUCT_ULTIMATE: + gb_printf("Ultimate"); + break; + + case PRODUCT_HOME_BASIC: + gb_printf("Home Basic"); + break; + + case PRODUCT_HOME_PREMIUM: + gb_printf("Home Premium"); + break; + + case PRODUCT_ENTERPRISE: + gb_printf("Enterprise"); + break; + + case PRODUCT_CORE: + gb_printf("Home Basic"); + break; + + case PRODUCT_HOME_BASIC_N: + gb_printf("Home Basic N"); + break; + + case PRODUCT_EDUCATION: + gb_printf("Education"); + break; + + case PRODUCT_EDUCATION_N: + gb_printf("Education N"); + break; + + case PRODUCT_BUSINESS: + gb_printf("Business"); + break; + + case PRODUCT_STANDARD_SERVER: + gb_printf("Standard Server"); + break; + + case PRODUCT_DATACENTER_SERVER: + gb_printf("Datacenter"); + break; + + case PRODUCT_SMALLBUSINESS_SERVER: + gb_printf("Windows Small Business Server"); + break; + + case PRODUCT_ENTERPRISE_SERVER: + gb_printf("Enterprise Server"); + break; + + case PRODUCT_STARTER: + gb_printf("Starter"); + break; + + case PRODUCT_DATACENTER_SERVER_CORE: + gb_printf("Datacenter Server Core"); + break; + + case PRODUCT_STANDARD_SERVER_CORE: + gb_printf("Server Standard Core"); + break; + + case PRODUCT_ENTERPRISE_SERVER_CORE: + gb_printf("Enterprise Server Core"); + break; + + case PRODUCT_BUSINESS_N: + gb_printf("Business N"); + break; + + case PRODUCT_HOME_SERVER: + gb_printf("Home Server"); + break; + + case PRODUCT_SERVER_FOR_SMALLBUSINESS: + gb_printf("Windows Server 2008 for Windows Essential Server Solutions"); + break; + + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + gb_printf("Small Business Server Premium"); + break; + + case PRODUCT_HOME_PREMIUM_N: + gb_printf("Home Premium N"); + break; + + case PRODUCT_ENTERPRISE_N: + gb_printf("Enterprise N"); + break; + + case PRODUCT_ULTIMATE_N: + gb_printf("Ultimate N"); + break; + + case PRODUCT_HYPERV: + gb_printf("HyperV"); + break; + + case PRODUCT_STARTER_N: + gb_printf("Starter N"); + break; + + case PRODUCT_PROFESSIONAL: + gb_printf("Professional"); + break; + + case PRODUCT_PROFESSIONAL_N: + gb_printf("Professional N"); + break; + + case PRODUCT_UNLICENSED: + gb_printf("Unlicensed"); + break; + + default: + gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType); + } +} +#endif + +gb_internal void odin_cpuid(int leaf, int result[]) { + #if defined(GB_CPU_ARM) + return; + + #elif defined(GB_CPU_X86) + + #if defined(GB_COMPILER_MSVC) + __cpuid(result, leaf); + #else + __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]); + #endif + + #endif +} + +gb_internal void report_cpu_info() { + gb_printf("\tCPU: "); + + #if defined(GB_CPU_X86) + + /* + Get extended leaf info + */ + int cpu[4]; + + odin_cpuid(0x80000000, &cpu[0]); + int number_of_extended_ids = cpu[0]; + + int brand[0x12] = {}; + + /* + Read CPU brand if supported. + */ + if (number_of_extended_ids >= 0x80000004) { + odin_cpuid(0x80000002, &brand[0]); + odin_cpuid(0x80000003, &brand[4]); + odin_cpuid(0x80000004, &brand[8]); + + /* + Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them. + */ + char * brand_name = (char *)&brand[0]; + for (; brand_name[0] == ' '; brand_name++) {} + + gb_printf("%s\n", brand_name); + } else { + gb_printf("Unable to retrieve.\n"); + } + + #elif defined(GB_CPU_ARM) + /* + TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`. + */ + #if defined(GB_ARCH_64_BIT) + gb_printf("ARM64\n"); + #else + gb_printf("ARM\n"); + #endif + #else + gb_printf("Unknown\n"); + #endif +} + +/* + Report the amount of installed RAM. +*/ +gb_internal void report_ram_info() { + gb_printf("\tRAM: "); + + #if defined(GB_SYSTEM_WINDOWS) + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx (&statex); + + gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1)); + + #elif defined(GB_SYSTEM_LINUX) + /* + Retrieve RAM info using `sysinfo()`, + */ + struct sysinfo info; + int result = sysinfo(&info); + + if (result == 0x0) { + gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1)); + } else { + gb_printf("Unknown.\n"); + } + #elif defined(GB_SYSTEM_OSX) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int mibs[] = { CTL_HW, HW_MEMSIZE }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); + } + #elif defined(GB_SYSTEM_OPENBSD) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int mibs[] = { CTL_HW, HW_PHYSMEM64 }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); + } + #elif defined(GB_SYSTEM_FREEBSD) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int mibs[] = { CTL_HW, HW_PHYSMEM }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1)); + } + #else + gb_printf("Unknown.\n"); + #endif +} + +gb_internal void report_os_info() { + gb_printf("\tOS: "); + + #if defined(GB_SYSTEM_WINDOWS) + /* + NOTE(Jeroen): + `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10. + `RtlGetVersion` will return the true version. + + Rather than include the WinDDK, we ask the kernel directly. + + `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release) + + */ + OSVERSIONINFOEXW osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + + typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*); + typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType); + + // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion"); + // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo + GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); + + NTSTATUS status = {}; + DWORD ProductType = {}; + if (RtlGetVersion != nullptr) { + status = RtlGetVersion((OSVERSIONINFOW*)&osvi); + } + + if (RtlGetVersion == nullptr || status != 0x0) { + gb_printf("Windows (Unknown Version)"); + } else { + if (GetProductInfo != nullptr) { + GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType); + } + + if (false) { + gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion); + gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion); + gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber); + gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId); + gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor); + gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor); + gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask); + gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType); + } + + gb_printf("Windows "); + + switch (osvi.dwMajorVersion) { + case 10: + /* + Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server + */ + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: // Workstation + if (osvi.dwBuildNumber < 22000) { + gb_printf("10 "); + } else { + gb_printf("11 "); + } + + report_windows_product_type(ProductType); + + break; + default: // Server or Domain Controller + switch(osvi.dwBuildNumber) { + case 14393: + gb_printf("2016 Server"); + break; + case 17763: + gb_printf("2019 Server"); + break; + case 20348: + gb_printf("2022 Server"); + break; + default: + gb_printf("Unknown Server"); + break; + } + } + break; + case 6: + switch (osvi.dwMinorVersion) { + case 0: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows Vista "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2008"); + break; + } + break; + + case 1: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 7 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2008 R2"); + break; + } + break; + case 2: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 8 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2012"); + break; + } + break; + case 3: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 8.1 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2012 R2"); + break; + } + break; + } + break; + case 5: + switch (osvi.dwMinorVersion) { + case 0: + gb_printf("Windows 2000"); + break; + case 1: + gb_printf("Windows XP"); + break; + case 2: + gb_printf("Windows Server 2003"); + break; + } + break; + default: + break; + } + + /* + Grab Windows DisplayVersion (like 20H02) + */ + LPDWORD ValueType = {}; + DWORD UBR; + char DisplayVersion[256]; + DWORD ValueSize = 256; + + status = RegGetValue( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + TEXT("DisplayVersion"), + RRF_RT_REG_SZ, + ValueType, + DisplayVersion, + &ValueSize + ); + + if (status == 0x0) { + gb_printf(" (version: %s)", DisplayVersion); + } + + /* + Now print build number. + */ + gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber); + + ValueSize = sizeof(UBR); + status = RegGetValue( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + TEXT("UBR"), + RRF_RT_REG_DWORD, + ValueType, + &UBR, + &ValueSize + ); + + if (status == 0x0) { + gb_printf(".%u", cast(unsigned)UBR); + } + gb_printf("\n"); + } + #elif defined(GB_SYSTEM_LINUX) + /* + Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` + */ + gbAllocator a = heap_allocator(); + + gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release"); + defer (gb_file_free_contents(&release)); + + b32 found = 0; + if (release.size) { + char *start = (char *)release.data; + char *end = (char *)release.data + release.size; + const char *needle = "PRETTY_NAME=\""; + isize needle_len = gb_strlen((needle)); + + char *c = start; + for (; c < end; c++) { + if (gb_strncmp(c, needle, needle_len) == 0) { + found = 1; + start = c + needle_len; + break; + } + } + + if (found) { + for (c = start; c < end; c++) { + if (*c == '"') { + // Found the closing quote. Replace it with \0 + *c = 0; + gb_printf("%s", (char *)start); + break; + } else if (*c == '\n') { + found = 0; + } + } + } + } + + if (!found) { + gb_printf("Unknown Linux Distro"); + } + + /* + Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname + */ + char buffer[1024]; + uname((struct utsname *)&buffer[0]); + + struct utsname *info; + info = (struct utsname *)&buffer[0]; + + gb_printf(", %s %s\n", info->sysname, info->release); + + #elif defined(GB_SYSTEM_OSX) + struct Darwin_To_Release { + const char* build; // 21G83 + int darwin[3]; // Darwin kernel triplet + const char* os_name; // OS X, MacOS + struct { + const char* name; // Monterey, Mojave, etc. + int version[3]; // 12.4, etc. + } release; + }; + + Darwin_To_Release macos_release_map[] = { + {"8A428", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, + {"8A432", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, + {"8B15", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, + {"8B17", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, + {"8C46", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8C47", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8E102", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8E45", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8E90", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8F46", { 8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}}, + {"8G32", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, + {"8G1165", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, + {"8H14", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, + {"8G1454", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, + {"8I127", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, + {"8I1119", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, + {"8J135", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8J2135a", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8K1079", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8N5107", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8L127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, + {"8L2127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, + {"8P135", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, + {"8P2137", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, + {"8R218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + {"8R2218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + {"8R2232", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + {"8S165", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, + {"8S2167", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, + {"9A581", { 9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}}, + {"9B18", { 9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}}, + {"9B2117", { 9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}}, + {"9C31", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, + {"9C7010", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, + {"9D34", { 9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}}, + {"9E17", { 9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}}, + {"9F33", { 9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}}, + {"9G55", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + {"9G66", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + {"9G71", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + {"9J61", { 9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}}, + {"9L30", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, + {"9L34", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, + {"10A432", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, + {"10A433", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, + {"10B504", {10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}}, + {"10C540", {10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}}, + {"10D573", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + {"10D575", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + {"10D578", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + {"10F569", {10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}}, + {"10H574", {10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}}, + {"10J567", {10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}}, + {"10J869", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + {"10J3250", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + {"10J4138", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + {"10K540", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, + {"10K549", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, + {"11A511", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, + {"11A511s", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, + {"11A2061", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, + {"11A2063", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, + {"11B26", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, + {"11B2118", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, + {"11C74", {11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}}, + {"11D50", {11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}}, + {"11E53", {11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}}, + {"11G56", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, + {"11G63", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, + {"12A269", {12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}}, + {"12B19", {12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}}, + {"12C54", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12C60", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12C2034", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12C3104", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12D78", {12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}}, + {"12E55", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + {"12E3067", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + {"12E4022", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + {"12F37", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F45", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2501", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2518", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2542", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2560", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"13A603", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}}, + {"13B42", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}}, + {"13C64", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, + {"13C1021", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, + {"13D65", {13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}}, + {"13E28", {13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}}, + {"13F34", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1066", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1077", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1096", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1112", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1134", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1507", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1603", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1712", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1808", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1911", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"14A389", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}}, + {"14B25", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}}, + {"14C109", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C1510", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C2043", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C1514", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C2513", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14D131", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, + {"14D136", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, + {"14E46", {14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}}, + {"14F27", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1021", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1505", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1509", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1605", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1713", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1808", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1909", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1912", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2009", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2109", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2315", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2411", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2511", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"15A284", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}}, + {"15B42", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}}, + {"15C50", {15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}}, + {"15D21", {15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}}, + {"15E65", {15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}}, + {"15F34", {15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}}, + {"15G31", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1004", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1011", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1108", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1212", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1217", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1421", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1510", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1611", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G17023", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G18013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G19009", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G20015", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G21013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G22010", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"16A323", {16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}}, + {"16B2555", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, + {"16B2657", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, + {"16C67", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, + {"16C68", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, + {"16D32", {16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}}, + {"16E195", {16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}}, + {"16F73", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, + {"16F2073", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, + {"16G29", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1036", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1114", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1212", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1314", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1408", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1510", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1618", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1710", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1815", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1917", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1918", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2016", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2127", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2128", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2136", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"17A365", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, + {"17A405", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, + {"17B48", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + {"17B1002", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + {"17B1003", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + {"17C88", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17C89", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17C205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17C2205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17D47", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17D2047", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17D102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17D2102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17E199", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, + {"17E202", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, + {"17F77", {17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}}, + {"17G65", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G2208", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G2307", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G3025", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G4015", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G5019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G6029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G6030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G7024", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G8029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G8030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G8037", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G9016", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G10021", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G11023", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G12034", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G13033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G13035", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G14019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G14033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G14042", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"18A391", {18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}}, + {"18B75", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + {"18B2107", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + {"18B3094", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + {"18C54", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}}, + {"18D42", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + {"18D43", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + {"18D109", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + {"18E226", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, + {"18E227", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, + {"18F132", {18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}}, + {"18G84", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G87", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G95", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G103", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G1012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G2022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G3020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G4032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G5033", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G6020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G6032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G6042", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G7016", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G8012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G8022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G9028", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G9216", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G9323", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"19A583", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + {"19A602", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + {"19A603", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + {"19B88", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}}, + {"19C57", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, + {"19C58", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, + {"19D76", {19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}}, + {"19E266", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, + {"19E287", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, + {"19F96", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, + {"19F101", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, + {"19G73", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, + {"19G2021", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, + {"19H2", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H4", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H15", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H114", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H512", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H524", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1030", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1217", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1323", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1417", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1419", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1519", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1615", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1713", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1715", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1824", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1922", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H2026", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"20A2411", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}}, + {"20B29", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, + {"20B50", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, + {"20C69", {20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}}, + {"20D64", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}}, + {"20D74", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, + {"20D75", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, + {"20D80", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}}, + {"20D91", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}}, + {"20E232", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}}, + {"20E241", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}}, + {"20F71", {20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}}, + {"20G71", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}}, + {"20G80", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}}, + {"20G95", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}}, + {"20G165", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}}, + {"20G224", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}}, + {"20G314", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}}, + {"20G415", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}}, + {"20G417", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}}, + {"20G527", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}}, + {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}}, + {"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}}, + {"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}}, + {"20G817", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}}, + {"20G918", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}}, + {"20G1020", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}}, + {"20G1116", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}}, + {"20G1120", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}}, + {"20G1225", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}}, + {"20G1231", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}}, + {"20G1345", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}}, + {"20G1351", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}}, + {"20G1426", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}}, + {"20G1427", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}}, + {"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}}, + {"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}}, + {"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}}, + {"21D49", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}}, + {"21D62", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}}, + {"21E230", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}}, + {"21E258", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}}, + {"21F79", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + {"21F2081", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + {"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}}, + {"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}}, + {"21G115", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}}, + {"21G217", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}}, + {"21G320", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}}, + {"21G419", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}}, + {"21G526", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}}, + {"21G531", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}}, + {"21G646", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}}, + {"21G651", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}}, + {"21G725", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}}, + {"21G726", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}}, + {"21G816", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}}, + {"21G920", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}}, + {"21G1974", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}}, + {"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}}, + {"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}}, + {"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}}, + {"22D49", {13, 2, 0}, "macOS", {"Ventura", {22, 3, 0}}}, + {"22D68", {13, 2, 1}, "macOS", {"Ventura", {22, 3, 0}}}, + {"22E252", {13, 3, 0}, "macOS", {"Ventura", {22, 4, 0}}}, + {"22E261", {13, 3, 1}, "macOS", {"Ventura", {22, 4, 0}}}, + {"22F66", {13, 4, 0}, "macOS", {"Ventura", {22, 5, 0}}}, + {"22F82", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}}, + {"22E772610a", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}}, + {"22F770820d", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}}, + {"22G74", {13, 5, 0}, "macOS", {"Ventura", {22, 6, 0}}}, + {"22G90", {13, 5, 1}, "macOS", {"Ventura", {22, 6, 0}}}, + {"22G91", {13, 5, 2}, "macOS", {"Ventura", {22, 6, 0}}}, + {"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}}, + {"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}}, + {"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}}, + {"23A344", {23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}}, + {"23B74", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}}, + {"23B81", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}}, + {"23B92", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}}, + {"23C64", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}}, + {"23C71", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}}, + }; + + + b32 build_found = 1; + b32 darwin_found = 1; + uint32_t major, minor, patch; + + #define MACOS_VERSION_BUFFER_SIZE 100 + char build_buffer[MACOS_VERSION_BUFFER_SIZE]; + char darwin_buffer[MACOS_VERSION_BUFFER_SIZE]; + size_t build_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1; + size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1; + #undef MACOS_VERSION_BUFFER_SIZE + + int build_mibs[] = { CTL_KERN, KERN_OSVERSION }; + if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) { + build_found = 0; + } + + int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE }; + if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) { + gb_printf("macOS Unknown\n"); + return; + } else { + if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) { + darwin_found = 0; + } + } + + // Scan table for match on BUILD + int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]); + Darwin_To_Release build_match = {}; + Darwin_To_Release kernel_match = {}; + + for (int build = 0; build < macos_release_count; build++) { + Darwin_To_Release rel = macos_release_map[build]; + + // Do we have an exact match on the BUILD? + if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) { + build_match = rel; + break; + } + + // Do we have an exact Darwin match? + if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) { + kernel_match = rel; + } + + // Major kernel version needs to match exactly, + if (rel.darwin[0] == major) { + // No major version match yet. + if (!kernel_match.os_name) { + kernel_match = rel; + } + if (minor >= rel.darwin[1]) { + kernel_match = rel; + if (patch >= rel.darwin[2]) { + kernel_match = rel; + } + } + } + } + + Darwin_To_Release match = {}; + if(!build_match.build) { + match = kernel_match; + } else { + match = build_match; + } + + if (match.os_name) { + gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]); + if (match.release.version[1] > 0 || match.release.version[2] > 0) { + gb_printf(".%d", match.release.version[1]); + } + if (match.release.version[2] > 0) { + gb_printf(".%d", match.release.version[2]); + } + if (build_found) { + gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]); + } else { + gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]); + } + return; + } + + if (build_found && darwin_found) { + gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch); + return; + } else if (build_found) { + gb_printf("macOS Unknown (build: %s)\n", build_buffer); + return; + } else if (darwin_found) { + gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch); + return; + } + #elif defined(GB_SYSTEM_OPENBSD) + struct utsname un; + + if (uname(&un) != -1) { + gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine); + } else { + gb_printf("OpenBSD: Unknown\n"); + } + #elif defined(GB_SYSTEM_FREEBSD) + #define freebsd_version_buffer 129 + char buffer[freebsd_version_buffer]; + size_t buffer_size = freebsd_version_buffer - 1; + #undef freebsd_version_buffer + + int mibs[] = { CTL_KERN, KERN_VERSION }; + if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) { + gb_printf("FreeBSD: Unknown\n"); + } else { + // KERN_VERSION can end in a \n, replace it with a \0 + for (int i = 0; i < buffer_size; i += 1) { + if (buffer[i] == '\n') buffer[i] = 0; + } + gb_printf("%s", &buffer[0]); + + // Retrieve kernel revision using `sysctl`, e.g. 199506 + mibs[1] = KERN_OSREV; + uint64_t revision; + size_t revision_size = sizeof(revision); + + if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) { + gb_printf("\n"); + } else { + gb_printf(", revision %ld\n", revision); + } + } + #else + gb_printf("Unknown"); + #endif +} + +gb_internal void report_backend_info() { + gb_printf("\tBackend: LLVM %s\n", LLVM_VERSION_STRING); +} + +// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting. +gb_internal void print_bug_report_help() { + gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n"); + gb_printf("\tWebsite: https://odin-lang.org\n"); + gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n"); + /* + Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site. + gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n"); + */ + gb_printf("\n\n"); + + gb_printf("Useful information to add to a bug report:\n\n"); + + gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION)); + + #ifdef NIGHTLY + gb_printf("-nightly"); + #endif + + #ifdef GIT_SHA + gb_printf(":%s", GIT_SHA); + #endif + + gb_printf("\n"); + + /* + Print OS information. + */ + report_os_info(); + + /* + Now print CPU info. + */ + report_cpu_info(); + + /* + And RAM info. + */ + report_ram_info(); + + report_backend_info(); +} diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d39be37a9..c85fb28d6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -5820,6 +5820,26 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } break; + case BuiltinProc_type_bit_set_backing_type: + { + Operand op = {}; + Type *type = check_type(c, ce->args[0]); + Type *bt = base_type(type); + if (bt == nullptr || bt == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (bt->kind != Type_BitSet) { + gbString s = type_to_string(type); + error(ce->args[0], "Expected a bit_set type for '%.*s', got %s", LIT(builtin_name), s); + return false; + } + + operand->mode = Addressing_Type; + operand->type = bit_set_to_int(bt); + break; + } + case BuiltinProc_type_equal_proc: { Operand op = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3ccf1b97a..2c0f7a7b8 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -210,6 +210,7 @@ gb_internal bool is_type_distinct(Ast *node) { case Ast_UnionType: case Ast_EnumType: case Ast_ProcType: + case Ast_BitFieldType: return true; case Ast_PointerType: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 11eb4b533..3a8cdf0b1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -100,7 +100,7 @@ gb_internal void check_union_type (CheckerContext *c, Type *un gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name); -gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); +gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0); gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key); gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem); @@ -1241,7 +1241,7 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T } case Type_Pointer: if (source->kind == Type_Pointer) { - isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem); + isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true); if (level > 0) { return true; } @@ -1413,7 +1413,9 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T return ok; } - // return check_is_assignable_to(c, &o, poly); + + // NOTE(bill): Check for subtypes of + // return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly); } return false; case Type_Tuple: @@ -1884,33 +1886,55 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i BigInt i = v.value_integer; - i64 bit_size = type_size_of(type); + i64 byte_size = type_size_of(type); BigInt umax = {}; BigInt imin = {}; BigInt imax = {}; - if (bit_size < 16) { - big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]); - big_int_from_i64(&imin, signed_integer_mins[bit_size]); - big_int_from_i64(&imax, signed_integer_maxs[bit_size]); - } else { + if (c->bit_field_bit_size > 0) { + i64 bit_size = gb_min(cast(i64)(8*byte_size), cast(i64)c->bit_field_bit_size); + big_int_from_u64(&umax, 1); big_int_from_i64(&imin, 1); big_int_from_i64(&imax, 1); - BigInt bi128 = {}; - BigInt bi127 = {}; - big_int_from_i64(&bi128, 128); - big_int_from_i64(&bi127, 127); + BigInt bu = {}; + BigInt bi = {}; + big_int_from_i64(&bu, bit_size); + big_int_from_i64(&bi, bit_size-1); - big_int_shl_eq(&umax, &bi128); + big_int_shl_eq(&umax, &bu); mp_decr(&umax); - big_int_shl_eq(&imin, &bi127); + big_int_shl_eq(&imin, &bi); big_int_neg(&imin, &imin); - big_int_shl_eq(&imax, &bi127); + big_int_shl_eq(&imax, &bi); mp_decr(&imax); + } else { + if (byte_size < 16) { + big_int_from_u64(&umax, unsigned_integer_maxs[byte_size]); + big_int_from_i64(&imin, signed_integer_mins[byte_size]); + big_int_from_i64(&imax, signed_integer_maxs[byte_size]); + } else { + big_int_from_u64(&umax, 1); + big_int_from_i64(&imin, 1); + big_int_from_i64(&imax, 1); + + BigInt bi128 = {}; + BigInt bi127 = {}; + big_int_from_i64(&bi128, 128); + big_int_from_i64(&bi127, 127); + + big_int_shl_eq(&umax, &bi128); + mp_decr(&umax); + + big_int_shl_eq(&imin, &bi127); + big_int_neg(&imin, &imin); + + big_int_shl_eq(&imax, &bi127); + mp_decr(&imax); + } } switch (type->Basic.kind) { @@ -2069,11 +2093,17 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i } -gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) { +gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0) { if (is_type_integer(type) && o->value.kind == ExactValue_Integer) { gbString b = type_to_string(type); i64 sz = type_size_of(type); + i64 bit_size = 8*sz; + bool size_changed = false; + if (max_bit_size > 0) { + size_changed = (bit_size != max_bit_size); + bit_size = gb_min(bit_size, max_bit_size); + } BigInt *bi = &o->value.value_integer; if (is_type_unsigned(type)) { if (big_int_is_neg(bi)) { @@ -2081,25 +2111,36 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } else { BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz); + BigInt bits = big_int_make_i64(bit_size); big_int_shl_eq(&max_size, &bits); big_int_sub_eq(&max_size, &one); String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { + error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + } } } else { BigInt zero = big_int_make_u64(0); BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz - 1); + BigInt bits = big_int_make_i64(bit_size - 1); big_int_shl_eq(&max_size, &bits); + + String max_size_str = {}; if (big_int_is_neg(bi)) { big_int_neg(&max_size, &max_size); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); } else { big_int_sub_eq(&max_size, &one); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); + } + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); } } @@ -2110,7 +2151,7 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } return false; } -gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { +gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); defer( @@ -2141,7 +2182,7 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o error_line("\t whereas slices in general are assumed to be mutable.\n"); } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) { error_line("\tSuggestion: the expression may be casted to %s\n", b); - } else if (check_integer_exceed_suggestion(c, o, type)) { + } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) { return; } } @@ -2215,13 +2256,18 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { + i64 max_bit_size = 0; + if (ctx->bit_field_bit_size) { + max_bit_size = ctx->bit_field_bit_size; + } + if (are_types_identical(o->type, type)) { error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b); } else { error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c); } - check_assignment_error_suggestion(ctx, o, type); + check_assignment_error_suggestion(ctx, o, type, max_bit_size); } } else { error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); @@ -2232,6 +2278,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ } gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { + if (o->expr && o->expr->kind == Ast_SelectorExpr) { + if (o->expr->SelectorExpr.is_bit_field) { + return true; + } + } if (o->mode == Addressing_OptionalOk) { Ast *expr = unselector_expr(o->expr); if (expr->kind != Ast_TypeAssertion) { @@ -2304,6 +2355,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * Entity *e = entity_of_node(ue->expr); if (e != nullptr && (e->flags & EntityFlag_Param) != 0) { error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str); + } else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) { + error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str); } else { switch (o->mode) { case Addressing_Constant: @@ -2877,6 +2930,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type } } + if (is_type_bit_field(src)) { + return are_types_identical(core_type(src->BitField.backing_type), dst); + } + if (is_type_bit_field(dst)) { + return are_types_identical(src, core_type(dst->BitField.backing_type)); + } + if (is_type_integer(src) && is_type_rune(dst)) { return true; } @@ -5065,6 +5125,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod operand->type = entity->type; operand->expr = node; + if (entity->flags & EntityFlag_BitFieldField) { + add_package_dependency(c, "runtime", "__write_bits"); + add_package_dependency(c, "runtime", "__read_bits"); + } + switch (entity->kind) { case Entity_Constant: operand->value = entity->Constant.value; @@ -5078,6 +5143,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } break; case Entity_Variable: + if (sel.is_bit_field) { + se->is_bit_field = true; + } if (sel.indirect) { operand->mode = Addressing_Variable; } else if (operand->mode == Addressing_Context) { @@ -8394,6 +8462,11 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice fields_visited_through_raw_union = {}; defer (string_map_destroy(&fields_visited_through_raw_union)); + String assignment_str = str_lit("structure literal"); + if (bt->kind == Type_BitField) { + assignment_str = str_lit("bit_field literal"); + } + for (Ast *elem : elems) { if (elem->kind != Ast_FieldValue) { error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); @@ -8415,17 +8488,26 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, SliceStruct.fields[sel.index[0]]; + Entity *field = nullptr; + if (bt->kind == Type_Struct) { + field = bt->Struct.fields[sel.index[0]]; + } else if (bt->kind == Type_BitField) { + field = bt->BitField.fields[sel.index[0]]; + } else { + GB_PANIC("Unknown type"); + } + + add_entity_use(c, fv->field, field); if (string_set_update(&fields_visited, name)) { if (sel.index.count > 1) { if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); } else { - error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string)); + error(fv->field, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str)); } } else { - error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string)); + error(fv->field, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str)); } continue; } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { @@ -8433,11 +8515,13 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicefield, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name)); + error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str)); continue; } if (sel.index.count > 1) { + GB_ASSERT(bt->kind == Type_Struct); + if (is_constant) { Type *ft = type; for (i32 index : sel.index) { @@ -8498,7 +8582,15 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicetype, str_lit("structure literal")); + u8 prev_bit_field_bit_size = c->bit_field_bit_size; + if (field->kind == Entity_Variable && field->Variable.bit_field_bit_size) { + // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case + c->bit_field_bit_size = field->Variable.bit_field_bit_size; + } + + check_assignment(c, &o, field->type, assignment_str); + + c->bit_field_bit_size = prev_bit_field_bit_size; } } @@ -9300,6 +9392,21 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } break; } + case Type_BitField: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + is_constant = false; + if (cl->elems[0]->kind != Ast_FieldValue) { + gbString type_str = type_to_string(type); + error(node, "%s ('bit_field') compound literals are only allowed to contain 'field = value' elements", type_str); + gb_string_free(type_str); + } else { + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); + } + break; + } + default: { if (cl->elems.count == 0) { @@ -11113,6 +11220,32 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan case_end; + case_ast_node(f, BitFieldField, node); + str = write_expr_to_string(str, f->name, shorthand); + str = gb_string_appendc(str, ": "); + str = write_expr_to_string(str, f->type, shorthand); + str = gb_string_appendc(str, " | "); + str = write_expr_to_string(str, f->bit_size, shorthand); + case_end; + case_ast_node(bf, BitFieldType, node); + str = gb_string_appendc(str, "bit_field "); + if (!shorthand) { + str = write_expr_to_string(str, bf->backing_type, shorthand); + } + str = gb_string_appendc(str, " {"); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + for_array(i, bf->fields) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, bf->fields[i], false); + } + } + str = gb_string_appendc(str, "}"); + case_end; + case_ast_node(ia, InlineAsmExpr, node); str = gb_string_appendc(str, "asm("); for_array(i, ia->param_types) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6897701d6..a7dd9743b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -485,7 +485,17 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } } + Entity *lhs_e = entity_of_node(lhs->expr); + u8 prev_bit_field_bit_size = ctx->bit_field_bit_size; + if (lhs_e && lhs_e->kind == Entity_Variable && lhs_e->Variable.bit_field_bit_size) { + // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case + ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size; + } + check_assignment(ctx, rhs, assignment_type, str_lit("assignment")); + + ctx->bit_field_bit_size = prev_bit_field_bit_size; + if (rhs->mode == Addressing_Invalid) { return nullptr; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 8a140d95e..dd77031a3 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -89,6 +89,8 @@ gb_internal bool does_field_type_allow_using(Type *t) { return true; } else if (is_type_array(t)) { return t->Array.count <= 4; + } else if (is_type_bit_field(t)) { + return true; } return false; } @@ -925,6 +927,202 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam enum_type->Enum.max_value_index = max_value_index; } +gb_internal bool is_valid_bit_field_backing_type(Type *type) { + if (type == nullptr) { + return false; + } + type = base_type(type); + if (is_type_untyped(type)) { + return false; + } + if (is_type_integer(type)) { + return true; + } + if (type->kind == Type_Array) { + return is_type_integer(type->Array.elem); + } + return false; +} + +gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Type *named_type, Ast *node) { + ast_node(bf, BitFieldType, node); + GB_ASSERT(is_type_bit_field(bit_field_type)); + + Type *backing_type = check_type(ctx, bf->backing_type); + if (backing_type == nullptr || !is_valid_bit_field_backing_type(backing_type)) { + error(node, "Backing type for a bit_field must be an integer or an array of an integer"); + return; + } + + bit_field_type->BitField.backing_type = backing_type; + bit_field_type->BitField.scope = ctx->scope; + + auto fields = array_make(permanent_allocator(), 0, bf->fields.count); + auto bit_sizes = array_make (permanent_allocator(), 0, bf->fields.count); + auto tags = array_make (permanent_allocator(), 0, bf->fields.count); + + u64 maximum_bit_size = 8 * type_size_of(backing_type); + u64 total_bit_size = 0; + + for_array(i, bf->fields) { + i32 field_src_index = cast(i32)i; + Ast *field = bf->fields[i]; + if (field->kind != Ast_BitFieldField) { + error(field, "Invalid AST for a bit_field"); + continue; + } + ast_node(f, BitFieldField, field); + if (f->name == nullptr || f->name->kind != Ast_Ident) { + error(field, "A bit_field's field name must be an identifier"); + continue; + } + CommentGroup *docs = f->docs; + CommentGroup *comment = f->comment; + + String name = f->name->Ident.token.string; + + if (f->type == nullptr) { + error(field, "A bit_field's field must have a type"); + continue; + } + + Type *type = check_type(ctx, f->type); + if (type_size_of(type) > 8) { + error(f->type, "The type of a bit_field's field must be <= 8 bytes, got %lld", cast(long long)type_size_of(type)); + } + + if (is_type_untyped(type)) { + gbString s = type_to_string(type); + error(f->type, "The type of a bit_field's field must be a typed integer, enum, or boolean, got %s", s); + gb_string_free(s); + } else if (!(is_type_integer(type) || is_type_enum(type) || is_type_boolean(type))) { + gbString s = type_to_string(type); + error(f->type, "The type of a bit_field's field must be an integer, enum, or boolean, got %s", s); + gb_string_free(s); + } + + if (f->bit_size == nullptr) { + error(field, "A bit_field's field must have a specified bit size"); + continue; + } + + + Operand o = {}; + check_expr(ctx, &o, f->bit_size); + if (o.mode != Addressing_Constant) { + error(f->bit_size, "A bit_field's specified bit size must be a constant"); + o.mode = Addressing_Invalid; + } + if (o.value.kind == ExactValue_Float) { + o.value = exact_value_to_integer(o.value); + } + if (f->bit_size->kind == Ast_BinaryExpr && f->bit_size->BinaryExpr.op.kind == Token_Or) { + gbString s = expr_to_string(f->bit_size); + error(f->bit_size, "Wrap the expression in parentheses, e.g. (%s)", s); + gb_string_free(s); + } + + ExactValue bit_size = o.value; + + if (bit_size.kind != ExactValue_Integer) { + gbString s = expr_to_string(f->bit_size); + error(f->bit_size, "Expected an integer constant value for the specified bit size, got %s", s); + gb_string_free(s); + } + + if (scope_lookup_current(ctx->scope, name) != nullptr) { + error(f->name, "'%.*s' is already declared in this bit_field", LIT(name)); + } else { + i64 bit_size_i64 = exact_value_to_i64(bit_size); + u8 bit_size_u8 = 0; + if (bit_size_i64 <= 0) { + error(f->bit_size, "A bit_field's specified bit size cannot be <= 0, got %lld", cast(long long)bit_size_i64); + bit_size_i64 = 1; + } + if (bit_size_i64 > 64) { + error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64); + bit_size_i64 = 64; + } + i64 sz = 8*type_size_of(type); + if (bit_size_i64 > sz) { + error(f->bit_size, "A bit_field's specified bit size cannot exceed its type, got %lld, expect <=%lld", cast(long long)bit_size_i64, cast(long long)sz); + bit_size_i64 = sz; + } + + bit_size_u8 = cast(u8)bit_size_i64; + + Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index); + e->Variable.docs = docs; + e->Variable.comment = comment; + e->Variable.bit_field_bit_size = bit_size_u8; + e->flags |= EntityFlag_BitFieldField; + + add_entity(ctx, ctx->scope, nullptr, e); + array_add(&fields, e); + array_add(&bit_sizes, bit_size_u8); + + String tag = f->tag.string; + if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) { + error(f->tag, "Invalid string literal"); + tag = {}; + } + array_add(&tags, tag); + + add_entity_use(ctx, field, e); + } + } + + GB_ASSERT(fields.count <= bf->fields.count); + + auto bit_offsets = slice_make(permanent_allocator(), fields.count); + i64 curr_offset = 0; + for_array(i, bit_sizes) { + bit_offsets[i] = curr_offset; + curr_offset += cast(i64)bit_sizes[i]; + } + + if (total_bit_size > maximum_bit_size) { + gbString s = type_to_string(backing_type); + error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu", + cast(unsigned long long)total_bit_size, + s, + cast(unsigned long long)maximum_bit_size); + gb_string_free(s); + } + + if (bit_sizes.count > 0 && is_type_integer(backing_type)) { + bool all_booleans = is_type_boolean(fields[0]->type); + bool all_ones = bit_sizes[0] == 1; + if (all_ones && all_booleans) { + for_array(i, bit_sizes) { + all_ones = bit_sizes[i] == 1; + if (!all_ones) { + break; + } + all_booleans = is_type_boolean(fields[i]->type); + if (!all_booleans) { + break; + } + } + if (all_ones && all_booleans) { + if (build_context.vet_flags & VetFlag_Style) { + char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)"; + error(node, msg); + } else { + char const *msg = "This 'bit_field' might be better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer"; + warning(node, msg); + } + } + } + } + + + bit_field_type->BitField.fields = slice_from_array(fields); + bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); + bit_field_type->BitField.bit_offsets = bit_offsets; + bit_field_type->BitField.tags = tags.data; +} + gb_internal bool is_type_valid_bit_set_range(Type *t) { if (is_type_integer(t)) { return true; @@ -3051,6 +3249,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T return true; case_end; + case_ast_node(bf, BitFieldType, e); + bool ips = ctx->in_polymorphic_specialization; + defer (ctx->in_polymorphic_specialization = ips); + ctx->in_polymorphic_specialization = false; + + *type = alloc_type_bit_field(); + set_base_type(named_type, *type); + check_open_scope(ctx, e); + check_bit_field_type(ctx, *type, named_type, e); + check_close_scope(ctx); + (*type)->BitField.node = e; + return true; + case_end; + case_ast_node(pt, ProcType, e); bool ips = ctx->in_polymorphic_specialization; diff --git a/src/checker.cpp b/src/checker.cpp index 569a3c76f..5827fc695 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -313,6 +313,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) { case Ast_StructType: node->StructType.scope = scope; break; case Ast_UnionType: node->UnionType.scope = scope; break; case Ast_EnumType: node->EnumType.scope = scope; break; + case Ast_BitFieldType: node->BitFieldType.scope = scope; break; default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); } } @@ -334,6 +335,7 @@ gb_internal Scope *scope_of_node(Ast *node) { case Ast_StructType: return node->StructType.scope; case Ast_UnionType: return node->UnionType.scope; case Ast_EnumType: return node->EnumType.scope; + case Ast_BitFieldType: return node->BitFieldType.scope; } GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); return nullptr; @@ -355,6 +357,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) { case Ast_EnumType: case Ast_UnionType: case Ast_BitSetType: + case Ast_BitFieldType: scope->flags |= ScopeFlag_Type; break; } @@ -2060,6 +2063,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_type_info_type_internal(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_type_info_type_internal(c, f->type); + } + break; case Type_Generic: break; @@ -2309,6 +2318,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_min_dep_type_info(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_min_dep_type_info(c, f->type); + } + break; + default: GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind])); break; @@ -2907,6 +2923,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer")); t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix")); t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer")); + t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -2936,6 +2953,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer); t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix); t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer); + t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); } gb_internal void init_mem_allocator(Checker *c) { diff --git a/src/checker.hpp b/src/checker.hpp index 9aee82257..066d6bb4a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -475,6 +475,7 @@ struct CheckerContext { bool hide_polymorphic_errors; bool in_polymorphic_specialization; bool allow_arrow_right_selector_expr; + u8 bit_field_bit_size; Scope * polymorphic_scope; Ast *assignment_lhs_hint; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 42ffa6938..c15ec7137 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -282,6 +282,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_field_index_of, + BuiltinProc_type_bit_set_backing_type, + BuiltinProc_type_equal_proc, BuiltinProc_type_hasher_proc, BuiltinProc_type_map_info, @@ -586,6 +588,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_bit_set_backing_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/docs_format.cpp b/src/docs_format.cpp index d0bca214b..779190e62 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -14,8 +14,8 @@ struct OdinDocVersionType { }; #define OdinDocVersionType_Major 0 -#define OdinDocVersionType_Minor 2 -#define OdinDocVersionType_Patch 4 +#define OdinDocVersionType_Minor 3 +#define OdinDocVersionType_Patch 0 struct OdinDocHeaderBase { u8 magic[8]; @@ -84,6 +84,7 @@ enum OdinDocTypeKind : u32 { OdinDocType_MultiPointer = 22, OdinDocType_Matrix = 23, OdinDocType_SoaPointer = 24, + OdinDocType_BitField = 25, }; enum OdinDocTypeFlag_Basic : u32 { @@ -170,6 +171,8 @@ enum OdinDocEntityFlag : u64 { OdinDocEntityFlag_Param_NoAlias = 1ull<<7, OdinDocEntityFlag_Param_AnyInt = 1ull<<8, + OdinDocEntityFlag_BitField_Field = 1ull<<19, + OdinDocEntityFlag_Type_Alias = 1ull<<20, OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30, @@ -192,7 +195,7 @@ struct OdinDocEntity { u32 reserved_for_init; OdinDocString comment; // line comment OdinDocString docs; // preceding comment - i32 field_group_index; + i32 field_group_index; // For `bit_field`s this is the "bit_size" OdinDocEntityIndex foreign_library; OdinDocString link_name; OdinDocArray attributes; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 6816ae8eb..1bc244918 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -615,6 +615,20 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { doc_type.types = odin_write_slice(w, types, gb_count_of(types)); } break; + case Type_BitField: + doc_type.kind = OdinDocType_BitField; + { + auto fields = array_make(heap_allocator(), type->BitField.fields.count); + defer (array_free(&fields)); + + for_array(i, type->BitField.fields) { + fields[i] = odin_doc_add_entity(w, type->BitField.fields[i]); + } + doc_type.entities = odin_write_slice(w, fields.data, fields.count); + doc_type.types = odin_doc_type_as_slice(w, type->BitField.backing_type); + } + break; + case Type_Struct: doc_type.kind = OdinDocType_Struct; if (type->Struct.soa_kind != StructSoa_None) { @@ -863,6 +877,10 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) } break; case Entity_Variable: + if (e->flags & EntityFlag_BitFieldField) { + flags |= OdinDocEntityFlag_BitField_Field; + } + if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; } if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; } if (e->Variable.thread_local_model != "") { @@ -873,7 +891,12 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) if (init_expr == nullptr) { init_expr = e->Variable.init_expr; } - field_group_index = e->Variable.field_group_index; + + if (e->flags & EntityFlag_BitFieldField) { + field_group_index = -cast(i32)e->Variable.bit_field_bit_size; + } else { + field_group_index = e->Variable.field_group_index; + } break; case Entity_Constant: field_group_index = e->Constant.field_group_index; diff --git a/src/entity.cpp b/src/entity.cpp index e6c46d37e..916c2b2bd 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -43,6 +43,7 @@ enum EntityFlag : u64 { EntityFlag_NoAlias = 1ull<<9, EntityFlag_TypeField = 1ull<<10, EntityFlag_Value = 1ull<<11, + EntityFlag_BitFieldField = 1ull<<12, @@ -212,6 +213,7 @@ struct Entity { Ast *init_expr; // only used for some variables within procedure bodies i32 field_index; i32 field_group_index; + u8 bit_field_bit_size; ParameterValue param_value; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index fa76ac22f..efba19f23 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1053,41 +1053,6 @@ struct lbGlobalVariable { bool is_initialized; }; -gb_internal lbProcedure *lb_create_startup_type_info(lbModule *m) { - if (build_context.no_rtti) { - return nullptr; - } - Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); - - lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type); - p->is_startup = true; - LLVMSetLinkage(p->value, LLVMInternalLinkage); - - lb_add_attribute_to_proc(m, p->value, "nounwind"); - // lb_add_attribute_to_proc(p->module, p->value, "mustprogress"); - // lb_add_attribute_to_proc(p->module, p->value, "nofree"); - // lb_add_attribute_to_proc(p->module, p->value, "norecurse"); - // lb_add_attribute_to_proc(p->module, p->value, "nosync"); - // lb_add_attribute_to_proc(p->module, p->value, "willreturn"); - if (!LB_USE_GIANT_PACKED_STRUCT) { - lb_add_attribute_to_proc(m, p->value, "optnone"); - lb_add_attribute_to_proc(m, p->value, "noinline"); - } - - lb_begin_procedure_body(p); - - lb_setup_type_info_data(p); - - lb_end_procedure_body(p); - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - return p; -} gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { if (build_context.metrics.os != TargetOs_darwin) { @@ -1129,7 +1094,7 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) { lb_end_procedure_body(p); } -gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime +gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin); lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); @@ -1139,9 +1104,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc lb_begin_procedure_body(p); - if (startup_type_info) { - LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, startup_type_info->type), startup_type_info->value, nullptr, 0, ""); - } + lb_setup_type_info_data(main_module); if (objc_names) { LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, ""); @@ -1201,7 +1164,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc lbValue data = lb_emit_struct_ep(p, var.var, 0); lbValue ti = lb_emit_struct_ep(p, var.var, 1); lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); - lb_emit_store(p, ti, lb_type_info(main_module, var_type)); + lb_emit_store(p, ti, lb_type_info(p, var_type)); } else { LLVMTypeRef vt = llvm_addr_type(p->module, var.var); lbValue src0 = lb_emit_conv(p, var.init, t); @@ -1426,7 +1389,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) { } if (m == &m->gen->default_module) { - lb_llvm_function_pass_per_function_internal(m, m->gen->startup_type_info); lb_llvm_function_pass_per_function_internal(m, m->gen->startup_runtime); lb_llvm_function_pass_per_function_internal(m, m->gen->cleanup_runtime); lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names); @@ -2691,17 +2653,19 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { { // Add type info data isize max_type_info_count = info->minimum_dependency_type_info_set.count+1; - Type *t = alloc_type_array(t_type_info, max_type_info_count); + Type *t = alloc_type_array(t_type_info_ptr, max_type_info_count); // IMPORTANT NOTE(bill): As LLVM does not have a union type, an array of unions cannot be initialized // at compile time without cheating in some way. This means to emulate an array of unions is to use // a giant packed struct of "corrected" data types. - LLVMTypeRef internal_llvm_type = lb_setup_type_info_data_internal_type(m, max_type_info_count); + LLVMTypeRef internal_llvm_type = lb_type(m, t); LLVMValueRef g = LLVMAddGlobal(m->mod, internal_llvm_type, LB_TYPE_INFO_DATA_NAME); LLVMSetInitializer(g, LLVMConstNull(internal_llvm_type)); LLVMSetLinkage(g, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage); + LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr); + LLVMSetGlobalConstant(g, /*true*/false); lbValue value = {}; value.value = g; @@ -2710,15 +2674,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lb_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(LB_TYPE_INFO_DATA_NAME), t, EntityState_Resolved); lb_add_entity(m, lb_global_type_info_data_entity, value); - if (LB_USE_GIANT_PACKED_STRUCT) { - LLVMSetLinkage(g, LLVMPrivateLinkage); - LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr); - LLVMSetGlobalConstant(g, /*true*/false); - } } { // Type info member buffer // NOTE(bill): Removes need for heap allocation by making it global memory isize count = 0; + isize offsets_extra = 0; for (Type *t : m->info->type_info_types) { isize index = lb_type_info_index(m->info, t, false); @@ -2736,6 +2696,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { case Type_Tuple: count += t->Tuple.variables.count; break; + case Type_BitField: + count += t->BitField.fields.count; + // Twice is needed for the bit_offsets + offsets_extra += t->BitField.fields.count; + break; } } @@ -2744,15 +2709,13 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); - if (LB_USE_GIANT_PACKED_STRUCT) { - lb_make_global_private_const(g); - } + lb_make_global_private_const(g); return lb_addr({g, alloc_type_pointer(t)}); }; lb_global_type_info_member_types = global_type_info_make(m, LB_TYPE_INFO_TYPES_NAME, t_type_info_ptr, count); lb_global_type_info_member_names = global_type_info_make(m, LB_TYPE_INFO_NAMES_NAME, t_string, count); - lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count); + lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count+offsets_extra); lb_global_type_info_member_usings = global_type_info_make(m, LB_TYPE_INFO_USINGS_NAME, t_bool, count); lb_global_type_info_member_tags = global_type_info_make(m, LB_TYPE_INFO_TAGS_NAME, t_string, count); } @@ -2915,12 +2878,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - TIME_SECTION("LLVM Runtime Type Information Creation"); - gen->startup_type_info = lb_create_startup_type_info(default_module); + TIME_SECTION("LLVM Runtime Objective-C Names Creation"); gen->objc_names = lb_create_objc_names(default_module); TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))"); - gen->startup_runtime = lb_create_startup_runtime(default_module, gen->startup_type_info, gen->objc_names, global_variables); + gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables); TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)"); gen->cleanup_runtime = lb_create_cleanup_runtime(default_module); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 026454c81..741557efd 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -84,6 +84,8 @@ enum lbAddrKind { lbAddr_Swizzle, lbAddr_SwizzleLarge, + + lbAddr_BitField, }; struct lbAddr { @@ -118,6 +120,12 @@ struct lbAddr { Type *type; Slice indices; } swizzle_large; + struct { + Type *type; + i64 index; + i64 bit_offset; + i64 bit_size; + } bitfield; }; }; @@ -217,7 +225,6 @@ struct lbGenerator : LinkerData { std::atomic global_array_index; std::atomic global_generated_index; - lbProcedure *startup_type_info; lbProcedure *startup_runtime; lbProcedure *cleanup_runtime; lbProcedure *objc_names; @@ -478,7 +485,7 @@ gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValu gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len); -gb_internal lbValue lb_type_info(lbModule *m, Type *type); +gb_internal lbValue lb_type_info(lbProcedure *p, Type *type); gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str); gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr); @@ -579,7 +586,6 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #define LB_CLEANUP_RUNTIME_PROC_NAME "__$cleanup_runtime" -#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info" #define LB_TYPE_INFO_DATA_NAME "__$type_info_data" #define LB_TYPE_INFO_TYPES_NAME "__$type_info_types_data" #define LB_TYPE_INFO_NAMES_NAME "__$type_info_names_data" diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index f45cf0cbc..7d3692a53 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -461,6 +461,42 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { lb_debug_type(m, type->Matrix.elem), subscripts, gb_count_of(subscripts)); } + + case Type_BitField: { + LLVMMetadataRef parent_scope = nullptr; + LLVMMetadataRef scope = nullptr; + LLVMMetadataRef file = nullptr; + unsigned line = 0; + u64 size_in_bits = 8*cast(u64)type_size_of(type); + u32 align_in_bits = 8*cast(u32)type_align_of(type); + LLVMDIFlags flags = LLVMDIFlagZero; + + unsigned element_count = cast(unsigned)type->BitField.fields.count; + LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); + + u64 offset_in_bits = 0; + for (unsigned i = 0; i < element_count; i++) { + Entity *f = type->BitField.fields[i]; + u8 bit_size = type->BitField.bit_sizes[i]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + unsigned field_line = 0; + LLVMDIFlags field_flags = LLVMDIFlagZero; + elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line, + bit_size, offset_in_bits, offset_in_bits, + field_flags, lb_debug_type(m, f->type) + ); + + offset_in_bits += bit_size; + } + + + return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line, + size_in_bits, align_in_bits, flags, + nullptr, elements, element_count, 0, nullptr, + "", 0 + ); + } } GB_PANIC("Invalid type %s", type_to_string(type)); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0c06c8c1b..5bc961af2 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1946,6 +1946,24 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } } + // bit_field <-> backing type + if (is_type_bit_field(src)) { + if (are_types_identical(src->BitField.backing_type, dst)) { + lbValue res = {}; + res.type = t; + res.value = value.value; + return res; + } + } + if (is_type_bit_field(dst)) { + if (are_types_identical(src, dst->BitField.backing_type)) { + lbValue res = {}; + res.type = t; + res.value = value.value; + return res; + } + } + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { @@ -4217,6 +4235,38 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { switch (bt->kind) { default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; + case Type_BitField: + for (Ast *elem : cl->elems) { + ast_node(fv, FieldValue, elem); + String name = fv->field->Ident.token.string; + Selection sel = lookup_field(bt, name, false); + GB_ASSERT(sel.is_bit_field); + GB_ASSERT(!sel.indirect); + GB_ASSERT(sel.index.count == 1); + GB_ASSERT(sel.entity != nullptr); + + i64 index = sel.index[0]; + i64 bit_offset = 0; + i64 bit_size = -1; + for_array(i, bt->BitField.fields) { + Entity *f = bt->BitField.fields[i]; + if (f == sel.entity) { + bit_offset = bt->BitField.bit_offsets[i]; + bit_size = bt->BitField.bit_sizes[i]; + break; + } + } + GB_ASSERT(bit_size > 0); + + Type *field_type = sel.entity->type; + lbValue field_expr = lb_build_expr(p, fv->value); + field_expr = lb_emit_conv(p, field_expr, field_type); + + lbAddr field_addr = lb_addr_bit_field(v.addr, field_type, index, bit_offset, bit_size); + lb_addr_store(p, field_addr, field_expr); + } + return v; + case Type_Struct: { // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment. // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR @@ -4634,6 +4684,30 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { return lb_addr(lb_find_value_from_entity(p->module, e)); } + if (sel.is_bit_field) { + lbAddr addr = lb_build_addr(p, se->expr); + + Selection sub_sel = sel; + sub_sel.index.count -= 1; + + lbValue ptr = lb_addr_get_ptr(p, addr); + if (sub_sel.index.count > 0) { + ptr = lb_emit_deep_field_gep(p, ptr, sub_sel); + } + + Type *bf_type = type_deref(ptr.type); + bf_type = base_type(type_deref(bf_type)); + GB_ASSERT(bf_type->kind == Type_BitField); + + i32 index = sel.index[sel.index.count-1]; + + Entity *f = bf_type->BitField.fields[index]; + u8 bit_size = bf_type->BitField.bit_sizes[index]; + i64 bit_offset = bf_type->BitField.bit_offsets[index]; + + return lb_addr_bit_field(ptr, f->type, index, bit_offset, bit_size); + } + { lbAddr addr = lb_build_addr(p, se->expr); if (addr.kind == lbAddr_Map) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f0f5327c6..4ff8482a7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -451,6 +451,20 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice(temporary_allocator(), 4); + args[0] = dst; + args[1] = lb_address_from_load_or_generate_local(p, value); + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__write_bits", args); + return; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); GB_ASSERT(rel_ptr->kind == Type_RelativePointer || rel_ptr->kind == Type_RelativeMultiPointer); @@ -1074,8 +1098,31 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { GB_ASSERT(addr.addr.value != nullptr); + if (addr.kind == lbAddr_BitField) { + lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true); + lbValue src = addr.addr; - if (addr.kind == lbAddr_RelativePointer) { + auto args = array_make(temporary_allocator(), 4); + args[0] = dst.addr; + args[1] = src; + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__read_bits", args); + + lbValue r = lb_addr_load(p, dst); + + if (!is_type_unsigned(core_type(addr.bitfield.type))) { + // Sign extension + // m := 1<<(bit_size-1) + // r = (r XOR m) - m + Type *t = addr.bitfield.type; + lbValue m = lb_const_int(p->module, t, 1ull<<(addr.bitfield.bit_size-1)); + r = lb_emit_arith(p, Token_Xor, r, m, t); + r = lb_emit_arith(p, Token_Sub, r, m, t); + } + + return r; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); Type *base_integer = nullptr; Type *pointer_type = nullptr; @@ -2216,7 +2263,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } return LLVMStructTypeInContext(ctx, fields, field_count, false); } - + + case Type_BitField: + return lb_type_internal(m, type->BitField.backing_type); } GB_PANIC("Invalid type %s", type_to_string(type)); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 9419f9a3c..2c94222cf 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1755,7 +1755,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu TypeAndValue tav = type_and_value_of_expr(arg); if (tav.mode == Addressing_Type) { Type *t = default_type(type_of_expr(arg)); - return lb_type_info(p->module, t); + return lb_type_info(p, t); } GB_ASSERT(is_type_typeid(tav.type)); @@ -3361,9 +3361,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { for (Ast *var_arg : variadic) { lbValue arg = lb_build_expr(p, var_arg); if (is_type_any(elem_type)) { - array_add(&args, lb_emit_conv(p, arg, default_type(arg.type))); + array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(default_type(arg.type)))); } else { - array_add(&args, lb_emit_conv(p, arg, elem_type)); + array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(elem_type))); } } break; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 002fef881..0de9c0bf9 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -748,7 +748,7 @@ gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_ i64 enum_count = t->Enum.fields.count; lbValue max_count = lb_const_int(m, t_int, enum_count); - lbValue ti = lb_type_info(m, t); + lbValue ti = lb_type_info(p, t); lbValue variant = lb_emit_struct_ep(p, ti, 4); lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr); lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2)); @@ -1843,7 +1843,11 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) { lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - LLVMBuildRetVoid(p->builder); + // Check for terminator in the defer stmts + LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + if (!lb_is_instr_terminating(instr)) { + LLVMBuildRetVoid(p->builder); + } } else { LLVMValueRef ret_val = res.value; LLVMTypeRef ret_type = p->abi_function_type->ret.type; @@ -1868,7 +1872,12 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) { } lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - LLVMBuildRet(p->builder, ret_val); + + // Check for terminator in the defer stmts + LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + if (!lb_is_instr_terminating(instr)) { + LLVMBuildRet(p->builder, ret_val); + } } } gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return_results) { @@ -1887,8 +1896,12 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return // No return values lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - - LLVMBuildRetVoid(p->builder); + + // Check for terminator in the defer stmts + LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + if (!lb_is_instr_terminating(instr)) { + LLVMBuildRetVoid(p->builder); + } return; } else if (return_count == 1) { Entity *e = tuple->variables[0]; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e291e40a5..aec1fb201 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -62,6 +62,7 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) { case Type_RelativePointer: kind = Typeid_Relative_Pointer; break; case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break; case Type_SoaPointer: kind = Typeid_SoaPointer; break; + case Type_BitField: kind = Typeid_Bit_Field; break; } return kind; @@ -110,16 +111,19 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) { return res; } -gb_internal lbValue lb_type_info(lbModule *m, Type *type) { +gb_internal lbValue lb_type_info(lbProcedure *p, Type *type) { GB_ASSERT(!build_context.no_rtti); type = default_type(type); + lbModule *m = p->module; isize index = lb_type_info_index(m->info, type); GB_ASSERT(index >= 0); - lbValue data = lb_global_type_info_data_ptr(m); - return lb_emit_array_epi(m, data, index); + lbValue global = lb_global_type_info_data_ptr(m); + + lbValue ptr = lb_emit_array_epi(p, global, index); + return lb_emit_load(p, ptr); } gb_internal LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) { @@ -180,16 +184,7 @@ gb_internal lbValue lb_type_info_member_tags_offset(lbModule *m, isize count, i6 return offset; } -// enum {LB_USE_GIANT_PACKED_STRUCT = LB_USE_NEW_PASS_SYSTEM}; -enum {LB_USE_GIANT_PACKED_STRUCT = 0}; - -gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize max_type_info_count) { - if (!LB_USE_GIANT_PACKED_STRUCT) { - Type *t = alloc_type_array(t_type_info, max_type_info_count); - return lb_type(m, t); - } - CheckerInfo *info = m->gen->info; - +gb_internal LLVMTypeRef *lb_setup_modified_types_for_type_info(lbModule *m, isize max_type_info_count) { LLVMTypeRef *element_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, max_type_info_count); defer (gb_free(heap_allocator(), element_types)); @@ -219,8 +214,8 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize variant_index = 4; } - LLVMTypeRef modified_types[32] = {}; - GB_ASSERT(gb_count_of(modified_types) >= ut->Union.variants.count); + LLVMTypeRef *modified_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, Typeid__COUNT); + GB_ASSERT(Typeid__COUNT == ut->Union.variants.count); modified_types[0] = element_types[0]; i64 tag_offset = ut->Union.variant_block_size; @@ -242,40 +237,24 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize modified_types[i] = modified_type; } - for_array(type_info_type_index, info->type_info_types) { - Type *t = info->type_info_types[type_info_type_index]; - if (t == nullptr || t == t_invalid) { - continue; - } - isize entry_index = lb_type_info_index(info, t, false); - if (entry_index <= 0) { - continue; - } - - if (entries_handled[entry_index]) { - continue; - } - entries_handled[entry_index] = true; - - - if (t->kind == Type_Named) { - element_types[entry_index] = modified_types[0]; - } else { - i64 variant_index = lb_typeid_kind(m, t); - element_types[entry_index] = modified_types[variant_index]; - } - - GB_ASSERT(element_types[entry_index] != nullptr); + for (isize i = 0; i < Typeid__COUNT; i++) { + GB_ASSERT_MSG(modified_types[i] != nullptr, "%td", ut->Union.variants.count); } - for_array(i, entries_handled) { - GB_ASSERT(entries_handled[i]); - } - - return LLVMStructType(element_types, cast(unsigned)max_type_info_count, true); + return modified_types; } -gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 global_type_info_data_entity_count, lbProcedure *p) { // NOTE(bill): Setup type_info data +gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_type_info_data_entity_count) { // NOTE(bill): Setup type_info data + auto const &ADD_GLOBAL_TYPE_INFO_ENTRY = [](lbModule *m, LLVMTypeRef type, isize index) -> LLVMValueRef { + char name[64] = {}; + gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index); + LLVMValueRef g = LLVMAddGlobal(m->mod, type, name); + LLVMSetLinkage(g, LLVMInternalLinkage); + LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr); + LLVMSetGlobalConstant(g, true); + return g; + }; + CheckerInfo *info = m->info; // Useful types @@ -292,19 +271,49 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl defer (gb_free(heap_allocator(), entries_handled.data)); entries_handled[0] = true; - LLVMValueRef giant_struct = lb_global_type_info_data_ptr(m).value; - LLVMTypeRef giant_struct_type = LLVMGlobalGetValueType(giant_struct); - GB_ASSERT(LLVMGetTypeKind(giant_struct_type) == LLVMStructTypeKind); - LLVMValueRef *giant_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, global_type_info_data_entity_count); defer (gb_free(heap_allocator(), giant_const_values)); - giant_const_values[0] = LLVMConstNull(LLVMStructGetTypeAtIndex(giant_struct_type, 0)); + // zero value is just zero data + giant_const_values[0] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, lb_type(m, t_type_info), 0); + LLVMSetInitializer(giant_const_values[0], LLVMConstNull(lb_type(m, t_type_info))); + + + LLVMTypeRef *modified_types = lb_setup_modified_types_for_type_info(m, global_type_info_data_entity_count); + defer (gb_free(heap_allocator(), modified_types)); + for_array(type_info_type_index, info->type_info_types) { + Type *t = info->type_info_types[type_info_type_index]; + if (t == nullptr || t == t_invalid) { + continue; + } + + isize entry_index = lb_type_info_index(info, t, false); + if (entry_index <= 0) { + continue; + } + + if (entries_handled[entry_index]) { + continue; + } + entries_handled[entry_index] = true; + + + LLVMTypeRef stype = nullptr; + if (t->kind == Type_Named) { + stype = modified_types[0]; + } else { + stype = modified_types[lb_typeid_kind(m, t)]; + } + giant_const_values[entry_index] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, stype, entry_index); + } + for (isize i = 1; i < global_type_info_data_entity_count; i++) { + entries_handled[i] = false; + } + LLVMValueRef *small_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, 6); defer (gb_free(heap_allocator(), small_const_values)); - #define type_info_allocate_values(name) \ LLVMValueRef *name##_values = gb_alloc_array(heap_allocator(), LLVMValueRef, type_deref(name.addr.type)->Array.count); \ defer (gb_free(heap_allocator(), name##_values)); \ @@ -316,7 +325,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl (name##_values)[i] = LLVMConstNull(elem); \ } \ } \ - LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \ + LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \ }) type_info_allocate_values(lb_global_type_info_member_types); @@ -326,27 +335,13 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl type_info_allocate_values(lb_global_type_info_member_tags); - i64 const type_info_struct_size = type_size_of(t_type_info); - LLVMTypeRef llvm_u8 = lb_type(m, t_u8); - LLVMTypeRef llvm_int = lb_type(m, t_int); - // LLVMTypeRef llvm_type_info_ptr = lb_type(m, t_type_info_ptr); - auto const get_type_info_ptr = [&](lbModule *m, Type *type) -> LLVMValueRef { type = default_type(type); isize index = lb_type_info_index(m->info, type); GB_ASSERT(index >= 0); - u64 offset = cast(u64)(index * type_info_struct_size); - - LLVMValueRef indices[1] = { - LLVMConstInt(llvm_int, offset, false) - }; - - // LLVMValueRef ptr = LLVMConstInBoundsGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices)); - LLVMValueRef ptr = LLVMConstGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices)); - return ptr; - // return LLVMConstPointerCast(ptr, llvm_type_info_ptr); + return giant_const_values[index]; }; for_array(type_info_type_index, info->type_info_types) { @@ -366,7 +361,12 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl entries_handled[entry_index] = true; - LLVMTypeRef stype = LLVMStructGetTypeAtIndex(giant_struct_type, cast(unsigned)entry_index); + LLVMTypeRef stype = nullptr; + if (t->kind == Type_Named) { + stype = modified_types[0]; + } else { + stype = modified_types[lb_typeid_kind(m, t)]; + } i64 size = type_size_of(t); i64 align = type_align_of(t); @@ -376,6 +376,10 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl lbValue type_info_flags = lb_const_int(m, t_type_info_flags, flags); + for (isize i = 0; i < 6; i++) { + small_const_values[i] = nullptr; + } + small_const_values[0] = LLVMConstInt(lb_type(m, t_int), size, true); small_const_values[1] = LLVMConstInt(lb_type(m, t_int), align, true); small_const_values[2] = type_info_flags.value; @@ -986,6 +990,69 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); } break; + + case Type_BitField: + { + tag_type = t_type_info_bit_field; + + LLVMValueRef vals[6] = {}; + vals[0] = get_type_info_ptr(m, t->BitField.backing_type); + isize count = t->BitField.fields.count; + if (count > 0) { + i64 names_offset = 0; + i64 types_offset = 0; + i64 bit_sizes_offset = 0; + i64 bit_offsets_offset = 0; + i64 tags_offset = 0; + lbValue memory_names = lb_type_info_member_names_offset (m, count, &names_offset); + lbValue memory_types = lb_type_info_member_types_offset (m, count, &types_offset); + lbValue memory_bit_sizes = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset); + lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset); + lbValue memory_tags = lb_type_info_member_tags_offset (m, count, &tags_offset); + + u64 bit_offset = 0; + for (isize source_index = 0; source_index < count; source_index++) { + Entity *f = t->BitField.fields[source_index]; + u64 bit_size = cast(u64)t->BitField.bit_sizes[source_index]; + + lbValue index = lb_const_int(m, t_int, source_index); + if (f->token.string.len > 0) { + lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value; + } + + lb_global_type_info_member_types_values[types_offset+source_index] = get_type_info_ptr(m, f->type); + + lb_global_type_info_member_offsets_values[bit_sizes_offset+source_index] = lb_const_int(m, t_uintptr, bit_size).value; + lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value; + + if (t->BitField.tags) { + String tag = t->BitField.tags[source_index]; + if (tag.len > 0) { + lb_global_type_info_member_tags_values[tags_offset+source_index] = lb_const_string(m, tag).value; + } + } + + bit_offset += bit_size; + } + + lbValue cv = lb_const_int(m, t_int, count); + vals[1] = llvm_const_slice(m, memory_names, cv); + vals[2] = llvm_const_slice(m, memory_types, cv); + vals[3] = llvm_const_slice(m, memory_bit_sizes, cv); + vals[4] = llvm_const_slice(m, memory_bit_offsets, cv); + vals[5] = llvm_const_slice(m, memory_tags, cv); + } + + + for (isize i = 0; i < gb_count_of(vals); i++) { + if (vals[i] == nullptr) { + vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag_type, i))); + } + } + + variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); + break; + } } @@ -994,6 +1061,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl if (tag_type != nullptr) { tag_index = union_variant_index(ut, tag_type); } + GB_ASSERT(tag_index <= Typeid__COUNT); LLVMValueRef full_variant_values[3] = {}; @@ -1024,788 +1092,43 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl small_const_values[variant_index] = full_variant_value; - giant_const_values[entry_index] = LLVMConstNamedStruct(stype, small_const_values, variant_index+1); + LLVMSetInitializer(giant_const_values[entry_index], LLVMConstNamedStruct(stype, small_const_values, variant_index+1)); + } + for (isize i = 0; i < global_type_info_data_entity_count; i++) { + giant_const_values[i] = LLVMConstPointerCast(giant_const_values[i], lb_type(m, t_type_info_ptr)); } - LLVMValueRef giant_const = LLVMConstNamedStruct(giant_struct_type, giant_const_values, cast(unsigned)global_type_info_data_entity_count); - LLVMSetInitializer(giant_struct, giant_const); + + LLVMValueRef giant_const = LLVMConstArray(lb_type(m, t_type_info_ptr), giant_const_values, cast(unsigned)global_type_info_data_entity_count); + LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value; + LLVMSetInitializer(giant_array, giant_const); } -gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data +gb_internal void lb_setup_type_info_data(lbModule *m) { // NOTE(bill): Setup type_info data if (build_context.no_rtti) { return; } - lbModule *m = p->module; - CheckerInfo *info = m->info; i64 global_type_info_data_entity_count = 0; - { - // NOTE(bill): Set the type_table slice with the global backing array - lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table")); - Type *type = base_type(lb_global_type_info_data_entity->type); - GB_ASSERT(type->kind == Type_Array); - global_type_info_data_entity_count = type->Array.count; - LLVMValueRef data = lb_global_type_info_data_ptr(m).value; - data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem))); - LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true); - Type *t = type_deref(global_type_table.type); - GB_ASSERT(is_type_slice(t)); - LLVMValueRef slice = llvm_const_slice_internal(m, data, len); + // NOTE(bill): Set the type_table slice with the global backing array + lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table")); + Type *type = base_type(lb_global_type_info_data_entity->type); + GB_ASSERT(type->kind == Type_Array); + global_type_info_data_entity_count = type->Array.count; - LLVMSetInitializer(global_type_table.value, slice); + if (true) { + lb_setup_type_info_data_giant_array(m, global_type_info_data_entity_count); } - if (LB_USE_GIANT_PACKED_STRUCT) { - lb_setup_type_info_data_giant_packed_struct(m, global_type_info_data_entity_count, p); - return; - } - - // Useful types - Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags")); - Type *t_type_info_flags = type_info_flags_entity->type; - - - auto entries_handled = slice_make(heap_allocator(), cast(isize)global_type_info_data_entity_count); - defer (gb_free(heap_allocator(), entries_handled.data)); - entries_handled[0] = true; - - for_array(type_info_type_index, info->type_info_types) { - Type *t = info->type_info_types[type_info_type_index]; - if (t == nullptr || t == t_invalid) { - continue; - } - - isize entry_index = lb_type_info_index(info, t, false); - if (entry_index <= 0) { - continue; - } - - if (entries_handled[entry_index]) { - continue; - } - entries_handled[entry_index] = true; - - lbValue global_data_ptr = lb_global_type_info_data_ptr(m); - lbValue tag = {}; - lbValue ti_ptr = lb_emit_array_epi(p, global_data_ptr, cast(i32)entry_index); - - i64 size = type_size_of(t); - i64 align = type_align_of(t); - u32 flags = type_info_flags_of_type(t); - lbValue id = lb_typeid(m, t); - GB_ASSERT_MSG(align != 0, "%lld %s", align, type_to_string(t)); - - lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, flags); - - lbValue size_ptr = lb_emit_struct_ep(p, ti_ptr, 0); - lbValue align_ptr = lb_emit_struct_ep(p, ti_ptr, 1); - lbValue flags_ptr = lb_emit_struct_ep(p, ti_ptr, 2); - lbValue id_ptr = lb_emit_struct_ep(p, ti_ptr, 3); - - lb_emit_store(p, size_ptr, lb_const_int(m, t_int, size)); - lb_emit_store(p, align_ptr, lb_const_int(m, t_int, align)); - lb_emit_store(p, flags_ptr, type_info_flags); - lb_emit_store(p, id_ptr, id); - - lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4); - - switch (t->kind) { - case Type_Named: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr); - - LLVMValueRef pkg_name = nullptr; - if (t->Named.type_name->pkg) { - pkg_name = lb_const_string(m, t->Named.type_name->pkg->name).value; - } else { - pkg_name = LLVMConstNull(lb_type(m, t_string)); - } - - String proc_name = {}; - if (t->Named.type_name->parent_proc_decl) { - DeclInfo *decl = t->Named.type_name->parent_proc_decl; - if (decl->entity && decl->entity->kind == Entity_Procedure) { - proc_name = decl->entity->token.string; - } - } - TokenPos pos = t->Named.type_name->token.pos; - - lbValue loc = lb_emit_source_code_location_const(p, proc_name, pos); - - LLVMValueRef vals[4] = { - lb_const_string(p->module, t->Named.type_name->token.string).value, - lb_type_info(m, t->Named.base).value, - pkg_name, - loc.value - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - - case Type_Basic: - switch (t->Basic.kind) { - case Basic_bool: - case Basic_b8: - case Basic_b16: - case Basic_b32: - case Basic_b64: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_boolean_ptr); - break; - - case Basic_i8: - case Basic_u8: - case Basic_i16: - case Basic_u16: - case Basic_i32: - case Basic_u32: - case Basic_i64: - case Basic_u64: - case Basic_i128: - case Basic_u128: - - case Basic_i16le: - case Basic_u16le: - case Basic_i32le: - case Basic_u32le: - case Basic_i64le: - case Basic_u64le: - case Basic_i128le: - case Basic_u128le: - case Basic_i16be: - case Basic_u16be: - case Basic_i32be: - case Basic_u32be: - case Basic_i64be: - case Basic_u64be: - case Basic_i128be: - case Basic_u128be: - - case Basic_int: - case Basic_uint: - case Basic_uintptr: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_integer_ptr); - - lbValue is_signed = lb_const_bool(m, t_bool, (t->Basic.flags & BasicFlag_Unsigned) == 0); - // NOTE(bill): This is matches the runtime layout - u8 endianness_value = 0; - if (t->Basic.flags & BasicFlag_EndianLittle) { - endianness_value = 1; - } else if (t->Basic.flags & BasicFlag_EndianBig) { - endianness_value = 2; - } - lbValue endianness = lb_const_int(m, t_u8, endianness_value); - - LLVMValueRef vals[2] = { - is_signed.value, - endianness.value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - - case Basic_rune: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_rune_ptr); - break; - - case Basic_f16: - case Basic_f32: - case Basic_f64: - case Basic_f16le: - case Basic_f32le: - case Basic_f64le: - case Basic_f16be: - case Basic_f32be: - case Basic_f64be: - { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr); - - // NOTE(bill): This is matches the runtime layout - u8 endianness_value = 0; - if (t->Basic.flags & BasicFlag_EndianLittle) { - endianness_value = 1; - } else if (t->Basic.flags & BasicFlag_EndianBig) { - endianness_value = 2; - } - lbValue endianness = lb_const_int(m, t_u8, endianness_value); - - LLVMValueRef vals[1] = { - endianness.value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - - case Basic_complex32: - case Basic_complex64: - case Basic_complex128: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_complex_ptr); - break; - - case Basic_quaternion64: - case Basic_quaternion128: - case Basic_quaternion256: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_quaternion_ptr); - break; - - case Basic_rawptr: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr); - break; - - case Basic_string: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr); - break; - - case Basic_cstring: - { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr); - LLVMValueRef vals[1] = { - lb_const_bool(m, t_bool, true).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - - case Basic_any: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_any_ptr); - break; - - case Basic_typeid: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_typeid_ptr); - break; - } - break; - - case Type_Pointer: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr); - lbValue gep = lb_type_info(m, t->Pointer.elem); - - LLVMValueRef vals[1] = { - gep.value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - case Type_MultiPointer: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_multi_pointer_ptr); - lbValue gep = lb_type_info(m, t->MultiPointer.elem); - - LLVMValueRef vals[1] = { - gep.value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - case Type_SoaPointer: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_soa_pointer_ptr); - lbValue gep = lb_type_info(m, t->SoaPointer.elem); - - LLVMValueRef vals[1] = { - gep.value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - case Type_Array: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_array_ptr); - i64 ez = type_size_of(t->Array.elem); - - LLVMValueRef vals[3] = { - lb_type_info(m, t->Array.elem).value, - lb_const_int(m, t_int, ez).value, - lb_const_int(m, t_int, t->Array.count).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - case Type_EnumeratedArray: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr); - - LLVMValueRef vals[7] = { - lb_type_info(m, t->EnumeratedArray.elem).value, - lb_type_info(m, t->EnumeratedArray.index).value, - lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value, - lb_const_int(m, t_int, t->EnumeratedArray.count).value, - - // Unions - LLVMConstNull(lb_type(m, t_type_info_enum_value)), - LLVMConstNull(lb_type(m, t_type_info_enum_value)), - - lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - - // NOTE(bill): Union assignment - lbValue min_value = lb_emit_struct_ep(p, tag, 4); - lbValue max_value = lb_emit_struct_ep(p, tag, 5); - - lbValue min_v = lb_const_value(m, t_i64, *t->EnumeratedArray.min_value); - lbValue max_v = lb_const_value(m, t_i64, *t->EnumeratedArray.max_value); - - lb_emit_store(p, min_value, min_v); - lb_emit_store(p, max_value, max_v); - break; - } - case Type_DynamicArray: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr); - - LLVMValueRef vals[2] = { - lb_type_info(m, t->DynamicArray.elem).value, - lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - case Type_Slice: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr); - - LLVMValueRef vals[2] = { - lb_type_info(m, t->Slice.elem).value, - lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - case Type_Proc: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_procedure_ptr); - - LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr)); - LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr)); - if (t->Proc.params != nullptr) { - params = lb_type_info(m, t->Proc.params).value; - } - if (t->Proc.results != nullptr) { - results = lb_type_info(m, t->Proc.results).value; - } - - LLVMValueRef vals[4] = { - params, - results, - lb_const_bool(m, t_bool, t->Proc.variadic).value, - lb_const_int(m, t_u8, t->Proc.calling_convention).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - case Type_Tuple: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_parameters_ptr); - - lbValue memory_types = lb_type_info_member_types_offset(m, t->Tuple.variables.count); - lbValue memory_names = lb_type_info_member_names_offset(m, t->Tuple.variables.count); - - - for_array(i, t->Tuple.variables) { - // NOTE(bill): offset is not used for tuples - Entity *f = t->Tuple.variables[i]; - - lbValue index = lb_const_int(m, t_int, i); - lbValue type_info = lb_emit_ptr_offset(p, memory_types, index); - - // TODO(bill): Make this constant if possible, 'lb_const_store' does not work - lb_emit_store(p, type_info, lb_type_info(m, f->type)); - if (f->token.string.len > 0) { - lbValue name = lb_emit_ptr_offset(p, memory_names, index); - lb_emit_store(p, name, lb_const_string(m, f->token.string)); - } - } - - lbValue count = lb_const_int(m, t_int, t->Tuple.variables.count); - - LLVMValueRef types_slice = llvm_const_slice(m, memory_types, count); - LLVMValueRef names_slice = llvm_const_slice(m, memory_names, count); - - LLVMValueRef vals[2] = { - types_slice, - names_slice, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - - break; - } - - case Type_Enum: - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enum_ptr); - - { - GB_ASSERT(t->Enum.base_type != nullptr); - // GB_ASSERT_MSG(type_size_of(t_type_info_enum_value) == 16, "%lld == 16", cast(long long)type_size_of(t_type_info_enum_value)); - - - LLVMValueRef vals[3] = {}; - vals[0] = lb_type_info(m, t->Enum.base_type).value; - if (t->Enum.fields.count > 0) { - auto fields = t->Enum.fields; - lbValue name_array = lb_generate_global_array(m, t_string, fields.count, - str_lit("$enum_names"), cast(i64)entry_index); - lbValue value_array = lb_generate_global_array(m, t_type_info_enum_value, fields.count, - str_lit("$enum_values"), cast(i64)entry_index); - - - LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); - LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count); - - GB_ASSERT(is_type_integer(t->Enum.base_type)); - - for_array(i, fields) { - name_values[i] = lb_const_string(m, fields[i]->token.string).value; - value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value; - } - - LLVMValueRef name_init = llvm_const_array(lb_type(m, t_string), name_values, cast(unsigned)fields.count); - LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count); - LLVMSetInitializer(name_array.value, name_init); - LLVMSetInitializer(value_array.value, value_init); - LLVMSetGlobalConstant(name_array.value, true); - LLVMSetGlobalConstant(value_array.value, true); - - lbValue v_count = lb_const_int(m, t_int, fields.count); - - vals[1] = llvm_const_slice(m, lb_array_elem(p, name_array), v_count); - vals[2] = llvm_const_slice(m, lb_array_elem(p, value_array), v_count); - } else { - vals[1] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[1]->type)); - vals[2] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[2]->type)); - } - - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - - case Type_Union: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); - - { - LLVMValueRef vals[7] = {}; - - isize variant_count = gb_max(0, t->Union.variants.count); - lbValue memory_types = lb_type_info_member_types_offset(m, variant_count); - - // NOTE(bill): Zeroth is nil so ignore it - for (isize variant_index = 0; variant_index < variant_count; variant_index++) { - Type *vt = t->Union.variants[variant_index]; - lbValue tip = lb_type_info(m, vt); - - lbValue index = lb_const_int(m, t_int, variant_index); - lbValue type_info = lb_emit_ptr_offset(p, memory_types, index); - lb_emit_store(p, type_info, lb_type_info(m, vt)); - } - - lbValue count = lb_const_int(m, t_int, variant_count); - vals[0] = llvm_const_slice(m, memory_types, count); - - i64 tag_size = union_tag_size(t); - if (tag_size > 0) { - i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size); - vals[1] = lb_const_int(m, t_uintptr, tag_offset).value; - vals[2] = lb_type_info(m, union_tag_type(t)).value; - } else { - vals[1] = lb_const_int(m, t_uintptr, 0).value; - vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr)); - } - - if (is_type_comparable(t) && !is_type_simple_compare(t)) { - vals[3] = lb_equal_proc_for_type(m, t).value; - } - - vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; - vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; - - for (isize i = 0; i < gb_count_of(vals); i++) { - if (vals[i] == nullptr) { - vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i))); - } - } - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - - break; - } - - case Type_Struct: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr); - - LLVMValueRef vals[13] = {}; - - - { - lbValue is_packed = lb_const_bool(m, t_bool, t->Struct.is_packed); - lbValue is_raw_union = lb_const_bool(m, t_bool, t->Struct.is_raw_union); - lbValue is_no_copy = lb_const_bool(m, t_bool, t->Struct.is_no_copy); - lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0); - vals[5] = is_packed.value; - vals[6] = is_raw_union.value; - vals[7] = is_no_copy.value; - vals[8] = is_custom_align.value; - if (is_type_comparable(t) && !is_type_simple_compare(t)) { - vals[9] = lb_equal_proc_for_type(m, t).value; - } - - - if (t->Struct.soa_kind != StructSoa_None) { - lbValue kind = lb_emit_struct_ep(p, tag, 10); - Type *kind_type = type_deref(kind.type); - - lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind)); - lbValue soa_type = lb_type_info(m, t->Struct.soa_elem); - lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count); - - vals[10] = soa_kind.value; - vals[11] = soa_type.value; - vals[12] = soa_len.value; - } - } - - isize count = t->Struct.fields.count; - if (count > 0) { - lbValue memory_types = lb_type_info_member_types_offset (m, count); - lbValue memory_names = lb_type_info_member_names_offset (m, count); - lbValue memory_offsets = lb_type_info_member_offsets_offset(m, count); - lbValue memory_usings = lb_type_info_member_usings_offset (m, count); - lbValue memory_tags = lb_type_info_member_tags_offset (m, count); - - type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet - for (isize source_index = 0; source_index < count; source_index++) { - Entity *f = t->Struct.fields[source_index]; - lbValue tip = lb_type_info(m, f->type); - i64 foffset = 0; - if (!t->Struct.is_raw_union) { - GB_ASSERT(t->Struct.offsets != nullptr); - GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count); - foffset = t->Struct.offsets[source_index]; - } - GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); - - lbValue index = lb_const_int(m, t_int, source_index); - lbValue type_info = lb_emit_ptr_offset(p, memory_types, index); - lbValue offset = lb_emit_ptr_offset(p, memory_offsets, index); - lbValue is_using = lb_emit_ptr_offset(p, memory_usings, index); - - lb_emit_store(p, type_info, lb_type_info(m, f->type)); - if (f->token.string.len > 0) { - lbValue name = lb_emit_ptr_offset(p, memory_names, index); - lb_emit_store(p, name, lb_const_string(m, f->token.string)); - } - lb_emit_store(p, offset, lb_const_int(m, t_uintptr, foffset)); - lb_emit_store(p, is_using, lb_const_bool(m, t_bool, (f->flags&EntityFlag_Using) != 0)); - - if (t->Struct.tags != nullptr) { - String tag_string = t->Struct.tags[source_index]; - if (tag_string.len > 0) { - lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index); - lb_emit_store(p, tag_ptr, lb_const_string(m, tag_string)); - } - } - - } - - lbValue cv = lb_const_int(m, t_int, count); - vals[0] = llvm_const_slice(m, memory_types, cv); - vals[1] = llvm_const_slice(m, memory_names, cv); - vals[2] = llvm_const_slice(m, memory_offsets, cv); - vals[3] = llvm_const_slice(m, memory_usings, cv); - vals[4] = llvm_const_slice(m, memory_tags, cv); - } - for (isize i = 0; i < gb_count_of(vals); i++) { - if (vals[i] == nullptr) { - vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i))); - } - } - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - - break; - } - - case Type_Map: { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr); - init_map_internal_types(t); - - LLVMValueRef vals[3] = { - lb_type_info(m, t->Map.key).value, - lb_type_info(m, t->Map.value).value, - lb_gen_map_info_ptr(p->module, t).value - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - break; - } - - case Type_BitSet: - { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_set_ptr); - - GB_ASSERT(is_type_typed(t->BitSet.elem)); - - - LLVMValueRef vals[4] = { - lb_type_info(m, t->BitSet.elem).value, - LLVMConstNull(lb_type(m, t_type_info_ptr)), - lb_const_int(m, t_i64, t->BitSet.lower).value, - lb_const_int(m, t_i64, t->BitSet.upper).value, - }; - if (t->BitSet.underlying != nullptr) { - vals[1] =lb_type_info(m, t->BitSet.underlying).value; - } - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - - case Type_SimdVector: - { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr); - - LLVMValueRef vals[3] = {}; - - vals[0] = lb_type_info(m, t->SimdVector.elem).value; - vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value; - vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - - case Type_RelativePointer: - { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_pointer_ptr); - LLVMValueRef vals[2] = { - lb_type_info(m, t->RelativePointer.pointer_type).value, - lb_type_info(m, t->RelativePointer.base_integer).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - - case Type_RelativeMultiPointer: - { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_multi_pointer_ptr); - LLVMValueRef vals[2] = { - lb_type_info(m, t->RelativeMultiPointer.pointer_type).value, - lb_type_info(m, t->RelativeMultiPointer.base_integer).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - - case Type_Matrix: - { - tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr); - i64 ez = type_size_of(t->Matrix.elem); - - LLVMValueRef vals[5] = { - lb_type_info(m, t->Matrix.elem).value, - lb_const_int(m, t_int, ez).value, - lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value, - lb_const_int(m, t_int, t->Matrix.row_count).value, - lb_const_int(m, t_int, t->Matrix.column_count).value, - }; - - lbValue res = {}; - res.type = type_deref(tag.type); - res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); - lb_emit_store(p, tag, res); - } - break; - } - - - if (tag.value != nullptr) { - Type *tag_type = type_deref(tag.type); - GB_ASSERT(is_type_named(tag_type)); - // lb_emit_store_union_variant(p, variant_ptr, lb_emit_load(p, tag), tag_type); - lb_emit_store_union_variant_tag(p, variant_ptr, tag_type); - } else { - if (t != t_llvm_bool) { - GB_PANIC("Unhandled Type_Info variant: %s", type_to_string(t)); - } - } - } - - for_array(i, entries_handled) { - if (!entries_handled[i]) { - GB_PANIC("UNHANDLED ENTRY %td (%td)", i, entries_handled.count); - } - } + LLVMValueRef data = lb_global_type_info_data_ptr(m).value; + data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem))); + LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true); + Type *t = type_deref(global_type_table.type); + GB_ASSERT(is_type_slice(t)); + LLVMValueRef slice = llvm_const_slice_internal(m, data, len); + + LLVMSetInitializer(global_type_table.value, slice); } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index bc5106601..5bd3cd8e2 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1332,7 +1332,7 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection if (index == 0) { type = t_rawptr; } else if (index == 1) { - type = t_type_info_ptr; + type = t_typeid; } e = lb_emit_struct_ep(p, e, index); break; diff --git a/src/parser.cpp b/src/parser.cpp index 78ac29dfd..14035d6d7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -350,6 +350,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->Field.names = clone_ast_array(n->Field.names, f); n->Field.type = clone_ast(n->Field.type, f); break; + case Ast_BitFieldField: + n->BitFieldField.name = clone_ast(n->BitFieldField.name, f); + n->BitFieldField.type = clone_ast(n->BitFieldField.type, f); + n->BitFieldField.bit_size = clone_ast(n->BitFieldField.bit_size, f); + break; case Ast_FieldList: n->FieldList.list = clone_ast_array(n->FieldList.list, f); break; @@ -406,6 +411,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->BitSetType.elem = clone_ast(n->BitSetType.elem, f); n->BitSetType.underlying = clone_ast(n->BitSetType.underlying, f); break; + case Ast_BitFieldType: + n->BitFieldType.backing_type = clone_ast(n->BitFieldType.backing_type, f); + n->BitFieldType.fields = clone_ast_array(n->BitFieldType.fields, f); + break; case Ast_MapType: n->MapType.count = clone_ast(n->MapType.count, f); n->MapType.key = clone_ast(n->MapType.key, f); @@ -1045,6 +1054,18 @@ gb_internal Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast return result; } +gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, Token tag, + CommentGroup *docs, CommentGroup *comment) { + Ast *result = alloc_ast_node(f, Ast_BitFieldField); + result->BitFieldField.name = name; + result->BitFieldField.type = type; + result->BitFieldField.bit_size = bit_size; + result->BitFieldField.tag = tag; + result->BitFieldField.docs = docs; + result->BitFieldField.comment = comment; + return result; +} + gb_internal Ast *ast_field_list(AstFile *f, Token token, Array const &list) { Ast *result = alloc_ast_node(f, Ast_FieldList); result->FieldList.token = token; @@ -1178,6 +1199,17 @@ gb_internal Ast *ast_bit_set_type(AstFile *f, Token token, Ast *elem, Ast *under return result; } +gb_internal Ast *ast_bit_field_type(AstFile *f, Token token, Ast *backing_type, Token open, Array const &fields, Token close) { + Ast *result = alloc_ast_node(f, Ast_BitFieldType); + result->BitFieldType.token = token; + result->BitFieldType.backing_type = backing_type; + result->BitFieldType.open = open; + result->BitFieldType.fields = slice_from_array(fields); + result->BitFieldType.close = close; + return result; +} + + gb_internal Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) { Ast *result = alloc_ast_node(f, Ast_MapType); result->MapType.token = token; @@ -2549,6 +2581,66 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { return ast_matrix_type(f, token, row_count, column_count, type); } break; + case Token_bit_field: { + Token token = expect_token(f, Token_bit_field); + isize prev_level; + + prev_level = f->expr_level; + f->expr_level = -1; + + Ast *backing_type = parse_type_or_ident(f); + if (backing_type == nullptr) { + Token token = advance_token(f); + syntax_error(token, "Expected a backing type for a 'bit_field'"); + backing_type = ast_bad_expr(f, token, f->curr_token); + } + + skip_possible_newline_for_literal(f); + Token open = expect_token_after(f, Token_OpenBrace, "bit_field"); + + + auto fields = array_make(ast_allocator(f), 0, 0); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + CommentGroup *docs = nullptr; + CommentGroup *comment = nullptr; + + Ast *name = parse_ident(f); + bool err_once = false; + while (allow_token(f, Token_Comma)) { + Ast *dummy_name = parse_ident(f); + if (!err_once) { + error(dummy_name, "'bit_field' fields do not support multiple names per field"); + err_once = true; + } + } + expect_token(f, Token_Colon); + Ast *type = parse_type(f); + expect_token(f, Token_Or); + Ast *bit_size = parse_expr(f, true); + + Token tag = {}; + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + } + + Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, tag, docs, comment); + array_add(&fields, bf_field); + + if (!allow_field_separator(f)) { + break; + } + } + + Token close = expect_closing_brace_of_field_list(f); + + f->expr_level = prev_level; + + return ast_bit_field_type(f, token, backing_type, open, fields, close); + } + + case Token_struct: { Token token = expect_token(f, Token_struct); Ast *polymorphic_params = nullptr; @@ -3923,6 +4015,10 @@ gb_internal Array convert_to_ident_list(AstFile *f, Array li case Ast_Ident: case Ast_BadExpr: break; + case Ast_Implicit: + syntax_error(ident, "Expected an identifier, '%.*s' which is a keyword", LIT(ident->Implicit.string)); + ident = ast_ident(f, blank_token); + break; case Ast_PolyType: if (allow_poly_names) { @@ -3936,6 +4032,7 @@ gb_internal Array convert_to_ident_list(AstFile *f, Array li } /*fallthrough*/ + default: syntax_error(ident, "Expected an identifier"); ident = ast_ident(f, blank_token); diff --git a/src/parser.hpp b/src/parser.hpp index 1edb1f9dd..f410419d4 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -429,6 +429,7 @@ AST_KIND(_ExprBegin, "", bool) \ Ast *expr, *selector; \ u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \ u8 swizzle_indices; /*2 bits per component*/ \ + bool is_bit_field; \ }) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { \ @@ -650,6 +651,14 @@ AST_KIND(_DeclEnd, "", bool) \ CommentGroup * docs; \ CommentGroup * comment; \ }) \ + AST_KIND(BitFieldField, "bit field field", struct { \ + Ast * name; \ + Ast * type; \ + Ast * bit_size; \ + Token tag; \ + CommentGroup *docs; \ + CommentGroup *comment; \ + }) \ AST_KIND(FieldList, "field list", struct { \ Token token; \ Slice list; \ @@ -742,6 +751,14 @@ AST_KIND(_TypeBegin, "", bool) \ Ast * elem; \ Ast * underlying; \ }) \ + AST_KIND(BitFieldType, "bit field type", struct { \ + Scope *scope; \ + Token token; \ + Ast * backing_type; \ + Token open; \ + Slice fields; /* BitFieldField */ \ + Token close; \ + }) \ AST_KIND(MapType, "map type", struct { \ Token token; \ Ast *count; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index f49c40f16..b2e12999b 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -111,6 +111,7 @@ gb_internal Token ast_token(Ast *node) { case Ast_UnionType: return node->UnionType.token; case Ast_EnumType: return node->EnumType.token; case Ast_BitSetType: return node->BitSetType.token; + case Ast_BitFieldType: return node->BitFieldType.token; case Ast_MapType: return node->MapType.token; case Ast_MatrixType: return node->MatrixType.token; } @@ -364,6 +365,8 @@ Token ast_end_token(Ast *node) { return ast_end_token(node->BitSetType.underlying); } return ast_end_token(node->BitSetType.elem); + case Ast_BitFieldType: + return node->BitFieldType.close; case Ast_MapType: return ast_end_token(node->MapType.value); case Ast_MatrixType: return ast_end_token(node->MatrixType.elem); } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index dd9908be5..3d5348074 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -106,6 +106,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_union, "union"), \ TOKEN_KIND(Token_enum, "enum"), \ TOKEN_KIND(Token_bit_set, "bit_set"), \ + TOKEN_KIND(Token_bit_field, "bit_field"), \ TOKEN_KIND(Token_map, "map"), \ TOKEN_KIND(Token_dynamic, "dynamic"), \ TOKEN_KIND(Token_auto_cast, "auto_cast"), \ diff --git a/src/types.cpp b/src/types.cpp index 2f1994574..57ae4e81d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -282,6 +282,15 @@ struct TypeProc { Type *generic_column_count; \ i64 stride_in_bytes; \ }) \ + TYPE_KIND(BitField, struct { \ + Scope * scope; \ + Type * backing_type; \ + Slice fields; \ + String * tags; /*count == fields.count*/ \ + Slice bit_sizes; \ + Slice bit_offsets; \ + Ast * node; \ + }) \ TYPE_KIND(SoaPointer, struct { Type *elem; }) @@ -355,6 +364,10 @@ enum Typeid_Kind : u8 { Typeid_Relative_Multi_Pointer, Typeid_Matrix, Typeid_SoaPointer, + Typeid_Bit_Field, + + Typeid__COUNT + }; // IMPORTANT NOTE(bill): This must match the same as the in core.odin @@ -376,6 +389,9 @@ enum : int { gb_internal bool is_type_comparable(Type *t); gb_internal bool is_type_simple_compare(Type *t); +gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false); +gb_internal Type *base_type(Type *t); +gb_internal Type *alloc_type_multi_pointer(Type *elem); gb_internal u32 type_info_flags_of_type(Type *type) { if (type == nullptr) { @@ -400,6 +416,7 @@ struct Selection { bool indirect; // Set if there was a pointer deref anywhere down the line u8 swizzle_count; // maximum components = 4 u8 swizzle_indices; // 2 bits per component, representing which swizzle index + bool is_bit_field; bool pseudo_field; }; gb_global Selection const empty_selection = {0}; @@ -548,6 +565,14 @@ gb_global Type *t_f16 = &basic_types[Basic_f16]; gb_global Type *t_f32 = &basic_types[Basic_f32]; gb_global Type *t_f64 = &basic_types[Basic_f64]; +gb_global Type *t_f16be = &basic_types[Basic_f16be]; +gb_global Type *t_f32be = &basic_types[Basic_f32be]; +gb_global Type *t_f64be = &basic_types[Basic_f64be]; + +gb_global Type *t_f16le = &basic_types[Basic_f16le]; +gb_global Type *t_f32le = &basic_types[Basic_f32le]; +gb_global Type *t_f64le = &basic_types[Basic_f64le]; + gb_global Type *t_complex32 = &basic_types[Basic_complex32]; gb_global Type *t_complex64 = &basic_types[Basic_complex64]; gb_global Type *t_complex128 = &basic_types[Basic_complex128]; @@ -641,6 +666,7 @@ gb_global Type *t_type_info_relative_pointer = nullptr; gb_global Type *t_type_info_relative_multi_pointer = nullptr; gb_global Type *t_type_info_matrix = nullptr; gb_global Type *t_type_info_soa_pointer = nullptr; +gb_global Type *t_type_info_bit_field = nullptr; gb_global Type *t_type_info_named_ptr = nullptr; gb_global Type *t_type_info_integer_ptr = nullptr; @@ -670,6 +696,7 @@ gb_global Type *t_type_info_relative_pointer_ptr = nullptr; gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr; gb_global Type *t_type_info_matrix_ptr = nullptr; gb_global Type *t_type_info_soa_pointer_ptr = nullptr; +gb_global Type *t_type_info_bit_field_ptr = nullptr; gb_global Type *t_allocator = nullptr; gb_global Type *t_allocator_ptr = nullptr; @@ -750,7 +777,6 @@ gb_internal bool is_type_proc(Type *t); gb_internal bool is_type_slice(Type *t); gb_internal bool is_type_integer(Type *t); gb_internal bool type_set_offsets(Type *t); -gb_internal Type *base_type(Type *t); gb_internal i64 type_size_of_internal(Type *t, TypePath *path); gb_internal i64 type_align_of_internal(Type *t, TypePath *path); @@ -1040,6 +1066,11 @@ gb_internal Type *alloc_type_enum() { return t; } +gb_internal Type *alloc_type_bit_field() { + Type *t = alloc_type(Type_BitField); + return t; +} + gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) { GB_ASSERT(is_type_pointer(pointer_type)); GB_ASSERT(is_type_integer(base_integer)); @@ -1140,7 +1171,7 @@ gb_internal Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_co //////////////////////////////////////////////////////////////// -gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false) { +gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) { if (t != nullptr) { Type *bt = base_type(t); if (bt == nullptr) { @@ -1707,6 +1738,10 @@ gb_internal bool is_type_bit_set(Type *t) { t = base_type(t); return (t->kind == Type_BitSet); } +gb_internal bool is_type_bit_field(Type *t) { + t = base_type(t); + return (t->kind == Type_BitField); +} gb_internal bool is_type_map(Type *t) { t = base_type(t); return t->kind == Type_Map; @@ -2795,6 +2830,49 @@ gb_internal Type *default_type(Type *type) { return type; } +// See https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions +gb_internal Type *c_vararg_promote_type(Type *type) { + GB_ASSERT(type != nullptr); + + Type *core = core_type(type); + + if (core->kind == Type_BitSet) { + core = core_type(bit_set_to_int(core)); + } + + if (core->kind == Type_Basic) { + switch (core->Basic.kind) { + case Basic_f32: + case Basic_UntypedFloat: + return t_f64; + case Basic_f32le: + return t_f64le; + case Basic_f32be: + return t_f64be; + + case Basic_UntypedBool: + case Basic_bool: + case Basic_b8: + case Basic_b16: + case Basic_i8: + case Basic_i16: + case Basic_u8: + case Basic_u16: + return t_i32; + + case Basic_i16le: + case Basic_u16le: + return t_i32le; + + case Basic_i16be: + case Basic_u16be: + return t_i32be; + } + } + + return type; +} + gb_internal bool union_variant_index_types_equal(Type *v, Type *vt) { if (are_types_identical(v, vt)) { return true; @@ -3168,6 +3246,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name else if (field_name == "a") mapped_field_name = str_lit("w"); return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident); } + } else if (type->kind == Type_BitField) { + for_array(i, type->BitField.fields) { + Entity *f = type->BitField.fields[i]; + if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { + continue; + } + String str = f->token.string; + if (field_name == str) { + selection_add_index(&sel, i); // HACK(bill): Leaky memory + sel.entity = f; + sel.is_bit_field = true; + return sel; + } + } + } else if (type->kind == Type_Basic) { switch (type->Basic.kind) { case Basic_any: { @@ -3568,6 +3661,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { case Type_Slice: return build_context.int_size; + case Type_BitField: + return type_align_of_internal(t->BitField.backing_type, path); case Type_Tuple: { i64 max = 1; @@ -3943,6 +4038,9 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { return stride_in_bytes * t->Matrix.column_count; } + case Type_BitField: + return type_size_of_internal(t->BitField.backing_type, path); + case Type_RelativePointer: return type_size_of_internal(t->RelativePointer.base_integer, path); case Type_RelativeMultiPointer: @@ -4114,8 +4212,10 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi } if (allow_polymorphic && dst_is_polymorphic) { Type *fb = base_type(type_deref(f->type)); - if (fb->kind == Type_Struct && fb->Struct.polymorphic_parent == dst) { - return true; + if (fb->kind == Type_Struct) { + if (fb->Struct.polymorphic_parent == dst) { + return true; + } } } @@ -4217,7 +4317,70 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_ return t; } - +// gb_internal Type *type_from_selection(Type *type, Selection const &sel) { +// for (i32 index : sel.index) { +// Type *bt = base_type(type_deref(type)); +// switch (bt->kind) { +// case Type_Struct: +// type = bt->Struct.fields[index]->type; +// break; +// case Type_Tuple: +// type = bt->Tuple.variables[index]->type; +// break; +// case Type_BitField: +// type = bt->BitField.fields[index]->type; +// break; +// case Type_Array: +// type = bt->Array.elem; +// break; +// case Type_EnumeratedArray: +// type = bt->Array.elem; +// break; +// case Type_Slice: +// switch (index) { +// case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break; +// case 1: type = t_int; break; +// } +// break; +// case Type_DynamicArray: +// switch (index) { +// case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break; +// case 1: type = t_int; break; +// case 2: type = t_int; break; +// case 3: type = t_allocator; break; +// } +// break; +// case Type_Map: +// switch (index) { +// case 0: type = t_uintptr; break; +// case 1: type = t_int; break; +// case 2: type = t_allocator; break; +// } +// break; +// case Type_Basic: +// if (is_type_complex_or_quaternion(bt)) { +// type = base_complex_elem_type(bt); +// } else { +// switch (type->Basic.kind) { +// case Basic_any: +// switch (index) { +// case 0: type = t_rawptr; break; +// case 1: type = t_typeid; break; +// } +// break; +// case Basic_string: +// switch (index) { +// case 0: type = t_u8_multi_ptr; break; +// case 1: type = t_int; break; +// } +// break; +// } +// } +// break; +// } +// } +// return type; +// } gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { if (type == nullptr) { @@ -4525,6 +4688,23 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count)); str = write_type_to_string(str, type->Matrix.elem); break; + + case Type_BitField: + str = gb_string_appendc(str, "bit_field "); + str = write_type_to_string(str, type->BitField.backing_type); + str = gb_string_appendc(str, " {"); + for (isize i = 0; i < type->BitField.fields.count; i++) { + Entity *f = type->BitField.fields[i]; + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]); + } + str = gb_string_appendc(str, " }"); + break; } return str; diff --git a/tests/core/Makefile b/tests/core/Makefile index 35321696f..1207eeec5 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -1,8 +1,11 @@ ODIN=../../odin PYTHON=$(shell which python3) +COMMON=-vet -strict-style +COLLECTION=-collection:tests=.. all: c_libc_test \ compress_test \ + container_test \ crypto_test \ download_test_assets \ encoding_test \ @@ -27,64 +30,67 @@ download_test_assets: $(PYTHON) download_assets.py image_test: - $(ODIN) run image/test_core_image.odin -file -out:test_core_image + $(ODIN) run image $(COMMON) -out:test_core_image compress_test: - $(ODIN) run compress/test_core_compress.odin -file -out:test_core_compress + $(ODIN) run compress $(COMMON) -out:test_core_compress + +container_test: + $(ODIN) run container $(COMMON) $(COLLECTION) -out:test_core_container strings_test: - $(ODIN) run strings/test_core_strings.odin -file -out:test_core_strings + $(ODIN) run strings $(COMMON) -out:test_core_strings hash_test: - $(ODIN) run hash -o:speed -no-bounds-check -out:test_hash + $(ODIN) run hash $(COMMON) -o:speed -no-bounds-check -out:test_hash crypto_test: - $(ODIN) run crypto -o:speed -no-bounds-check -out:test_crypto + $(ODIN) run crypto $(COMMON) -o:speed -no-bounds-check -out:test_crypto noise_test: - $(ODIN) run math/noise -out:test_noise + $(ODIN) run math/noise $(COMMON) -out:test_noise encoding_test: - $(ODIN) run encoding/hxa -out:test_hxa -collection:tests=.. - $(ODIN) run encoding/json -out:test_json - $(ODIN) run encoding/varint -out:test_varint - $(ODIN) run encoding/xml -out:test_xml + $(ODIN) run encoding/hxa $(COMMON) $(COLLECTION) -out:test_hxa + $(ODIN) run encoding/json $(COMMON) -out:test_json + $(ODIN) run encoding/varint $(COMMON) -out:test_varint + $(ODIN) run encoding/xml $(COMMON) -out:test_xml math_test: - $(ODIN) run math/test_core_math.odin -file -collection:tests=.. -out:test_core_math + $(ODIN) run math $(COMMON) $(COLLECTION) -out:test_core_math linalg_glsl_math_test: - $(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -file -collection:tests=.. -out:test_linalg_glsl_math + $(ODIN) run math/linalg/glsl $(COMMON) $(COLLECTION) -out:test_linalg_glsl_math filepath_test: - $(ODIN) run path/filepath/test_core_filepath.odin -file -collection:tests=.. -out:test_core_filepath + $(ODIN) run path/filepath $(COMMON) $(COLLECTION) -out:test_core_filepath reflect_test: - $(ODIN) run reflect/test_core_reflect.odin -file -collection:tests=.. -out:test_core_reflect + $(ODIN) run reflect $(COMMON) $(COLLECTION) -out:test_core_reflect slice_test: - $(ODIN) run slice/test_core_slice.odin -file -out:test_core_slice + $(ODIN) run slice $(COMMON) -out:test_core_slice os_exit_test: $(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0 i18n_test: - $(ODIN) run text/i18n -out:test_core_i18n + $(ODIN) run text/i18n $(COMMON) -out:test_core_i18n match_test: - $(ODIN) run text/match -out:test_core_match + $(ODIN) run text/match $(COMMON) -out:test_core_match c_libc_test: - $(ODIN) run c/libc -out:test_core_libc + $(ODIN) run c/libc $(COMMON) -out:test_core_libc net_test: - $(ODIN) run net -out:test_core_net + $(ODIN) run net $(COMMON) -out:test_core_net fmt_test: - $(ODIN) run fmt -out:test_core_fmt + $(ODIN) run fmt $(COMMON) -out:test_core_fmt thread_test: - $(ODIN) run thread -out:test_core_thread + $(ODIN) run thread $(COMMON) -out:test_core_thread runtime_test: - $(ODIN) run runtime -out:test_core_runtime + $(ODIN) run runtime $(COMMON) -out:test_core_runtime diff --git a/tests/core/c/libc/test_core_libc.odin b/tests/core/c/libc/test_core_libc.odin index 6ad37ac6d..9b5014dee 100644 --- a/tests/core/c/libc/test_core_libc.odin +++ b/tests/core/c/libc/test_core_libc.odin @@ -2,7 +2,6 @@ package test_core_libc import "core:fmt" import "core:os" -import "core:strings" import "core:testing" TEST_count := 0 diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin new file mode 100644 index 000000000..f6343c5ea --- /dev/null +++ b/tests/core/container/test_core_avl.odin @@ -0,0 +1,161 @@ +package test_core_container + +import "core:container/avl" +import "core:math/rand" +import "core:slice" +import "core:testing" + +import tc "tests:common" + +@(test) +test_avl :: proc(t: ^testing.T) { + tc.log(t, "Testing avl") + + // Initialization. + tree: avl.Tree(int) + avl.init(&tree, slice.cmp_proc(int)) + tc.expect(t, avl.len(&tree) == 0, "empty: len should be 0") + tc.expect(t, avl.first(&tree) == nil, "empty: first should be nil") + tc.expect(t, avl.last(&tree) == nil, "empty: last should be nil") + + iter := avl.iterator(&tree, avl.Direction.Forward) + tc.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil") + + // Test insertion. + NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. + inserted_map := make(map[int]^avl.Node(int)) + for i := 0; i < NR_INSERTS; i += 1 { + v := int(rand.uint32() & 0x1f) + existing_node, in_map := inserted_map[v] + + n, ok, _ := avl.find_or_insert(&tree, v) + tc.expect(t, in_map != ok, "insert: ok should match inverse of map lookup") + if ok { + inserted_map[v] = n + } else { + tc.expect(t, existing_node == n, "insert: expecting existing node") + } + } + nrEntries := len(inserted_map) + tc.expect(t, avl.len(&tree) == nrEntries, "insert: len after") + tree_validate(t, &tree) + + // Ensure that all entries can be found. + for k, v in inserted_map { + tc.expect(t, v == avl.find(&tree, k), "Find(): Node") + tc.expect(t, k == v.value, "Find(): Node value") + } + + // Test the forward/backward iterators. + inserted_values: [dynamic]int + for k in inserted_map { + append(&inserted_values, k) + } + slice.sort(inserted_values[:]) + + iter = avl.iterator(&tree, avl.Direction.Forward) + visited: int + for node in avl.iterator_next(&iter) { + v, idx := node.value, visited + tc.expect(t, inserted_values[idx] == v, "iterator/forward: value") + tc.expect(t, node == avl.iterator_get(&iter), "iterator/forward: get") + visited += 1 + } + tc.expect(t, visited == nrEntries, "iterator/forward: visited") + + slice.reverse(inserted_values[:]) + iter = avl.iterator(&tree, avl.Direction.Backward) + visited = 0 + for node in avl.iterator_next(&iter) { + v, idx := node.value, visited + tc.expect(t, inserted_values[idx] == v, "iterator/backward: value") + visited += 1 + } + tc.expect(t, visited == nrEntries, "iterator/backward: visited") + + // Test removal. + rand.shuffle(inserted_values[:]) + for v, i in inserted_values { + node := avl.find(&tree, v) + tc.expect(t, node != nil, "remove: find (pre)") + + ok := avl.remove(&tree, v) + tc.expect(t, ok, "remove: succeeds") + tc.expect(t, nrEntries - (i + 1) == avl.len(&tree), "remove: len (post)") + tree_validate(t, &tree) + + tc.expect(t, nil == avl.find(&tree, v), "remove: find (post") + } + tc.expect(t, avl.len(&tree) == 0, "remove: len should be 0") + tc.expect(t, avl.first(&tree) == nil, "remove: first should be nil") + tc.expect(t, avl.last(&tree) == nil, "remove: last should be nil") + + // Refill the tree. + for v in inserted_values { + avl.find_or_insert(&tree, v) + } + + // Test that removing the node doesn't break the iterator. + iter = avl.iterator(&tree, avl.Direction.Forward) + if node := avl.iterator_get(&iter); node != nil { + v := node.value + + ok := avl.iterator_remove(&iter) + tc.expect(t, ok, "iterator/remove: success") + + ok = avl.iterator_remove(&iter) + tc.expect(t, !ok, "iterator/remove: redundant removes should fail") + + tc.expect(t, avl.find(&tree, v) == nil, "iterator/remove: node should be gone") + tc.expect(t, avl.iterator_get(&iter) == nil, "iterator/remove: get should return nil") + + // Ensure that iterator_next still works. + node, ok = avl.iterator_next(&iter) + tc.expect(t, ok == (avl.len(&tree) > 0), "iterator/remove: next should return false") + tc.expect(t, node == avl.first(&tree), "iterator/remove: next should return first") + + tree_validate(t, &tree) + } + tc.expect(t, avl.len(&tree) == nrEntries - 1, "iterator/remove: len should drop by 1") + + avl.destroy(&tree) + tc.expect(t, avl.len(&tree) == 0, "destroy: len should be 0") +} + +@(private) +tree_validate :: proc(t: ^testing.T, tree: ^avl.Tree($Value)) { + tree_check_invariants(t, tree, tree._root, nil) +} + +@(private) +tree_check_invariants :: proc( + t: ^testing.T, + tree: ^avl.Tree($Value), + node, parent: ^avl.Node(Value), +) -> int { + if node == nil { + return 0 + } + + // Validate the parent pointer. + tc.expect(t, parent == node._parent, "invalid parent pointer") + + // Validate that the balance factor is -1, 0, 1. + tc.expect( + t, + node._balance == -1 || node._balance == 0 || node._balance == 1, + "invalid balance factor", + ) + + // Recursively derive the height of the left and right sub-trees. + l_height := tree_check_invariants(t, tree, node._left, node) + r_height := tree_check_invariants(t, tree, node._right, node) + + // Validate the AVL invariant and the balance factor. + tc.expect(t, int(node._balance) == r_height - l_height, "AVL balance factor invariant violated") + if l_height > r_height { + return l_height + 1 + } + + return r_height + 1 +} diff --git a/tests/core/container/test_core_container.odin b/tests/core/container/test_core_container.odin new file mode 100644 index 000000000..f816a6bcb --- /dev/null +++ b/tests/core/container/test_core_container.odin @@ -0,0 +1,26 @@ +package test_core_container + +import "core:fmt" +import "core:testing" + +import tc "tests:common" + +expect_equal :: proc(t: ^testing.T, the_slice, expected: []int, loc := #caller_location) { + _eq :: proc(a, b: []int) -> bool { + if len(a) != len(b) do return false + for a, i in a { + if b[i] != a do return false + } + return true + } + tc.expect(t, _eq(the_slice, expected), fmt.tprintf("Expected %v, got %v\n", the_slice, expected), loc) +} + +main :: proc() { + t := testing.T{} + + test_avl(&t) + test_small_array(&t) + + tc.report(&t) +} diff --git a/tests/core/container/test_core_small_array.odin b/tests/core/container/test_core_small_array.odin index 88bc8e532..78998de16 100644 --- a/tests/core/container/test_core_small_array.odin +++ b/tests/core/container/test_core_small_array.odin @@ -1,29 +1,19 @@ -package test_core_compress +package test_core_container -import "core:fmt" import "core:testing" import "core:container/small_array" + import tc "tests:common" -main :: proc() { - t := testing.T{} - test_small_array_removes(&t) - test_small_array_inject_at(&t) - tc.report(&t) +@(test) +test_small_array :: proc(t: ^testing.T) { + tc.log(t, "Testing small_array") + + test_small_array_removes(t) + test_small_array_inject_at(t) } -expect_equal :: proc(t: ^testing.T, the_slice, expected: []int, loc := #caller_location) { - _eq :: proc(a, b: []int) -> bool { - if len(a) != len(b) do return false - for a, i in a { - if b[i] != a do return false - } - return true - } - tc.expect(t, _eq(the_slice, expected), fmt.tprintf("Expected %v, got %v\n", the_slice, expected), loc) -} - -@test +@(test) test_small_array_removes :: proc(t: ^testing.T) { array: small_array.Small_Array(10, int) small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) @@ -42,7 +32,7 @@ test_small_array_removes :: proc(t: ^testing.T) { expect_equal(t, small_array.slice(&array), []int { 9, 2, 7, 4 }) } -@test +@(test) test_small_array_inject_at :: proc(t: ^testing.T) { array: small_array.Small_Array(13, int) small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 5326e5023..579298904 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -15,6 +15,7 @@ import "core:mem" import "core:fmt" import "core:net" import "core:strconv" +import "core:sync" import "core:time" import "core:thread" import "core:os" @@ -62,11 +63,7 @@ main :: proc() { address_parsing_test(t) - when ODIN_OS != .Windows { - fmt.printf("IMPORTANT: `core:thread` seems to still be a bit wonky on Linux and MacOS, so we can't run tests relying on them.\n", ODIN_OS) - } else { - tcp_tests(t) - } + tcp_tests(t) split_url_test(t) join_url_test(t) @@ -338,174 +335,129 @@ IP_Address_Parsing_Test_Vectors :: []IP_Address_Parsing_Test_Vector{ { .IP6, "c0a8", "", ""}, } +tcp_tests :: proc(t: ^testing.T) { + fmt.println("Testing two servers trying to bind to the same endpoint...") + two_servers_binding_same_endpoint(t) + fmt.println("Testing client connecting to a closed port...") + client_connects_to_closed_port(t) + fmt.println("Testing client sending server data...") + client_sends_server_data(t) +} ENDPOINT := net.Endpoint{ net.IP4_Address{127, 0, 0, 1}, 9999, } -CONTENT := "Hellope!" - -SEND_TIMEOUT :: time.Duration(1 * time.Second) -RECV_TIMEOUT :: time.Duration(1 * time.Second) - -Thread_Data :: struct { - skt: net.Any_Socket, - err: net.Network_Error, - tid: ^thread.Thread, - - no_accept: bool, // Tell the server proc not to accept. - - data: [1024]u8, // Received data and its length - length: int, -} - -thread_data := [3]Thread_Data{} - -/* - This runs a bunch of socket tests using threads: - - two servers trying to bind the same endpoint - - client trying to connect to closed port - - client trying to connect to an open port with a non-accepting server - - client sending server data and server sending client data - - etc. -*/ -tcp_tests :: proc(t: ^testing.T) { - fmt.println("Testing two servers trying to bind to the same endpoint...") - two_servers_binding_same_endpoint(t) - fmt.println("Testing client connecting to a closed port...") - client_connects_to_closed_port(t) - fmt.println("Testing client connecting to port that doesn't accept...") - client_connects_to_open_but_non_accepting_port(t) - fmt.println("Testing client sending server data...") - client_sends_server_data(t) -} - -tcp_client :: proc(retval: rawptr) { - send :: proc(content: []u8) -> (err: net.Network_Error) { - skt := net.dial_tcp(ENDPOINT) or_return - defer net.close(skt) - - net.set_option(skt, .Send_Timeout, SEND_TIMEOUT) - net.set_option(skt, .Receive_Timeout, RECV_TIMEOUT) - - _, err = net.send(skt, content) - return - } - - r := transmute(^Thread_Data)retval - r.err = send(transmute([]u8)CONTENT) - return -} - -tcp_server :: proc(retval: rawptr) { - r := transmute(^Thread_Data)retval - - if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil { - return - } - defer net.close(r.skt) - - if r.no_accept { - // Don't accept any connections, just listen. - return - } - - client: net.TCP_Socket - if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil { - return - } - defer net.close(client) - - - r.length, r.err = net.recv_tcp(client, r.data[:]) - return -} - -cleanup_thread :: proc(data: Thread_Data) { - net.close(data.skt) - - thread.terminate(data.tid, 1) - thread.destroy(data.tid) -} - +@(test) two_servers_binding_same_endpoint :: proc(t: ^testing.T) { - thread_data = {} + skt1, err1 := net.listen_tcp(ENDPOINT) + defer net.close(skt1) + skt2, err2 := net.listen_tcp(ENDPOINT) + defer net.close(skt2) - thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context) - thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_server, context) - - defer { - cleanup_thread(thread_data[0]) - cleanup_thread(thread_data[1]) - } - - // Give the two servers enough time to try and bind the same endpoint - time.sleep(1 * time.Second) - - first_won := thread_data[0].err == nil && thread_data[1].err == net.Bind_Error.Address_In_Use - second_won := thread_data[1].err == nil && thread_data[0].err == net.Bind_Error.Address_In_Use - - okay := first_won || second_won - msg := fmt.tprintf("Expected servers to return `nil` and `Address_In_Use`, got %v and %v", thread_data[0].err, thread_data[1].err) - expect(t, okay, msg) + expect(t, err1 == nil, "expected first server binding to endpoint to do so without error") + expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use") } +@(test) client_connects_to_closed_port :: proc(t: ^testing.T) { - thread_data = {} - - thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_client, context) - - defer { - cleanup_thread(thread_data[0]) - } - - // Give the socket enough time to return `Refused` - time.sleep(4 * time.Second) - - okay := thread_data[0].err == net.Dial_Error.Refused - msg := fmt.tprintf("Expected client to return `Refused` connecting to closed port, got %v", thread_data[0].err) - expect(t, okay, msg) -} - -client_connects_to_open_but_non_accepting_port :: proc(t: ^testing.T) { - thread_data = {} - - // Tell server proc not to accept - thread_data[0].no_accept = true - - thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context) - thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_client, context) - - defer { - cleanup_thread(thread_data[0]) - cleanup_thread(thread_data[1]) - } - - // Give the two servers enough time to try and bind the same endpoint - time.sleep(4 * time.Second) - - okay := thread_data[0].err == nil && thread_data[1].err == net.Dial_Error.Refused - msg := fmt.tprintf("Expected server and client to return `nil` and `Refused`, got %v and %v", thread_data[0].err, thread_data[1].err) - expect(t, okay, msg) + skt, err := net.dial_tcp(ENDPOINT) + defer net.close(skt) + expect(t, err == net.Dial_Error.Refused, "expected dial of a closed endpoint to return .Refused") } +@(test) client_sends_server_data :: proc(t: ^testing.T) { - thread_data = {} + CONTENT: string: "Hellope!" - // Tell server proc not to accept - // thread_data[0].no_accept = true + SEND_TIMEOUT :: time.Duration(1 * time.Second) + RECV_TIMEOUT :: time.Duration(1 * time.Second) + Thread_Data :: struct { + t: ^testing.T, + skt: net.Any_Socket, + err: net.Network_Error, + tid: ^thread.Thread, + + data: [1024]u8, // Received data and its length + length: int, + wg: ^sync.Wait_Group, + } + + tcp_client :: proc(thread_data: rawptr) { + r := transmute(^Thread_Data)thread_data + + defer sync.wait_group_done(r.wg) + + if r.skt, r.err = net.dial_tcp(ENDPOINT); r.err != nil { + log(r.t, r.err) + return + } + + net.set_option(r.skt, .Send_Timeout, SEND_TIMEOUT) + + _, r.err = net.send(r.skt, transmute([]byte)CONTENT) + } + + tcp_server :: proc(thread_data: rawptr) { + r := transmute(^Thread_Data)thread_data + + defer sync.wait_group_done(r.wg) + + log(r.t, "tcp_server listen") + if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil { + sync.wait_group_done(r.wg) + log(r.t, r.err) + return + } + + sync.wait_group_done(r.wg) + + log(r.t, "tcp_server accept") + client: net.TCP_Socket + if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil { + log(r.t, r.err) + return + } + defer net.close(client) + + net.set_option(client, .Receive_Timeout, RECV_TIMEOUT) + + r.length, r.err = net.recv_tcp(client, r.data[:]) + return + } + + thread_data := [2]Thread_Data{} + + wg: sync.Wait_Group + sync.wait_group_add(&wg, 1) + + thread_data[0].t = t + thread_data[0].wg = &wg thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context) + + log(t, "waiting for server to start listening") + sync.wait_group_wait(&wg) + log(t, "starting up client") + + sync.wait_group_add(&wg, 2) + + thread_data[1].t = t + thread_data[1].wg = &wg thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_client, context) defer { - cleanup_thread(thread_data[0]) - cleanup_thread(thread_data[1]) + net.close(thread_data[0].skt) + thread.destroy(thread_data[0].tid) + + net.close(thread_data[1].skt) + thread.destroy(thread_data[1].tid) } - // Give the two servers enough time to try and bind the same endpoint - time.sleep(1 * time.Second) + log(t, "waiting for threads to finish") + sync.wait_group_wait(&wg) + log(t, "threads finished") okay := thread_data[0].err == nil && thread_data[1].err == nil msg := fmt.tprintf("Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err) diff --git a/tests/core/text/match/test_core_text_match.odin b/tests/core/text/match/test_core_text_match.odin index 79defb849..b72190f78 100644 --- a/tests/core/text/match/test_core_text_match.odin +++ b/tests/core/text/match/test_core_text_match.odin @@ -202,8 +202,11 @@ test_captures :: proc(t: ^testing.T) { // match all captures compare_captures :: proc(t: ^testing.T, test: ^Temp, haystack: string, comp: []string, loc := #caller_location) { length, err := match.find_aux(haystack, test.pattern, 0, false, &test.captures) - if failed(t, len(comp) == length) { - logf(t, "Captures Compare Failed -> Lengths %d != %d\n", len(comp), length) + result := len(comp) == length && err == .OK + if failed(t, result == true) { + logf(t, "Captures Compare Failed!\n") + logf(t, "\tErr: %v\n", err) + logf(t, "\tLengths: %v != %v\n", len(comp), length) } for i in 0.. Display --- + Initialize :: proc(display: Display, major: ^i32, minor: ^i32) -> i32 --- + BindAPI :: proc(api: u32) -> i32 --- + ChooseConfig :: proc(display: Display, attrib_list: ^i32, configs: ^Context, config_size: i32, num_config: ^i32) -> i32 --- + CreateWindowSurface :: proc(display: Display, config: Config, native_window: NativeWindowType, attrib_list: ^i32) -> Surface --- + CreateContext :: proc(display: Display, config: Config, share_context: Context, attrib_list: ^i32) -> Context --- + MakeCurrent :: proc(display: Display, draw: Surface, read: Surface, ctx: Context) -> i32 --- + SwapInterval :: proc(display: Display, interval: i32) -> i32 --- + SwapBuffers :: proc(display: Display, surface: Surface) -> i32 --- + GetProcAddress :: proc(name: cstring) -> rawptr --- +} + +gl_set_proc_address :: proc(p: rawptr, name: cstring) { + (^rawptr)(p)^ = GetProcAddress(name) +} diff --git a/vendor/glfw/wrapper.odin b/vendor/glfw/wrapper.odin index db0e8364e..6ef46c183 100644 --- a/vendor/glfw/wrapper.odin +++ b/vendor/glfw/wrapper.odin @@ -149,8 +149,9 @@ WaitEvents :: glfw.WaitEvents WaitEventsTimeout :: glfw.WaitEventsTimeout PostEmptyEvent :: glfw.PostEmptyEvent -GetInputMode :: glfw.GetInputMode -SetInputMode :: glfw.SetInputMode +RawMouseMotionSupported :: glfw.RawMouseMotionSupported +GetInputMode :: glfw.GetInputMode +SetInputMode :: glfw.SetInputMode GetMouseButton :: glfw.GetMouseButton GetCursorPos :: proc "c" (window: WindowHandle) -> (xpos, ypos: f64) { diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index 7a350f6a7..576be29e7 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -318,11 +318,11 @@ GlyphInfo :: struct { // Font type, includes texture and charSet array data Font :: struct { baseSize: c.int, // Base size (default chars height) - charsCount: c.int, // Number of characters - charsPadding: c.int, // Padding around the chars + glyphCount: c.int, // Number of characters + glyphPadding: c.int, // Padding around the chars texture: Texture2D, // Characters texture atlas recs: [^]Rectangle, // Characters rectangles in texture - chars: [^]GlyphInfo, // Characters info data + glyphs: [^]GlyphInfo, // Characters info data } // Camera type, defines a camera position/orientation in 3d space @@ -404,7 +404,7 @@ BoneInfo :: struct { } // Model type -Model :: struct { +Model :: struct #align(align_of(uintptr)) { transform: Matrix, // Local transform matrix meshCount: c.int, // Number of meshes @@ -425,6 +425,7 @@ ModelAnimation :: struct { frameCount: c.int, // Number of animation frames bones: [^]BoneInfo, // Bones information (skeleton) framePoses: [^][^]Transform, // Poses array by frame + name: [32]byte, // Animation name } // Ray type (useful for raycast) @@ -490,7 +491,6 @@ VrDeviceInfo :: struct { vResolution: c.int, // Vertical resolution in pixels hScreenSize: f32, // Horizontal size in meters vScreenSize: f32, // Vertical size in meters - vScreenCenter: f32, // Screen center in meters eyeToScreenDistance: f32, // Distance between eye and display in meters lensSeparationDistance: f32, // Lens separation distance in meters interpupillaryDistance: f32, // IPD (distance between pupils) in meters @@ -499,7 +499,7 @@ VrDeviceInfo :: struct { } // VR Stereo rendering configuration for simulator -VrStereoConfig :: struct { +VrStereoConfig :: struct #align(4) { projection: [2]Matrix, // VR projection matrices (per eye) viewOffset: [2]Matrix, // VR view offset matrices (per eye) leftLensCenter: [2]f32, // VR left lens center diff --git a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py index 531460af2..65f53758f 100644 --- a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py +++ b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py @@ -565,7 +565,7 @@ def parse_structs(f): # The second way has many fields that are each 1 bit elif int(fname) == 1: bit_field_type = do_type(bit_field[0], prev_name, fname) - ffields.append(tuple(["bit_field", bit_field_type, comment])) + ffields.append(tuple(["bitfield", bit_field_type, comment])) break diff --git a/vendor/vulkan/structs.odin b/vendor/vulkan/structs.odin index 33613947e..e16a49dc3 100644 --- a/vendor/vulkan/structs.odin +++ b/vendor/vulkan/structs.odin @@ -7032,7 +7032,7 @@ WaylandSurfaceCreateInfoKHR :: struct { } VideoH264SpsVuiFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH264HrdParameters :: struct { @@ -7069,7 +7069,7 @@ VideoH264SequenceParameterSetVui :: struct { } VideoH264SpsFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH264ScalingLists :: struct { @@ -7108,7 +7108,7 @@ VideoH264SequenceParameterSet :: struct { } VideoH264PpsFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH264PictureParameterSet :: struct { @@ -7140,7 +7140,7 @@ VideoH265SubLayerHrdParameters :: struct { } VideoH265HrdFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH265HrdParameters :: struct { @@ -7162,11 +7162,11 @@ VideoH265HrdParameters :: struct { } VideoH265VpsFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH265ProfileTierLevelFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH265ProfileTierLevel :: struct { @@ -7200,7 +7200,7 @@ VideoH265ScalingLists :: struct { } VideoH265SpsVuiFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH265SequenceParameterSetVui :: struct { @@ -7237,11 +7237,11 @@ VideoH265PredictorPaletteEntries :: struct { } VideoH265SpsFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH265ShortTermRefPicSetFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH265ShortTermRefPicSet :: struct { @@ -7309,7 +7309,7 @@ VideoH265SequenceParameterSet :: struct { } VideoH265PpsFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoH265PictureParameterSet :: struct { @@ -7352,7 +7352,7 @@ VideoH265PictureParameterSet :: struct { } VideoDecodeH264PictureInfoFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoDecodeH264PictureInfo :: struct { @@ -7367,7 +7367,7 @@ VideoDecodeH264PictureInfo :: struct { } VideoDecodeH264ReferenceInfoFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoDecodeH264ReferenceInfo :: struct { @@ -7378,7 +7378,7 @@ VideoDecodeH264ReferenceInfo :: struct { } VideoDecodeH265PictureInfoFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoDecodeH265PictureInfo :: struct { @@ -7396,7 +7396,7 @@ VideoDecodeH265PictureInfo :: struct { } VideoDecodeH265ReferenceInfoFlags :: struct { - bit_field: u32, + bitfield: u32, } VideoDecodeH265ReferenceInfo :: struct {