From 9329f87ed2607b89c903ac716a933a38b1f5bf8a Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 2 Feb 2025 19:19:40 +1100 Subject: [PATCH 01/74] Add indirect_pointer_vector class --- .../Vector/IndirectPointerVector.cpp | 111 ++++++++++++++++++ .../ComputeCxx/Vector/IndirectPointerVector.h | 109 +++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 Sources/ComputeCxx/Vector/IndirectPointerVector.cpp create mode 100644 Sources/ComputeCxx/Vector/IndirectPointerVector.h diff --git a/Sources/ComputeCxx/Vector/IndirectPointerVector.cpp b/Sources/ComputeCxx/Vector/IndirectPointerVector.cpp new file mode 100644 index 0000000..ebf9c4f --- /dev/null +++ b/Sources/ComputeCxx/Vector/IndirectPointerVector.cpp @@ -0,0 +1,111 @@ +#include "IndirectPointerVector.h" + +namespace AG { + +template + requires std::unsigned_integral +indirect_pointer_vector::~indirect_pointer_vector() { + clear(); +}; + +template + requires std::unsigned_integral +void indirect_pointer_vector::clear() { + if (this->has_vector()) { + vector_type *vector = this->get_vector(); + if (vector != nullptr) { + delete vector; + } + } + _data = nullptr; +} + +template + requires std::unsigned_integral +indirect_pointer_vector::iterator indirect_pointer_vector::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +indirect_pointer_vector::iterator indirect_pointer_vector::erase(iterator first, + iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + if (has_vector()) { + return get_vector()->erase(first, last); + } else { + assert(count <= 1); + if (count == 1 && first == begin()) { + _data = nullptr; + } + return end(); + } +} + +template + requires std::unsigned_integral +void indirect_pointer_vector::push_back(const T &value) { + if (this->has_vector()) { + this->get_vector()->push_back(value); + } else { + if (_data == nullptr) { + _data = value; + } else { + vector_type *vector = new vector_type(); + vector->push_back(this->get_element()); + vector->push_back(value); + _data = vector | 1; + } + } +} + +template + requires std::unsigned_integral +void indirect_pointer_vector::push_back(T &&value) { + if (this->has_vector()) { + this->get_vector()->push_back(value); + } else { + if (_data == nullptr) { + _data = std::move(value); + } else { + vector_type *vector = new vector_type(); + vector->push_back(this->get_element()); + vector->push_back(value); + _data = vector | 1; + } + } +} + +template + requires std::unsigned_integral +void indirect_pointer_vector::resize(size_type count) { + if (this->has_element()) { + if (count == 1) { + if (_data == nullptr) { + _data = NullElement; + } + return; + } + if (count == 0) { + if (_data) { + delete _data; + } + _data = nullptr; + return; + } + // put single element into vector + vector_type *vector = new vector_type(); + vector->push_back(this->get_element()); + _data = vector | 1; + } + + this->get_vector()->resize(count); +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Vector/IndirectPointerVector.h b/Sources/ComputeCxx/Vector/IndirectPointerVector.h new file mode 100644 index 0000000..a7c811d --- /dev/null +++ b/Sources/ComputeCxx/Vector/IndirectPointerVector.h @@ -0,0 +1,109 @@ +#pragma once + +#include "CoreFoundation/CFBase.h" +#include +#include + +#include "Vector.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +/// A vector that efficiently stores a single element as a pointer or stores multiple elements as a vector. +template + requires std::unsigned_integral +class indirect_pointer_vector { + public: + using value_type = T; + using pointer = value_type *_Nonnull; + using const_pointer = const value_type *_Nonnull; + using reference = value_type &; + using const_reference = const value_type &; + using vector_type = vector; + using iterator = pointer *_Nonnull; + using const_iterator = const pointer *_Nonnull; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + private: + uintptr_t _data; + enum { + TagMask = 0x1, + NullElement = 0x2, + }; + + bool has_vector() const { return (_data & TagMask) == 1; }; + vector_type &get_vector() { + assert(has_vector()); + return *reinterpret_cast(_data & ~TagMask); + }; + const vector_type &get_vector() const { + assert(has_vector()); + return *reinterpret_cast(_data & ~TagMask); + }; + + public: + indirect_pointer_vector(); + ~indirect_pointer_vector(); + + // Element access + + reference front() { return has_vector() ? *get_vector().front() : *reinterpret_cast(_data); }; + const_reference front() const { + return has_vector() ? *get_vector().front() : *reinterpret_cast(_data); + }; + + // Iterators + + iterator begin() { return has_vector() ? get_vector().begin() : reinterpret_cast(&_data); }; + iterator end() { + if (has_vector()) { + return get_vector().end(); + } else { + size_type size = reinterpret_cast(_data) == nullptr ? 0 : 1; + return &reinterpret_cast(&_data)[size]; + } + }; + const_iterator cbegin() const { + return has_vector() ? get_vector().cbegin() : reinterpret_cast(&_data); + }; + const_iterator cend() const { + if (has_vector()) { + return get_vector().cend(); + } else { + size_type size = reinterpret_cast(_data) == nullptr ? 0 : 1; + return &reinterpret_cast(&_data)[(T *)_data != nullptr ? 1 : 0]; + } + }; + const_iterator begin() const { return cbegin(); }; + const_iterator end() const { return cend(); }; + + reverse_iterator rbegin() { return std::reverse_iterator(end()); }; + reverse_iterator rend() { return std::reverse_iterator(begin()); }; + const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; + const_reverse_iterator crend() const { return std::reverse_iterator(cbegin()); }; + const_reverse_iterator rbegin() const { return crbegin(); }; + const_reverse_iterator rend() const { return crend(); }; + + // Capacity + + bool empty() const { return has_vector() ? get_vector().empty() : reinterpret_cast(_data) == nullptr; }; + size_type size() const { return has_vector() ? get_vector()->size() : _data == nullptr ? 0 : 1; }; + + // Modifiers + + void clear(); + + iterator erase(iterator pos); + iterator erase(iterator first, iterator last); + + void push_back(const T &value); + void push_back(T &&value); + + void resize(size_type count); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END From 9adb975d23d165cbe59fe1a0db2ae6a6e97645d5 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 2 Feb 2025 19:20:19 +1100 Subject: [PATCH 02/74] Add erase to vector and other updates --- Sources/ComputeCxx/Vector/Vector.h | 80 +++++++++++++++++----------- Sources/ComputeCxx/Vector/Vector.tpp | 59 ++++++++++++++++++-- 2 files changed, 103 insertions(+), 36 deletions(-) diff --git a/Sources/ComputeCxx/Vector/Vector.h b/Sources/ComputeCxx/Vector/Vector.h index 04cf947..1d3af68 100644 --- a/Sources/ComputeCxx/Vector/Vector.h +++ b/Sources/ComputeCxx/Vector/Vector.h @@ -9,17 +9,9 @@ CF_ASSUME_NONNULL_BEGIN namespace AG { -template - requires std::unsigned_integral +template + requires std::unsigned_integral<_size_type> class vector { - private: - T _stack_buffer[_stack_size]; - T *_Nullable _buffer = nullptr; - size_type _size = 0; - size_type _capacity = _stack_size; - - void reserve_slow(size_type new_cap); - public: using value_type = T; using reference = value_type &; @@ -28,7 +20,17 @@ class vector { using const_iterator = const value_type *_Nonnull; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; + using size_type = _size_type; + private: + T _stack_buffer[_stack_size]; + T *_Nullable _buffer = nullptr; + size_type _size = 0; + size_type _capacity = _stack_size; + + void reserve_slow(size_type new_cap); + + public: ~vector(); // Element access @@ -47,14 +49,18 @@ class vector { // Iterators iterator begin() { return iterator(&data()[0]); }; - const_iterator cbegin() const { return const_iterator(&data()[0]); }; iterator end() { return iterator(&data()[_size]); }; + const_iterator cbegin() const { return const_iterator(&data()[0]); }; const_iterator cend() const { return const_iterator(&data()[_size]); }; + const_iterator begin() const { return cbegin(); }; + const_iterator end() const { return cend(); }; reverse_iterator rbegin() { return std::reverse_iterator(end()); }; - const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; reverse_iterator rend() { return std::reverse_iterator(begin()); }; + const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; const_reverse_iterator crend() const { return std::reverse_iterator(cbegin()); }; + const_reverse_iterator rbegin() const { return crbegin(); }; + const_reverse_iterator rend() const { return crend(); }; // Capacity @@ -68,6 +74,9 @@ class vector { void clear(); + iterator erase(iterator pos); + iterator erase(iterator first, iterator last); + void push_back(const T &value); void push_back(T &&value); void pop_back(); @@ -81,16 +90,9 @@ static_assert(std::contiguous_iterator::const_iterator>); // MARK: Specialization for empty stack buffer -template - requires std::unsigned_integral -class vector { - private: - T *_Nullable _buffer = nullptr; - size_type _size = 0; - size_type _capacity = 0; - - void reserve_slow(size_type new_cap); - +template + requires std::unsigned_integral<_size_type> +class vector { public: using value_type = T; using reference = value_type &; @@ -99,7 +101,16 @@ class vector { using const_iterator = const value_type *_Nonnull; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; + using size_type = _size_type; + + private: + T *_Nullable _buffer = nullptr; + size_type _size = 0; + size_type _capacity = 0; + + void reserve_slow(size_type new_cap); + public: ~vector(); // Element access @@ -143,6 +154,9 @@ class vector { void clear(); + iterator erase(iterator pos); + iterator erase(iterator first, iterator last); + void push_back(const T &value); void push_back(T &&value); void pop_back(); @@ -153,16 +167,9 @@ class vector { // MARK: Specialization for unique_ptr -template - requires std::unsigned_integral -class vector, 0, size_type> { - private: - std::unique_ptr *_Nullable _buffer = nullptr; - size_type _size = 0; - size_type _capacity = 0; - - void reserve_slow(size_type new_cap); - +template + requires std::unsigned_integral<_size_type> +class vector, 0, _size_type> { public: using value_type = std::unique_ptr; using reference = value_type &; @@ -171,7 +178,16 @@ class vector, 0, size_type> { using const_iterator = const value_type *_Nonnull; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; + using size_type = _size_type; + + private: + std::unique_ptr *_Nullable _buffer = nullptr; + size_type _size = 0; + size_type _capacity = 0; + void reserve_slow(size_type new_cap); + + public: ~vector(); // Element access diff --git a/Sources/ComputeCxx/Vector/Vector.tpp b/Sources/ComputeCxx/Vector/Vector.tpp index ac6e71f..51d00a0 100644 --- a/Sources/ComputeCxx/Vector/Vector.tpp +++ b/Sources/ComputeCxx/Vector/Vector.tpp @@ -1,9 +1,9 @@ #include "Vector.h" #include +#include #include #include -#include #include "Errors/Errors.h" @@ -96,6 +96,32 @@ void vector::clear() { _size = 0; } +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator first, iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + for (auto iter = first; iter != last; iter++) { + iter->~T(); + } + for (auto iter = last, old_end = end(); iter != old_end; iter++) { + std::swap(*(iter - count), *iter); + } + _size -= count; + return end(); +} + template requires std::unsigned_integral void vector::push_back(const T &value) { @@ -211,11 +237,10 @@ void vector::reserve(size_type new_cap) { } template -requires std::unsigned_integral + requires std::unsigned_integral void vector::shrink_to_fit() { if (capacity() > size()) { - _buffer = - reinterpret_cast(details::realloc_vector(_buffer, &_capacity, 0)); + _buffer = reinterpret_cast(details::realloc_vector(_buffer, &_capacity, 0)); } } @@ -228,6 +253,32 @@ void vector::clear() { _size = 0; } +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator first, iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + for (auto iter = first; iter != last; iter++) { + iter->~T(); + } + for (auto iter = last, old_end = end(); iter != old_end; iter++) { + std::swap(*(iter - count), *iter); + } + _size -= count; + return end(); +} + template requires std::unsigned_integral void vector::push_back(const T &value) { From 618f5f34b7854514a120d90215b69a5bd352e5ac Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 2 Feb 2025 19:20:48 +1100 Subject: [PATCH 03/74] Add AGUniqueID function --- Sources/ComputeCxx/UniqueID/AGUniqueID.cpp | 6 ++++++ Sources/ComputeCxx/UniqueID/AGUniqueID.h | 14 ++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 Sources/ComputeCxx/UniqueID/AGUniqueID.cpp create mode 100644 Sources/ComputeCxx/UniqueID/AGUniqueID.h diff --git a/Sources/ComputeCxx/UniqueID/AGUniqueID.cpp b/Sources/ComputeCxx/UniqueID/AGUniqueID.cpp new file mode 100644 index 0000000..d1b97dc --- /dev/null +++ b/Sources/ComputeCxx/UniqueID/AGUniqueID.cpp @@ -0,0 +1,6 @@ +#include "AGUniqueID.h" + +uint64_t AGMakeUniqueID() { + static uint64_t counter = 0; + return counter++; +} diff --git a/Sources/ComputeCxx/UniqueID/AGUniqueID.h b/Sources/ComputeCxx/UniqueID/AGUniqueID.h new file mode 100644 index 0000000..9d4df3c --- /dev/null +++ b/Sources/ComputeCxx/UniqueID/AGUniqueID.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +uint64_t AGMakeUniqueID(); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END From f8de88ff110b3cda67f4db4a898b378727304127 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 2 Feb 2025 19:21:11 +1100 Subject: [PATCH 04/74] Fix using alignment instead of alignment mask in swift::metadata --- Sources/ComputeCxx/Swift/Metadata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/Swift/Metadata.cpp b/Sources/ComputeCxx/Swift/Metadata.cpp index c61095c..8c7b33b 100644 --- a/Sources/ComputeCxx/Swift/Metadata.cpp +++ b/Sources/ComputeCxx/Swift/Metadata.cpp @@ -305,7 +305,7 @@ void metadata::copy_on_write_heap_object(void **object_ref) const { assert(::swift::isHeapMetadataKind(getKind())); auto heap_metadata = reinterpret_cast(this); - ::swift::HeapObject *copy = ::swift::swift_allocObject(heap_metadata, vw_size(), vw_alignment()); + ::swift::HeapObject *copy = ::swift::swift_allocObject(heap_metadata, vw_size(), getValueWitnesses()->getAlignmentMask()); vw_initializeWithCopy(reinterpret_cast(copy), reinterpret_cast(*object_ref)); ::swift::swift_release(reinterpret_cast<::swift::HeapObject *>(*object_ref)); *object_ref = copy; From c506721c9c889850795b4b33f4b03ae13d68ac31 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 2 Feb 2025 19:23:31 +1100 Subject: [PATCH 05/74] Implement Subgraph --- Sources/ComputeCxx/Attribute/AttributeID.cpp | 4 +- Sources/ComputeCxx/Attribute/AttributeID.h | 27 +- .../ComputeCxx/Attribute/AttributeType.cpp | 14 + Sources/ComputeCxx/Attribute/AttributeType.h | 60 +- .../Attribute/Node/IndirectNode.cpp | 8 +- .../ComputeCxx/Attribute/Node/IndirectNode.h | 35 +- Sources/ComputeCxx/Attribute/Node/InputEdge.h | 14 + Sources/ComputeCxx/Attribute/Node/Node.cpp | 44 +- Sources/ComputeCxx/Attribute/Node/Node.h | 140 +- .../ComputeCxx/Attribute/Node/OutputEdge.h | 10 + .../ComputeCxx/Attribute/WeakAttributeID.h | 2 + Sources/ComputeCxx/Closure/ClosureFunction.h | 57 + Sources/ComputeCxx/Data/Page.h | 11 +- Sources/ComputeCxx/Data/Pointer.h | 5 +- Sources/ComputeCxx/Data/Zone.cpp | 4 +- Sources/ComputeCxx/Data/Zone.h | 12 +- Sources/ComputeCxx/Encoder/Encoder.h | 19 + Sources/ComputeCxx/Graph/Context.h | 21 + Sources/ComputeCxx/Graph/Graph.cpp | 523 ++++++- Sources/ComputeCxx/Graph/Graph.h | 191 ++- Sources/ComputeCxx/Graph/KeyTable.cpp | 36 + Sources/ComputeCxx/Graph/KeyTable.h | 30 + Sources/ComputeCxx/Graph/Trace.cpp | 12 + Sources/ComputeCxx/Graph/Trace.h | 91 ++ Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp | 26 + Sources/ComputeCxx/Graph/Tree/AGTreeValue.h | 29 + Sources/ComputeCxx/Graph/Tree/TreeElement.cpp | 15 + Sources/ComputeCxx/Graph/Tree/TreeElement.h | 51 + Sources/ComputeCxx/Log/Log.cpp | 12 + Sources/ComputeCxx/Log/Log.h | 13 + Sources/ComputeCxx/Private/CFRuntime.h | 280 ++++ Sources/ComputeCxx/Subgraph/NodeCache.cpp | 59 + Sources/ComputeCxx/Subgraph/NodeCache.h | 61 + Sources/ComputeCxx/Subgraph/Subgraph.cpp | 1204 +++++++++++++++++ Sources/ComputeCxx/Subgraph/Subgraph.h | 171 ++- Sources/ComputeCxx/Vector/Vector.tpp | 2 +- 36 files changed, 3194 insertions(+), 99 deletions(-) create mode 100644 Sources/ComputeCxx/Attribute/AttributeType.cpp create mode 100644 Sources/ComputeCxx/Attribute/Node/InputEdge.h create mode 100644 Sources/ComputeCxx/Attribute/Node/OutputEdge.h create mode 100644 Sources/ComputeCxx/Closure/ClosureFunction.h create mode 100644 Sources/ComputeCxx/Encoder/Encoder.h create mode 100644 Sources/ComputeCxx/Graph/Context.h create mode 100644 Sources/ComputeCxx/Graph/KeyTable.cpp create mode 100644 Sources/ComputeCxx/Graph/KeyTable.h create mode 100644 Sources/ComputeCxx/Graph/Trace.cpp create mode 100644 Sources/ComputeCxx/Graph/Trace.h create mode 100644 Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp create mode 100644 Sources/ComputeCxx/Graph/Tree/AGTreeValue.h create mode 100644 Sources/ComputeCxx/Graph/Tree/TreeElement.cpp create mode 100644 Sources/ComputeCxx/Graph/Tree/TreeElement.h create mode 100644 Sources/ComputeCxx/Log/Log.cpp create mode 100644 Sources/ComputeCxx/Log/Log.h create mode 100644 Sources/ComputeCxx/Private/CFRuntime.h create mode 100644 Sources/ComputeCxx/Subgraph/NodeCache.cpp create mode 100644 Sources/ComputeCxx/Subgraph/NodeCache.h create mode 100644 Sources/ComputeCxx/Subgraph/Subgraph.cpp diff --git a/Sources/ComputeCxx/Attribute/AttributeID.cpp b/Sources/ComputeCxx/Attribute/AttributeID.cpp index 9dde0a2..401ce1b 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.cpp +++ b/Sources/ComputeCxx/Attribute/AttributeID.cpp @@ -13,7 +13,7 @@ namespace AG { std::optional AttributeID::size() const { if (is_direct()) { - const AttributeType &attribute_type = subgraph()->graph().attribute_type(to_node().type_id()); + const AttributeType &attribute_type = subgraph()->graph()->attribute_type(to_node().type_id()); size_t size = attribute_type.value_metadata().vw_size(); return std::optional(size); } @@ -67,7 +67,7 @@ OffsetAttributeID AttributeID::resolve_slow(TraversalOptions options) const { if (dependency) { auto subgraph = dependency.subgraph(); if (subgraph) { - subgraph->graph().update_attribute(dependency, false); + subgraph->graph()->update_attribute(dependency, false); } } } diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index 754504f..66bd415 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -8,7 +8,6 @@ #include "Data/Page.h" #include "Data/Pointer.h" #include "Data/Zone.h" -#include "Subgraph/Subgraph.h" CF_ASSUME_NONNULL_BEGIN @@ -18,13 +17,13 @@ class Subgraph; class Node; class IndirectNode; class OffsetAttributeID; +class RelativeAttributeID; class AttributeID { private: static constexpr uint32_t KindMask = 0x3; uint32_t _value; - AttributeID(uint32_t value) : _value(value){}; public: enum Kind : uint32_t { @@ -54,30 +53,43 @@ class AttributeID { EvaluateWeakReferences = 1 << 4, }; + explicit AttributeID(uint32_t value) : _value(value){}; AttributeID(data::ptr node) : _value(node | Kind::Direct){}; AttributeID(data::ptr indirect_node) : _value(indirect_node | Kind::Indirect){}; static AttributeID make_nil() { return AttributeID(Kind::NilAttribute); }; operator bool() const { return _value == 0; }; + uint32_t value() { return _value; }; + Kind kind() const { return Kind(_value & KindMask); }; AttributeID with_kind(Kind kind) const { return AttributeID((_value & ~KindMask) | kind); }; + AttributeID without_kind() const { return AttributeID((_value & ~KindMask)); }; bool is_direct() const { return kind() == Kind::Direct; }; bool is_indirect() const { return kind() == Kind::Indirect; }; bool is_nil() const { return kind() == Kind::NilAttribute; }; - const Node &to_node() const { + // TODO: make these data::ptr<> + Node &to_node() const { assert(is_direct()); return *data::ptr(_value & ~KindMask); }; + data::ptr to_node_ptr() const { + assert(is_direct()); + return data::ptr(_value & ~KindMask); + }; - const IndirectNode &to_indirect_node() const { + IndirectNode &to_indirect_node() const { assert(is_indirect()); return *data::ptr(_value & ~KindMask); }; + data::ptr to_indirect_node_ptr() const { + assert(is_indirect()); + return data::ptr(_value & ~KindMask); + }; - Subgraph *_Nullable subgraph() const { return static_cast(page_ptr()->zone); } + Subgraph *_Nullable subgraph() const { return reinterpret_cast(page_ptr()->zone); } data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; @@ -90,6 +102,11 @@ class AttributeID { OffsetAttributeID resolve_slow(TraversalOptions options) const; }; +class RelativeAttributeID { + private: + uint16_t _value; +}; + } // namespace AG CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Attribute/AttributeType.cpp b/Sources/ComputeCxx/Attribute/AttributeType.cpp new file mode 100644 index 0000000..6b2cfa8 --- /dev/null +++ b/Sources/ComputeCxx/Attribute/AttributeType.cpp @@ -0,0 +1,14 @@ +#include "AttributeType.h" + +#include "Attribute/Node/Node.h" + +namespace AG { + +void AttributeType::update_attribute_offset() { + uint32_t alignment_mask = uint32_t(self_metadata().getValueWitnesses()->getAlignmentMask()); + _attribute_offset = (sizeof(Node) + alignment_mask) & ~alignment_mask; +} + +static_assert(sizeof(Node) == 0x1c); + +} // namespace AG diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index 814ed47..627f9aa 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -2,46 +2,84 @@ #include +#include "Layout/LayoutDescriptor.h" #include "Swift/Metadata.h" CF_ASSUME_NONNULL_BEGIN namespace AG { +class AttributeID; class AttributeType; class AttributeVTable { public: - enum Flags : uint8_t { - HasDestroySelf = 1 << 2, - }; - using Callback = void (*)(AttributeType *attribute_type, void *body); + Callback _unknown_0x00; + Callback _unknown_0x08; Callback destroy_self; + Callback _unknown_0x18; + Callback _unknown_0x20; + Callback _update_stack_callback; // maybe initialize value }; class AttributeType { + public: + enum Flags : uint32_t { + ComparisonModeMask = 0x3, + + HasDestroySelf = 1 << 2, // 0x04 + InitialValueOfNodeState2And3 = 1 << 3, // 0x08 + UseGraphAsInitialValue = 1 << 4, // 0x10 + Unknown0x20 = 1 << 5, // 0x20 + }; + + using UpdateFunction = void (*)(void *body, AttributeID attribute); + private: swift::metadata *_self_metadata; swift::metadata *_value_metadata; - void *_field1; - void *_field2; - AttributeVTable *_v_table; - uint8_t _v_table_flags; + UpdateFunction _update_function; + void *_update_context; + AttributeVTable *_vtable; + Flags _flags; uint32_t _attribute_offset; + ValueLayout _layout; public: + class deleter {}; + const swift::metadata &self_metadata() const { return *_self_metadata; }; const swift::metadata &value_metadata() const { return *_value_metadata; }; + uint8_t node_initial_state() const { + uint8_t flag = _flags >> 3 & 1; + return flag << 3 | flag << 2; + }; + bool use_graph_as_initial_value() const { return _flags & Flags::UseGraphAsInitialValue; }; + bool unknown_0x20() const { return _flags & Flags::Unknown0x20; }; + /// Returns the offset in bytes from a Node to the attribute body, /// aligned to the body's alignment. uint32_t attribute_offset() const { return _attribute_offset; }; + void update_attribute_offset(); + + ValueLayout layout() const { return _layout; }; + void set_layout(ValueLayout layout) { _layout = layout; }; + LayoutDescriptor::ComparisonMode comparison_mode() const { + return LayoutDescriptor::ComparisonMode(_flags & Flags::ComparisonModeMask); + }; + void update_layout() { + if (!_layout) { + auto comparison_mode = LayoutDescriptor::ComparisonMode(_flags & Flags::ComparisonModeMask); + _layout = LayoutDescriptor::fetch(value_metadata(), comparison_mode, 1); + } + }; // V table methods - void v_destroy_self(void *body) { - if (_v_table_flags & AttributeVTable::Flags::HasDestroySelf) { - _v_table->destroy_self(this, body); + void vt_destroy_self(void *body) { + if (_flags & Flags::HasDestroySelf) { + _vtable->destroy_self(this, body); } } }; diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp b/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp index 844adda..d9cd851 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp @@ -4,14 +4,14 @@ namespace AG { -const MutableIndirectNode &IndirectNode::to_mutable() const { +MutableIndirectNode &IndirectNode::to_mutable() { assert(is_mutable()); - return static_cast(*this); + return static_cast(*this); } -void IndirectNode::modify(WeakAttributeID source, size_t size) { +void IndirectNode::modify(WeakAttributeID source, uint32_t offset) { _source = source; - _info.size = uint32_t(size); + _info.offset = offset; } } // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index fb5bba3..a512d3e 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -4,6 +4,7 @@ #include "Attribute/AttributeID.h" #include "Attribute/WeakAttributeID.h" +#include "OutputEdge.h" CF_ASSUME_NONNULL_BEGIN @@ -17,38 +18,56 @@ class IndirectNode { unsigned int is_mutable : 1; unsigned int traverses_graph_contexts : 1; unsigned int offset : 30; - unsigned int size : 32; }; - static_assert(sizeof(Info) == 8); - static constexpr uint32_t InvalidSize = 0xffff; + static_assert(sizeof(Info) == 4); + static constexpr uint16_t InvalidSize = 0xffff; WeakAttributeID _source; Info _info; + uint16_t _size; + uint16_t _relative_offset; // could be relative offset, see Subgraph::insert_attribute public: + IndirectNode(WeakAttributeID source, bool traverses_graph_contexts, uint32_t offset, uint16_t size); + + const WeakAttributeID &source() const { return _source; }; + bool is_mutable() const { return _info.is_mutable; }; - const MutableIndirectNode &to_mutable() const; + MutableIndirectNode &to_mutable(); bool traverses_graph_contexts() const { return _info.traverses_graph_contexts; }; uint32_t offset() const { return _info.offset; }; std::optional size() const { - return _info.size != InvalidSize ? std::optional(size_t(_info.size)) : std::optional(); + return _size != InvalidSize ? std::optional(size_t(_size)) : std::optional(); }; - const WeakAttributeID &source() const { return _source; }; - - void modify(WeakAttributeID source, size_t size); + uint16_t relative_offset() const { return _relative_offset; }; + void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; + + void modify(WeakAttributeID source, uint32_t offset); }; +static_assert(sizeof(IndirectNode) == 0x10); + class MutableIndirectNode : public IndirectNode { private: AttributeID _dependency; + data::ptr _output; + uint32_t something; + WeakAttributeID _initial_source; + uint32_t _initial_offset; public: + MutableIndirectNode(WeakAttributeID source, bool traverses_graph_contexts, uint32_t offset, uint16_t size, + WeakAttributeID initial_source, uint32_t initial_offset); + const AttributeID &dependency() const { return _dependency; }; + void set_dependency(const AttributeID &dependency) { _dependency = dependency; }; }; +static_assert(sizeof(MutableIndirectNode) == 0x28); + } // namespace AG CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Attribute/Node/InputEdge.h b/Sources/ComputeCxx/Attribute/Node/InputEdge.h new file mode 100644 index 0000000..cdd4650 --- /dev/null +++ b/Sources/ComputeCxx/Attribute/Node/InputEdge.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Data/Pointer.h" + +namespace AG { + +class Node; + +struct InputEdge { + data::ptr value; + uint8_t flags; +}; + +} // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/Node.cpp b/Sources/ComputeCxx/Attribute/Node/Node.cpp index 900277e..23d3a3d 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.cpp +++ b/Sources/ComputeCxx/Attribute/Node/Node.cpp @@ -9,14 +9,14 @@ namespace AG { void Node::update_self(const Graph &graph, void *new_self) { - auto type = graph.attribute_type(_type_id); + auto type = graph.attribute_type(_info.type_id); void *self = ((char *)this + type.attribute_offset()); - if (has_indirect_self()) { + if (_flags.has_indirect_self()) { self = *(void **)self; } - if (!_state.is_self_initialized()) { - _state = _state.with_self_initialized(true); + if (!state().is_self_initialized()) { + set_state(state().with_self_initialized(true)); type.self_metadata().vw_initializeWithCopy(static_cast(self), static_cast(new_self)); } else { @@ -26,18 +26,18 @@ void Node::update_self(const Graph &graph, void *new_self) { } void Node::destroy_self(const Graph &graph) { - if (!_state.is_self_initialized()) { + if (!state().is_self_initialized()) { return; } - _state = _state.with_self_initialized(false); + set_state(state().with_self_initialized(false)); - auto type = graph.attribute_type(_type_id); + auto type = graph.attribute_type(_info.type_id); void *self = ((char *)this + type.attribute_offset()); - if (has_indirect_self()) { + if (_flags.has_indirect_self()) { self = *(void **)self; } - type.v_destroy_self(self); + type.vt_destroy_self(self); type.self_metadata().vw_destroy(static_cast(self)); } @@ -46,11 +46,11 @@ void Node::allocate_value(Graph &graph, data::zone &zone) { return; } - auto type = graph.attribute_type(_type_id); + auto type = graph.attribute_type(_info.type_id); size_t size = type.value_metadata().vw_size(); - size_t alignment = type.value_metadata().vw_alignment(); + size_t alignment = type.value_metadata().getValueWitnesses()->getAlignmentMask(); - if (has_indirect_value()) { + if (_flags.has_indirect_value()) { _value = zone.alloc_bytes_recycle(sizeof(void *), sizeof(void *) - 1); void *value = zone.alloc_persistent(size); *(static_cast>(_value)).get() = value; @@ -66,14 +66,14 @@ void Node::allocate_value(Graph &graph, data::zone &zone) { } void Node::destroy_value(Graph &graph) { - if (!_state.is_value_initialized()) { + if (!state().is_value_initialized()) { return; } - _state = _state.with_value_initialized(false); + set_state(state().with_value_initialized(false)); - auto type = graph.attribute_type(_type_id); + auto type = graph.attribute_type(_info.type_id); void *value = _value.get(); - if (has_indirect_value()) { + if (_flags.has_indirect_value()) { value = *(void **)value; } @@ -81,11 +81,11 @@ void Node::destroy_value(Graph &graph) { } void Node::destroy(Graph &graph) { - auto type = graph.attribute_type(_type_id); + auto type = graph.attribute_type(_info.type_id); - if (_state.is_value_initialized()) { + if (state().is_value_initialized()) { void *value = _value.get(); - if (has_indirect_value()) { + if (_flags.has_indirect_value()) { value = *(void **)value; } type.value_metadata().vw_destroy(static_cast(value)); @@ -94,13 +94,13 @@ void Node::destroy(Graph &graph) { graph.did_destroy_node_value(type.value_metadata().vw_size()); } - if (_state.is_self_initialized()) { + if (state().is_self_initialized()) { void *self = ((char *)this + type.attribute_offset()); - if (has_indirect_self()) { + if (_flags.has_indirect_self()) { self = *(void **)self; } - type.v_destroy_self(self); + type.vt_destroy_self(self); type.self_metadata().vw_destroy(static_cast(self)); } } diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 30080fa..25d4774 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -3,6 +3,7 @@ #include #include "Data/Pointer.h" +#include "InputEdge.h" CF_ASSUME_NONNULL_BEGIN @@ -14,20 +15,98 @@ class zone; class AttributeType; class Graph; -class Node { +class NodeFlags { + public: + enum Flags1 : uint8_t { + // TODO: check these + IndirectAttribute = 1 << 0, // 0x1 + NilAttribute = 1 << 1, // 0x2 + }; + enum Flags2 : uint8_t {}; + enum Flags3 : uint8_t {}; + enum Flags4 : uint8_t { + HasIndirectSelf = 1 << 0, // 0x01 + HasIndirectValue = 1 << 1, // 0x02 + + Unknown0x04 = 1 << 2, // 0x04 + Unknown0x08 = 1 << 3, // 0x08 + Unknown0x10 = 1 << 4, // 0x10 + + Unknown0x20 = 1 << 5, // 0x20 - initial value + Unknown0x40 = 1 << 6, // 0x40 - didn't call mark_changed + }; + private: + uint16_t _relative_offset; + uint8_t _value3; + uint8_t _value4; + + public: + uint16_t relative_offset() const { return _relative_offset; }; + void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; + + // Flags 3 + uint8_t value3() { return _value3; }; + void set_value3(uint8_t value3) { _value3 = value3; }; + + // Flags 4 + bool has_indirect_self() const { return _value4 & Flags4::HasIndirectSelf; } + void set_has_indirect_self(bool value) { + _value4 = (_value4 & ~Flags4::HasIndirectSelf) | (value ? Flags4::HasIndirectSelf : 0); + }; + bool has_indirect_value() const { return _value4 & Flags4::HasIndirectValue; } + void set_has_indirect_value(bool value) { + _value4 = (_value4 & ~Flags4::HasIndirectValue) | (value ? Flags4::HasIndirectValue : 0); + }; + bool value4_unknown0x10() { return _value4 & Flags4::Unknown0x10; }; + void set_value4_unknown0x10(bool value) { + _value4 = (_value4 & ~Flags4::Unknown0x10) | (value ? Flags4::Unknown0x10 : 0); + }; + bool value4_unknown0x20() { return _value4 & Flags4::Unknown0x20; }; + void set_value4_unknown0x20(bool value) { + _value4 = (_value4 & ~Flags4::Unknown0x20) | (value ? Flags4::Unknown0x20 : 0); + }; + bool value4_unknown0x40() { return _value4 & Flags4::Unknown0x40; }; + void set_value4_unknown0x40(bool value) { + _value4 = (_value4 & ~Flags4::Unknown0x40) | (value ? Flags4::Unknown0x40 : 0); + }; +}; + +class Node { + public: class State { public: enum : uint8_t { - ValueInitialized = 1 << 4, - SelfInitialized = 1 << 5, + Dirty = 1 << 0, // 0x01 // Unknown0 = 1 << 0, + Pending = 1 << 1, // 0x02 // Unknown1 = 1 << 1, + Unknown2 = 1 << 2, // 0x04 set from attribute type flags & 8 + Unknown3 = 1 << 3, // 0x08 set from attribute type flags & 8 + + ValueInitialized = 1 << 4, // 0x10 + SelfInitialized = 1 << 5, // 0x20 + InUpdateStack = 1 << 6, // 0x40 + Unknown7 = 1 << 7, // 0x80 + + IsEvaluating = InUpdateStack | Unknown7, }; private: uint8_t _data; - explicit constexpr State(uint8_t data) : _data(data){}; public: + explicit constexpr State(uint8_t data) : _data(data){}; + uint8_t data() { return _data; }; + + bool is_dirty() { return _data & Dirty; } + State with_dirty(bool value) const { return State((_data & ~Dirty) | (value ? Dirty : 0)); }; + + bool is_pending() { return _data & Pending; } + State with_pending(bool value) const { return State((_data & ~Pending) | (value ? Pending : 0)); }; + + bool is_unknown2() { return _data & Unknown2; } + bool is_unknown3() { return _data & Unknown3; } + State with_unknown3(bool value) const { return State((_data & ~Unknown3) | (value ? Unknown3 : 0)); }; + bool is_value_initialized() { return _data & ValueInitialized; }; State with_value_initialized(bool value) const { return State((_data & ~ValueInitialized) | (value ? ValueInitialized : 0)); @@ -37,34 +116,65 @@ class Node { State with_self_initialized(bool value) const { return State((_data & ~SelfInitialized) | (value ? SelfInitialized : 0)); }; + + bool is_evaluating() { return _data & InUpdateStack || _data & Unknown7; } }; - enum Flags : uint8_t { - HasIndirectSelf = 1 << 0, - HasIndirectValue = 1 << 1, + private: + struct Info { + unsigned int state : 8; + unsigned int type_id : 24; + }; + static_assert(sizeof(Info) == 4); + + struct TreeInfo { + unsigned int flags : 5; + unsigned int num_input_edges : 11; + unsigned int other_flag : 16; }; + static_assert(sizeof(TreeInfo) == 4); - State _state; - uint32_t _type_id; - uint8_t _field1; - uint8_t _field2; - Flags _flags; + Info _info; + NodeFlags _flags; data::ptr _value; + TreeInfo _tree_info; // 0x0c - 5 bits of flags than count of parents?? + data::ptr _input_edges; // 0x10 + uint32_t _field0x14; // 0x14 - TODO: verify + data::ptr _next_child; // 0x18 - TODO: verify + public: - uint32_t type_id() const { return _type_id; }; + Node(State state, uint32_t type_id, uint8_t flags4); + + State state() { return State(_info.state); }; + void set_state(State state) { _info.state = state.data(); }; + uint32_t type_id() const { return uint32_t(_info.type_id); }; + + NodeFlags &flags() { return _flags; }; + uint32_t field0x14() { return _field0x14; }; - bool has_indirect_self() const { return _flags & Flags::HasIndirectSelf; }; void update_self(const Graph &graph, void *new_self); void destroy_self(const Graph &graph); - bool has_indirect_value() const { return _flags * Flags::HasIndirectValue; }; + void *get_value() { return _value.get(); }; void allocate_value(Graph &graph, data::zone &zone); void destroy_value(Graph &graph); void destroy(Graph &graph); + + uint32_t num_input_edges() { return _tree_info.num_input_edges; }; + template + requires std::invocable + void foreach_input_edge(T body) { + InputEdge *array = _input_edges.get(); + for (uint32_t i = 0, end = num_input_edges(); i != end; ++i) { + body(array[i]); + } + } }; +static_assert(sizeof(Node) == 0x1c); + } // namespace AG CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h new file mode 100644 index 0000000..7f35c19 --- /dev/null +++ b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h @@ -0,0 +1,10 @@ +#pragma once + +namespace AG { + +class OutputEdge { +public: + data::ptr node_ptr; +}; + +} // namespace AG diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.h b/Sources/ComputeCxx/Attribute/WeakAttributeID.h index 3f3eabf..d6aad89 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.h +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.h @@ -15,6 +15,8 @@ class WeakAttributeID { uint32_t _zone_id; public: + WeakAttributeID(AttributeID attribute, uint32_t zone_id) : _attribute(attribute), _zone_id(zone_id){}; + bool expired() const; const AttributeID &attribute() const; }; diff --git a/Sources/ComputeCxx/Closure/ClosureFunction.h b/Sources/ComputeCxx/Closure/ClosureFunction.h new file mode 100644 index 0000000..a7a8374 --- /dev/null +++ b/Sources/ComputeCxx/Closure/ClosureFunction.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include "AGSwiftSupport.h" + +namespace AG { + +template class ClosureFunction { + public: + using Context = const void *_Nullable; + using Callable = AG_SWIFT_CC(swift) const ReturnType (*_Nullable)(Args..., Context AG_SWIFT_CONTEXT); + + private: + Callable _function; + Context _context; + + public: + const ReturnType operator()(Args... args) const noexcept { + return _function(std::forward(args)..., _context); + } + + void release_context() { ::swift::swift_release((::swift::HeapObject *)_context); }; +}; + +// void * +template + requires std::is_pointer_v +using ClosureFunctionVP = ClosureFunction; + +// void +template + requires std::same_as +using ClosureFunctionVV = ClosureFunction; + +// unsigned long, AGUnownedGraphContext * +template + requires std::unsigned_integral +using ClosureFunctionCI = ClosureFunction; + +// void, void * +template + requires std::same_as +using ClosureFunctionPV = ClosureFunction; + +// void, unsigned int +template + requires std::same_as && std::unsigned_integral +using ClosureFunctionAV = ClosureFunction; + +// bool, unsigned int +template + requires std::same_as && std::unsigned_integral +using ClosureFunctionAB = ClosureFunction; + +} // namespace AG diff --git a/Sources/ComputeCxx/Data/Page.h b/Sources/ComputeCxx/Data/Page.h index 2e758db..0aa34c9 100644 --- a/Sources/ComputeCxx/Data/Page.h +++ b/Sources/ComputeCxx/Data/Page.h @@ -13,11 +13,14 @@ namespace data { class zone; struct page { - zone *zone; - ptr previous; - uint32_t total; - uint32_t in_use; + zone *zone; // 0 + ptr previous; // 8 + uint32_t total; // 12 + uint32_t in_use; // 16 + uint16_t relative_offset_1; // 0x14/20 + uint16_t relative_offset_2; // 0x16/22 }; +static_assert(sizeof(page) == 0x18); } // namespace data } // namespace AG diff --git a/Sources/ComputeCxx/Data/Pointer.h b/Sources/ComputeCxx/Data/Pointer.h index df9b83f..8cbfb71 100644 --- a/Sources/ComputeCxx/Data/Pointer.h +++ b/Sources/ComputeCxx/Data/Pointer.h @@ -42,7 +42,8 @@ template class ptr { ptr page_ptr() const noexcept { return ptr(_offset & ~page_alignment_mask); } - difference_type page_relative_offset() const noexcept { return _offset & page_alignment_mask; } + difference_type offset() const noexcept { return _offset; } + difference_type offset_from_page() const noexcept { return _offset & page_alignment_mask; } template ptr aligned(difference_type alignment_mask = sizeof(difference_type) - 1) const { return ptr((_offset + alignment_mask) & ~alignment_mask); @@ -52,6 +53,8 @@ template class ptr { std::add_lvalue_reference_t operator*() const noexcept { return *get(); }; T *_Nonnull operator->() const noexcept { return get(); }; + bool operator==(const ptr &other) const noexcept { return _offset == other._offset; }; + bool operator!=(const ptr &other) const noexcept { return _offset != other._offset; }; bool operator==(nullptr_t) const noexcept { return _offset == 0; }; bool operator!=(nullptr_t) const noexcept { return _offset != 0; }; diff --git a/Sources/ComputeCxx/Data/Zone.cpp b/Sources/ComputeCxx/Data/Zone.cpp index 4551d66..f511b6d 100644 --- a/Sources/ComputeCxx/Data/Zone.cpp +++ b/Sources/ComputeCxx/Data/Zone.cpp @@ -27,8 +27,8 @@ void zone::clear() { void zone::realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask) { if (new_size > size && *buffer) { auto page = buffer->page_ptr(); - if ((page->in_use == buffer->page_relative_offset() + size && - page->total >= buffer->page_relative_offset() + new_size)) { + if ((page->in_use == buffer->offset_from_page() + size && + page->total >= buffer->offset_from_page() + new_size)) { page->in_use += new_size - size; } else { ptr new_buffer = alloc_bytes_recycle(new_size, alignment_mask); diff --git a/Sources/ComputeCxx/Data/Zone.h b/Sources/ComputeCxx/Data/Zone.h index f7ef1fe..4fbbdfb 100644 --- a/Sources/ComputeCxx/Data/Zone.h +++ b/Sources/ComputeCxx/Data/Zone.h @@ -16,7 +16,10 @@ class zone { public: class info { private: - constexpr static uint32_t zone_id_mask = 0x7fffffff; + enum { + zone_id_mask = 0x7fffffff, + invalidated = 0x80000000, + }; uint32_t _value; info(uint32_t value) : _value(value){}; @@ -24,6 +27,8 @@ class zone { uint32_t zone_id() { return _value & zone_id_mask; }; info with_zone_id(uint32_t zone_id) const { return info((_value & ~zone_id_mask) | (zone_id & zone_id_mask)); }; + info with_invalidated() const { return info(_value | invalidated); }; + uint32_t to_raw_value() { return _value; }; static info from_raw_value(uint32_t value) { return info(value); }; }; @@ -44,6 +49,9 @@ class zone { ~zone(); info info() { return _info; }; + void set_invalidated_flag() { _info = _info.with_invalidated(); }; + + ptr last_page() { return _last_page; }; void clear(); void realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask); @@ -57,7 +65,7 @@ class zone { void *alloc_persistent(size_t size); // Printing - void print_header(); + static void print_header(); void print(); }; diff --git a/Sources/ComputeCxx/Encoder/Encoder.h b/Sources/ComputeCxx/Encoder/Encoder.h new file mode 100644 index 0000000..c94cb7b --- /dev/null +++ b/Sources/ComputeCxx/Encoder/Encoder.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Encoder { + public: + void encode_varint(uint64_t value); + + void begin_length_delimited(); + void end_length_delimited(); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h new file mode 100644 index 0000000..d0dfe57 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Context.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Graph::Context { + private: + Graph *_graph; + + public: + Graph &graph() const { return *_graph; }; + + uint64_t unique_id(); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 1c16cdd..a13cdb4 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1,24 +1,28 @@ #include "Graph.h" +#include + #include "Attribute/AttributeType.h" +#include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" +#include "Attribute/OffsetAttributeID.h" +#include "Attribute/WeakAttributeID.h" +#include "Errors/Errors.h" +#include "KeyTable.h" +#include "Log/Log.h" +#include "Subgraph/Subgraph.h" +#include "Swift/Metadata.h" +#include "Trace.h" namespace AG { -void Graph::trace_assertion_failure(bool all_stop_tracing, const char *format, ...) { - // TODO: Not implemented -} +const AttributeType &Graph::attribute_type(uint32_t type_id) const { return *_types[type_id]; } -const AttributeType &Graph::attribute_type(uint32_t type_id) const { - // TODO: Not implemented - throw; -} - -const AttributeType &Graph::attribute_ref(data::ptr attribute, const void *_Nullable *_Nullable ref_out) const { - auto &type = attribute_type(attribute->type_id()); +const AttributeType &Graph::attribute_ref(data::ptr node, const void *_Nullable *_Nullable ref_out) const { + auto &type = attribute_type(node->type_id()); if (ref_out) { - void *self = ((char *)attribute.get() + type.attribute_offset()); - if (attribute->has_indirect_self()) { + void *self = ((char *)node.get() + type.attribute_offset()); + if (node->flags().has_indirect_self()) { self = *(void **)self; } *ref_out = self; @@ -26,16 +30,503 @@ const AttributeType &Graph::attribute_ref(data::ptr attribute, const void return type; } -void Graph::did_allocate_node_value(size_t size) { - // TODO: Not implemented +void Graph::attribute_modify(data::ptr node, const swift::metadata &metadata, + ClosureFunctionPV modify, bool flag) { + if (!node->state().is_self_initialized()) { + precondition_failure("no self data: %u", node); + } + + auto type = attribute_type(node->type_id()); + if (&type.self_metadata() != &metadata) { + precondition_failure("self type mismatch: %u", node); + } + + foreach_trace([&node](Trace &trace) { trace.begin_modify(node); }); + + void *body = (uint8_t *)node.get() + type.attribute_offset(); + if (node->flags().has_indirect_self()) { + body = *(void **)body; + } + modify(body); + + foreach_trace([&node](Trace &trace) { trace.end_modify(node); }); + + if (flag) { + node->flags().set_value4_unknown0x40(true); + mark_pending(node, node.get()); + } } -void Graph::did_destroy_node_value(size_t size) { - // TODO: Not implemented +// MARK: Attributes + +data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *value) { + const AttributeType &type = attribute_type(type_id); + + void *effective_value = nullptr; + if (type.use_graph_as_initial_value()) { + effective_value = this; + } + if (value != nullptr || type.value_metadata().vw_size() != 0) { + effective_value = value; + } + + void *buffer = nullptr; + size_t size = type.self_metadata().vw_size(); + size_t alignment_mask = type.self_metadata().getValueWitnesses()->getAlignmentMask(); + if (!type.self_metadata().getValueWitnesses()->isBitwiseTakable() || type.self_metadata().vw_size() > 0x80) { + size = sizeof(void *); + alignment_mask = 7; + buffer = subgraph.alloc_persistent(size); + } + + size_t total_size = ((sizeof(Node) + alignment_mask) & ~alignment_mask) + size; + + data::ptr node; + if (total_size > 0x10) { + node = (data::ptr)subgraph.alloc_bytes_recycle(uint32_t(total_size), uint32_t(alignment_mask | 3)); + } else { + node = (data::ptr)subgraph.alloc_bytes(uint32_t(total_size), uint32_t(alignment_mask | 3)); + } + *node = Node(Node::State(type.node_initial_state()), type_id, 0x20); + + if (type_id >= 0x100000) { + precondition_failure("too many node types allocated"); + } + + void *self = (uint8_t *)node.get() + type.attribute_offset(); + + node->set_state(node->state().with_self_initialized(true)); + if (node->state().is_unknown3() && !type.value_metadata().getValueWitnesses()->isPOD()) { + node->flags().set_value4_unknown0x20(!type.unknown_0x20()); // toggle + } else { + node->flags().set_value4_unknown0x20(false); + } + if (buffer != nullptr) { + node->flags().set_has_indirect_self(true); + *(void **)self = buffer; + } + + if (!type.value_metadata().getValueWitnesses()->isBitwiseTakable() || type.value_metadata().vw_size() > 0x80) { + node->flags().set_has_indirect_value(true); + } + + _num_nodes += 1; + _num_node_values += 1; + + if (type.self_metadata().vw_size() != 0) { + void *self_dest = self; + if (node->flags().has_indirect_self()) { + self_dest = *(void **)self_dest; + } + type.self_metadata().vw_initializeWithCopy((swift::opaque_value *)self_dest, (swift::opaque_value *)body); + } + + if (effective_value != nullptr) { + value_set_internal(node, *node.get(), effective_value, type.value_metadata()); + } else { + node->set_state(node->state().with_dirty(true).with_pending(true)); + subgraph.add_dirty_flags(node->flags().value3()); + } + + subgraph.add_node(node); + return node; } void Graph::update_attribute(AttributeID attribute, bool option) { // TODO: Not implemented } +#pragma mark - Indirect attributes + +void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, + std::optional size, bool is_mutable) { + if (subgraph.graph() != attribute.subgraph()->graph()) { + precondition_failure("attribute references can't cross graph namespaces"); + } + + auto offset_attribute = attribute.resolve(AttributeID::TraversalOptions::SkipMutableReference); + attribute = offset_attribute.attribute(); + if (__builtin_add_overflow(offset, offset_attribute.offset(), &offset) || + offset + offset_attribute.offset() > 0x3ffffffe) { + precondition_failure("indirect attribute overflowed: %lu + %lu", offset, offset_attribute.offset()); + } + + if (size.has_value()) { + auto attribute_size = attribute.size(); + if (attribute_size.has_value() && attribute_size.value() < offset + size.value()) { + // TODO: check args + precondition_failure("invalid size for indirect attribute: %d vs %u", attribute_size.has_value(), + offset_attribute.offset()); + } + } + + if (is_mutable) { + auto indirect_node = (data::ptr)subgraph.alloc_bytes(sizeof(MutableIndirectNode), 3); + + uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; + auto source = WeakAttributeID(attribute, zone_id); + bool traversers_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); + uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; + *indirect_node = MutableIndirectNode(source, traversers_graph_contexts, offset, node_size, source, offset); + + add_input_dependencies(AttributeID(indirect_node).with_kind(AttributeID::Kind::Indirect), attribute); + subgraph.add_indirect((data::ptr)indirect_node, true); + } else { + auto indirect_node = (data::ptr)subgraph.alloc_bytes_recycle(sizeof(Node), 3); + + uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; + auto source = WeakAttributeID(attribute, zone_id); + bool traversers_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); + uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; + *indirect_node = IndirectNode(source, traversers_graph_contexts, offset, node_size); + + subgraph.add_indirect(indirect_node, &subgraph != attribute.subgraph()); + } +} + +void Graph::indirect_attribute_set(data::ptr attribute, AttributeID source) { + if (!attribute->is_mutable()) { + precondition_failure("not an indirect attribute: %u", attribute); + } + if (AttributeID(attribute).subgraph()->graph() != source.subgraph()->graph()) { + precondition_failure("attribute references can't cross graph namespaces"); + } + + foreach_trace([&attribute, &source](Trace &trace) { trace.set_source(attribute, source); }); + + OffsetAttributeID resolved_source = source.resolve(AttributeID::TraversalOptions::SkipMutableReference); + + // TODO: ... +} + +void Graph::indirect_attribute_reset(data::ptr attribute, bool flag) {} + +const AttributeID &Graph::indirect_attribute_dependency(data::ptr attribute) { + // Status: Verified + if (!attribute->is_mutable()) { + precondition_failure("not an indirect attribute: %u", attribute); + } + return attribute->to_mutable().dependency(); +} + +void Graph::indirect_attribute_set_dependency(data::ptr attribute, AttributeID dependency) { + // Status: Verified + if (dependency.without_kind()) { + if (!dependency.is_direct()) { + precondition_failure("indirect dependencies must be attributes"); + } + if (AttributeID(attribute).subgraph() != dependency.subgraph()) { + precondition_failure("indirect dependencies must share a subgraph with their attribute"); + } + } else { + dependency = AttributeID(0); + } + if (!attribute->is_mutable()) { + precondition_failure("not an indirect attribute: %u", attribute); + } + + foreach_trace([&attribute, &dependency](Trace &trace) { trace.set_dependency(attribute, dependency); }); + + AttributeID old_dependency = attribute->to_mutable().dependency(); + if (old_dependency != dependency) { + AttributeID indirect_attribute = AttributeID(attribute).with_kind(AttributeID::Kind::Indirect); + if (old_dependency) { + remove_output_edge((data::ptr)old_dependency, indirect_attribute); + } + attribute->to_mutable().set_dependency(dependency); + if (dependency) { + add_output_edge((data::ptr)dependency, indirect_attribute); + if (dependency.to_node().state().is_dirty()) { + propagate_dirty(indirect_attribute); + } + } + } +} + +#pragma mark - Values + +// Status: Verified +bool Graph::value_exists(data::ptr node) { return node->state().is_value_initialized(); } + +AGValueState Graph::value_state(AttributeID attribute) { + if (!attribute.is_direct()) { + auto resolved_attribute = attribute.resolve(AttributeID::TraversalOptions::AssertNotNil); + attribute = resolved_attribute.attribute(); + } + if (!attribute.is_direct()) { + return 0; + } + + auto node = attribute.to_node(); + return (node.state().is_dirty() ? 1 : 0) << 0 | (node.state().is_pending() ? 1 : 0) << 1 | + (node.state().is_evaluating() ? 1 : 0) << 2 | (node.state().is_value_initialized() ? 1 : 0) << 3 | + (node.state().is_unknown2() ? 1 : 0) << 4 | (node.flags().value4_unknown0x20() ? 1 : 0) << 5 | + (node.state().is_unknown3() ? 1 : 0) << 6 | (node.flags().value4_unknown0x40() ? 1 : 0) << 7; +} + +void Graph::value_mark(data::ptr node) { + // iVar2 = tpidrro_el0; + // uVar6 = *(uint *)(iVar2 + _current_update_key * 8); + // if (((((uVar6 & 1) == 0) && + // (puVar7 = (undefined8 *)(uVar6 & 0xfffffffffffffffe), puVar7 != (undefined8 *)0x0)) && + // ((Graph *)*puVar7 == this)) && (((*node & 0xc0) != 0 || (0x1f < node[5])))) { + // /* WARNING: Subroutine does not return */ + // precondition_failure("setting value during update: %u",(char)node_ptr,in_w2); + // } + + foreach_trace([&node](Trace &trace) { trace.mark_value(node); }); + + AttributeType &type = *_types[node->type_id()]; + if (type.use_graph_as_initial_value()) { + mark_changed(node, nullptr, nullptr, nullptr); + } else { + node->flags().set_value4_unknown0x40(true); + + if (!node->state().is_dirty()) { + foreach_trace([&node](Trace &trace) { trace.set_dirty(node, true); }); + node->set_state(node->state().with_dirty(true)); + } + if (!node->state().is_pending()) { + foreach_trace([&node](Trace &trace) { trace.set_pending(node, true); }); + node->set_state(node->state().with_pending(true)); + } + if (node->flags().value3()) { + Subgraph *subgraph = AttributeID(node).subgraph(); + subgraph->add_dirty_flags(node->flags().value3()); + } + } + + propagate_dirty(node); +} + +void Graph::value_mark_all() { + // iVar8 = tpidrro_el0; + // uVar5 = *(uint *)(iVar8 + _current_update_key * 8); + // if (1 < uVar5 && (uVar5 & 1) == 0) { + // /* WARNING: Subroutine does not return */ + // precondition_failure("invalidating all values during update",in_w1,in_w2); + // } + + for (auto subgraph : _subgraphs) { + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; // TODO: check if this should break out of entire loop + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } + + if (attribute.is_direct()) { + auto node = attribute.to_node(); + AttributeType &type = *_types[node.type_id()]; + if (!type.use_graph_as_initial_value()) { + node.set_state(node.state().with_unknown3(true)); + subgraph->add_dirty_flags(node.flags().value3()); + } + node.foreach_input_edge([](InputEdge &edge) { edge.flags |= 8; }); + } + } + } + } +} + +bool Graph::value_set(data::ptr node, const swift::metadata &value_type, const void *value) { + if (node->num_input_edges() > 0 && node->state().is_value_initialized()) { + precondition_failure("can only set initial value of computed attributes: %u", node); + } + + // iVar1 = tpidrro_el0; + // uVar4 = *(uint *)(iVar1 + _current_update_key * 8); + // if (((((uVar4 & 1) == 0) && + // (puVar5 = (undefined8 *)(uVar4 & 0xfffffffffffffffe), puVar5 != (undefined8 *)0x0)) && + // ((Graph *)*puVar5 == this)) && + // ((0x1f < node->child || ((node->lifecycle_flags & 0xc0U) != 0)))) { + // /* WARNING: Subroutine does not return */ + // precondition_failure("setting value during update: %u",(char)attribute,(char)node); + // } + + bool changed = value_set_internal(node, *node.get(), value, value_type); + if (changed) { + propagate_dirty(node); + } + return changed; +} + +bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void *value, + const swift::metadata &value_type) { + foreach_trace([&node_ptr, &value](Trace &trace) { trace.set_value(node_ptr, value); }); + + AttributeType &type = *_types[node.type_id()]; + if (&type.value_metadata() != &value_type) { + precondition_failure("invalid value type for attribute: %u (saw %s, expected %s)", + type.value_metadata().name(false), value_type.name(false)); + } + + if (node.state().is_value_initialized()) { + // already initialized + void *value_dest = node.get_value(); + if (node.flags().has_indirect_value()) { + value_dest = *(void **)value_dest; + } + + LayoutDescriptor::ComparisonOptions comparison_options = + LayoutDescriptor::ComparisonOptions(type.comparison_mode()) | + LayoutDescriptor::ComparisonOptions::CopyOnWrite | LayoutDescriptor::ComparisonOptions::ReportFailures; + if (type.layout() == nullptr) { + type.set_layout(LayoutDescriptor::fetch(value_type, comparison_options, 0)); + } + ValueLayout layout = type.layout() == ValueLayoutEmpty ? nullptr : type.layout(); + + // TODO: make void * and rename to dest and source + if (LayoutDescriptor::compare(layout, (const unsigned char *)value_dest, (const unsigned char *)value, + value_type.vw_size(), comparison_options)) { + return false; + } + + if (_traces.empty()) { + // TODO: finish + } else { + mark_changed(AttributeID(node_ptr), type, value_dest, value, 0); + } + + value_type.vw_assignWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); + } else { + // not initialized yet + node.allocate_value(*this, *AttributeID(node_ptr).subgraph()); + + // TODO: wrap in initialize_value on Node... + node.set_state(node.state().with_value_initialized(true)); + mark_changed(node_ptr, nullptr, nullptr, nullptr); + + void *value_dest = node.get_value(); + if (node.flags().has_indirect_value()) { + value_dest = *(void **)value_dest; + } + value_type.vw_initializeWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); + } +} + +// MARK: Marks + +void Graph::mark_pending(data::ptr node_ptr, Node *node) { + if (!node->state().is_pending()) { + foreach_trace([&node_ptr](Trace &trace) { trace.set_pending(node_ptr, true); }); + node->set_state(node->state().with_pending(true)); + } + if (!node->state().is_dirty()) { + foreach_trace([&node_ptr](Trace &trace) { trace.set_dirty(node_ptr, true); }); + node->set_state(node->state().with_dirty(true)); + + uint8_t dirty_flags = node->flags().value3(); + Subgraph *subgraph = AttributeID(node_ptr).subgraph(); + if (dirty_flags && subgraph != nullptr) { + subgraph->add_dirty_flags(dirty_flags); + } + + propagate_dirty(node_ptr); + } +} + +#pragma mark - Interning + +uint32_t Graph::intern_key(const char *key) { + if (_keys == nullptr) { + _keys = new Graph::KeyTable(&_heap); + } + const char *found = nullptr; + uint32_t key_id = _keys->lookup(key, &found); + if (found == nullptr) { + key_id = _keys->insert(key); + } + return key_id; +} + +const char *Graph::key_name(uint32_t key_id) { + if (_keys != nullptr && key_id < _keys->size()) { + return _keys->get(key_id); + } + AG::precondition_failure("invalid string key id: %u", key_id); +} + +uint32_t Graph::intern_type(swift::metadata *metadata, ClosureFunctionVP make_type) { + uint32_t type_id = uint32_t(reinterpret_cast(_type_ids_by_metadata.lookup(metadata, nullptr))); + if (type_id) { + return type_id; + } + + AttributeType *type = (AttributeType *)make_type(); + type->update_attribute_offset(); + + static bool prefetch_layouts = []() -> bool { + char *result = getenv("AG_PREFETCH_LAYOUTS"); + if (result) { + return atoi(result) != 0; + } + return false; + }(); + if (prefetch_layouts) { + type->update_layout(); + } + + type_id = _types.size(); + if (type_id >= 0xffffff) { + precondition_failure("overflowed max type id: %u", type_id); + } + _types.push_back(type); + _type_ids_by_metadata.insert(metadata, reinterpret_cast(uintptr_t(type_id))); + + size_t self_size = type->self_metadata().vw_size(); + if (self_size >= 0x2000) { + os_log_info(misc_log(), "large attribute self: %u bytes, %s", uint(self_size), + type->self_metadata().name(false)); + } + + size_t value_size = type->value_metadata().vw_size(); + if (value_size >= 0x2000) { + os_log_info(misc_log(), "large attribute value: %u bytes, %s -> %s", uint(value_size), + type->self_metadata().name(false), type->value_metadata().name(false)); + } + + return type_id; +} + +#pragma mark - Tracing + +void Graph::add_trace(Trace *_Nullable trace) { + if (trace == nullptr) { + return; + } + trace->begin_trace(*this); + _traces.push_back(trace); +} + +void Graph::remove_trace(uint64_t trace_id) { + auto iter = std::remove_if(_traces.begin(), _traces.end(), + [&trace_id](auto trace) -> bool { return trace->trace_id() == trace_id; }); + Trace *trace = *iter; + trace->end_trace(*this); + // destructor? + _traces.erase(iter); +} + +void Graph::trace_assertion_failure(bool all_stop_tracing, const char *format, ...) { + // TODO: Not implemented +} + +#pragma mark - Printing + +void Graph::print_data() { + data::table::shared().print(); + data::zone::print_header(); + for (auto subgraph : _subgraphs) { + subgraph->data::zone::print(); // TODO: make first field.. + } +} + } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index d1305f6..13439c1 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -2,26 +2,209 @@ #include #include +#include #include "Attribute/AttributeID.h" +#include "Closure/ClosureFunction.h" +#include "Util/HashTable.h" +#include "Util/Heap.h" CF_ASSUME_NONNULL_BEGIN +typedef uint8_t AGValueState; // TODO: move + namespace AG { +namespace swift { +class metadata; +} class AttributeType; +class Subgraph; +class Encoder; +class Trace; class Graph { public: - static void trace_assertion_failure(bool all_stop_tracing, const char *format, ...); + class Context; + class KeyTable; + class UpdateStack; + enum class UpdateStatus; + + struct TreeElement; + struct TreeValue; + class TreeDataElement { + using TreeElementNodePair = std::pair, data::ptr>; + + private: + vector _nodes; + bool _sorted; + + public: + vector nodes() { return _nodes; }; + void sort_nodes(); + void push_back(TreeElementNodePair pair) { _nodes.push_back(pair); }; + }; + + private: + Graph *_prev; + Graph *_next; + util::Heap _heap; + util::UntypedTable _type_ids_by_metadata; + vector _types; + + vector _traces; + uint64_t _field_0xf0; + + size_t _allocated_node_values_size = 0; + + std::unique_ptr> _tree_data_elements_by_subgraph; + KeyTable *_Nullable _keys; + + // TODO: check field offets + vector _subgraphs; + vector _subgraphs_with_cached_nodes; + vector _invalidated_subgraphs; // TODO: check is 2 stack length + + // TODO: check field offets + uint64_t _num_nodes; // probably this, not sure + uint64_t _num_node_values; // probably this, not sure + + bool _needs_update; // 0x199 + + uint64_t _counter_0x1b8; + + public: + bool needs_update() { return _needs_update; }; + void call_update(); + + bool thread_is_updating(); + + void increment_counter_0x1b8() { _counter_0x1b8 += 1; }; + + // MARK: Subgraphs + + bool is_validating_0x198(); + void set_validating_0x198(bool value); + + bool field_0xf0(); + + void invalidate_subgraphs(); + void will_invalidate_subgraph(Subgraph &subgraph) { _invalidated_subgraphs.push_back(&subgraph); }; + + vector &subgraphs(); + void remove_subgraph(const Subgraph &subgraph); + + void add_subgraphs_with_cached_node(Subgraph *subgraph) { + _subgraphs_with_cached_nodes.push_back(subgraph); + } + + // MARK: Attributes const AttributeType &attribute_type(uint32_t type_id) const; - const AttributeType &attribute_ref(data::ptr attribute, const void *_Nullable *_Nullable ref_out) const; + const AttributeType &attribute_ref(data::ptr node, const void *_Nullable *_Nullable ref_out) const; + + void attribute_modify(data::ptr node, const swift::metadata &type, ClosureFunctionPV closure, + bool flag); - void did_allocate_node_value(size_t size); - void did_destroy_node_value(size_t size); + data::ptr add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *_Nullable value); + void remove_node(data::ptr node); void update_attribute(AttributeID attribute, bool option); + + void did_allocate_node_value(size_t size) { _allocated_node_values_size += size; }; + void did_destroy_node_value(size_t size) { _allocated_node_values_size -= size; }; + + void did_destroy_node(); // decrement counter 0x100 + + // MARK: Indirect attributes + + void add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, std::optional size, + bool is_mutable); + void remove_indirect_node(data::ptr node); + + void indirect_attribute_set(data::ptr, AttributeID source); + void indirect_attribute_reset(data::ptr, bool flag); + + const AttributeID &indirect_attribute_dependency(data::ptr indirect_node); + void indirect_attribute_set_dependency(data::ptr indirect_node, AttributeID dependency); + + // MARK: Edges + + void add_input(data::ptr node, AttributeID attribute, bool flag, uint32_t option); + void add_input_dependencies(AttributeID attribute, AttributeID source); + void remove_all_inputs(data::ptr node); + + void any_inputs_changed(data::ptr node, const uint32_t *arg1, uint64_t arg2); + void all_inputs_removed(data::ptr node); + + template void add_output_edge(data::ptr node, AttributeID attribute); + template void remove_output_edge(data::ptr node, AttributeID attribute); + + // MARK: Values + + bool value_exists(data::ptr node); + AGValueState value_state(AttributeID attribute); + + void value_mark(data::ptr node); + void value_mark_all(); + + bool value_set(data::ptr node, const swift::metadata &value_type, const void *value); + bool value_set_internal(data::ptr node_ptr, Node &node, const void *value, const swift::metadata &type); + + void propagate_dirty(AttributeID attribute); + + // MARK: Marks + + void mark_changed(data::ptr node, AttributeType *_Nullable type, const void *_Nullable destination_value, + const void *_Nullable source_value); + void mark_changed(AttributeID attribute, AttributeType &type, const void *_Nullable destination_value, + const void *_Nullable source_value, uint64_t option); + + void mark_pending(data::ptr node_ptr, Node *node); + + Graph::TreeDataElement &tree_data_element_for_subgraph(Subgraph *subgraph) { + if (!_tree_data_elements_by_subgraph) { + _tree_data_elements_by_subgraph.reset(new std::unordered_map()); + } + return _tree_data_elements_by_subgraph->try_emplace(subgraph).first->second; + }; + + std::unordered_map *tree_data_elements() { + return _tree_data_elements_by_subgraph.get(); + }; + + // MARK: Intern + + uint32_t intern_key(const char *key); + const char *key_name(uint32_t key_id); + + uint32_t intern_type(swift::metadata *metadata, ClosureFunctionVP make_type); + + // MARK: Tracing + + void add_trace(Trace *_Nullable trace); + void remove_trace(uint64_t trace_id); + + template + requires std::invocable + void foreach_trace(T body) { + for (auto trace = _traces.rbegin(), end = _traces.rend(); trace != end; ++trace) { + body(**trace); + } + }; + + static void trace_assertion_failure(bool all_stop_tracing, const char *format, ...); + + // MARK: Encoding + + void encode_node(Encoder &encoder, const Node &node, bool flag); + void encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_node); + + void encode_tree(Encoder &encoder, data::ptr tree); + + // MARK: Printing + + void print_data(); }; } // namespace AG diff --git a/Sources/ComputeCxx/Graph/KeyTable.cpp b/Sources/ComputeCxx/Graph/KeyTable.cpp new file mode 100644 index 0000000..d6e7c98 --- /dev/null +++ b/Sources/ComputeCxx/Graph/KeyTable.cpp @@ -0,0 +1,36 @@ +#include "KeyTable.h" + +namespace AG { + +Graph::KeyTable::KeyTable(util::Heap *_Nullable heap) + : _table([](const void *v) { return (uint64_t)util::string_hash(reinterpret_cast(v)); }, + [](const void *a, const void *b) { + return std::strcmp(reinterpret_cast(a), reinterpret_cast(b)) == 0; + }, + nullptr, nullptr, heap){}; + +uint32_t Graph::KeyTable::lookup(const char *key, const char *_Nullable *_Nullable found_key) const { + auto result = _table.lookup(key, reinterpret_cast(found_key)); + return (uint32_t)(uintptr_t)result; +} + +uint32_t Graph::KeyTable::size() { + return _keys.size(); +} + +uint32_t Graph::KeyTable::insert(const char *key) { + const char *duplicate = strdup(key); + if (!duplicate) { + precondition_failure("memory allocation failure"); + } + uint32_t key_id = _keys.size(); + _keys.push_back(duplicate); + _table.insert(duplicate, (void *)(uintptr_t)key_id); + return key_id; +} + +const char *Graph::KeyTable::get(uint32_t key_id) { + return _keys[key_id]; +} + +}; // namespace AG diff --git a/Sources/ComputeCxx/Graph/KeyTable.h b/Sources/ComputeCxx/Graph/KeyTable.h new file mode 100644 index 0000000..3ac7e23 --- /dev/null +++ b/Sources/ComputeCxx/Graph/KeyTable.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "Graph.h" +#include "Util/HashTable.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Graph::KeyTable { + private: + vector _keys; + util::UntypedTable _table; + + public: + KeyTable(util::Heap *_Nullable heap); + + uint32_t size(); + + uint32_t lookup(const char *key, const char *_Nullable *_Nullable found_key) const; + const char *get(uint32_t key_id); + + uint32_t insert(const char *key); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Trace.cpp b/Sources/ComputeCxx/Graph/Trace.cpp new file mode 100644 index 0000000..69dc13f --- /dev/null +++ b/Sources/ComputeCxx/Graph/Trace.cpp @@ -0,0 +1,12 @@ +#include "Trace.h" + +namespace AG { + +void Trace::log_message(const char *format, ...) { + va_list args; + va_start(args, format); + log_message_v(format, args); + va_end(args); +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Trace.h b/Sources/ComputeCxx/Graph/Trace.h new file mode 100644 index 0000000..4ff17b2 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Trace.h @@ -0,0 +1,91 @@ +#pragma once + +#include + +#include "Graph.h" + +namespace AG { + +class Node; +class Subgraph; + +class Trace { + private: + uint64_t _trace_id; + + public: + uint64_t trace_id() { return _trace_id; } + + // Trace + virtual void begin_trace(const Graph &graph); + virtual void end_trace(const Graph &graph); + virtual void sync_trace(); + + // Log + virtual void log_message_v(const char *format, va_list args); + void log_message(const char *format, ...); + + // Updates + virtual void begin_update(const Subgraph &subgraph, uint32_t options); + virtual void end_update(const Subgraph &subgraph); + virtual void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options); + virtual void end_update(const Graph::UpdateStack &update_stack, data::ptr node, + Graph::UpdateStatus update_status); + virtual void begin_update(data::ptr node); + virtual void end_update(data::ptr node, bool changed); + virtual void begin_update(const Graph::Context &context); + virtual void end_update(const Graph::Context &context); + + virtual void begin_invalidation(const Graph::Context &context, data::ptr node); + virtual void end_invalidation(const Graph::Context &context, data::ptr node); + + virtual void begin_modify(data::ptr node); + virtual void end_modify(data::ptr node); + + virtual void begin_event(data::ptr node, uint32_t event); + virtual void end_event(data::ptr node, uint32_t event); + + virtual void created(const Graph::Context &context); + virtual void destroy(const Graph::Context &context); + virtual void needs_update(const Graph::Context &context); + + virtual void created(const Subgraph &subgraph); + virtual void invalidate(const Subgraph &subgraph); + virtual void destroy(const Subgraph &subgraph); + + virtual void add_child(const Subgraph &subgraph, const Subgraph &child); + virtual void remove_child(const Subgraph &subgraph, const Subgraph &child); + + virtual void added(data::ptr node); + + virtual void add_edge(data::ptr node, AttributeID attribute, uint32_t options); + virtual void remove_edge(data::ptr node, uint32_t options); + virtual void set_edge_pending(data::ptr node, AttributeID attribute, bool flag); + + virtual void set_dirty(data::ptr node, bool flag); + virtual void set_pending(data::ptr node, bool flag); + + virtual void set_value(data::ptr node, const void *value); + virtual void mark_value(data::ptr node); + + virtual void added(data::ptr indirect_node); + + virtual void set_source(data::ptr indirect_node, AttributeID source); + virtual void set_dependency(data::ptr indirect_node, AttributeID dependency); + + virtual void set_deadline(uint64_t deadline); + virtual void passed_deadline(); + + virtual void mark_profile(const Graph &graph, uint32_t options); + + virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, + const swift::metadata &type); + virtual bool named_event(const Graph::Context &context, uint32_t arg2, uint64_t arg3, const uint32_t *arg4, + CFDataRef arg5, uint32_t arg6); + virtual bool named_event_enabled(uint32_t options); + + virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t lhs_offset, + size_t rhs_offset, const swift::metadata &type); +}; + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp new file mode 100644 index 0000000..b14dc2b --- /dev/null +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp @@ -0,0 +1,26 @@ +#include "AGTreeValue.h" + +#include "Graph/Graph.h" +#include "Subgraph/Subgraph.h" +#include "TreeElement.h" + +AGTypeID AGTreeValueGetType(AGTreeValue tree_value) { + auto tree_value_id = AG::TreeValueID(tree_value); + return AGTypeID(tree_value_id.to_tree_value().type); +} + +AGAttribute AGTreeValueGetValue(AGTreeValue tree_value) { + auto tree_value_id = AG::TreeValueID(tree_value); + return AGAttribute(tree_value_id.to_tree_value().value); +} + +const char *AGTreeValueGetKey(AGTreeValue tree_value) { + auto tree_value_id = AG::TreeValueID(tree_value); + auto key_id = tree_value_id.to_tree_value().value; + return tree_value_id.subgraph()->graph()->key_name(key_id); +} + +uint32_t AGTreeValueGetFlags(AGTreeValue tree_value) { + auto tree_value_id = AG::TreeValueID(tree_value); + return tree_value_id.to_tree_value().flags; +} diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.h b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.h new file mode 100644 index 0000000..035880c --- /dev/null +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "Attribute/AGAttribute.h" +#include "Swift/AGType.h" + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +typedef uint32_t AGTreeValue; + +CF_EXPORT +AGTypeID AGTreeValueGetType(AGTreeValue tree_value); + +CF_EXPORT +AGAttribute AGTreeValueGetValue(AGTreeValue tree_value); + +CF_EXPORT +const char *AGTreeValueGetKey(AGTreeValue tree_value); + +CF_EXPORT +uint32_t AGTreeValueGetFlags(AGTreeValue tree_value); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Tree/TreeElement.cpp b/Sources/ComputeCxx/Graph/Tree/TreeElement.cpp new file mode 100644 index 0000000..26e115b --- /dev/null +++ b/Sources/ComputeCxx/Graph/Tree/TreeElement.cpp @@ -0,0 +1,15 @@ +#include "TreeElement.h" + +namespace AG { + +void Graph::TreeDataElement::sort_nodes() { + if (!_sorted) { + std::sort( + _nodes.begin(), _nodes.end(), [](const TreeElementNodePair &a, const TreeElementNodePair &b) -> bool { + return a.first != b.first ? a.first.offset() < b.first.offset() : a.second.offset() < b.second.offset(); + }); + _sorted = true; + } +}; + +}; // namespace AG diff --git a/Sources/ComputeCxx/Graph/Tree/TreeElement.h b/Sources/ComputeCxx/Graph/Tree/TreeElement.h new file mode 100644 index 0000000..5dd3c33 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Tree/TreeElement.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include "Data/Pointer.h" +#include "Graph/Graph.h" +#include "Swift/Metadata.h" +#include "Vector/Vector.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +struct Graph::TreeElement { + const swift::metadata *type; + + AttributeID owner; + uint32_t flags; + + data::ptr parent; + data::ptr next; + data::ptr old_parent; + data::ptr last_value; +}; +static_assert(sizeof(Graph::TreeElement) == 0x20); + +struct Graph::TreeValue { + const swift::metadata *_Nonnull type; + AttributeID value; + uint32_t key_id; + uint32_t flags; + data::ptr previous_sibling; +}; +static_assert(sizeof(Graph::TreeValue) == 0x18); + +class TreeValueID { + private: + uint32_t _value; + + public: + TreeValueID(uint32_t value) : _value(value){}; + + const Graph::TreeValue &to_tree_value() const { return *data::ptr(_value); }; + + Subgraph *_Nullable subgraph() const { return reinterpret_cast(page_ptr()->zone); } + data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Log/Log.cpp b/Sources/ComputeCxx/Log/Log.cpp new file mode 100644 index 0000000..437574c --- /dev/null +++ b/Sources/ComputeCxx/Log/Log.cpp @@ -0,0 +1,12 @@ +#include + +#include "Graph/Graph.h" + +namespace AG { + +os_log_t misc_log() { + static os_log_t log = os_log_create("dev.incrematic.compute", "misc"); + return log; +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Log/Log.h b/Sources/ComputeCxx/Log/Log.h new file mode 100644 index 0000000..7d7d174 --- /dev/null +++ b/Sources/ComputeCxx/Log/Log.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +os_log_t misc_log(); + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Private/CFRuntime.h b/Sources/ComputeCxx/Private/CFRuntime.h new file mode 100644 index 0000000..538f683 --- /dev/null +++ b/Sources/ComputeCxx/Private/CFRuntime.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* CFRuntime.h + Copyright (c) 1999-2014, Apple Inc. All rights reserved. + */ + +#if !defined(__COREFOUNDATION_CFRUNTIME__) +#define __COREFOUNDATION_CFRUNTIME__ 1 + +#include +#include +#include + +CF_EXTERN_C_BEGIN + +#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) + +// GC: until we link against ObjC must use indirect functions. Overridden in CFSetupFoundationBridging +CF_EXPORT bool kCFUseCollectableAllocator; +CF_EXPORT bool (*__CFObjCIsCollectable)(void *); + +CF_INLINE Boolean _CFAllocatorIsSystemDefault(CFAllocatorRef allocator) { + if (allocator == kCFAllocatorSystemDefault) return true; + if (NULL == allocator || kCFAllocatorDefault == allocator) { + return (kCFAllocatorSystemDefault == CFAllocatorGetDefault()); + } + return false; +} + +// is GC on? +#define CF_USING_COLLECTABLE_MEMORY (kCFUseCollectableAllocator) +// is GC on and is this the GC allocator? +#define CF_IS_COLLECTABLE_ALLOCATOR(allocator) (kCFUseCollectableAllocator && (NULL == (allocator) || kCFAllocatorSystemDefault == (allocator) || 0)) +// is this allocated by the collector? +#define CF_IS_COLLECTABLE(obj) (__CFObjCIsCollectable ? __CFObjCIsCollectable((void*)obj) : false) + +#else + +#define kCFUseCollectableAllocator 0 +#define __CFObjCIsCollectable 0 + +CF_INLINE Boolean _CFAllocatorIsSystemDefault(CFAllocatorRef allocator) { + if (allocator == kCFAllocatorSystemDefault) return true; + if (NULL == allocator || kCFAllocatorDefault == allocator) { + return (kCFAllocatorSystemDefault == CFAllocatorGetDefault()); + } + return false; +} + +#define CF_USING_COLLECTABLE_MEMORY 0 +#define CF_IS_COLLECTABLE_ALLOCATOR(allocator) 0 +#define CF_IS_COLLECTABLE(obj) 0 +#endif + +enum { + _kCFRuntimeNotATypeID = 0 +}; + +enum { // Version field constants + _kCFRuntimeScannedObject = (1UL << 0), + _kCFRuntimeResourcefulObject = (1UL << 2), // tells CFRuntime to make use of the reclaim field + _kCFRuntimeCustomRefCount = (1UL << 3), // tells CFRuntime to make use of the refcount field + _kCFRuntimeRequiresAlignment = (1UL << 4), // tells CFRuntime to make use of the requiredAlignment field +}; + +typedef struct __CFRuntimeClass { + CFIndex version; + const char *className; // must be a pure ASCII string, nul-terminated + void (*init)(CFTypeRef cf); + CFTypeRef (*copy)(CFAllocatorRef allocator, CFTypeRef cf); + void (*finalize)(CFTypeRef cf); + Boolean (*equal)(CFTypeRef cf1, CFTypeRef cf2); + CFHashCode (*hash)(CFTypeRef cf); + CFStringRef (*copyFormattingDesc)(CFTypeRef cf, CFDictionaryRef formatOptions); // return str with retain + CFStringRef (*copyDebugDesc)(CFTypeRef cf); // return str with retain + + #define CF_RECLAIM_AVAILABLE 1 + void (*reclaim)(CFTypeRef cf); // Or in _kCFRuntimeResourcefulObject in the .version to indicate this field should be used + + #define CF_REFCOUNT_AVAILABLE 1 + uint32_t (*refcount)(intptr_t op, CFTypeRef cf); // Or in _kCFRuntimeCustomRefCount in the .version to indicate this field should be used + // this field must be non-NULL when _kCFRuntimeCustomRefCount is in the .version field + // - if the callback is passed 1 in 'op' it should increment the 'cf's reference count and return 0 + // - if the callback is passed 0 in 'op' it should return the 'cf's reference count, up to 32 bits + // - if the callback is passed -1 in 'op' it should decrement the 'cf's reference count; if it is now zero, 'cf' should be cleaned up and deallocated (the finalize callback above will NOT be called unless the process is running under GC, and CF does not deallocate the memory for you; if running under GC, finalize should do the object tear-down and free the object memory); then return 0 + // remember to use saturation arithmetic logic and stop incrementing and decrementing when the ref count hits UINT32_MAX, or you will have a security bug + // remember that reference count incrementing/decrementing must be done thread-safely/atomically + // objects should be created/initialized with a custom ref-count of 1 by the class creation functions + // do not attempt to use any bits within the CFRuntimeBase for your reference count; store that in some additional field in your CF object + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" + #define CF_REQUIRED_ALIGNMENT_AVAILABLE 1 + uintptr_t requiredAlignment; // Or in _kCFRuntimeRequiresAlignment in the .version field to indicate this field should be used; the allocator to _CFRuntimeCreateInstance() will be ignored in this case; if this is less than the minimum alignment the system supports, you'll get higher alignment; if this is not an alignment the system supports (e.g., most systems will only support powers of two, or if it is too high), the result (consequences) will be up to CF or the system to decide + +} CFRuntimeClass; + +#define RADAR_5115468_FIXED 1 + +/* Note that CF runtime class registration and unregistration is not currently + * thread-safe, which should not currently be a problem, as long as unregistration + * is done only when valid to do so. + */ + +CF_EXPORT CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls); +/* Registers a new class with the CF runtime. Pass in a + * pointer to a CFRuntimeClass structure. The pointer is + * remembered by the CF runtime -- the structure is NOT + * copied. + * + * - version field must be zero currently. + * - className field points to a null-terminated C string + * containing only ASCII (0 - 127) characters; this field + * may NOT be NULL. + * - init field points to a function which classes can use to + * apply some generic initialization to instances as they + * are created; this function is called by both + * _CFRuntimeCreateInstance and _CFRuntimeInitInstance; if + * this field is NULL, no function is called; the instance + * has been initialized enough that the polymorphic funcs + * CFGetTypeID(), CFRetain(), CFRelease(), CFGetRetainCount(), + * and CFGetAllocator() are valid on it when the init + * function if any is called. + * - copy field should always be NULL. Generic copying of CF + * objects has never been defined (and is unlikely). + * - finalize field points to a function which destroys an + * instance when the retain count has fallen to zero; if + * this is NULL, finalization does nothing. Note that if + * the class-specific functions which create or initialize + * instances more fully decide that a half-initialized + * instance must be destroyed, the finalize function for + * that class has to be able to deal with half-initialized + * instances. The finalize function should NOT destroy the + * memory for the instance itself; that is done by the + * CF runtime after this finalize callout returns. + * - equal field points to an equality-testing function; this + * field may be NULL, in which case only pointer/reference + * equality is performed on instances of this class. + * Pointer equality is tested, and the type IDs are checked + * for equality, before this function is called (so, the + * two instances are not pointer-equal but are of the same + * class before this function is called). + * NOTE: the equal function must implement an immutable + * equality relation, satisfying the reflexive, symmetric, + * and transitive properties, and remains the same across + * time and immutable operations (that is, if equal(A,B) at + * some point, then later equal(A,B) provided neither + * A or B has been mutated). + * - hash field points to a hash-code-computing function for + * instances of this class; this field may be NULL in which + * case the pointer value of an instance is converted into + * a hash. + * NOTE: the hash function and equal function must satisfy + * the relationship "equal(A,B) implies hash(A) == hash(B)"; + * that is, if two instances are equal, their hash codes must + * be equal too. (However, the converse is not true!) + * - copyFormattingDesc field points to a function returning a + * CFStringRef with a human-readable description of the + * instance; if this is NULL, the type does not have special + * human-readable string-formats. + * - copyDebugDesc field points to a function returning a + * CFStringRef with a debugging description of the instance; + * if this is NULL, a simple description is generated. + * + * This function returns _kCFRuntimeNotATypeID on failure, or + * on success, returns the CFTypeID for the new class. This + * CFTypeID is what the class uses to allocate or initialize + * instances of the class. It is also returned from the + * conventional *GetTypeID() function, which returns the + * class's CFTypeID so that clients can compare the + * CFTypeID of instances with that of a class. + * + * The function to compute a human-readable string is very + * optional, and is really only interesting for classes, + * like strings or numbers, where it makes sense to format + * the instance using just its contents. + */ + +CF_EXPORT const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID); +/* Returns the pointer to the CFRuntimeClass which was + * assigned the specified CFTypeID. + */ + +CF_EXPORT void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID); +/* Unregisters the class with the given type ID. It is + * undefined whether type IDs are reused or not (expect + * that they will be). + * + * Whether or not unregistering the class is a good idea or + * not is not CF's responsibility. In particular you must + * be quite sure all instances are gone, and there are no + * valid weak refs to such in other threads. + */ + +/* All CF "instances" start with this structure. Never refer to + * these fields directly -- they are for CF's use and may be added + * to or removed or change format without warning. Binary + * compatibility for uses of this struct is not guaranteed from + * release to release. + */ +typedef struct __CFRuntimeBase { + uintptr_t _cfisa; + uint8_t _cfinfo[4]; +#if __LP64__ + uint32_t _rc; +#endif +} CFRuntimeBase; + +#if __BIG_ENDIAN__ +#define INIT_CFRUNTIME_BASE(...) {0, {0, 0, 0, 0x80}} +#else +#define INIT_CFRUNTIME_BASE(...) {0, {0x80, 0, 0, 0}} +#endif + +CF_EXPORT CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, CFIndex extraBytes, unsigned char *category); +/* Creates a new CF instance of the class specified by the + * given CFTypeID, using the given allocator, and returns it. + * If the allocator returns NULL, this function returns NULL. + * A CFRuntimeBase structure is initialized at the beginning + * of the returned instance. extraBytes is the additional + * number of bytes to allocate for the instance (BEYOND that + * needed for the CFRuntimeBase). If the specified CFTypeID + * is unknown to the CF runtime, this function returns NULL. + * No part of the new memory other than base header is + * initialized (the extra bytes are not zeroed, for example). + * All instances created with this function must be destroyed + * only through use of the CFRelease() function -- instances + * must not be destroyed by using CFAllocatorDeallocate() + * directly, even in the initialization or creation functions + * of a class. Pass NULL for the category parameter. + */ + +CF_EXPORT void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID); +/* This function changes the typeID of the given instance. + * If the specified CFTypeID is unknown to the CF runtime, + * this function does nothing. This function CANNOT be used + * to initialize an instance. It is for advanced usages such + * as faulting. You cannot change the CFTypeID of an object + * of a _kCFRuntimeCustomRefCount class, or to a + * _kCFRuntimeCustomRefCount class. + */ + +CF_EXPORT void _CFRuntimeInitStaticInstance(void *memory, CFTypeID typeID); +/* This function initializes a memory block to be a constant + * (unreleaseable) CF object of the given typeID. + * If the specified CFTypeID is unknown to the CF runtime, + * this function does nothing. The memory block should + * be a chunk of in-binary writeable static memory, and at + * least as large as sizeof(CFRuntimeBase) on the platform + * the code is being compiled for. The init function of the + * CFRuntimeClass is invoked on the memory as well, if the + * class has one. Static instances cannot be initialized to + * _kCFRuntimeCustomRefCount classes. + */ +#define CF_HAS_INIT_STATIC_INSTANCE 1 + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFRUNTIME__ */ diff --git a/Sources/ComputeCxx/Subgraph/NodeCache.cpp b/Sources/ComputeCxx/Subgraph/NodeCache.cpp new file mode 100644 index 0000000..caecfeb --- /dev/null +++ b/Sources/ComputeCxx/Subgraph/NodeCache.cpp @@ -0,0 +1,59 @@ +#include "NodeCache.h" + +#include "Attribute/AttributeType.h" +#include "Swift/EquatableSupport.h" + +namespace AG { + +Subgraph::NodeCache::NodeCache() noexcept + : _heap(nullptr, 0, 0), _types(nullptr, nullptr, nullptr, nullptr, &_heap), + _table2([](const ItemKey *item) -> uint64_t { return item->field_0x00 >> 8; }, + [](const ItemKey *a, const ItemKey *b) -> bool { + if (a == b) { + return true; + } + + if (a->equals_item != b->equals_item || (a->field_0x00 ^ b->field_0x00) > 0xff) { + return false; + } + + void *a_body = nullptr; + if (a->node) { + data::ptr a_node = a->node; + auto a_type = AttributeID(a->node).subgraph()->graph()->attribute_type(a_node->type_id()); + a_body = (uint8_t *)a_node.get() + a_type.attribute_offset(); + if (a_node->flags().has_indirect_self()) { + a_body = *(void **)a_body; + } + } else { + a_body = a->body; // next or body? + } + + void *b_body = nullptr; + if (b->node) { + data::ptr b_node = b->node; + auto b_type = AttributeID(b->node).subgraph()->graph()->attribute_type(b_node->type_id()); + b_body = (uint8_t *)b_node.get() + b_type.attribute_offset(); + if (b_node->flags().has_indirect_self()) { + b_body = *(void **)b_body; + } + } else { + b_body = b->body; // next or body? + } + + return AGDispatchEquatable(a_body, b_body, a->equals_item->type, a->equals_item->equatable); + }, + nullptr, nullptr, &_heap), + _items(nullptr, nullptr, nullptr, nullptr, &_heap) {} + +Subgraph::NodeCache::~NodeCache() noexcept { + _items.for_each( + [](data::ptr node, NodeCache::Item *item, const void *context) { + if (item) { + delete item; + } + }, + nullptr); +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Subgraph/NodeCache.h b/Sources/ComputeCxx/Subgraph/NodeCache.h new file mode 100644 index 0000000..6f0a93a --- /dev/null +++ b/Sources/ComputeCxx/Subgraph/NodeCache.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include "Subgraph.h" +#include "Swift/Metadata.h" +#include "Util/HashTable.h" +#include "Util/Heap.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Subgraph::NodeCache { + public: + struct Item; + struct Type { + const swift::metadata *type; + const swift::equatable_witness_table *equatable; + + // doubly-linked list + Item *last_item; + Item *first_item; + + uint32_t type_id; + }; + static_assert(sizeof(Type) == 0x28); + struct Item { + uint64_t field_0x00; + data::ptr equals_item; + data::ptr node; + Item *prev; + Item *next; + }; + static_assert(sizeof(Item) == 0x20); + struct ItemKey { + uint64_t field_0x00; + data::ptr equals_item; + data::ptr node; + void *body; + }; + + private: + os_unfair_lock _lock; // can't find if this is used + util::Heap _heap; + util::Table> _types; + util::Table _table2; + util::Table, Item *> _items; + + public: + NodeCache() noexcept; + ~NodeCache() noexcept; + + util::Table> &types() { return _types; }; + util::Table &table2() { return _table2; }; + util::Table, Item *> &items() { return _items; }; +}; + +}; // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp new file mode 100644 index 0000000..d6ad64c --- /dev/null +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -0,0 +1,1204 @@ +#include "Subgraph.h" + +#include + +#include "Attribute/AttributeType.h" +#include "Attribute/Node/IndirectNode.h" +#include "Attribute/Node/Node.h" +#include "Attribute/OffsetAttributeID.h" +#include "Encoder/Encoder.h" +#include "Errors/Errors.h" +#include "Graph/Context.h" +#include "Graph/Graph.h" +#include "Graph/Trace.h" +#include "Graph/Tree/TreeElement.h" +#include "NodeCache.h" +#include "UniqueID/AGUniqueID.h" +#include "Util/CFPointer.h" + +namespace AG { + +pthread_key_t Subgraph::_current_subgraph_key; + +void Subgraph::make_current_subgraph_key() { pthread_key_create(&Subgraph::_current_subgraph_key, 0); } + +Subgraph *Subgraph::current_subgraph() { return (Subgraph *)pthread_getspecific(Subgraph::_current_subgraph_key); } + +void Subgraph::set_current_subgraph(Subgraph *subgraph) { + pthread_setspecific(Subgraph::_current_subgraph_key, subgraph); +} + +Subgraph::Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID owner) { + _object = object; + + Graph &graph = context.graph(); + _graph = &graph; + _graph_context_id = context.unique_id(); + + _invalidated = false; + + begin_tree(owner, nullptr, 0); + + graph.foreach_trace([*this](Trace &trace) { trace.created(*this); }); +} + +Subgraph::~Subgraph() { + if (_observers) { + notify_observers(); + // delete *_observers + } + // if (_node_cache) { + // _node_cache::~NodeCache(); + // } +} + +#pragma mark - CoreFoundation + +Subgraph *Subgraph::from_cf(SubgraphObject *object) { return object->subgraph(); } + +SubgraphObject *Subgraph::to_cf() { return _object; } + +void Subgraph::clear_object() { + auto object = _object; + if (object) { + object->clear_subgraph(); + _object = nullptr; + + if (current_subgraph() == this) { + set_current_subgraph(nullptr); + CFRelease(object); + } + } +} + +#pragma mark - Graph + +void Subgraph::graph_destroyed() { + bool old_invalidated = _invalidated; + _invalidated = true; + + if (!old_invalidated) { + graph()->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + } + notify_observers(); + + for (data::ptr page = last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; // TODO: check if this should break out of entire loop + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } + + if (attribute.is_direct()) { + attribute.to_node().destroy(*_graph); + } + } + } + + _parents.clear(); + _children.clear(); + clear(); +} + +#pragma mark - Managing children + +void Subgraph::add_child(Subgraph &child, SubgraphChild::Flags flags) { + if (child.graph() != graph()) { + precondition_failure("child subgraph must have same graph"); + } + for (auto parent : child._parents) { + if (parent == this) { + precondition_failure("child already attached to new parent"); + } + } + graph()->foreach_trace([this, &child](Trace &trace) { trace.add_child(*this, child); }); + _children.push_back(SubgraphChild(&child, flags)); + + uint8_t new_flags = child._flags.value1 | child._flags.value2; + if (new_flags & ~_flags.value2) { + _flags.value2 |= new_flags; // value2 so can't call add_flags + propagate_flags(); + } + + uint8_t dirty_flags = child._flags.value3 | child._flags.value4; + if (dirty_flags & ~_flags.value4) { + _flags.value4 |= dirty_flags; // value4 so can't call add_dirty_flags + propagate_dirty_flags(); + } + + child._parents.push_back(*this); +} + +void Subgraph::remove_child(Subgraph &child, bool without_trace) { + auto parents_end = std::remove(child._parents.begin(), child._parents.end(), this); + child._parents.erase(parents_end, child._parents.end()); + + if (!without_trace) { + graph()->foreach_trace([this, &child](Trace &trace) { trace.remove_child(*this, child); }); + } + + auto children_end = std::remove(_children.begin(), _children.end(), &child); + _children.erase(children_end, _children.end()); +} + +bool Subgraph::ancestor_of(const Subgraph &other) { + auto other_parents = std::stack>(); + const Subgraph *candidate = &other; + while (true) { + if (candidate == nullptr) { + // previous candidate was top level + if (other_parents.empty()) { + return false; + } + candidate = other_parents.top(); + other_parents.pop(); + } + + if (candidate == this) { + return true; + } + + candidate = candidate->_parents.empty() ? nullptr : &candidate->_parents.front(); + for (auto iter = other._parents.begin() + 1, end = other._parents.end(); iter != end; ++iter) { + auto parent = *iter; + other_parents.push(parent); + } + } +} + +template + requires std::invocable && std::same_as, bool> +void Subgraph::foreach_ancestor(Callable body) { + // Status: Verified + for (auto iter = _parents.rbegin(), end = _parents.rend(); iter != end; ++iter) { + auto parent = *iter; + if (body(*parent)) { + parent->foreach_ancestor(body); + } + } +} + +#pragma mark - Attributes + +void Subgraph::add_node(data::ptr node) { + node->flags().set_value3(0); + insert_attribute(AttributeID(node), true); + + if (_tree_root) { + auto tree_data_element = graph()->tree_data_element_for_subgraph(this); + tree_data_element.push_back({ + _tree_root, + node, + }); + } + + graph()->foreach_trace([&node](Trace &trace) { trace.added(node); }); +} + +void Subgraph::add_indirect(data::ptr node, bool flag) { + insert_attribute(AttributeID(node), flag); // make sure adds Indirect kind to node + + graph()->foreach_trace([&node](Trace &trace) { trace.added(node); }); +} + +void Subgraph::insert_attribute(AttributeID attribute, bool flag) { + AttributeID source = AttributeID::make_nil(); + + if (flag) { + if (!attribute.is_direct() || attribute.to_node().flags().value3() == 0) { + uint16_t relative_offset = attribute.page_ptr()->relative_offset_1; + while (relative_offset) { + AttributeID next_attribute = AttributeID(attribute.page_ptr().offset() + relative_offset); + if (next_attribute.is_direct()) { + auto node = next_attribute.to_node(); + if (node.flags().value3() == 0) { + break; + } + source = next_attribute; + relative_offset = node.flags().relative_offset(); + if (relative_offset == 0) { + break; + } + } + } + } + } + + // TODO: add RelativeAttributeID and simplify this + + uint16_t next_value = (attribute.without_kind() - attribute.page_ptr()) | attribute.kind(); + + uint16_t previous_value = 0; + if (source.is_direct()) { + previous_value = source.to_node().flags().relative_offset(); + source.to_node().flags().set_relative_offset(next_value); + } else if (source.is_indirect()) { + previous_value = source.to_indirect_node().relative_offset(); + source.to_indirect_node().set_relative_offset(next_value); + } else { + if (flag) { + previous_value = source.page_ptr()->relative_offset_1; + source.page_ptr()->relative_offset_1 = next_value; + } else { + previous_value = source.page_ptr()->relative_offset_2; + source.page_ptr()->relative_offset_2 = next_value; + } + } + + if (attribute.is_direct()) { + attribute.to_node().flags().set_relative_offset(previous_value); + } else if (attribute.is_indirect()) { + attribute.to_indirect_node().set_relative_offset(previous_value); + } +} + +void Subgraph::unlink_attribute(AttributeID attribute) { + + uint16_t relative_offset = attribute.page_ptr()->relative_offset_1; + + // Find the attribute before the given attribute + // TODO: what happens if attribute is not found + AttributeID before_attribute = AttributeID::make_nil(); + while (relative_offset) { + AttributeID next_attribute = AttributeID(attribute.page_ptr().offset() + relative_offset); + if (next_attribute.is_nil()) { + break; + } + + if (next_attribute == attribute) { + break; + } + + if (next_attribute.is_direct()) { + relative_offset = next_attribute.to_node().flags().relative_offset(); + before_attribute = next_attribute; + } else if (next_attribute.is_indirect()) { + relative_offset = next_attribute.to_indirect_node().relative_offset(); + before_attribute = next_attribute; + } + } + + uint16_t old_value = 0; + if (attribute.is_direct()) { + old_value = attribute.to_node().flags().relative_offset(); + attribute.to_node().flags().set_relative_offset(0); + } else { + old_value = attribute.to_indirect_node().relative_offset(); + attribute.to_indirect_node().set_relative_offset(0); + } + + if (before_attribute.is_direct()) { + before_attribute.to_node().flags().set_relative_offset(old_value); + } else if (before_attribute.is_indirect()) { + before_attribute.to_indirect_node().set_relative_offset(old_value); + } else { + attribute.page_ptr()->relative_offset_1 = old_value; + } +} + +void Subgraph::invalidate_now(Graph &graph) { + // TODO: double check graph param vs _graph instance var + + // maybe this is a lock on subgraphs + graph.set_validating_0x198(true); // TODO: check member var against other usages of set_validating() + + auto removed_subgraphs = vector(); + auto stack = std::stack>(); + + bool was_invalidated = _invalidated; + if (!_invalidated) { + _invalidated = true; + if (!was_invalidated) { + _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + } + clear_object(); + stack.push(this); + + while (!stack.empty()) { + Subgraph *subgraph = stack.top(); + stack.pop(); + + _graph->foreach_trace([subgraph](Trace &trace) { trace.destroy(*subgraph); }); + + notify_observers(); + _graph->remove_subgraph(*subgraph); + + subgraph->set_invalidated_flag(); + removed_subgraphs.push_back(subgraph); + + for (auto child : subgraph->_children) { + Subgraph *child_subgraph = child.subgraph(); + if (child_subgraph->_graph_context_id == _graph_context_id) { + + // for each other parent of the child, remove the child from that parent + for (auto parent : child_subgraph->_parents) { + if (parent != subgraph) { + auto r = std::remove_if(parent->_children.begin(), parent->_children.end(), + [child_subgraph](auto other_child) { + return other_child.subgraph() == child_subgraph; + }); + parent->_children.erase(r); + + // for (auto other_child : parent->_children) { + // if (other_child.subgraph() == child) { + // parent->_children[i] = + // parent->_children[parent->_children.size() - 1]; + // parent->_children.pop_back(); + // } + // } + } + } + + child_subgraph->_parents.clear(); + + bool child_was_invalidated = child_subgraph->_invalidated; + if (!child_was_invalidated) { + child_subgraph->_invalidated = true; + if (!child_was_invalidated) { + _graph->foreach_trace( + [child_subgraph](Trace &trace) { trace.invalidate(*child_subgraph); }); + } + child_subgraph->clear_object(); + stack.push(child_subgraph); + } + } else { + // remove the subgraph from the parents vector of each child + auto r = std::remove(child_subgraph->_parents.begin(), child_subgraph->_parents.end(), subgraph); + child_subgraph->_parents.erase(r); + + // for (auto parent : child_subgraph->_parents) { + // if (parent == subgraph) { + // child._parents[i] = child._parents[child._parents.size() - 1]; + // child._parents.resize(child._parents.size() - 1); + // break; + // } + // } + } + } + } + } + + for (auto removed_subgraph : removed_subgraphs) { + for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { + bool found_nil_attribute = false; + + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + graph.remove_node(attribute.to_node_ptr()); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + graph.remove_indirect_node(attribute.to_indirect_node_ptr()); + } else if (attribute.is_nil()) { + found_nil_attribute = true; + } + } + if (found_nil_attribute) { + break; + } + } + } + + for (auto removed_subgraph : removed_subgraphs) { + for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { + bool found_nil_attribute = false; + + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else if (attribute.is_nil()) { + found_nil_attribute = true; + } + + if (attribute.is_direct()) { + attribute.to_node().destroy(*_graph); + + _graph->did_destroy_node(); // decrement counter + } + } + if (found_nil_attribute) { + break; + } + } + } + + // TODO: does this execute anyway... + for (auto removed_subgraph : removed_subgraphs) { + removed_subgraph->~Subgraph(); + free(removed_subgraph); // or delete? + } + + graph.set_validating_0x198(false); // TODO: check instance var +} + +void Subgraph::invalidate_and_delete_(bool flag) { + if (flag) { + set_invalidated_flag(); + } + + // comparison is actually (2 < (dword)(this->invalidated - 1), is this a flag? + if (!_invalidated) { + for (auto parent : _parents) { + parent->remove_child(*this, true); + } + _parents.clear(); + + if (!_graph->is_validating_0x198() && _graph->field_0xf0() == 0) { + invalidate_now(*_graph); + _graph->invalidate_subgraphs(); + return; + } + + bool was_invalidated = _invalidated; + if (!was_invalidated) { + _graph->will_invalidate_subgraph(*this); + _invalidated = true; + if (!was_invalidated) { + _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + } + } + } +} + +void Subgraph::update(uint8_t flags) { + if (_graph->needs_update()) { + if (!_graph->thread_is_updating()) { + _graph->call_update(); + } + } + + if (!_invalidated) { + if ((flags & (_flags.value1 | _flags.value2))) { + + _graph->foreach_trace([*this, &flags](Trace &trace) { trace.begin_update(*this, flags); }); + + _last_traversal_seed += 1; // TODO: check atomics + + auto stack = + std::stack, AG::vector, 32, uint64_t>>(); + auto nodes_to_update = vector, 256, uint64_t>(); + + stack.push(std::move(to_cf())); + _traversal_seed = _last_traversal_seed; + + bool thread_is_updating = false; + while (!stack.empty()) { + + util::cf_ptr object = stack.top(); + stack.pop(); + + Subgraph *subgraph = Subgraph::from_cf(object); + if (subgraph) { + + while (!subgraph->_invalidated) { + if ((flags & subgraph->_flags.value3) == 0) { + // LABEL: LAB_1afe6ac70 + + if (flags & subgraph->_flags.value4) { + subgraph->_flags.value4 &= ~flags; + for (auto child : subgraph->_children) { + Subgraph *child_subgraph = child.subgraph(); + // TODO: check child has 0x3 pointer tag that needs to be masked... + if (flags & (child_subgraph->_flags.value3 | child_subgraph->_flags.value4) && + child_subgraph->_traversal_seed != _traversal_seed) { + stack.push(std::move(child_subgraph->to_cf())); + child_subgraph->_traversal_seed = _traversal_seed; + } + } + } + break; + } + + uint8_t old_flags_1 = subgraph->_flags.value1; + subgraph->_flags.value3 &= ~flags; + if (flags == 0 || (old_flags_1 & flags)) { + for (data::ptr page = subgraph->last_page(); page != nullptr; + page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + auto node = attribute.to_node(); + if (flags) { + if (node.flags().value3() == 0) { + break; + } + if ((node.flags().value3() & flags) == 0) { + continue; + } + } + if (node.state().is_dirty()) { + nodes_to_update.push_back(attribute.to_node_ptr()); + } + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + if (flags) { + break; + } + } + } + } + } + + if (nodes_to_update.size() == 0) { + if (subgraph->_invalidated == false) { + // goto LAB_1afe6ac70 + } + break; + } + + for (auto node : nodes_to_update) { + if (!thread_is_updating) { + thread_is_updating = _graph->thread_is_updating(); + if (!thread_is_updating) { + _graph->increment_counter_0x1b8(); + } + _graph->update_attribute(node, true); + + if (subgraph->_invalidated) { + break; + } + } + } + nodes_to_update.clear(); + } + + _graph->invalidate_subgraphs(); + } + + // CFRelease happens automatically + + } // while !stack.empty + + _graph->invalidate_subgraphs(); + _graph->foreach_trace([*this](Trace &trace) { trace.end_update(*this); }); + } + } +} + +#pragma mark - Traversal + +std::atomic Subgraph::_last_traversal_seed = {}; + +void Subgraph::apply(Flags flags, ClosureFunctionAV body) { + // Status: Verified, needs checks for atomics + if (_invalidated) { + return; + } + if ((flags.value1 & (_flags.value1 | _flags.value2)) == 0) { + return; + } + + bool was_validating = graph()->is_validating_0x198(); + graph()->set_validating_0x198(true); + + _last_traversal_seed += 1; // TODO: check atomics + + auto stack = std::stack>(); + + stack.push(this); + _traversal_seed = _last_traversal_seed; + + while (!stack.empty()) { + auto subgraph = stack.top(); + stack.pop(); + + if (subgraph->_invalidated) { + continue; + } + + if (flags.value4 & 1 || subgraph->_graph_context_id == _graph_context_id) { + if (flags.is_null() || (flags.value1 & subgraph->_flags.value1)) { + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; // TODO: check if this should break out of entire loop + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + + if (!flags.is_null()) { + if (attribute.to_node().flags().value3() == 0) { + break; + } + if (flags.value3 & (attribute.to_node().flags().value3() == 0)) { + continue; + } + } + + body(attribute); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + if (!flags.is_null()) { + break; + } + } + } + } + } + + for (auto child : subgraph->_children) { + Subgraph *child_subgraph = child.subgraph(); + if (flags.value1 & (child_subgraph->_flags.value1 | child_subgraph->_flags.value2) && + child_subgraph->_traversal_seed != _last_traversal_seed) { + stack.push(child_subgraph); + child_subgraph->_traversal_seed = _last_traversal_seed; + } + } + } + } + + if (graph() != nullptr && !was_validating) { + graph()->set_validating_0x198(false); + graph()->invalidate_subgraphs(); + } +} + +// MARK: - Tree + +void Subgraph::begin_tree(AttributeID owner, const swift::metadata *type, uint32_t flags) { + + data::ptr tree = (data::ptr)alloc_bytes(sizeof(Graph::TreeElement), 7); + tree->type = type; + tree->owner = owner; + tree->flags = flags; + tree->parent = _tree_root; + tree->old_parent = nullptr; + + auto old_root = _tree_root; + _tree_root = tree; + + if (old_root) { + _tree_root->old_parent = old_root->next; + old_root->next = _tree_root; + } +} + +void Subgraph::end_tree(AttributeID attribute) { + if (_tree_root && _tree_root->parent) { + _tree_root = _tree_root->parent; + } +} + +void Subgraph::set_tree_owner(AttributeID owner) { + if (_tree_root) { + return; + } + if (_tree_root->parent) { + precondition_failure("setting owner of non-root tree"); + } + _tree_root->owner = owner; +} + +void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type, const char *key, uint32_t flags) { + if (!_tree_root) { + return; + } + + auto key_id = graph()->intern_key(key); + + data::ptr tree_value = (data::ptr)alloc_bytes(sizeof(Graph::TreeValue), 7); + tree_value->type = type; + tree_value->value = attribute; + tree_value->key_id = key_id; + tree_value->flags = flags; + tree_value->previous_sibling = _tree_root->last_value; + + _tree_root->last_value = tree_value; +} + +/// Returns the node after the given tree element. +AttributeID Subgraph::tree_node_at_index(data::ptr tree_element, uint64_t index) { + if (auto map = graph()->tree_data_elements()) { + auto tree_data_element = map->find(this); + if (tree_data_element != map->end()) { + tree_data_element->second.sort_nodes(); + + auto nodes = tree_data_element->second.nodes(); + std::pair, data::ptr> *found = std::find_if( + nodes.begin(), nodes.end(), [&tree_element](auto node) { return node.first == tree_element; }); + + uint64_t i = index; + for (auto node = found; node != nodes.end(); ++node) { + if (found->first != tree_element) { + break; + } + if (i == 0) { + return AttributeID(node->second); + } + --index; + } + } + } + return AttributeID::make_nil(); +} + +uint32_t Subgraph::tree_subgraph_child(data::ptr tree_element) { + auto map = _graph->tree_data_elements(); + if (!map) { + return 0; + } + auto tree_data_element = map->find(this); + tree_data_element->second.sort_nodes(); + + auto nodes = tree_data_element->second.nodes(); + if (!nodes.empty()) { + return; + } + + // TODO: verify this is lower_bound + auto iter = std::lower_bound(nodes.begin(), nodes.end(), tree_element, + [](auto iter, auto value) -> bool { return iter.first.offset() < value; }); + if (iter == nodes.end()) { + return; + } + + auto subgraph_vector = vector(); + + for (auto subgraph : _graph->subgraphs()) { + if (subgraph->_invalidated) { + continue; + } + if (subgraph->_tree_root == nullptr) { + continue; + } + AttributeID owner = subgraph->_tree_root->owner; + if (owner.without_kind() == 0) { + continue; + } + OffsetAttributeID resolved = owner.resolve(AttributeID::TraversalOptions::None); + owner = resolved.attribute(); + if (owner.is_direct()) { + for (auto node_iter = iter; node_iter != nodes.end(); ++node_iter) { + if (node_iter->first->owner == owner) { + subgraph_vector.push_back(subgraph); + } + } + } + } + + std::sort(subgraph_vector.begin(), subgraph_vector.end()); + + // TODO: not sure what is happening here + uint32_t result = 0; + uint32_t last_old_parent = 0; + for (auto subgraph : subgraph_vector) { + result = subgraph->_tree_root; + subgraph->_tree_root->old_parent = last_old_parent; + last_old_parent = result; + } + return result; +} + +// MARK: - Flags + +void Subgraph::set_flags(data::ptr node, NodeFlags::Flags3 flags) { + if (node->flags().value3() == flags) { + return; + } + if (node->flags().value3() == 0 || flags == 0) { + unlink_attribute(node); + node->flags().set_value3(flags); + insert_attribute(node, true); + } else { + node->flags().set_value3(flags); + } + + add_flags(flags); + if (node->state().is_dirty()) { + add_dirty_flags(flags); + } +} + +void Subgraph::add_flags(uint8_t flags) { + // Status: doesn't exist in decompile + if (flags & ~_flags.value1) { + _flags.value1 |= flags; + propagate_flags(); + } +} + +void Subgraph::add_dirty_flags(uint8_t dirty_flags) { + // Status: Verified + if (dirty_flags & ~_flags.value3) { + _flags.value3 |= dirty_flags; + propagate_dirty_flags(); + } +} + +void Subgraph::propagate_flags() { + // Status: doesn't exist so not sure if new_flags is _flags.value1 | _flags.value2; + uint8_t new_flags = _flags.value1 | _flags.value2; + foreach_ancestor([&new_flags](Subgraph &ancestor) -> bool { + if (new_flags & ~ancestor._flags.value2) { + ancestor._flags.value2 |= new_flags; + return true; + } + return false; + }); +} + +void Subgraph::propagate_dirty_flags() { + // Status: Verified + uint8_t arg = _flags.value3 | _flags.value4; + foreach_ancestor([&arg](Subgraph &ancestor) -> bool { + if (arg & ancestor._flags.value4) { + ancestor._flags.value4 |= arg; + return true; + } + return false; + }); +} + +// MARK: - Observers + +uint64_t Subgraph::add_observer(ClosureFunctionVV observer) { + if (!_observers) { + _observers = (data::ptr)alloc_bytes(sizeof(observers_vector), 7); + *_observers = observers_vector(); + } + + auto observer_id = AGMakeUniqueID(); + _observers->push_back({ + observer, + observer_id, + }); + return observer_id; +} + +void Subgraph::remove_observer(uint64_t observer_id) { + if (_observers) { + auto iter = std::remove_if(_observers->begin(), _observers->end(), [&observer_id](auto pair) -> bool { + if (pair.second == observer_id) { + pair.first.release_context(); // TODO: where is retain? + return true; + } + return false; + }); + _observers->erase(iter, _observers->end()); + } +} + +void Subgraph::notify_observers() { + while (!_observers->empty()) { + auto observer = _observers->back(); + observer.first(); + observer.first.release_context(); // TODO: where is retain? + _observers->pop_back(); + } +} + +#pragma mark - Cache + +data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata &metadata, void *body, + ClosureFunctionCI closure) { + if (_cache == nullptr) { + _cache = (data::ptr)alloc_bytes(sizeof(NodeCache), 7); + } + + auto type = _cache->types().lookup(&metadata, nullptr); + if (type == nullptr) { + auto equatable = metadata.equatable(); + if (equatable == nullptr) { + precondition_failure("cache key must be equatable: %s", metadata.name(false)); + } + + type = (data::ptr)alloc_bytes(sizeof(NodeCache::Type), 7); + type->type = &metadata; + type->equatable = equatable; + type->last_item = nullptr; + type->first_item = nullptr; + type->type_id = (uint32_t)closure(_graph); + _cache->types().insert(&metadata, type); + } + + NodeCache::ItemKey item_lookup_key = {identifier << 8, type, nullptr, body}; + NodeCache::Item *item = _cache->table2().lookup(&item_lookup_key, nullptr); + if (item == nullptr) { + // if (closure == nullptr) { + // return 0; + // } + + item = type->first_item; + if (item == 0 || item->field_0x00 < 2) { + data::ptr node = graph()->add_attribute(*this, type->type_id, body, nullptr); + + item = new NodeCache::Item(item_lookup_key.field_0x00, item_lookup_key.equals_item, node, nullptr, nullptr); + + node->flags().set_value4_unknown0x10(true); + + _cache->items().insert(node, item); + } else { + // remove item from linked list + if (item->prev != nullptr) { + item->prev->next = item->next; + } else { + type->first_item = item->next; + } + if (item->next != nullptr) { + item->next->prev = item->prev; + } else { + type->last_item = item->prev; + } + + if (item->field_0x00 != 0xff) { + _cache->table2().remove_ptr((const NodeCache::ItemKey *)item); + } + + data::ptr node = item->node; + _graph->remove_all_inputs(node); + node->set_state(node->state().with_dirty(true).with_pending(true)); + node->update_self(*_graph, body); + item->field_0x00 |= identifier << 8; + } + + _cache->table2().insert((const NodeCache::ItemKey *)item, item); + } else { + if (item->field_0x00 & 0xff) { + // remove item from linked list + if (item->prev != nullptr) { + item->prev->next = item->next; + } else { + type->first_item = item->next; + } + if (item->next != nullptr) { + item->next->prev = item->prev; + } else { + type->last_item = item->prev; + } + } + } + item->field_0x00 &= 0xffffffffffffff00; + return item->node; +} + +void Subgraph::cache_insert(data::ptr node) { + if (_invalidated) { + return; + } + + if (node->flags().value4_unknown0x10() && !node->state().is_evaluating() && node->field0x14() < 0x20) { + // TODO: one of these flags must indicate it is cached + + const AttributeType &attribute_type = _graph->attribute_type(node->type_id()); + data::ptr type = _cache->types().lookup(&attribute_type.self_metadata(), nullptr); + + NodeCache::Item *item = _cache->items().lookup(node, nullptr); + item->field_0x00 += 1; + + // insert into linked list + item->prev = type->last_item; + item->next = nullptr; + type->last_item = item; + if (item->prev != nullptr) { + item->prev->next = item; + } else { + type->first_item = item; + } + + if ((_other_state & OtherState::Option1) == 0) { + if ((_other_state & OtherState::Option2) == 0) { + _graph->add_subgraphs_with_cached_node(this); + } + _other_state |= OtherState::Option1; + } + } +} + +void Subgraph::cache_collect() { + _other_state &= ~OtherState::Option1; // turn off 0x1 bit + + std::pair context = {this, _cache.get()}; + if (_cache != nullptr && !_invalidated) { + _cache->types().for_each( + [](const swift::metadata *metadata, const data::ptr type, const void *context) { + Subgraph *subgraph = reinterpret_cast *>(context)->first; + NodeCache *cache = reinterpret_cast *>(context)->second; + + NodeCache::Item *item = type->last_item; + while (item) { + if ((~item->field_0x00 & 0xff) == 0) { + return; // identifier == 0xff + } + item->field_0x00 += 1; + if ((~item->field_0x00 & 0xff) == 0) { + // identifier == 0xff + cache->table2().remove_ptr((const NodeCache::ItemKey *)item); + data::ptr node = item->node; + node->destroy_self(*subgraph->_graph); + node->destroy_value(*subgraph->_graph); + subgraph->_graph->remove_all_inputs(node); + } else { + subgraph->_other_state |= OtherState::Option1; + } + item = item->prev; + } + }, + &context); + } +} + +#pragma mark - Encoding + +void Subgraph::encode(Encoder &encoder) { + + auto zone_id = info().zone_id(); + if (zone_id != 0) { + encoder.encode_varint(8); + encoder.encode_varint(zone_id); + } + + if (_graph_context_id != 0) { + encoder.encode_varint(0x10); + encoder.encode_varint(_graph_context_id); + } + + for (auto parent : _parents) { + auto zone_id = parent->info().zone_id(); + if (zone_id != 0) { + encoder.encode_varint(0x18); + encoder.encode_varint(zone_id); + } + } + + for (auto child : _children) { + auto zone_id = child.subgraph()->info().zone_id(); + if (zone_id != 0) { + encoder.encode_varint(0x20); + encoder.encode_varint(zone_id); + } + } + + if (_invalidated) { + encoder.encode_varint(0x28); + encoder.encode_varint(true); + } + + if (last_page() == nullptr) { + if (_tree_root) { + encoder.encode_varint(0x3a); + encoder.begin_length_delimited(); + _graph->encode_tree(encoder, _tree_root); + encoder.end_length_delimited(); + } + return; + } + + for (data::ptr page = last_page(); page != nullptr; page = page->previous) { + bool bVar4 = true; + bool bVar3 = true; + do { + bVar3 = bVar4; + + uint16_t relative_offset = bVar3 ? page->relative_offset_1 : page->relative_offset_2; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + + if (attribute.is_direct()) { + attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + attribute.to_indirect_node().relative_offset(); + } else { + if (attribute.is_nil() || relative_offset == 0) { + break; + } + continue; + } + + encoder.encode_varint(0x32); + encoder.begin_length_delimited(); + + if (attribute.to_node_ptr() == nullptr) { + encoder.encode_varint(0x12); + encoder.begin_length_delimited(); + _graph->encode_node(encoder, attribute.to_node(), false); + encoder.end_length_delimited(); + } else { + encoder.encode_varint(8); + encoder.encode_varint(attribute); + + if (attribute.is_direct()) { + encoder.encode_varint(0x12); + encoder.begin_length_delimited(); + _graph->encode_node(encoder, attribute.to_node(), false); + encoder.end_length_delimited(); + } else if (attribute.is_indirect()) { + encoder.encode_varint(0x1a); + encoder.begin_length_delimited(); + _graph->encode_indirect_node(encoder, attribute.to_indirect_node()); + encoder.end_length_delimited(); + } + } + + encoder.end_length_delimited(); + } + bVar4 = false; + } while (bVar3); + } +} + +#pragma mark - Printing + +void Subgraph::print(uint32_t indent_level) { + uint64_t indent_length = 2 * indent_level; + char *indent_string = (char *)alloca(indent_length + 1); + memset(indent_string, ' ', indent_length); + indent_string[indent_length] = '\0'; + + fprintf(stdout, "%s+ %p: %u in %lu [", indent_string, this, info().zone_id(), (unsigned long)_graph_context_id); + + bool first = true; + for (data::ptr page = last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } + + if (attribute.is_direct()) { + fprintf(stdout, "%s%u", first ? "" : " ", attribute.value()); + if (attribute.to_node().flags().value3()) { + fprintf(stdout, "(%u)", attribute.to_node().flags().value3()); + } + first = false; + } + } + } + + fwrite("]\n", 2, 1, stdout); + + for (auto child : _children) { + child.subgraph()->print(indent_level + 1); + } +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 34f2535..40bf9b9 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -1,21 +1,188 @@ #pragma once #include +#include +#include "Attribute/AttributeID.h" +#include "Attribute/Node/Node.h" +#include "Closure/ClosureFunction.h" +#include "Data/Pointer.h" #include "Data/Zone.h" +#include "Graph/Graph.h" +#include "Private/CFRuntime.h" +#include "Vector/IndirectPointerVector.h" CF_ASSUME_NONNULL_BEGIN namespace AG { +namespace swift { +class metadata; +} class Graph; +class Node; +class Encoder; + +class SubgraphObject { + private: + CFRuntimeBase _base; + Subgraph *_subgraph; + + public: + Subgraph *subgraph() { return _subgraph; }; + void clear_subgraph() { _subgraph = nullptr; }; +}; class Subgraph : public data::zone { + public: + class NodeCache; + class SubgraphChild { + private: + uintptr_t _data; + + public: + enum Flags : uint32_t {}; + SubgraphChild(Subgraph *subgraph, Flags flags) { _data = (uintptr_t)subgraph | (flags & 0x3); }; + Subgraph *subgraph() const { return reinterpret_cast(_data & ~0x3); }; + bool operator==(const Subgraph *other) const { return subgraph() == other; }; + }; + + struct Flags { + uint8_t value1; + uint8_t value2; + uint8_t value3; + uint8_t value4; + bool is_null() const { return value1 == 0 && value2 == 0 and value3 == 0 && value4 == 0; }; + }; + + struct Observer {}; + + enum OtherState : uint8_t { + Option1 = 1 << 0, + Option2 = 1 << 1, + }; + private: - Graph *_graph; + static pthread_key_t _current_subgraph_key; + + SubgraphObject *_object; + Graph *_Nullable _graph; + uint64_t _graph_context_id; + + indirect_pointer_vector _parents; + vector _children; + + using observers_vector = vector, uint64_t>, 0, uint64_t>; + data::ptr _observers; + uint32_t _traversal_seed; + + uint32_t _index; // TODO: get and set + + data::ptr _cache; + data::ptr _tree_root; + + Flags _flags; + bool _invalidated; + uint8_t _other_state; public: - Graph &graph() const { return *_graph; }; + static void make_current_subgraph_key(); + static Subgraph *_Nullable current_subgraph(); + static void set_current_subgraph(Subgraph *_Nullable subgraph); + + Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID attribute); + ~Subgraph(); + + // MARK: CoreFoundation + + static Subgraph *from_cf(SubgraphObject *object); + SubgraphObject *to_cf(); + void clear_object(); + + // MARK: Graph + + Graph *_Nullable graph() const { return _graph; }; + uint64_t graph_context_id() { return _graph_context_id; }; + + void graph_destroyed(); + + // MARK: Managing children + + void add_child(Subgraph &child, SubgraphChild::Flags flags); + void remove_child(Subgraph &child, bool flag); + + bool ancestor_of(const Subgraph &other); + + template + requires std::invocable && std::same_as, bool> + void foreach_ancestor(Callable body); + + // MARK: Attributes + + void add_node(data::ptr node); + void add_indirect(data::ptr node, bool flag); + + void insert_attribute(AttributeID attribute, bool dirty); + + void unlink_attribute(AttributeID attribute); + + void invalidate_now(Graph &graph); + void invalidate_and_delete_(bool flag); + + void update(uint8_t flags); + + // MARK: Traversal + + static std::atomic _last_traversal_seed; + + void apply(Flags flags, ClosureFunctionAV body); + + // MARK: Tree + + void begin_tree(AttributeID attribute, const swift::metadata *_Nullable type, + uint32_t flags); // TODO: check can be null from Subgraph() + void end_tree(AttributeID attribute); + + void set_tree_owner(AttributeID owner); + void add_tree_value(AttributeID attribute, const swift::metadata *type, const char *value, uint32_t flags); + + AttributeID tree_node_at_index(data::ptr tree_element, uint64_t index); + uint32_t tree_subgraph_child(data::ptr tree_element); + + // MARK: Managing flags + + // flags 2 and 4 control propogation + // flags 1 and 3 are the values themselvs + // flags 3 and 4 are the dirty subset of 1 and 2 + + void set_flags(data::ptr node, NodeFlags::Flags3 flags3); + + void add_flags(uint8_t flags); + void add_dirty_flags(uint8_t dirty_flags); + + void propagate_flags(); + void propagate_dirty_flags(); + + // MARK: Managing observers + + uint64_t add_observer(ClosureFunctionVV observer); + void remove_observer(uint64_t observer_id); + void notify_observers(); + + // MARK: Cache + + data::ptr cache_fetch(uint64_t identifier, const swift::metadata &type, void *body, + ClosureFunctionCI closure); + void cache_insert(data::ptr node); + void cache_collect(); + + // MARK: Encoding + + void encode(Encoder &encoder); + + // MARK: Printing + + void print(uint32_t indent_level); }; } // namespace AG diff --git a/Sources/ComputeCxx/Vector/Vector.tpp b/Sources/ComputeCxx/Vector/Vector.tpp index 51d00a0..289fa6e 100644 --- a/Sources/ComputeCxx/Vector/Vector.tpp +++ b/Sources/ComputeCxx/Vector/Vector.tpp @@ -28,7 +28,7 @@ void *realloc_vector(void *buffer, void *stack_buffer, size_type stack_size, siz } size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size_bytes); - size_type new_size = new_size_bytes / element_size_bytes; + size_type new_size = (size_type)(new_size_bytes / element_size_bytes); if (new_size == *size) { // nothing to do return buffer; From 2ab2b82eb185723633a17837f710ebbe09c0b85a Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Feb 2025 17:34:35 +1100 Subject: [PATCH 06/74] Implement Graph::update_attribute --- Sources/ComputeCxx/Attribute/AttributeType.h | 6 +- Sources/ComputeCxx/Attribute/Node/InputEdge.h | 2 + Sources/ComputeCxx/Attribute/Node/Node.cpp | 37 +-- Sources/ComputeCxx/Attribute/Node/Node.h | 19 +- Sources/ComputeCxx/Graph/Graph.cpp | 214 +++++++++++++++-- Sources/ComputeCxx/Graph/Graph.h | 215 +++++++++++++++--- Sources/ComputeCxx/Graph/UpdateStack.cpp | 88 +++++++ Sources/ComputeCxx/Graph/UpdateStack.h | 57 +++++ Sources/ComputeCxx/Subgraph/NodeCache.cpp | 10 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 27 +-- Sources/ComputeCxx/Subgraph/Subgraph.h | 9 +- 11 files changed, 578 insertions(+), 106 deletions(-) create mode 100644 Sources/ComputeCxx/Graph/UpdateStack.cpp create mode 100644 Sources/ComputeCxx/Graph/UpdateStack.h diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index 627f9aa..236d891 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -14,7 +14,7 @@ class AttributeType; class AttributeVTable { public: - using Callback = void (*)(AttributeType *attribute_type, void *body); + using Callback = void (*)(const AttributeType *attribute_type, void *body); Callback _unknown_0x00; Callback _unknown_0x08; Callback destroy_self; @@ -82,6 +82,10 @@ class AttributeType { _vtable->destroy_self(this, body); } } + + AttributeVTable::Callback vt_get_update_stack_callback() const { + return _vtable->_update_stack_callback; + } }; } // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/InputEdge.h b/Sources/ComputeCxx/Attribute/Node/InputEdge.h index cdd4650..d2218d6 100644 --- a/Sources/ComputeCxx/Attribute/Node/InputEdge.h +++ b/Sources/ComputeCxx/Attribute/Node/InputEdge.h @@ -7,6 +7,8 @@ namespace AG { class Node; struct InputEdge { + struct Comparator {}; + data::ptr value; uint8_t flags; }; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.cpp b/Sources/ComputeCxx/Attribute/Node/Node.cpp index 23d3a3d..6f3901f 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.cpp +++ b/Sources/ComputeCxx/Attribute/Node/Node.cpp @@ -8,12 +8,17 @@ namespace AG { -void Node::update_self(const Graph &graph, void *new_self) { - auto type = graph.attribute_type(_info.type_id); +void *Node::get_self(const AttributeType &type) { void *self = ((char *)this + type.attribute_offset()); if (_flags.has_indirect_self()) { self = *(void **)self; } + return self; +} + +void Node::update_self(const Graph &graph, void *new_self) { + auto type = graph.attribute_type(_info.type_id); + void *self = get_self(type); if (!state().is_self_initialized()) { set_state(state().with_self_initialized(true)); @@ -32,15 +37,20 @@ void Node::destroy_self(const Graph &graph) { set_state(state().with_self_initialized(false)); auto type = graph.attribute_type(_info.type_id); - void *self = ((char *)this + type.attribute_offset()); - if (_flags.has_indirect_self()) { - self = *(void **)self; - } + void *self = get_self(type); type.vt_destroy_self(self); type.self_metadata().vw_destroy(static_cast(self)); } +void *Node::get_value() { + void *value = _value.get(); + if (_flags.has_indirect_value()) { + value = *(void **)value; + } + return value; +} + void Node::allocate_value(Graph &graph, data::zone &zone) { if (_value) { return; @@ -72,10 +82,7 @@ void Node::destroy_value(Graph &graph) { set_state(state().with_value_initialized(false)); auto type = graph.attribute_type(_info.type_id); - void *value = _value.get(); - if (_flags.has_indirect_value()) { - value = *(void **)value; - } + void *value = get_value(); type.value_metadata().vw_destroy(static_cast(value)); } @@ -84,10 +91,7 @@ void Node::destroy(Graph &graph) { auto type = graph.attribute_type(_info.type_id); if (state().is_value_initialized()) { - void *value = _value.get(); - if (_flags.has_indirect_value()) { - value = *(void **)value; - } + void *value = get_value(); type.value_metadata().vw_destroy(static_cast(value)); } if (_value) { @@ -95,10 +99,7 @@ void Node::destroy(Graph &graph) { } if (state().is_self_initialized()) { - void *self = ((char *)this + type.attribute_offset()); - if (_flags.has_indirect_self()) { - self = *(void **)self; - } + void *self = get_self(type); type.vt_destroy_self(self); type.self_metadata().vw_destroy(static_cast(self)); diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 25d4774..08bbb82 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -77,10 +77,10 @@ class Node { class State { public: enum : uint8_t { - Dirty = 1 << 0, // 0x01 // Unknown0 = 1 << 0, - Pending = 1 << 1, // 0x02 // Unknown1 = 1 << 1, - Unknown2 = 1 << 2, // 0x04 set from attribute type flags & 8 - Unknown3 = 1 << 3, // 0x08 set from attribute type flags & 8 + Dirty = 1 << 0, // 0x01 // Unknown0 = 1 << 0, + Pending = 1 << 1, // 0x02 // Unknown1 = 1 << 1, + UpdatesOnMain = 1 << 2, // 0x04 set from attribute type flags & 8 + Unknown3 = 1 << 3, // 0x08 set from attribute type flags & 8 ValueInitialized = 1 << 4, // 0x10 SelfInitialized = 1 << 5, // 0x20 @@ -103,7 +103,7 @@ class Node { bool is_pending() { return _data & Pending; } State with_pending(bool value) const { return State((_data & ~Pending) | (value ? Pending : 0)); }; - bool is_unknown2() { return _data & Unknown2; } + bool updates_on_main() { return _data & UpdatesOnMain; } bool is_unknown3() { return _data & Unknown3; } State with_unknown3(bool value) const { return State((_data & ~Unknown3) | (value ? Unknown3 : 0)); }; @@ -117,6 +117,10 @@ class Node { return State((_data & ~SelfInitialized) | (value ? SelfInitialized : 0)); }; + State with_in_update_stack(bool value) const { + return State((_data & ~InUpdateStack) | (value ? InUpdateStack : 0)); + }; + bool is_evaluating() { return _data & InUpdateStack || _data & Unknown7; } }; @@ -140,7 +144,7 @@ class Node { TreeInfo _tree_info; // 0x0c - 5 bits of flags than count of parents?? data::ptr _input_edges; // 0x10 - uint32_t _field0x14; // 0x14 - TODO: verify + uint32_t _field0x14; // 0x14 - TODO: verify data::ptr _next_child; // 0x18 - TODO: verify public: @@ -153,10 +157,11 @@ class Node { NodeFlags &flags() { return _flags; }; uint32_t field0x14() { return _field0x14; }; + void *get_self(const AttributeType &type); // TODO: inline void update_self(const Graph &graph, void *new_self); void destroy_self(const Graph &graph); - void *get_value() { return _value.get(); }; + void *get_value(); void allocate_value(Graph &graph, data::zone &zone); void destroy_value(Graph &graph); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index a13cdb4..501d0ab 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -13,19 +13,129 @@ #include "Subgraph/Subgraph.h" #include "Swift/Metadata.h" #include "Trace.h" +#include "UpdateStack.h" namespace AG { +#pragma mark - Invalidating + +Graph::without_invalidating::without_invalidating(Graph *graph) { + _graph = graph; + _graph_was_deferring_invalidation = graph->_deferring_invalidation; + graph->_deferring_invalidation = true; +} + +Graph::without_invalidating::~without_invalidating() { + if (_graph && _graph_was_deferring_invalidation == false) { + _graph->_deferring_invalidation = false; + _graph->invalidate_subgraphs(); + } +} + +#pragma mark - Context + +#pragma mark - Subgraphs + +void Graph::remove_subgraph(Subgraph &subgraph) { + auto iter = std::remove(_subgraphs.begin(), _subgraphs.end(), &subgraph); + _subgraphs.erase(iter); + + if (auto map = _tree_data_elements_by_subgraph.get()) { + auto iter = map->find(&subgraph); + if (iter != map->end()) { + map->erase(iter); + } + } + + if (subgraph.other_state() & Subgraph::CacheState::Option1) { + subgraph.set_other_state(subgraph.other_state() & ~Subgraph::CacheState::Option1); // added to graph + + auto iter = std::remove(_subgraphs_with_cached_nodes.begin(), _subgraphs_with_cached_nodes.end(), &subgraph); + _subgraphs_with_cached_nodes.erase(iter); + } + + _num_subgraphs -= 1; +} + +void Graph::invalidate_subgraphs() { + if (_deferring_invalidation) { + return; + } + + if (_main_handler == nullptr) { + auto iter = _subgraphs_with_cached_nodes.begin(), end = _subgraphs_with_cached_nodes.end(); + while (iter != end) { + auto subgraph = *iter; + subgraph->set_other_state(subgraph->other_state() | Subgraph::CacheState::Option2); + subgraph->cache_collect(); + uint8_t cache_state = subgraph->other_state(); + subgraph->set_other_state(subgraph->other_state() & ~Subgraph::CacheState::Option2); + + if ((cache_state & Subgraph::CacheState::Option1) == 0) { + end = _subgraphs_with_cached_nodes.erase(iter); + } else { + ++iter; + } + } + while (!_invalidated_subgraphs.empty()) { + auto subgraph = _invalidated_subgraphs.back(); + subgraph->invalidate_now(*this); + _invalidated_subgraphs.pop_back(); + } + } +} + +#pragma mark - Updates + +TaggedPointer Graph::current_update() { + return TaggedPointer((UpdateStack *)pthread_getspecific(_current_update_key)); +} + +void Graph::set_current_update(TaggedPointer current_update) { + pthread_setspecific(_current_update_key, (void *)current_update.value()); +} + +void Graph::call_main_handler(void *context, void (*body)(void *)) { + assert(_main_handler); + + struct MainTrampoline { + Graph *graph; + pthread_t thread; + void *context; + void (*handler)(void *); + + static void thunk(void *arg) { + auto trampoline = reinterpret_cast(arg); + trampoline->handler(trampoline->context); + }; + }; + + auto current_update_thread = _current_update_thread; + auto main_handler = _main_handler; + auto main_handler_context = _main_handler_context; + + _current_update_thread = 0; + _main_handler = nullptr; + _main_handler_context = nullptr; + + MainTrampoline trampoline = {this, current_update_thread, context, body}; + main_handler(MainTrampoline::thunk, &trampoline); + + _main_handler = main_handler; + _main_handler_context = main_handler_context; + _current_update_thread = current_update_thread; +} + +void Graph::with_update(data::ptr node, ClosureFunctionVV body) {} + +#pragma mark - Attributes + const AttributeType &Graph::attribute_type(uint32_t type_id) const { return *_types[type_id]; } -const AttributeType &Graph::attribute_ref(data::ptr node, const void *_Nullable *_Nullable ref_out) const { +const AttributeType &Graph::attribute_ref(data::ptr node, const void *_Nullable *_Nullable self_out) const { auto &type = attribute_type(node->type_id()); - if (ref_out) { - void *self = ((char *)node.get() + type.attribute_offset()); - if (node->flags().has_indirect_self()) { - self = *(void **)self; - } - *ref_out = self; + if (self_out) { + *self_out = node->get_self(type); } return type; } @@ -43,10 +153,7 @@ void Graph::attribute_modify(data::ptr node, const swift::metadata &metada foreach_trace([&node](Trace &trace) { trace.begin_modify(node); }); - void *body = (uint8_t *)node.get() + type.attribute_offset(); - if (node->flags().has_indirect_self()) { - body = *(void **)body; - } + void *body = node->get_self(type); modify(body); foreach_trace([&node](Trace &trace) { trace.end_modify(node); }); @@ -57,8 +164,6 @@ void Graph::attribute_modify(data::ptr node, const swift::metadata &metada } } -// MARK: Attributes - data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *value) { const AttributeType &type = attribute_type(type_id); @@ -132,8 +237,79 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void return node; } -void Graph::update_attribute(AttributeID attribute, bool option) { - // TODO: Not implemented +Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t options) { + if (!(options & 1) && _needs_update) { + if (!thread_is_updating()) { + call_update(); + } + } + + Node &node = attribute.to_node(); + if (node.state().is_value_initialized() && !node.state().is_dirty()) { + return UpdateStatus::Option0; + } + + _update_attribute_count += 1; + if (node.state().updates_on_main()) { + _update_attribute_on_main_count += 1; + } + + pthread_t thread = pthread_self(); + UpdateStack update_stack = UpdateStack(this, thread, current_update(), _current_update_thread); + update_stack.set_options(options); + if (update_stack.previous() != nullptr) { + update_stack.set_options((update_stack.previous().get()->options() & 4) | options); + } + + _current_update_thread = thread; + + if (_deferring_invalidation == false) { + _deferring_invalidation = true; + update_stack.set_was_deferring_invalidation(true); + } + + set_current_update(TaggedPointer(&update_stack, options >> 3 & 1)); + + foreach_trace([&update_stack, &attribute, &options](Trace &trace) { + trace.begin_update(update_stack, attribute.to_node_ptr(), options); + }); + + UpdateStatus status = UpdateStatus::Option1; + if (update_stack.push(attribute.to_node_ptr(), node, false, (options & 1) == 0)) { + + status = update_stack.update(); + if (status == UpdateStatus::NeedsCallMainHandler) { + std::pair context = {&update_stack, UpdateStatus::NeedsCallMainHandler}; + call_main_handler(&context, [](void *void_context) { + auto inner_context = reinterpret_cast *>(void_context); + TaggedPointer previous = Graph::current_update(); + inner_context->second = inner_context->first->update(); + Graph::set_current_update(previous); + }); + status = context.second; + + _update_attribute_on_main_count += 1; + } + } + + foreach_trace([&update_stack, &attribute, &status](Trace &trace) { + trace.end_update(update_stack, attribute.to_node_ptr(), status); + }); + + for (auto frame : update_stack.frames()) { + frame.attribute->set_state(frame.attribute->state().with_in_update_stack(false)); + } + + if (update_stack.thread() != _current_update_thread) { + non_fatal_precondition_failure("invalid graph update (access from multiple threads?)"); + } + + _current_update_thread = update_stack.previous_thread(); + set_current_update(update_stack.previous()); + + if (update_stack.was_deferring_invalidation()) { + _deferring_invalidation = false; + } } #pragma mark - Indirect attributes @@ -260,7 +436,7 @@ AGValueState Graph::value_state(AttributeID attribute) { auto node = attribute.to_node(); return (node.state().is_dirty() ? 1 : 0) << 0 | (node.state().is_pending() ? 1 : 0) << 1 | (node.state().is_evaluating() ? 1 : 0) << 2 | (node.state().is_value_initialized() ? 1 : 0) << 3 | - (node.state().is_unknown2() ? 1 : 0) << 4 | (node.flags().value4_unknown0x20() ? 1 : 0) << 5 | + (node.state().updates_on_main() ? 1 : 0) << 4 | (node.flags().value4_unknown0x20() ? 1 : 0) << 5 | (node.state().is_unknown3() ? 1 : 0) << 6 | (node.flags().value4_unknown0x40() ? 1 : 0) << 7; } @@ -371,9 +547,6 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void if (node.state().is_value_initialized()) { // already initialized void *value_dest = node.get_value(); - if (node.flags().has_indirect_value()) { - value_dest = *(void **)value_dest; - } LayoutDescriptor::ComparisonOptions comparison_options = LayoutDescriptor::ComparisonOptions(type.comparison_mode()) | @@ -405,9 +578,6 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void mark_changed(node_ptr, nullptr, nullptr, nullptr); void *value_dest = node.get_value(); - if (node.flags().has_indirect_value()) { - value_dest = *(void **)value_dest; - } value_type.vw_initializeWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); } } diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 13439c1..f19aa9b 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -1,10 +1,13 @@ #pragma once #include +#include +#include #include #include #include "Attribute/AttributeID.h" +#include "Attribute/Node/InputEdge.h" #include "Closure/ClosureFunction.h" #include "Util/HashTable.h" #include "Util/Heap.h" @@ -15,6 +18,25 @@ typedef uint8_t AGValueState; // TODO: move namespace AG { +// TODO: move somewhere else +template class TaggedPointer { + private: + uintptr_t _value; + + public: + TaggedPointer() : _value(0){}; + TaggedPointer(T *_Nullable value) : _value((uintptr_t)value){}; + TaggedPointer(T *_Nullable value, bool tag) : _value(((uintptr_t)value & ~0x1) | (tag ? 1 : 0)){}; + + uintptr_t value() { return _value; }; + bool tag() { return static_cast(_value & 0x1); }; + + T *_Nullable get() { return reinterpret_cast(_value & ~0x1); }; + + bool operator==(nullptr_t) const noexcept { return _value == 0; }; + bool operator!=(nullptr_t) const noexcept { return _value != 0; }; +}; + namespace swift { class metadata; } @@ -28,7 +50,14 @@ class Graph { class Context; class KeyTable; class UpdateStack; - enum class UpdateStatus; + class UpdateStackRef; + + enum UpdateStatus : uint32_t { + Option0 = 0, + Option1 = 1, + Option2 = 2, + NeedsCallMainHandler = 3, + }; struct TreeElement; struct TreeValue; @@ -45,7 +74,11 @@ class Graph { void push_back(TreeElementNodePair pair) { _nodes.push_back(pair); }; }; + using MainHandler = void (*)(void (*thunk)(void *), void *thunk_context); // needs AG_SWIFT_CC(swift) ? + private: + static pthread_key_t _current_update_key; + Graph *_prev; Graph *_next; util::Heap _heap; @@ -53,7 +86,9 @@ class Graph { vector _types; vector _traces; - uint64_t _field_0xf0; + + MainHandler _Nullable _main_handler; + void *_Nullable _main_handler_context; size_t _allocated_node_values_size = 0; @@ -64,58 +99,98 @@ class Graph { vector _subgraphs; vector _subgraphs_with_cached_nodes; vector _invalidated_subgraphs; // TODO: check is 2 stack length + bool _deferring_invalidation; // used to batch invalidate subgraphs // TODO: check field offets uint64_t _num_nodes; // probably this, not sure uint64_t _num_node_values; // probably this, not sure + uint64_t _num_subgraphs; bool _needs_update; // 0x199 + pthread_t _current_update_thread; + uint64_t _counter_0x1b8; + uint64_t _update_attribute_count; + uint64_t _update_attribute_on_main_count; public: - bool needs_update() { return _needs_update; }; - void call_update(); - - bool thread_is_updating(); + // MARK: Context - void increment_counter_0x1b8() { _counter_0x1b8 += 1; }; + bool is_context_updating(uint64_t context_id); + void main_context(); // MARK: Subgraphs - bool is_validating_0x198(); - void set_validating_0x198(bool value); + class without_invalidating { + private: + Graph *_graph; + bool _graph_was_deferring_invalidation; - bool field_0xf0(); + public: + without_invalidating(Graph *graph); + ~without_invalidating(); + }; + + vector &subgraphs(); + + void add_subgraph(Subgraph &subgraph); // called from constructor of Subgraph + void remove_subgraph(Subgraph &subgraph); - void invalidate_subgraphs(); void will_invalidate_subgraph(Subgraph &subgraph) { _invalidated_subgraphs.push_back(&subgraph); }; - vector &subgraphs(); - void remove_subgraph(const Subgraph &subgraph); - - void add_subgraphs_with_cached_node(Subgraph *subgraph) { - _subgraphs_with_cached_nodes.push_back(subgraph); - } + void invalidate_subgraphs(); + bool deferring_invalidation() { return _deferring_invalidation; }; + void set_deferring_invalidation(bool value) { _deferring_invalidation = value; }; + + void remove_subgraphs_with_cached_node(Subgraph *subgraph); // overload with iter? + void add_subgraphs_with_cached_node(Subgraph *subgraph) { _subgraphs_with_cached_nodes.push_back(subgraph); } + + void increment_counter_0x1b8() { _counter_0x1b8 += 1; }; + + // MARK: Updates + + static TaggedPointer current_update(); + static void set_current_update(TaggedPointer current_update); + + bool thread_is_updating(); + + bool needs_update() { return _needs_update; }; + void call_update(); + void reset_update(data::ptr node); + + void collect_stack(vector, 0, uint64_t> &stack); + + void with_update(data::ptr node, ClosureFunctionVV body); + + MainHandler _Nullable main_handler() { return _main_handler; }; + void call_main_handler(void *context, void (*body)(void *context)); + + bool passed_deadline(); + bool passed_deadline_slow(); // MARK: Attributes const AttributeType &attribute_type(uint32_t type_id) const; - const AttributeType &attribute_ref(data::ptr node, const void *_Nullable *_Nullable ref_out) const; + const AttributeType &attribute_ref(data::ptr node, const void *_Nullable *_Nullable self_out) const; void attribute_modify(data::ptr node, const swift::metadata &type, ClosureFunctionPV closure, bool flag); data::ptr add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *_Nullable value); - void remove_node(data::ptr node); - void update_attribute(AttributeID attribute, bool option); + UpdateStatus update_attribute(AttributeID attribute, uint8_t options); + void update_main_refs(AttributeID attribute); + + void remove_node(data::ptr node); void did_allocate_node_value(size_t size) { _allocated_node_values_size += size; }; void did_destroy_node_value(size_t size) { _allocated_node_values_size -= size; }; void did_destroy_node(); // decrement counter 0x100 + void breadth_first_search(AttributeID attribute, uint32_t arg, ClosureFunctionAB predicate) const; + // MARK: Indirect attributes void add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, std::optional size, @@ -128,30 +203,57 @@ class Graph { const AttributeID &indirect_attribute_dependency(data::ptr indirect_node); void indirect_attribute_set_dependency(data::ptr indirect_node, AttributeID dependency); - // MARK: Edges + // MARK: Values + + void *value_ref(AttributeID attribute, const swift::metadata &value_type, const void *value); + + bool value_set(data::ptr node, const swift::metadata &value_type, const void *value); + bool value_set_internal(data::ptr node_ptr, Node &node, const void *value, const swift::metadata &type); + + bool value_exists(data::ptr node); + AGValueState value_state(AttributeID attribute); + + void value_mark(data::ptr node); + void value_mark_all(); + + void propagate_dirty(AttributeID attribute); + + // MARK: Inputs + + void input_value_ref(data::ptr node, AttributeID attribute, uint32_t arg3, uint32_t arg4, + const swift::metadata *type, char *arg5, uint64_t arg6); + void input_value_ref_slow(data::ptr node, AttributeID attribute, uint32_t arg3, uint32_t arg4, + const swift::metadata *type, char *arg5, uint64_t arg6); void add_input(data::ptr node, AttributeID attribute, bool flag, uint32_t option); - void add_input_dependencies(AttributeID attribute, AttributeID source); + void remove_input(data::ptr node, uint64_t arg); void remove_all_inputs(data::ptr node); + bool compare_edge_values(InputEdge input_edge, const AttributeType &type, void const *lhs, void const *rhs); + + void input_value_add(data::ptr node, AttributeID attribute, uint32_t arg); + void any_inputs_changed(data::ptr node, const uint32_t *arg1, uint64_t arg2); void all_inputs_removed(data::ptr node); - template void add_output_edge(data::ptr node, AttributeID attribute); - template void remove_output_edge(data::ptr node, AttributeID attribute); + void add_input_dependencies(AttributeID attribute, AttributeID source); + void remove_input_dependencies(AttributeID attribute, AttributeID source); - // MARK: Values + void remove_input_edge(data::ptr node_ptr, Node &node, uint32_t arg); - bool value_exists(data::ptr node); - AGValueState value_state(AttributeID attribute); + void remove_removed_input(AttributeID attribute, AttributeID source); - void value_mark(data::ptr node); - void value_mark_all(); + uint32_t index_of_input(data::ptr node, InputEdge::Comparator comparator); + uint32_t index_of_input_slow(data::ptr node, InputEdge::Comparator comparator); - bool value_set(data::ptr node, const swift::metadata &value_type, const void *value); - bool value_set_internal(data::ptr node_ptr, Node &node, const void *value, const swift::metadata &type); + // MARK: Outputs - void propagate_dirty(AttributeID attribute); + void output_value_ref(data::ptr node, const swift::metadata &type); + + template void add_output_edge(data::ptr node, AttributeID attribute); + template void remove_output_edge(data::ptr node, AttributeID attribute); + + void remove_removed_output(AttributeID attribute, AttributeID source, bool flag); // MARK: Marks @@ -182,9 +284,16 @@ class Graph { // MARK: Tracing + void prepare_trace(Trace &trace); + void add_trace(Trace *_Nullable trace); void remove_trace(uint64_t trace_id); + void start_tracing(uint32_t arg, std::span span); + void stop_tracing(); + void sync_tracing(); + void copy_trace_path(); + template requires std::invocable void foreach_trace(T body) { @@ -193,8 +302,30 @@ class Graph { } }; + static void all_start_tracing(); + static void all_stop_tracing(); + static void all_sync_tracing(); + static void all_copy_trace_path(); + static void trace_assertion_failure(bool all_stop_tracing, const char *format, ...); + // MARK: Profile + + void begin_profile_event(data::ptr node, const char *event_name); + void end_profile_event(data::ptr node, const char *event_name, uint64_t arg1, bool arg2); + + void add_profile_update(data::ptr node, uint64_t arg2, bool arg3); + + void start_profiling(uint32_t arg); + void stop_profiling(); + void mark_profile(uint32_t arg1, uint64_t arg2); + void reset_profile(); + + static void all_start_profiling(uint32_t arg); + static void all_stop_profiling(); + static void all_mark_profile(const char *event_name); + static void all_reset_profile(); + // MARK: Encoding void encode_node(Encoder &encoder, const Node &node, bool flag); @@ -202,9 +333,29 @@ class Graph { void encode_tree(Encoder &encoder, data::ptr tree); + // MARK: Description + + void description(CFDictionaryRef options); + void description(data::ptr node); + + char *description_graph_dot(CFDictionaryRef options); + char *description_stack(CFDictionaryRef options); + char *description_stack_frame(CFDictionaryRef options); + char *description_stack_nodes(CFDictionaryRef options); + + vector description_graph(CFDictionaryRef options); + + void write_to_file(const char *filename, uint32_t arg); + // MARK: Printing + void print(); + + void print_attribute(data::ptr node); + void print_cycle(data::ptr node); + void print_data(); + void print_stack(); }; } // namespace AG diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp new file mode 100644 index 0000000..f93b2fb --- /dev/null +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -0,0 +1,88 @@ +#include "UpdateStack.h" + +#include "Attribute/AttributeType.h" +#include "Attribute/Node/Node.h" + +namespace AG { + +Graph::UpdateStack::Frame *Graph::UpdateStack::global_top() { + for (TaggedPointer update_stack = this; update_stack != nullptr; + update_stack = update_stack.get()->previous()) { + if (!update_stack.get()->frames().empty()) { + return &update_stack.get()->frames().back(); + } + } + return nullptr; +} + +bool Graph::UpdateStack::push(data::ptr attribute, Node &node, bool flag1, bool flag2) { + if (!node.state().is_evaluating()) { + node.set_state(node.state().with_in_update_stack(true)); + + // TODO: check where capacity is being checked + uint32_t frame_flags = 0; + if (node.state().is_pending() || (!node.state().is_value_initialized() && flag2)) { + frame_flags = 1; + } + _frames.push_back({attribute, frame_flags}); + return true; + } + + return push_slow(attribute, node, flag1, flag2); +} + +bool Graph::UpdateStack::push_slow(data::ptr attribute, Node &node, bool flag1, bool flag2) { + uint8_t is_evaluating = node.state().is_evaluating(); + + if (is_evaluating) { + // is already updating + if (!flag1) { + Graph::UpdateStack::Frame *top = global_top(); + if (top != nullptr && (top->flags & 2) == 0) { + this->_graph->print_cycle(attribute); + } + + if (!node.state().is_value_initialized()) { + const AttributeType &attribute_type = _graph->attribute_type(node.type_id()); + + auto callback = attribute_type.vt_get_update_stack_callback(); + if (callback != nullptr) { + + Graph::UpdateStack::Frame frame = {attribute, 0}; + if (node.state().is_pending() || (!node.state().is_value_initialized() && flag2)) { + frame.flags = 1; + } + + _frames.push_back(frame); + + void *self = node.get_self(attribute_type); + callback(&attribute_type, self); + + _frames.pop_back(); + + if (node.state().is_value_initialized()) { + return false; + } + } + if (is_evaluating == 3) { // both flags set + precondition_failure("cyclic graph: %u", attribute); + } + } + } + } + + node.set_state(node.state().with_in_update_stack(true)); + + Graph::UpdateStack::Frame frame = {attribute, 0}; + if (node.state().is_pending() || (!node.state().is_value_initialized() && flag2)) { + frame.flags = 1; + } + if (is_evaluating) { + frame.flags |= 0x2; // mark so we can print cycles + } + _frames.push_back(frame); + + return true; +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h new file mode 100644 index 0000000..ba763cf --- /dev/null +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "Graph.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Graph::UpdateStack { + public: + struct Frame { + data::ptr attribute; + uint32_t flags; + }; + enum Option : uint8_t { + WasDeferringInvalidation = 1 << 4, + }; + + private: + Graph *_graph; + pthread_t _thread; + TaggedPointer _previous; + pthread_t _previous_thread; + vector _frames; + uint8_t _options; + + public: + UpdateStack(Graph *graph, pthread_t thread, TaggedPointer previous, pthread_t previous_thread); + + pthread_t thread() { return _thread; }; + + uint8_t options() { return _options; }; + void set_options(uint8_t options) { _options = options; }; + + void set_was_deferring_invalidation(bool value) { + _options = (_options & ~Option::WasDeferringInvalidation) | (value ? Option::WasDeferringInvalidation : 0); + }; + bool was_deferring_invalidation() { return _options & Option::WasDeferringInvalidation; }; + + TaggedPointer previous() { return _previous; }; + pthread_t previous_thread() { return _previous_thread; }; + + vector &frames() { return _frames; }; + + Frame *global_top(); + + Graph::UpdateStatus update(); + + bool push(data::ptr attribute, Node &node, bool flag1, bool flag2); + bool push_slow(data::ptr attribute, Node &node, bool flag1, bool flag2); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Subgraph/NodeCache.cpp b/Sources/ComputeCxx/Subgraph/NodeCache.cpp index caecfeb..6b7eb28 100644 --- a/Sources/ComputeCxx/Subgraph/NodeCache.cpp +++ b/Sources/ComputeCxx/Subgraph/NodeCache.cpp @@ -21,10 +21,7 @@ Subgraph::NodeCache::NodeCache() noexcept if (a->node) { data::ptr a_node = a->node; auto a_type = AttributeID(a->node).subgraph()->graph()->attribute_type(a_node->type_id()); - a_body = (uint8_t *)a_node.get() + a_type.attribute_offset(); - if (a_node->flags().has_indirect_self()) { - a_body = *(void **)a_body; - } + a_body = a_node->get_self(a_type); } else { a_body = a->body; // next or body? } @@ -33,10 +30,7 @@ Subgraph::NodeCache::NodeCache() noexcept if (b->node) { data::ptr b_node = b->node; auto b_type = AttributeID(b->node).subgraph()->graph()->attribute_type(b_node->type_id()); - b_body = (uint8_t *)b_node.get() + b_type.attribute_offset(); - if (b_node->flags().has_indirect_self()) { - b_body = *(void **)b_body; - } + b_body = b_node->get_self(b_type); } else { b_body = b->body; // next or body? } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index d6ad64c..2df7e5a 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -306,8 +306,7 @@ void Subgraph::unlink_attribute(AttributeID attribute) { void Subgraph::invalidate_now(Graph &graph) { // TODO: double check graph param vs _graph instance var - // maybe this is a lock on subgraphs - graph.set_validating_0x198(true); // TODO: check member var against other usages of set_validating() + graph.set_deferring_invalidation(true); auto removed_subgraphs = vector(); auto stack = std::stack>(); @@ -442,7 +441,7 @@ void Subgraph::invalidate_now(Graph &graph) { free(removed_subgraph); // or delete? } - graph.set_validating_0x198(false); // TODO: check instance var + graph.set_deferring_invalidation(false); } void Subgraph::invalidate_and_delete_(bool flag) { @@ -457,7 +456,8 @@ void Subgraph::invalidate_and_delete_(bool flag) { } _parents.clear(); - if (!_graph->is_validating_0x198() && _graph->field_0xf0() == 0) { + // Check Graph::invalidate_subgraphs + if (_graph->deferring_invalidation() == false && _graph->main_handler() == nullptr) { invalidate_now(*_graph); _graph->invalidate_subgraphs(); return; @@ -608,8 +608,8 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { return; } - bool was_validating = graph()->is_validating_0x198(); - graph()->set_validating_0x198(true); + // Defer invalidation until the end of this method's scope + auto without_invalidating = Graph::without_invalidating(graph()); _last_traversal_seed += 1; // TODO: check atomics @@ -670,10 +670,7 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { } } - if (graph() != nullptr && !was_validating) { - graph()->set_validating_0x198(false); - graph()->invalidate_subgraphs(); - } + // ~without_invalidating(); } // MARK: - Tree @@ -1018,17 +1015,17 @@ void Subgraph::cache_insert(data::ptr node) { type->first_item = item; } - if ((_other_state & OtherState::Option1) == 0) { - if ((_other_state & OtherState::Option2) == 0) { + if ((_other_state & CacheState::Option1) == 0) { + if ((_other_state & CacheState::Option2) == 0) { _graph->add_subgraphs_with_cached_node(this); } - _other_state |= OtherState::Option1; + _other_state |= CacheState::Option1; } } } void Subgraph::cache_collect() { - _other_state &= ~OtherState::Option1; // turn off 0x1 bit + _other_state &= ~CacheState::Option1; // turn off 0x1 bit std::pair context = {this, _cache.get()}; if (_cache != nullptr && !_invalidated) { @@ -1051,7 +1048,7 @@ void Subgraph::cache_collect() { node->destroy_value(*subgraph->_graph); subgraph->_graph->remove_all_inputs(node); } else { - subgraph->_other_state |= OtherState::Option1; + subgraph->_other_state |= CacheState::Option1; } item = item->prev; } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 40bf9b9..dfb3f03 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -57,9 +57,9 @@ class Subgraph : public data::zone { struct Observer {}; - enum OtherState : uint8_t { - Option1 = 1 << 0, - Option2 = 1 << 1, + enum CacheState : uint8_t { + Option1 = 1 << 0, // added to graph._subgraphs_with_cached_nodes, or needs collect? + Option2 = 1 << 1, // Is calling cache collect }; private: @@ -106,6 +106,9 @@ class Subgraph : public data::zone { void graph_destroyed(); + uint8_t other_state() { return _other_state; }; + void set_other_state(uint8_t other_state) { _other_state = other_state; }; + // MARK: Managing children void add_child(Subgraph &child, SubgraphChild::Flags flags); From 7f3bb204bca2a6b66d7f2736c76502b388401a69 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Feb 2025 19:04:18 +1100 Subject: [PATCH 07/74] Implement some update methods on Graph --- Sources/ComputeCxx/Graph/Context.h | 2 + Sources/ComputeCxx/Graph/Graph.cpp | 110 ++++++++++++++++------- Sources/ComputeCxx/Graph/Graph.h | 7 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 38 ++++++++ Sources/ComputeCxx/Graph/UpdateStack.h | 19 ++-- 5 files changed, 131 insertions(+), 45 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h index d0dfe57..71b5fe7 100644 --- a/Sources/ComputeCxx/Graph/Context.h +++ b/Sources/ComputeCxx/Graph/Context.h @@ -14,6 +14,8 @@ class Graph::Context { Graph &graph() const { return *_graph; }; uint64_t unique_id(); + + void call_update(); }; } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 501d0ab..e45b0c0 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1,5 +1,6 @@ #include "Graph.h" +#include #include #include "Attribute/AttributeType.h" @@ -7,6 +8,7 @@ #include "Attribute/Node/Node.h" #include "Attribute/OffsetAttributeID.h" #include "Attribute/WeakAttributeID.h" +#include "Context.h" #include "Errors/Errors.h" #include "KeyTable.h" #include "Log/Log.h" @@ -95,6 +97,60 @@ void Graph::set_current_update(TaggedPointer current_update) { pthread_setspecific(_current_update_key, (void *)current_update.value()); } +bool Graph::thread_is_updating() { + for (auto update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { + if (update_stack.get()->graph() == this) { + return true; + } + } + return false; +} + +void Graph::call_update() { + while (_needs_update) { + _needs_update = false; + _contexts_by_id.for_each([](uint64_t context_id, Context *graph_context, + const void *closure_context) { graph_context->call_update(); }, + nullptr); + } +} + +void Graph::reset_update(data::ptr node) { + for (auto update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { + for (auto frame : update_stack.get()->frames()) { + if (frame.attribute == node) { + frame.flags &= 0xf; + } + } + } +} + +void Graph::collect_stack(vector, 0, uint64_t> &nodes) { + for (auto update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { + auto frames = update_stack.get()->frames(); + for (auto iter = frames.rbegin(), end = frames.rend(); iter != end; ++iter) { + nodes.push_back(iter->attribute); + } + } +} + +void Graph::with_update(data::ptr node, ClosureFunctionVV body) { + class scoped_update { + private: + UpdateStack _base; + + public: + scoped_update(UpdateStack base, data::ptr node) : _base(base) { + _base.frames().push_back({node, node->state().is_pending()}); + }; + ~scoped_update() { _base.frames().pop_back(); } + }; + + scoped_update update = scoped_update(UpdateStack(this, 0), node); + body(); + // ~scoped_update called +} + void Graph::call_main_handler(void *context, void (*body)(void *)) { assert(_main_handler); @@ -126,7 +182,28 @@ void Graph::call_main_handler(void *context, void (*body)(void *)) { _current_update_thread = current_update_thread; } -void Graph::with_update(data::ptr node, ClosureFunctionVV body) {} +bool Graph::passed_deadline() { + if (_deadline == -1) { + return false; + } + return passed_deadline_slow(); +} + +bool Graph::passed_deadline_slow() { + if (_deadline == 0) { + return true; + } + + uint64_t time = mach_absolute_time(); + if (time < _deadline) { + return false; + } + + foreach_trace([](Trace &trace) { trace.passed_deadline(); }); + _deadline = 0; + + return true; +} #pragma mark - Attributes @@ -254,21 +331,7 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio _update_attribute_on_main_count += 1; } - pthread_t thread = pthread_self(); - UpdateStack update_stack = UpdateStack(this, thread, current_update(), _current_update_thread); - update_stack.set_options(options); - if (update_stack.previous() != nullptr) { - update_stack.set_options((update_stack.previous().get()->options() & 4) | options); - } - - _current_update_thread = thread; - - if (_deferring_invalidation == false) { - _deferring_invalidation = true; - update_stack.set_was_deferring_invalidation(true); - } - - set_current_update(TaggedPointer(&update_stack, options >> 3 & 1)); + UpdateStack update_stack = UpdateStack(this, options); foreach_trace([&update_stack, &attribute, &options](Trace &trace) { trace.begin_update(update_stack, attribute.to_node_ptr(), options); @@ -296,20 +359,7 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio trace.end_update(update_stack, attribute.to_node_ptr(), status); }); - for (auto frame : update_stack.frames()) { - frame.attribute->set_state(frame.attribute->state().with_in_update_stack(false)); - } - - if (update_stack.thread() != _current_update_thread) { - non_fatal_precondition_failure("invalid graph update (access from multiple threads?)"); - } - - _current_update_thread = update_stack.previous_thread(); - set_current_update(update_stack.previous()); - - if (update_stack.was_deferring_invalidation()) { - _deferring_invalidation = false; - } + // ~UpdateStatus called } #pragma mark - Indirect attributes diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index f19aa9b..ec632f6 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -82,9 +82,12 @@ class Graph { Graph *_prev; Graph *_next; util::Heap _heap; + util::UntypedTable _type_ids_by_metadata; vector _types; + util::Table _contexts_by_id; + vector _traces; MainHandler _Nullable _main_handler; @@ -110,6 +113,8 @@ class Graph { pthread_t _current_update_thread; + uint64_t _deadline; + uint64_t _counter_0x1b8; uint64_t _update_attribute_count; uint64_t _update_attribute_on_main_count; @@ -159,7 +164,7 @@ class Graph { void call_update(); void reset_update(data::ptr node); - void collect_stack(vector, 0, uint64_t> &stack); + void collect_stack(vector, 0, uint64_t> &nodes); void with_update(data::ptr node, ClosureFunctionVV body); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index f93b2fb..359c30e 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -5,6 +5,44 @@ namespace AG { +Graph::UpdateStack::UpdateStack(Graph *graph, uint8_t options) { + _graph = graph; + _thread = pthread_self(); + _previous = current_update(); + _previous_thread = graph->_current_update_thread; + + _options = options; + if (_previous != nullptr) { + _options = _options | (_previous.get()->_options & 4); + } + + graph->_current_update_thread = _thread; + + if (graph->_deferring_invalidation == false) { + graph->_deferring_invalidation = true; + _options &= Option::WasNotDeferringInvalidation; + } + + Graph::set_current_update(TaggedPointer(this, options >> 3 & 1)); +} + +Graph::UpdateStack::~UpdateStack() { + for (auto frame : _frames) { + frame.attribute->set_state(frame.attribute->state().with_in_update_stack(false)); + } + + if (_thread != _graph->_current_update_thread) { + non_fatal_precondition_failure("invalid graph update (access from multiple threads?)"); + } + + _graph->_current_update_thread = _previous_thread; + Graph::set_current_update(_previous); + + if (_options & Option::WasNotDeferringInvalidation) { + _graph->_deferring_invalidation = false; + } +} + Graph::UpdateStack::Frame *Graph::UpdateStack::global_top() { for (TaggedPointer update_stack = this; update_stack != nullptr; update_stack = update_stack.get()->previous()) { diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index ba763cf..7a04cbd 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -12,10 +12,10 @@ class Graph::UpdateStack { public: struct Frame { data::ptr attribute; - uint32_t flags; + uint32_t flags; // first four bits are base }; enum Option : uint8_t { - WasDeferringInvalidation = 1 << 4, + WasNotDeferringInvalidation = 1 << 4, }; private: @@ -27,20 +27,11 @@ class Graph::UpdateStack { uint8_t _options; public: - UpdateStack(Graph *graph, pthread_t thread, TaggedPointer previous, pthread_t previous_thread); - - pthread_t thread() { return _thread; }; - - uint8_t options() { return _options; }; - void set_options(uint8_t options) { _options = options; }; - - void set_was_deferring_invalidation(bool value) { - _options = (_options & ~Option::WasDeferringInvalidation) | (value ? Option::WasDeferringInvalidation : 0); - }; - bool was_deferring_invalidation() { return _options & Option::WasDeferringInvalidation; }; + UpdateStack(Graph *graph, uint8_t options); + ~UpdateStack(); + Graph *graph() { return _graph; }; TaggedPointer previous() { return _previous; }; - pthread_t previous_thread() { return _previous_thread; }; vector &frames() { return _frames; }; From d507895b275d93c2c23b85b791c227ebcd15d62d Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Feb 2025 19:31:39 +1100 Subject: [PATCH 08/74] Implement Graph::remove_node() --- Sources/ComputeCxx/Attribute/Node/Node.h | 31 +++++++++++++------ .../ComputeCxx/Attribute/Node/OutputEdge.h | 2 +- Sources/ComputeCxx/Graph/Graph.cpp | 16 ++++++++++ Sources/ComputeCxx/Graph/Graph.h | 1 + Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 08bbb82..0c73a7f 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -4,6 +4,7 @@ #include "Data/Pointer.h" #include "InputEdge.h" +#include "OutputEdge.h" CF_ASSUME_NONNULL_BEGIN @@ -131,21 +132,21 @@ class Node { }; static_assert(sizeof(Info) == 4); - struct TreeInfo { + struct EdgeInfo { unsigned int flags : 5; - unsigned int num_input_edges : 11; + unsigned int num_edges : 11; unsigned int other_flag : 16; }; - static_assert(sizeof(TreeInfo) == 4); + static_assert(sizeof(EdgeInfo) == 4); Info _info; NodeFlags _flags; data::ptr _value; - TreeInfo _tree_info; // 0x0c - 5 bits of flags than count of parents?? - data::ptr _input_edges; // 0x10 - uint32_t _field0x14; // 0x14 - TODO: verify - data::ptr _next_child; // 0x18 - TODO: verify + EdgeInfo _inputs_info; + data::ptr _inputs; + EdgeInfo _outputs_info; + data::ptr _outputs; public: Node(State state, uint32_t type_id, uint8_t flags4); @@ -155,7 +156,6 @@ class Node { uint32_t type_id() const { return uint32_t(_info.type_id); }; NodeFlags &flags() { return _flags; }; - uint32_t field0x14() { return _field0x14; }; void *get_self(const AttributeType &type); // TODO: inline void update_self(const Graph &graph, void *new_self); @@ -167,15 +167,26 @@ class Node { void destroy(Graph &graph); - uint32_t num_input_edges() { return _tree_info.num_input_edges; }; + uint32_t num_input_edges() { return _inputs_info.num_edges; }; + uint32_t num_output_edges() { return _outputs_info.num_edges; }; + template requires std::invocable void foreach_input_edge(T body) { - InputEdge *array = _input_edges.get(); + InputEdge *array = _inputs.get(); for (uint32_t i = 0, end = num_input_edges(); i != end; ++i) { body(array[i]); } } + + template + requires std::invocable + void foreach_output_edge(T body) { + OutputEdge *array = _outputs.get(); + for (uint32_t i = 0, end = num_output_edges(); i != end; ++i) { + body(array[i]); + } + } }; static_assert(sizeof(Node) == 0x1c); diff --git a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h index 7f35c19..4cc54fe 100644 --- a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h +++ b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h @@ -4,7 +4,7 @@ namespace AG { class OutputEdge { public: - data::ptr node_ptr; + data::ptr value; }; } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index e45b0c0..474c421 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -362,6 +362,22 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio // ~UpdateStatus called } +void Graph::remove_node(data::ptr node) { + if (node->state().is_evaluating()) { + precondition_failure("deleting updating attribute: %u\n", node); + } + + node->foreach_input_edge( + [this, &node](InputEdge &input_edge) { this->remove_removed_input(node, input_edge.value); }); + + node->foreach_output_edge( + [this, &node](OutputEdge &output_edge) { this->remove_removed_output(node, output_edge.value, false); }); + + // if (_profile_data != nullptr) { + // TODO: _profile_data->remove_node(); + // } +} + #pragma mark - Indirect attributes void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index ec632f6..047c381 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -8,6 +8,7 @@ #include "Attribute/AttributeID.h" #include "Attribute/Node/InputEdge.h" +#include "Attribute/Node/OutputEdge.h" #include "Closure/ClosureFunction.h" #include "Util/HashTable.h" #include "Util/Heap.h" diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 2df7e5a..5ebd449 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -996,7 +996,7 @@ void Subgraph::cache_insert(data::ptr node) { return; } - if (node->flags().value4_unknown0x10() && !node->state().is_evaluating() && node->field0x14() < 0x20) { + if (node->flags().value4_unknown0x10() && !node->state().is_evaluating() && node->num_output_edges() == 0) { // TODO: one of these flags must indicate it is cached const AttributeType &attribute_type = _graph->attribute_type(node->type_id()); From 5353b205385fdbb9a6b0f20bf2bbd5251cd1bc89 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Feb 2025 16:28:19 +1100 Subject: [PATCH 09/74] Implement attribute-related methods in Graph --- Sources/ComputeCxx/Array/ArrayRef.h | 76 ++++++++ Sources/ComputeCxx/Attribute/AttributeID.h | 2 +- Sources/ComputeCxx/Attribute/AttributeType.h | 2 +- .../ComputeCxx/Attribute/Node/IndirectNode.h | 18 +- Sources/ComputeCxx/Attribute/Node/InputEdge.h | 7 +- Sources/ComputeCxx/Attribute/Node/Node.h | 33 ++-- .../ComputeCxx/Attribute/Node/OutputEdge.h | 15 +- Sources/ComputeCxx/Data/Pointer.h | 6 +- Sources/ComputeCxx/Graph/Graph.cpp | 166 ++++++++++++++++-- Sources/ComputeCxx/Graph/Graph.h | 9 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- Sources/ComputeCxx/Vector/Vector.tpp | 7 +- 12 files changed, 297 insertions(+), 46 deletions(-) create mode 100644 Sources/ComputeCxx/Array/ArrayRef.h diff --git a/Sources/ComputeCxx/Array/ArrayRef.h b/Sources/ComputeCxx/Array/ArrayRef.h new file mode 100644 index 0000000..fcf815a --- /dev/null +++ b/Sources/ComputeCxx/Array/ArrayRef.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +template class ArrayRef { + public: + using value_type = T; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + + private: + const T *_Nullable _data = nullptr; + size_type _size = 0; + + public: + ArrayRef() = default; + ArrayRef(T *_Nullable data, size_t size) : _data(data), _size(size){}; + + // TODO: see if this is really needed, or disable operator new in data::table managed objects... + inline void *operator new(std::size_t n, const ArrayRef *_Nonnull ptr) { return (void *)ptr; }; + + // Element access + + const T &operator[](size_t pos) const { + assert(pos < _size); + return _data[pos]; + }; + + const T &front() const { + assert(!empty()); + return _data[0]; + }; + const T &back() const { + assert(!empty()); + return _data[_size - 1]; + }; + + const T *_Nonnull data() const { return _data; }; + + // Iterators + + iterator _Nullable begin() { return _data; }; + iterator _Nullable end() { return _data + _size; }; + const_iterator _Nullable cbegin() const { return _data; }; + const_iterator _Nullable cend() const { return _data + _size; }; + const_iterator _Nullable begin() const { return cbegin(); }; + const_iterator _Nullable end() const { return cend(); }; + + reverse_iterator rbegin() { return std::reverse_iterator(end()); }; + reverse_iterator rend() { return std::reverse_iterator(begin()); }; + const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; + const_reverse_iterator crend() const { return std::reverse_iterator(cbegin()); }; + const_reverse_iterator rbegin() const { return crbegin(); }; + const_reverse_iterator rend() const { return crend(); }; + + // Capacity + + bool empty() const { return _size == 0; }; + size_type size() const { return _size; }; +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index 66bd415..d262c60 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -68,7 +68,7 @@ class AttributeID { bool is_direct() const { return kind() == Kind::Direct; }; bool is_indirect() const { return kind() == Kind::Indirect; }; - bool is_nil() const { return kind() == Kind::NilAttribute; }; + bool is_nil() const { return kind() == Kind::NilAttribute; }; // TODO: return true if whole thing is zero? // TODO: make these data::ptr<> Node &to_node() const { diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index 236d891..a740874 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -31,7 +31,7 @@ class AttributeType { HasDestroySelf = 1 << 2, // 0x04 InitialValueOfNodeState2And3 = 1 << 3, // 0x08 UseGraphAsInitialValue = 1 << 4, // 0x10 - Unknown0x20 = 1 << 5, // 0x20 + Unknown0x20 = 1 << 5, // 0x20 // used in update_main_refs }; using UpdateFunction = void (*)(void *body, AttributeID attribute); diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index a512d3e..86b5ed8 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -52,9 +52,16 @@ static_assert(sizeof(IndirectNode) == 0x10); class MutableIndirectNode : public IndirectNode { private: + struct EdgeInfo { + unsigned int flags : 5; + unsigned int num_edges : 11; + unsigned int other_flag : 16; + }; + static_assert(sizeof(EdgeInfo) == 4); + AttributeID _dependency; - data::ptr _output; - uint32_t something; + EdgeInfo _outputs_info; + data::ptr _outputs; WeakAttributeID _initial_source; uint32_t _initial_offset; @@ -64,6 +71,13 @@ class MutableIndirectNode : public IndirectNode { const AttributeID &dependency() const { return _dependency; }; void set_dependency(const AttributeID &dependency) { _dependency = dependency; }; + + ConstOutputEdgeArrayRef outputs() { + return { + _outputs.get(), + _outputs_info.num_edges, + }; + }; }; static_assert(sizeof(MutableIndirectNode) == 0x28); diff --git a/Sources/ComputeCxx/Attribute/Node/InputEdge.h b/Sources/ComputeCxx/Attribute/Node/InputEdge.h index d2218d6..33670f9 100644 --- a/Sources/ComputeCxx/Attribute/Node/InputEdge.h +++ b/Sources/ComputeCxx/Attribute/Node/InputEdge.h @@ -1,6 +1,7 @@ #pragma once -#include "Data/Pointer.h" +#include "Array/ArrayRef.h" +#include "Attribute/AttributeID.h" namespace AG { @@ -9,8 +10,10 @@ class Node; struct InputEdge { struct Comparator {}; - data::ptr value; + AttributeID value; uint8_t flags; }; +using ConstInputEdgeArrayRef = const ArrayRef; + } // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 0c73a7f..f1c5382 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -167,26 +167,19 @@ class Node { void destroy(Graph &graph); - uint32_t num_input_edges() { return _inputs_info.num_edges; }; - uint32_t num_output_edges() { return _outputs_info.num_edges; }; - - template - requires std::invocable - void foreach_input_edge(T body) { - InputEdge *array = _inputs.get(); - for (uint32_t i = 0, end = num_input_edges(); i != end; ++i) { - body(array[i]); - } - } - - template - requires std::invocable - void foreach_output_edge(T body) { - OutputEdge *array = _outputs.get(); - for (uint32_t i = 0, end = num_output_edges(); i != end; ++i) { - body(array[i]); - } - } + ConstInputEdgeArrayRef inputs() { + return { + _inputs.get(), + _inputs_info.num_edges, + }; + }; + + ConstOutputEdgeArrayRef outputs() { + return { + _outputs.get(), + _outputs_info.num_edges, + }; + }; }; static_assert(sizeof(Node) == 0x1c); diff --git a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h index 4cc54fe..8f06656 100644 --- a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h +++ b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h @@ -1,10 +1,21 @@ #pragma once +#include + +#include "Array/ArrayRef.h" +#include "Attribute/AttributeID.h" + +CF_ASSUME_NONNULL_BEGIN + namespace AG { class OutputEdge { -public: - data::ptr value; + public: + AttributeID value; }; +using ConstOutputEdgeArrayRef = const ArrayRef; + } // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Data/Pointer.h b/Sources/ComputeCxx/Data/Pointer.h index 8cbfb71..8689ce0 100644 --- a/Sources/ComputeCxx/Data/Pointer.h +++ b/Sources/ComputeCxx/Data/Pointer.h @@ -35,8 +35,10 @@ template class ptr { } } - element_type *_Nonnull get() const noexcept { - assert(_offset != 0); + element_type *_Nullable get() const noexcept { + if (_offset == 0) { + return nullptr; + } return reinterpret_cast(table::shared().ptr_base() + _offset); } diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 474c421..91e5caf 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" @@ -362,22 +363,163 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio // ~UpdateStatus called } +void Graph::update_main_refs(AttributeID attribute) { + if (attribute.is_nil()) { + return; + } + + auto output_edge_arrays = vector(); + auto update_unknown0x20 = [this, &output_edge_arrays](const AttributeID &attribute) { + if (attribute.is_nil()) { + return; + } + + if (attribute.is_direct()) { + Node &node = attribute.to_node(); + const AttributeType &type = this->attribute_type(node.type_id()); + + bool new_unknown0x20 = false; + if (type.value_metadata().getValueWitnesses()->isPOD() || type.unknown_0x20()) { + new_unknown0x20 = false; + } else { + if (node.state().is_unknown3()) { + new_unknown0x20 = true; + } else { + new_unknown0x20 = + std::any_of(node.inputs().begin(), node.inputs().end(), [](auto input_edge) -> bool { + auto resolved = + input_edge.value.resolve(AttributeID::TraversalOptions::EvaluateWeakReferences); + if (resolved.attribute().is_direct() && + resolved.attribute().to_node().flags().value4_unknown0x20()) { + return true; + } + }); + } + } + if (node.flags().value4_unknown0x20() != new_unknown0x20) { + node.flags().set_value4_unknown0x20(new_unknown0x20); + output_edge_arrays.push_back(node.outputs()); + } + } else if (attribute.is_indirect() && attribute.to_indirect_node().is_mutable()) { + MutableIndirectNode &node = attribute.to_indirect_node().to_mutable(); + output_edge_arrays.push_back(node.outputs()); + } + }; + + update_unknown0x20(attribute); + + while (!output_edge_arrays.empty()) { + ConstOutputEdgeArrayRef array = output_edge_arrays.back(); + output_edge_arrays.pop_back(); + + for (auto output_edge = array.rbegin(), output_edge_end = array.rend(); output_edge != output_edge_end; + ++output_edge) { + update_unknown0x20(output_edge->value); + } + } +} + void Graph::remove_node(data::ptr node) { if (node->state().is_evaluating()) { precondition_failure("deleting updating attribute: %u\n", node); } - node->foreach_input_edge( - [this, &node](InputEdge &input_edge) { this->remove_removed_input(node, input_edge.value); }); - - node->foreach_output_edge( - [this, &node](OutputEdge &output_edge) { this->remove_removed_output(node, output_edge.value, false); }); + for (auto input_edge : node->inputs()) { + this->remove_removed_input(node, input_edge.value); + } + for (auto output_edge : node->outputs()) { + this->remove_removed_output(node, output_edge.value, false); + } // if (_profile_data != nullptr) { // TODO: _profile_data->remove_node(); // } } +bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, + ClosureFunctionAB predicate) const { + auto resolved = attribute.resolve(AttributeID::TraversalOptions::SkipMutableReference); + if (resolved.attribute().without_kind() == 0) { + return false; + } + + auto seen = std::set(); + + auto queue = std::deque(); + queue.push_back(resolved.attribute()); + + while (!queue.empty()) { + AttributeID candidate = queue.front(); + queue.pop_front(); + + if (candidate.is_nil()) { + continue; + } + + if (candidate.is_direct() && predicate(candidate)) { + return true; + } + + if (options & SearchOptions::SearchInputs) { + if (candidate.is_direct()) { + for (auto input_edge : candidate.to_node().inputs()) { + auto input = + input_edge.value.resolve(AttributeID::TraversalOptions::SkipMutableReference).attribute(); + + if (seen.contains(input)) { + continue; + } + if (options & SearchOptions::TraverseGraphContexts || + candidate.subgraph()->graph_context_id() == input.subgraph()->graph_context_id()) { + seen.insert(input); + queue.push_back(input); + } + } + } else if (candidate.is_indirect()) { + // TODO: inputs view on IndirectNode with source? + AttributeID source = candidate.to_indirect_node().source().attribute(); + source = source.resolve(AttributeID::TraversalOptions::SkipMutableReference).attribute(); + + if (!seen.contains(source)) { + if (options & SearchOptions::TraverseGraphContexts || + candidate.subgraph()->graph_context_id() == source.subgraph()->graph_context_id()) { + seen.insert(source); + queue.push_back(source); + } + } + } + } + if (options & SearchOptions::SearchOutputs) { + if (candidate.is_direct()) { + for (auto output_edge : candidate.to_node().outputs()) { + if (seen.contains(output_edge.value)) { + continue; + } + if (options & SearchOptions::TraverseGraphContexts || + candidate.subgraph()->graph_context_id() == output_edge.value.subgraph()->graph_context_id()) { + seen.insert(output_edge.value); + queue.push_back(output_edge.value); + } + } + } else if (candidate.is_indirect()) { + // TODO: how to know it is mutable? + for (auto output_edge : candidate.to_indirect_node().to_mutable().outputs()) { + if (seen.contains(output_edge.value)) { + continue; + } + if (options & SearchOptions::TraverseGraphContexts || + candidate.subgraph()->graph_context_id() == output_edge.value.subgraph()->graph_context_id()) { + seen.insert(output_edge.value); + queue.push_back(output_edge.value); + } + } + } + } + } + + return false; +} + #pragma mark - Indirect attributes void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, @@ -407,9 +549,9 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); - bool traversers_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); + bool traverses_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; - *indirect_node = MutableIndirectNode(source, traversers_graph_contexts, offset, node_size, source, offset); + *indirect_node = MutableIndirectNode(source, traverses_graph_contexts, offset, node_size, source, offset); add_input_dependencies(AttributeID(indirect_node).with_kind(AttributeID::Kind::Indirect), attribute); subgraph.add_indirect((data::ptr)indirect_node, true); @@ -418,9 +560,9 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); - bool traversers_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); + bool traverses_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; - *indirect_node = IndirectNode(source, traversers_graph_contexts, offset, node_size); + *indirect_node = IndirectNode(source, traverses_graph_contexts, offset, node_size); subgraph.add_indirect(indirect_node, &subgraph != attribute.subgraph()); } @@ -571,7 +713,9 @@ void Graph::value_mark_all() { node.set_state(node.state().with_unknown3(true)); subgraph->add_dirty_flags(node.flags().value3()); } - node.foreach_input_edge([](InputEdge &edge) { edge.flags |= 8; }); + for (auto input_edge : node.inputs()) { + input_edge.flags |= 8; + } } } } @@ -579,7 +723,7 @@ void Graph::value_mark_all() { } bool Graph::value_set(data::ptr node, const swift::metadata &value_type, const void *value) { - if (node->num_input_edges() > 0 && node->state().is_value_initialized()) { + if (!node->inputs().empty() && node->state().is_value_initialized()) { precondition_failure("can only set initial value of computed attributes: %u", node); } diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 047c381..4a539ca 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -53,6 +53,12 @@ class Graph { class UpdateStack; class UpdateStackRef; + enum SearchOptions : uint32_t { + SearchInputs = 1 << 0, + SearchOutputs = 1 << 1, + TraverseGraphContexts = 1 << 2, + }; + enum UpdateStatus : uint32_t { Option0 = 0, Option1 = 1, @@ -195,7 +201,8 @@ class Graph { void did_destroy_node(); // decrement counter 0x100 - void breadth_first_search(AttributeID attribute, uint32_t arg, ClosureFunctionAB predicate) const; + bool breadth_first_search(AttributeID attribute, SearchOptions options, + ClosureFunctionAB predicate) const; // MARK: Indirect attributes diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 5ebd449..80ea213 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -996,7 +996,7 @@ void Subgraph::cache_insert(data::ptr node) { return; } - if (node->flags().value4_unknown0x10() && !node->state().is_evaluating() && node->num_output_edges() == 0) { + if (node->flags().value4_unknown0x10() && !node->state().is_evaluating() && node->outputs().empty()) { // TODO: one of these flags must indicate it is cached const AttributeType &attribute_type = _graph->attribute_type(node->type_id()); diff --git a/Sources/ComputeCxx/Vector/Vector.tpp b/Sources/ComputeCxx/Vector/Vector.tpp index 289fa6e..9c96357 100644 --- a/Sources/ComputeCxx/Vector/Vector.tpp +++ b/Sources/ComputeCxx/Vector/Vector.tpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "Errors/Errors.h" @@ -57,7 +58,7 @@ vector::~vector() { data()[i].~T(); } if (_buffer) { - free(_buffer); + free((void *)_buffer); } } @@ -65,8 +66,8 @@ template requires std::unsigned_integral void vector::reserve_slow(size_type new_cap) { size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); - _buffer = reinterpret_cast(details::realloc_vector(_buffer, _stack_buffer, _stack_size, - &_capacity, effective_new_cap)); + _buffer = reinterpret_cast(details::realloc_vector( + (void *)_buffer, (void *)_stack_buffer, _stack_size, &_capacity, effective_new_cap)); } template From f70cf2ab38b40e94c84506e69042b200050cc4fd Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Feb 2025 21:32:00 +1100 Subject: [PATCH 10/74] Implement indirect attribute methods on Graph --- Sources/ComputeCxx/Attribute/AttributeID.cpp | 4 +- Sources/ComputeCxx/Attribute/AttributeID.h | 4 + .../ComputeCxx/Attribute/Node/IndirectNode.h | 4 + .../ComputeCxx/Attribute/WeakAttributeID.cpp | 6 +- .../ComputeCxx/Attribute/WeakAttributeID.h | 3 + Sources/ComputeCxx/Data/Zone.h | 6 +- Sources/ComputeCxx/Graph/Graph.cpp | 286 ++++++++++---- Sources/ComputeCxx/Graph/Graph.h | 6 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 373 +++++++++--------- Sources/ComputeCxx/Subgraph/Subgraph.h | 21 +- 10 files changed, 427 insertions(+), 286 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/AttributeID.cpp b/Sources/ComputeCxx/Attribute/AttributeID.cpp index 401ce1b..2e98fe9 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.cpp +++ b/Sources/ComputeCxx/Attribute/AttributeID.cpp @@ -11,6 +11,8 @@ namespace AG { +AttributeID AttributeIDNil = AttributeID::make_nil(); + std::optional AttributeID::size() const { if (is_direct()) { const AttributeType &attribute_type = subgraph()->graph()->attribute_type(to_node().type_id()); @@ -27,7 +29,7 @@ bool AttributeID::traverses(AttributeID other, TraversalOptions options) const { if (!is_indirect()) { return *this == other; } - + if (with_kind(Kind::Indirect) == other) { return true; } diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index d262c60..2348b07 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -19,6 +19,8 @@ class IndirectNode; class OffsetAttributeID; class RelativeAttributeID; + + class AttributeID { private: static constexpr uint32_t KindMask = 0x3; @@ -107,6 +109,8 @@ class RelativeAttributeID { uint16_t _value; }; +extern AttributeID AttributeIDNil; + } // namespace AG CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index 86b5ed8..049d3bf 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -35,6 +35,7 @@ class IndirectNode { bool is_mutable() const { return _info.is_mutable; }; MutableIndirectNode &to_mutable(); + void set_traverses_graph_contexts(bool value) { _info.traverses_graph_contexts = value; }; bool traverses_graph_contexts() const { return _info.traverses_graph_contexts; }; uint32_t offset() const { return _info.offset; }; @@ -72,6 +73,9 @@ class MutableIndirectNode : public IndirectNode { const AttributeID &dependency() const { return _dependency; }; void set_dependency(const AttributeID &dependency) { _dependency = dependency; }; + WeakAttributeID initial_source() { return _initial_source; }; + uint32_t initial_offset() { return _initial_offset; }; + ConstOutputEdgeArrayRef outputs() { return { _outputs.get(), diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp b/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp index 1d0c8e4..34f6d25 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp @@ -17,8 +17,10 @@ bool WeakAttributeID::expired() const { return true; } -const AttributeID &WeakAttributeID::attribute() const { - return _attribute; +const AttributeID &WeakAttributeID::attribute() const { return _attribute; }; + +const AttributeID &WeakAttributeID::evaluate() const { + return _attribute.without_kind() != 0 && !expired() ? _attribute : AttributeIDNil; }; } // namespace AG diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.h b/Sources/ComputeCxx/Attribute/WeakAttributeID.h index d6aad89..1a3a7a8 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.h +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.h @@ -19,6 +19,9 @@ class WeakAttributeID { bool expired() const; const AttributeID &attribute() const; + + /// Returns the attribute it is has not expired, otherwise returns the nil attribute. + const AttributeID &evaluate() const; }; } // namespace AG diff --git a/Sources/ComputeCxx/Data/Zone.h b/Sources/ComputeCxx/Data/Zone.h index 4fbbdfb..e054530 100644 --- a/Sources/ComputeCxx/Data/Zone.h +++ b/Sources/ComputeCxx/Data/Zone.h @@ -18,7 +18,7 @@ class zone { private: enum { zone_id_mask = 0x7fffffff, - invalidated = 0x80000000, + deleted = 0x80000000, }; uint32_t _value; info(uint32_t value) : _value(value){}; @@ -27,7 +27,7 @@ class zone { uint32_t zone_id() { return _value & zone_id_mask; }; info with_zone_id(uint32_t zone_id) const { return info((_value & ~zone_id_mask) | (zone_id & zone_id_mask)); }; - info with_invalidated() const { return info(_value | invalidated); }; + info with_deleted() const { return info(_value | deleted); }; uint32_t to_raw_value() { return _value; }; static info from_raw_value(uint32_t value) { return info(value); }; @@ -49,7 +49,7 @@ class zone { ~zone(); info info() { return _info; }; - void set_invalidated_flag() { _info = _info.with_invalidated(); }; + void mark_deleted() { _info = _info.with_deleted(); }; ptr last_page() { return _last_page; }; diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 91e5caf..6173931 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -568,6 +568,66 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui } } +void Graph::remove_indirect_node(data::ptr indirect_node_ptr) { + IndirectNode &indirect_node = *indirect_node_ptr; + if (indirect_node_ptr->is_mutable()) { + AttributeID attribute = AttributeID(indirect_node_ptr); + + remove_removed_input(attribute, indirect_node.source().attribute()); + AttributeID dependency = indirect_node.to_mutable().dependency(); + if (dependency != 0) { // TODO: == nullptr operator... + remove_removed_input(attribute, dependency); + } + for (auto output_edge : indirect_node.to_mutable().outputs()) { + remove_removed_output(attribute, output_edge.value, false); + } + return; + } + + while (true) { + if (indirect_node_ptr->source().expired()) { + return; + } + + AttributeID source = indirect_node_ptr->source().attribute(); + if (source.subgraph()->validation_state() == Subgraph::ValidationState::Invalidated) { + break; + } + + if (source.is_direct()) { + // auto removed_outputs = vector(); + auto removed_outputs = vector(); + for (auto output_edge : source.to_node().outputs()) { + if (remove_removed_output(AttributeID(indirect_node_ptr), output_edge.value, false)) { + removed_outputs.push_back(output_edge.value); + } + } + for (auto output : removed_outputs) { + remove_removed_input(AttributeID(output), AttributeID(indirect_node_ptr)); + // remove_removed_input(output, attribute); + } + break; + } else if (source.is_indirect()) { + if (source.to_indirect_node().is_mutable()) { + auto removed_outputs = vector(); + for (auto output_edge : source.to_indirect_node().to_mutable().outputs()) { + if (remove_removed_output(AttributeID(indirect_node_ptr), output_edge.value, false)) { + removed_outputs.push_back(output_edge.value); + } + } + for (auto output : removed_outputs) { + remove_removed_input(AttributeID(output), AttributeID(indirect_node_ptr)); + } + break; + } else { + indirect_node_ptr = source.to_indirect_node_ptr(); + } + } else { + break; + } + } +} + void Graph::indirect_attribute_set(data::ptr attribute, AttributeID source) { if (!attribute->is_mutable()) { precondition_failure("not an indirect attribute: %u", attribute); @@ -579,11 +639,74 @@ void Graph::indirect_attribute_set(data::ptr attribute, AttributeI foreach_trace([&attribute, &source](Trace &trace) { trace.set_source(attribute, source); }); OffsetAttributeID resolved_source = source.resolve(AttributeID::TraversalOptions::SkipMutableReference); + source = resolved_source.attribute(); + uint32_t offset = resolved_source.offset(); + + AttributeID previous_source = attribute->source().attribute(); + if (resolved_source.attribute() == previous_source) { + if (resolved_source.offset() == attribute.offset()) { + return; + } + } else { + remove_input_dependencies(AttributeID(attribute), previous_source); + } - // TODO: ... + // common method with this and _reset from here? + + // TODO: check zone id + attribute->modify(WeakAttributeID(resolved_source.attribute(), 0), resolved_source.offset()); + attribute->set_traverses_graph_contexts(AttributeID(attribute).subgraph()->graph_context_id() != + resolved_source.attribute().subgraph()->graph_context_id()); + + if (resolved_source.attribute() != previous_source) { + add_input_dependencies(AttributeID(attribute), resolved_source.attribute()); + } + + mark_changed(AttributeID(attribute), nullptr, 0, 0, 0); + propagate_dirty(AttributeID(attribute)); } -void Graph::indirect_attribute_reset(data::ptr attribute, bool flag) {} +bool Graph::indirect_attribute_reset(data::ptr attribute, bool non_nil) { + if (!attribute->is_mutable()) { + precondition_failure("not an indirect attribute: %u", attribute); + } + + auto initial_source = attribute->to_mutable().initial_source(); + if (initial_source.attribute().without_kind() == 0 && non_nil) { + return false; + } + + WeakAttributeID new_source = {AttributeID::make_nil(), 0}; + uint32_t new_offset = 0; + if (!initial_source.expired()) { + new_source = initial_source; + new_offset = attribute->to_mutable().initial_offset(); + } + + // common method with this and _set from here? + + AttributeID old_source_or_nil = attribute->source().evaluate(); + AttributeID new_source_or_nil = new_source.evaluate(); + + foreach_trace([&attribute, &new_source_or_nil](Trace &trace) { trace.set_source(attribute, new_source_or_nil); }); + + if (old_source_or_nil != new_source_or_nil) { + remove_input_dependencies(AttributeID(attribute), old_source_or_nil); + } + + attribute->modify(new_source, new_offset); + attribute->set_traverses_graph_contexts(AttributeID(attribute).subgraph()->graph_context_id() != + new_source_or_nil.subgraph()->graph_context_id()); + + if (old_source_or_nil != new_source_or_nil) { + add_input_dependencies(AttributeID(attribute), new_source_or_nil); + } + + mark_changed(AttributeID(attribute), nullptr, 0, 0, 0); + propagate_dirty(AttributeID(attribute)); + + return true; +} const AttributeID &Graph::indirect_attribute_dependency(data::ptr attribute) { // Status: Verified @@ -629,6 +752,72 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, #pragma mark - Values +bool Graph::value_set(data::ptr node, const swift::metadata &value_type, const void *value) { + if (!node->inputs().empty() && node->state().is_value_initialized()) { + precondition_failure("can only set initial value of computed attributes: %u", node); + } + + auto update = current_update(); + if (update.tag() == 0 && update.get() != nullptr && update.get()->graph() == this && + (!node->outputs().empty() || node->state().is_evaluating())) { + precondition_failure("setting value during update: %u", node); + } + + bool changed = value_set_internal(node, *node.get(), value, value_type); + if (changed) { + propagate_dirty(node); + } + return changed; +} + +bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void *value, + const swift::metadata &value_type) { + foreach_trace([&node_ptr, &value](Trace &trace) { trace.set_value(node_ptr, value); }); + + AttributeType &type = *_types[node.type_id()]; + if (&type.value_metadata() != &value_type) { + precondition_failure("invalid value type for attribute: %u (saw %s, expected %s)", + type.value_metadata().name(false), value_type.name(false)); + } + + if (node.state().is_value_initialized()) { + // already initialized + void *value_dest = node.get_value(); + + LayoutDescriptor::ComparisonOptions comparison_options = + LayoutDescriptor::ComparisonOptions(type.comparison_mode()) | + LayoutDescriptor::ComparisonOptions::CopyOnWrite | LayoutDescriptor::ComparisonOptions::ReportFailures; + if (type.layout() == nullptr) { + type.set_layout(LayoutDescriptor::fetch(value_type, comparison_options, 0)); + } + ValueLayout layout = type.layout() == ValueLayoutEmpty ? nullptr : type.layout(); + + // TODO: make void * and rename to dest and source + if (LayoutDescriptor::compare(layout, (const unsigned char *)value_dest, (const unsigned char *)value, + value_type.vw_size(), comparison_options)) { + return false; + } + + if (_traces.empty()) { + // TODO: finish + } else { + mark_changed(AttributeID(node_ptr), &type, value_dest, value, 0); + } + + value_type.vw_assignWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); + } else { + // not initialized yet + node.allocate_value(*this, *AttributeID(node_ptr).subgraph()); + + // TODO: wrap in initialize_value on Node... + node.set_state(node.state().with_value_initialized(true)); + mark_changed(node_ptr, nullptr, nullptr, nullptr); + + void *value_dest = node.get_value(); + value_type.vw_initializeWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); + } +} + // Status: Verified bool Graph::value_exists(data::ptr node) { return node->state().is_value_initialized(); } @@ -649,14 +838,11 @@ AGValueState Graph::value_state(AttributeID attribute) { } void Graph::value_mark(data::ptr node) { - // iVar2 = tpidrro_el0; - // uVar6 = *(uint *)(iVar2 + _current_update_key * 8); - // if (((((uVar6 & 1) == 0) && - // (puVar7 = (undefined8 *)(uVar6 & 0xfffffffffffffffe), puVar7 != (undefined8 *)0x0)) && - // ((Graph *)*puVar7 == this)) && (((*node & 0xc0) != 0 || (0x1f < node[5])))) { - // /* WARNING: Subroutine does not return */ - // precondition_failure("setting value during update: %u",(char)node_ptr,in_w2); - // } + auto update = current_update(); + if (update.tag() == 0 && update.get() != nullptr && update.get()->graph() == this && + (!node->outputs().empty() || node->state().is_evaluating())) { + precondition_failure("setting value during update: %u", node); + } foreach_trace([&node](Trace &trace) { trace.mark_value(node); }); @@ -684,12 +870,10 @@ void Graph::value_mark(data::ptr node) { } void Graph::value_mark_all() { - // iVar8 = tpidrro_el0; - // uVar5 = *(uint *)(iVar8 + _current_update_key * 8); - // if (1 < uVar5 && (uVar5 & 1) == 0) { - // /* WARNING: Subroutine does not return */ - // precondition_failure("invalidating all values during update",in_w1,in_w2); - // } + auto update = current_update(); + if (update.tag() == 0 && update.get() != nullptr) { + precondition_failure("invalidating all values during update"); + } for (auto subgraph : _subgraphs) { for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { @@ -722,76 +906,6 @@ void Graph::value_mark_all() { } } -bool Graph::value_set(data::ptr node, const swift::metadata &value_type, const void *value) { - if (!node->inputs().empty() && node->state().is_value_initialized()) { - precondition_failure("can only set initial value of computed attributes: %u", node); - } - - // iVar1 = tpidrro_el0; - // uVar4 = *(uint *)(iVar1 + _current_update_key * 8); - // if (((((uVar4 & 1) == 0) && - // (puVar5 = (undefined8 *)(uVar4 & 0xfffffffffffffffe), puVar5 != (undefined8 *)0x0)) && - // ((Graph *)*puVar5 == this)) && - // ((0x1f < node->child || ((node->lifecycle_flags & 0xc0U) != 0)))) { - // /* WARNING: Subroutine does not return */ - // precondition_failure("setting value during update: %u",(char)attribute,(char)node); - // } - - bool changed = value_set_internal(node, *node.get(), value, value_type); - if (changed) { - propagate_dirty(node); - } - return changed; -} - -bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void *value, - const swift::metadata &value_type) { - foreach_trace([&node_ptr, &value](Trace &trace) { trace.set_value(node_ptr, value); }); - - AttributeType &type = *_types[node.type_id()]; - if (&type.value_metadata() != &value_type) { - precondition_failure("invalid value type for attribute: %u (saw %s, expected %s)", - type.value_metadata().name(false), value_type.name(false)); - } - - if (node.state().is_value_initialized()) { - // already initialized - void *value_dest = node.get_value(); - - LayoutDescriptor::ComparisonOptions comparison_options = - LayoutDescriptor::ComparisonOptions(type.comparison_mode()) | - LayoutDescriptor::ComparisonOptions::CopyOnWrite | LayoutDescriptor::ComparisonOptions::ReportFailures; - if (type.layout() == nullptr) { - type.set_layout(LayoutDescriptor::fetch(value_type, comparison_options, 0)); - } - ValueLayout layout = type.layout() == ValueLayoutEmpty ? nullptr : type.layout(); - - // TODO: make void * and rename to dest and source - if (LayoutDescriptor::compare(layout, (const unsigned char *)value_dest, (const unsigned char *)value, - value_type.vw_size(), comparison_options)) { - return false; - } - - if (_traces.empty()) { - // TODO: finish - } else { - mark_changed(AttributeID(node_ptr), type, value_dest, value, 0); - } - - value_type.vw_assignWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); - } else { - // not initialized yet - node.allocate_value(*this, *AttributeID(node_ptr).subgraph()); - - // TODO: wrap in initialize_value on Node... - node.set_state(node.state().with_value_initialized(true)); - mark_changed(node_ptr, nullptr, nullptr, nullptr); - - void *value_dest = node.get_value(); - value_type.vw_initializeWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); - } -} - // MARK: Marks void Graph::mark_pending(data::ptr node_ptr, Node *node) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 4a539ca..af7175b 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -211,7 +211,7 @@ class Graph { void remove_indirect_node(data::ptr node); void indirect_attribute_set(data::ptr, AttributeID source); - void indirect_attribute_reset(data::ptr, bool flag); + bool indirect_attribute_reset(data::ptr, bool non_nil); const AttributeID &indirect_attribute_dependency(data::ptr indirect_node); void indirect_attribute_set_dependency(data::ptr indirect_node, AttributeID dependency); @@ -266,13 +266,13 @@ class Graph { template void add_output_edge(data::ptr node, AttributeID attribute); template void remove_output_edge(data::ptr node, AttributeID attribute); - void remove_removed_output(AttributeID attribute, AttributeID source, bool flag); + bool remove_removed_output(AttributeID attribute, AttributeID source, bool flag); // MARK: Marks void mark_changed(data::ptr node, AttributeType *_Nullable type, const void *_Nullable destination_value, const void *_Nullable source_value); - void mark_changed(AttributeID attribute, AttributeType &type, const void *_Nullable destination_value, + void mark_changed(AttributeID attribute, AttributeType *_Nullable type, const void *_Nullable destination_value, const void *_Nullable source_value, uint64_t option); void mark_pending(data::ptr node_ptr, Node *node); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 80ea213..66a4ac9 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -35,7 +35,7 @@ Subgraph::Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID _graph = &graph; _graph_context_id = context.unique_id(); - _invalidated = false; + _validation_state = ValidationState::Valid; begin_tree(owner, nullptr, 0); @@ -73,11 +73,182 @@ void Subgraph::clear_object() { #pragma mark - Graph +void Subgraph::invalidate_and_delete_(bool delete_subgraph) { + if (delete_subgraph) { + mark_deleted(); + } + + // TODO: does this rely on underflow to catch 0? + if (ValidationState::Invalidated < _validation_state - 1) { + for (auto parent : _parents) { + parent->remove_child(*this, true); + } + _parents.clear(); + + // Check Graph::invalidate_subgraphs + if (_graph->deferring_invalidation() == false && _graph->main_handler() == nullptr) { + invalidate_now(*_graph); + _graph->invalidate_subgraphs(); + return; + } + + bool was_valid = is_valid(); + if (_validation_state != ValidationState::InvalidationScheduled) { + _graph->will_invalidate_subgraph(*this); + _validation_state = ValidationState::InvalidationScheduled; + if (was_valid) { + _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + } + } + } +} + +void Subgraph::invalidate_now(Graph &graph) { + // TODO: double check graph param vs _graph instance var + + graph.set_deferring_invalidation(true); + + auto removed_subgraphs = vector(); + auto stack = std::stack>(); + + bool was_valid = is_valid(); + if (_validation_state != ValidationState::Invalidated) { + _validation_state = ValidationState::Invalidated; + if (was_valid) { + _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + } + clear_object(); + stack.push(this); + + while (!stack.empty()) { + Subgraph *subgraph = stack.top(); + stack.pop(); + + _graph->foreach_trace([subgraph](Trace &trace) { trace.destroy(*subgraph); }); + + notify_observers(); + _graph->remove_subgraph(*subgraph); + + subgraph->mark_deleted(); + removed_subgraphs.push_back(subgraph); + + for (auto child : subgraph->_children) { + Subgraph *child_subgraph = child.subgraph(); + if (child_subgraph->_graph_context_id == _graph_context_id) { + + // for each other parent of the child, remove the child from that parent + for (auto parent : child_subgraph->_parents) { + if (parent != subgraph) { + auto r = std::remove_if(parent->_children.begin(), parent->_children.end(), + [child_subgraph](auto other_child) { + return other_child.subgraph() == child_subgraph; + }); + parent->_children.erase(r); + + // for (auto other_child : parent->_children) { + // if (other_child.subgraph() == child) { + // parent->_children[i] = + // parent->_children[parent->_children.size() - 1]; + // parent->_children.pop_back(); + // } + // } + } + } + + child_subgraph->_parents.clear(); + + bool child_was_valid = child_subgraph->is_valid(); + if (child_subgraph->_validation_state != ValidationState::Invalidated) { + child_subgraph->_validation_state = ValidationState::Invalidated; + if (child_was_valid) { + _graph->foreach_trace( + [child_subgraph](Trace &trace) { trace.invalidate(*child_subgraph); }); + } + child_subgraph->clear_object(); + stack.push(child_subgraph); + } + } else { + // remove the subgraph from the parents vector of each child + auto r = std::remove(child_subgraph->_parents.begin(), child_subgraph->_parents.end(), subgraph); + child_subgraph->_parents.erase(r); + + // for (auto parent : child_subgraph->_parents) { + // if (parent == subgraph) { + // child._parents[i] = child._parents[child._parents.size() - 1]; + // child._parents.resize(child._parents.size() - 1); + // break; + // } + // } + } + } + } + } + + for (auto removed_subgraph : removed_subgraphs) { + for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { + bool found_nil_attribute = false; + + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + graph.remove_node(attribute.to_node_ptr()); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + graph.remove_indirect_node(attribute.to_indirect_node_ptr()); + } else if (attribute.is_nil()) { + found_nil_attribute = true; + } + } + if (found_nil_attribute) { + break; + } + } + } + + for (auto removed_subgraph : removed_subgraphs) { + for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { + bool found_nil_attribute = false; + + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else if (attribute.is_nil()) { + found_nil_attribute = true; + } + + if (attribute.is_direct()) { + attribute.to_node().destroy(*_graph); + + _graph->did_destroy_node(); // decrement counter + } + } + if (found_nil_attribute) { + break; + } + } + } + + // TODO: does this execute anyway... + for (auto removed_subgraph : removed_subgraphs) { + removed_subgraph->~Subgraph(); + free(removed_subgraph); // or delete? + } + + graph.set_deferring_invalidation(false); +} + void Subgraph::graph_destroyed() { - bool old_invalidated = _invalidated; - _invalidated = true; + bool was_valid = is_valid(); + _validation_state = ValidationState::GraphDestroyed; - if (!old_invalidated) { + if (was_valid) { graph()->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); } notify_observers(); @@ -303,185 +474,16 @@ void Subgraph::unlink_attribute(AttributeID attribute) { } } -void Subgraph::invalidate_now(Graph &graph) { - // TODO: double check graph param vs _graph instance var - - graph.set_deferring_invalidation(true); - - auto removed_subgraphs = vector(); - auto stack = std::stack>(); - - bool was_invalidated = _invalidated; - if (!_invalidated) { - _invalidated = true; - if (!was_invalidated) { - _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); - } - clear_object(); - stack.push(this); - - while (!stack.empty()) { - Subgraph *subgraph = stack.top(); - stack.pop(); - - _graph->foreach_trace([subgraph](Trace &trace) { trace.destroy(*subgraph); }); - - notify_observers(); - _graph->remove_subgraph(*subgraph); - - subgraph->set_invalidated_flag(); - removed_subgraphs.push_back(subgraph); - - for (auto child : subgraph->_children) { - Subgraph *child_subgraph = child.subgraph(); - if (child_subgraph->_graph_context_id == _graph_context_id) { - - // for each other parent of the child, remove the child from that parent - for (auto parent : child_subgraph->_parents) { - if (parent != subgraph) { - auto r = std::remove_if(parent->_children.begin(), parent->_children.end(), - [child_subgraph](auto other_child) { - return other_child.subgraph() == child_subgraph; - }); - parent->_children.erase(r); - - // for (auto other_child : parent->_children) { - // if (other_child.subgraph() == child) { - // parent->_children[i] = - // parent->_children[parent->_children.size() - 1]; - // parent->_children.pop_back(); - // } - // } - } - } - - child_subgraph->_parents.clear(); - - bool child_was_invalidated = child_subgraph->_invalidated; - if (!child_was_invalidated) { - child_subgraph->_invalidated = true; - if (!child_was_invalidated) { - _graph->foreach_trace( - [child_subgraph](Trace &trace) { trace.invalidate(*child_subgraph); }); - } - child_subgraph->clear_object(); - stack.push(child_subgraph); - } - } else { - // remove the subgraph from the parents vector of each child - auto r = std::remove(child_subgraph->_parents.begin(), child_subgraph->_parents.end(), subgraph); - child_subgraph->_parents.erase(r); - - // for (auto parent : child_subgraph->_parents) { - // if (parent == subgraph) { - // child._parents[i] = child._parents[child._parents.size() - 1]; - // child._parents.resize(child._parents.size() - 1); - // break; - // } - // } - } - } - } - } - - for (auto removed_subgraph : removed_subgraphs) { - for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { - bool found_nil_attribute = false; - - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - graph.remove_node(attribute.to_node_ptr()); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - graph.remove_indirect_node(attribute.to_indirect_node_ptr()); - } else if (attribute.is_nil()) { - found_nil_attribute = true; - } - } - if (found_nil_attribute) { - break; - } - } - } - - for (auto removed_subgraph : removed_subgraphs) { - for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { - bool found_nil_attribute = false; - - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else if (attribute.is_nil()) { - found_nil_attribute = true; - } - - if (attribute.is_direct()) { - attribute.to_node().destroy(*_graph); - - _graph->did_destroy_node(); // decrement counter - } - } - if (found_nil_attribute) { - break; - } - } - } - - // TODO: does this execute anyway... - for (auto removed_subgraph : removed_subgraphs) { - removed_subgraph->~Subgraph(); - free(removed_subgraph); // or delete? - } - - graph.set_deferring_invalidation(false); -} - -void Subgraph::invalidate_and_delete_(bool flag) { - if (flag) { - set_invalidated_flag(); - } - - // comparison is actually (2 < (dword)(this->invalidated - 1), is this a flag? - if (!_invalidated) { - for (auto parent : _parents) { - parent->remove_child(*this, true); - } - _parents.clear(); - - // Check Graph::invalidate_subgraphs - if (_graph->deferring_invalidation() == false && _graph->main_handler() == nullptr) { - invalidate_now(*_graph); - _graph->invalidate_subgraphs(); - return; - } - - bool was_invalidated = _invalidated; - if (!was_invalidated) { - _graph->will_invalidate_subgraph(*this); - _invalidated = true; - if (!was_invalidated) { - _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); - } - } - } -} - void Subgraph::update(uint8_t flags) { + // TODO: redo this method + if (_graph->needs_update()) { if (!_graph->thread_is_updating()) { _graph->call_update(); } } - if (!_invalidated) { + if (is_valid()) { if ((flags & (_flags.value1 | _flags.value2))) { _graph->foreach_trace([*this, &flags](Trace &trace) { trace.begin_update(*this, flags); }); @@ -504,7 +506,7 @@ void Subgraph::update(uint8_t flags) { Subgraph *subgraph = Subgraph::from_cf(object); if (subgraph) { - while (!subgraph->_invalidated) { + while (subgraph->is_valid()) { if ((flags & subgraph->_flags.value3) == 0) { // LABEL: LAB_1afe6ac70 @@ -560,7 +562,7 @@ void Subgraph::update(uint8_t flags) { } if (nodes_to_update.size() == 0) { - if (subgraph->_invalidated == false) { + if (subgraph->is_valid()) { // goto LAB_1afe6ac70 } break; @@ -574,7 +576,7 @@ void Subgraph::update(uint8_t flags) { } _graph->update_attribute(node, true); - if (subgraph->_invalidated) { + if (!subgraph->is_valid()) { break; } } @@ -601,7 +603,7 @@ std::atomic Subgraph::_last_traversal_seed = {}; void Subgraph::apply(Flags flags, ClosureFunctionAV body) { // Status: Verified, needs checks for atomics - if (_invalidated) { + if (!is_valid()) { return; } if ((flags.value1 & (_flags.value1 | _flags.value2)) == 0) { @@ -622,7 +624,8 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { auto subgraph = stack.top(); stack.pop(); - if (subgraph->_invalidated) { + // TODO: check + if (!subgraph->is_valid()) { continue; } @@ -775,7 +778,7 @@ uint32_t Subgraph::tree_subgraph_child(data::ptr tree_elemen auto subgraph_vector = vector(); for (auto subgraph : _graph->subgraphs()) { - if (subgraph->_invalidated) { + if (!subgraph->is_valid()) { continue; } if (subgraph->_tree_root == nullptr) { @@ -992,7 +995,7 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata } void Subgraph::cache_insert(data::ptr node) { - if (_invalidated) { + if (!is_valid()) { return; } @@ -1028,7 +1031,7 @@ void Subgraph::cache_collect() { _other_state &= ~CacheState::Option1; // turn off 0x1 bit std::pair context = {this, _cache.get()}; - if (_cache != nullptr && !_invalidated) { + if (_cache != nullptr && is_valid()) { _cache->types().for_each( [](const swift::metadata *metadata, const data::ptr type, const void *context) { Subgraph *subgraph = reinterpret_cast *>(context)->first; @@ -1088,7 +1091,7 @@ void Subgraph::encode(Encoder &encoder) { } } - if (_invalidated) { + if (!is_valid()) { encoder.encode_varint(0x28); encoder.encode_varint(true); } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index dfb3f03..a426e0d 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -58,10 +58,17 @@ class Subgraph : public data::zone { struct Observer {}; enum CacheState : uint8_t { - Option1 = 1 << 0, // added to graph._subgraphs_with_cached_nodes, or needs collect? + Option1 = 1 << 0, // added to graph._subgraphs_with_cached_nodes, or needs collect? Option2 = 1 << 1, // Is calling cache collect }; + enum ValidationState : uint8_t { + Valid = 0, + InvalidationScheduled = 1, + Invalidated = 2, + GraphDestroyed = 3, + }; + private: static pthread_key_t _current_subgraph_key; @@ -82,7 +89,7 @@ class Subgraph : public data::zone { data::ptr _tree_root; Flags _flags; - bool _invalidated; + ValidationState _validation_state; uint8_t _other_state; public: @@ -104,11 +111,16 @@ class Subgraph : public data::zone { Graph *_Nullable graph() const { return _graph; }; uint64_t graph_context_id() { return _graph_context_id; }; - void graph_destroyed(); + bool is_valid() { return _validation_state == ValidationState::Valid; }; + ValidationState validation_state() { return _validation_state; }; uint8_t other_state() { return _other_state; }; void set_other_state(uint8_t other_state) { _other_state = other_state; }; + void invalidate_and_delete_(bool delete_subgraph); + void invalidate_now(Graph &graph); + void graph_destroyed(); + // MARK: Managing children void add_child(Subgraph &child, SubgraphChild::Flags flags); @@ -129,9 +141,6 @@ class Subgraph : public data::zone { void unlink_attribute(AttributeID attribute); - void invalidate_now(Graph &graph); - void invalidate_and_delete_(bool flag); - void update(uint8_t flags); // MARK: Traversal From 580e37921d57c51da309ef902374260b041fca38 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 7 Feb 2025 16:18:38 +1100 Subject: [PATCH 11/74] Implement value methods in Graph --- Sources/ComputeCxx/Attribute/AttributeID.h | 45 ++-- Sources/ComputeCxx/Attribute/Node/InputEdge.h | 2 +- Sources/ComputeCxx/Attribute/Node/Node.h | 6 + .../ComputeCxx/Attribute/Node/OutputEdge.h | 4 +- .../{Array => Containers}/ArrayRef.h | 0 Sources/ComputeCxx/Containers/ForwardList.h | 118 +++++++++ .../IndirectPointerVector.cpp | 0 .../IndirectPointerVector.h | 0 .../{Vector => Containers}/Vector.h | 0 .../{Vector => Containers}/Vector.tpp | 0 Sources/ComputeCxx/Data/Table.h | 2 +- Sources/ComputeCxx/Graph/Context.h | 9 +- Sources/ComputeCxx/Graph/Graph.cpp | 227 ++++++++++++++++-- Sources/ComputeCxx/Graph/Graph.h | 4 +- Sources/ComputeCxx/Graph/Tree/TreeElement.h | 2 +- Sources/ComputeCxx/Layout/Builder.h | 2 +- Sources/ComputeCxx/Layout/Compare.h | 2 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 2 +- Sources/ComputeCxx/Swift/ContextDescriptor.h | 2 +- Sources/ComputeCxx/Swift/Metadata.cpp | 3 +- 21 files changed, 377 insertions(+), 55 deletions(-) rename Sources/ComputeCxx/{Array => Containers}/ArrayRef.h (100%) create mode 100644 Sources/ComputeCxx/Containers/ForwardList.h rename Sources/ComputeCxx/{Vector => Containers}/IndirectPointerVector.cpp (100%) rename Sources/ComputeCxx/{Vector => Containers}/IndirectPointerVector.h (100%) rename Sources/ComputeCxx/{Vector => Containers}/Vector.h (100%) rename Sources/ComputeCxx/{Vector => Containers}/Vector.tpp (100%) diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index 2348b07..872407e 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -19,7 +19,31 @@ class IndirectNode; class OffsetAttributeID; class RelativeAttributeID; +enum TraversalOptions : uint32_t { + None = 0, + /// Updates indirect node dependencies prior to traversing. + UpdateDependencies = 1 << 0, + + /// Guarantees the resolved attribute is not nil, otherwise traps. + AssertNotNil = 1 << 1, + + /// When set, only statically evaluable references are traversed. + /// The returned attribute may be a mutable indirect node. + SkipMutableReference = 1 << 2, + + /// When set, the returned offset will be 0 if no indirection was traversed, + /// otherwise it will be the the actual offset + 1. + ReportIndirectionInOffset = 1 << 3, + + /// When set and `AssertNotNil` is not also set, returns the nil attribute + /// if any weak references evaluate to nil. + EvaluateWeakReferences = 1 << 4, +}; + +inline TraversalOptions operator|(TraversalOptions lhs, TraversalOptions rhs) { + return static_cast(lhs | rhs); +} class AttributeID { private: @@ -33,27 +57,6 @@ class AttributeID { Indirect = 1 << 0, NilAttribute = 1 << 1, }; - enum TraversalOptions : uint32_t { - None = 0, - - /// Updates indirect node dependencies prior to traversing. - UpdateDependencies = 1 << 0, - - /// Guarantees the resolved attribute is not nil, otherwise traps. - AssertNotNil = 1 << 1, - - /// When set, only statically evaluable references are traversed. - /// The returned attribute may be a mutable indirect node. - SkipMutableReference = 1 << 2, - - /// When set, the returned offset will be 0 if no indirection was traversed, - /// otherwise it will be the the actual offset + 1. - ReportIndirectionInOffset = 1 << 3, - - /// When set and `AssertNotNil` is not also set, returns the nil attribute - /// if any weak references evaluate to nil. - EvaluateWeakReferences = 1 << 4, - }; explicit AttributeID(uint32_t value) : _value(value){}; AttributeID(data::ptr node) : _value(node | Kind::Direct){}; diff --git a/Sources/ComputeCxx/Attribute/Node/InputEdge.h b/Sources/ComputeCxx/Attribute/Node/InputEdge.h index 33670f9..a6f0f40 100644 --- a/Sources/ComputeCxx/Attribute/Node/InputEdge.h +++ b/Sources/ComputeCxx/Attribute/Node/InputEdge.h @@ -1,7 +1,7 @@ #pragma once -#include "Array/ArrayRef.h" #include "Attribute/AttributeID.h" +#include "Containers/ArrayRef.h" namespace AG { diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index f1c5382..89981f3 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -59,6 +59,10 @@ class NodeFlags { void set_has_indirect_value(bool value) { _value4 = (_value4 & ~Flags4::HasIndirectValue) | (value ? Flags4::HasIndirectValue : 0); }; + bool value4_unknown0x04() { return _value4 & Flags4::Unknown0x04; }; + void set_value4_unknown0x04(bool value) { + _value4 = (_value4 & ~Flags4::Unknown0x04) | (value ? Flags4::Unknown0x04 : 0); + }; bool value4_unknown0x10() { return _value4 & Flags4::Unknown0x10; }; void set_value4_unknown0x10(bool value) { _value4 = (_value4 & ~Flags4::Unknown0x10) | (value ? Flags4::Unknown0x10 : 0); @@ -105,6 +109,8 @@ class Node { State with_pending(bool value) const { return State((_data & ~Pending) | (value ? Pending : 0)); }; bool updates_on_main() { return _data & UpdatesOnMain; } + State with_updates_on_main(bool value) const { return State((_data & ~UpdatesOnMain) | (value ? UpdatesOnMain : 0)); }; + bool is_unknown3() { return _data & Unknown3; } State with_unknown3(bool value) const { return State((_data & ~Unknown3) | (value ? Unknown3 : 0)); }; diff --git a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h index 8f06656..59b22a2 100644 --- a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h +++ b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h @@ -2,8 +2,8 @@ #include -#include "Array/ArrayRef.h" #include "Attribute/AttributeID.h" +#include "Containers/ArrayRef.h" CF_ASSUME_NONNULL_BEGIN @@ -14,7 +14,7 @@ class OutputEdge { AttributeID value; }; -using ConstOutputEdgeArrayRef = const ArrayRef; +using ConstOutputEdgeArrayRef = ArrayRef; } // namespace AG diff --git a/Sources/ComputeCxx/Array/ArrayRef.h b/Sources/ComputeCxx/Containers/ArrayRef.h similarity index 100% rename from Sources/ComputeCxx/Array/ArrayRef.h rename to Sources/ComputeCxx/Containers/ArrayRef.h diff --git a/Sources/ComputeCxx/Containers/ForwardList.h b/Sources/ComputeCxx/Containers/ForwardList.h new file mode 100644 index 0000000..5b81b09 --- /dev/null +++ b/Sources/ComputeCxx/Containers/ForwardList.h @@ -0,0 +1,118 @@ +#pragma once + +#include + +#include "Util/Heap.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +/// ForwardList is a linked list container that uses util::Heap to allocate nodes, +/// reusing previously removed nodes where possible. +template class ForwardList { + public: + using reference = T &; + using const_reference = const T &; + + private: + struct Node { + Node *_Nullable next; + T value; + }; + + util::Heap *_heap; + Node *_Nullable _front; + Node *_Nullable _spare; + bool _is_heap_owner; + + public: + ForwardList() { + _heap = new util::Heap(nullptr, 0, util::Heap::minimum_increment); + _is_heap_owner = true; + }; + ForwardList(util::Heap *heap) { + _heap = heap; + _is_heap_owner = false; + }; + ~ForwardList() { + if (_is_heap_owner && _heap) { + delete _heap; + } + }; + + // MARK: Element access + + reference front() { + assert(!empty()); + return _front->value; + } + + const_reference front() const { + assert(!empty()); + return _front->value; + } + + // MARK: Capacity + + bool empty() const noexcept { return _front == nullptr; } + + // MARK: Modifiers + + void push_front(const T &value) { + Node *new_node; + if (_spare != nullptr) { + new_node = _spare; + _spare = _spare->previous; + } else { + new_node = _heap->alloc(); + } + new_node->next = _front; + new_node->value = value; + _front = new_node; + } + + void push_front(T &&value) { + Node *new_node; + if (_spare != nullptr) { + new_node = _spare; + _spare = _spare->previous; + } else { + new_node = _heap->alloc(); + } + new_node->next = _front; + new_node->value = std::move(value); + _front = new_node; + } + + template void emplace_front(Args &&...args) { + Node *new_node; + if (_spare != nullptr) { + new_node = _spare; + _spare = _spare->next; + } else { + new_node = _heap->alloc(); + } + new_node->next = _front; + new (&new_node->value) T(args...); + _front = new_node; + } + + void pop_front() { + if (_front == nullptr) { + return; + } + + Node *next = _front->next; + T value = _front->value; + + _front->next = _spare; + _spare = _front; + + _front = next; + } +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Vector/IndirectPointerVector.cpp b/Sources/ComputeCxx/Containers/IndirectPointerVector.cpp similarity index 100% rename from Sources/ComputeCxx/Vector/IndirectPointerVector.cpp rename to Sources/ComputeCxx/Containers/IndirectPointerVector.cpp diff --git a/Sources/ComputeCxx/Vector/IndirectPointerVector.h b/Sources/ComputeCxx/Containers/IndirectPointerVector.h similarity index 100% rename from Sources/ComputeCxx/Vector/IndirectPointerVector.h rename to Sources/ComputeCxx/Containers/IndirectPointerVector.h diff --git a/Sources/ComputeCxx/Vector/Vector.h b/Sources/ComputeCxx/Containers/Vector.h similarity index 100% rename from Sources/ComputeCxx/Vector/Vector.h rename to Sources/ComputeCxx/Containers/Vector.h diff --git a/Sources/ComputeCxx/Vector/Vector.tpp b/Sources/ComputeCxx/Containers/Vector.tpp similarity index 100% rename from Sources/ComputeCxx/Vector/Vector.tpp rename to Sources/ComputeCxx/Containers/Vector.tpp diff --git a/Sources/ComputeCxx/Data/Table.h b/Sources/ComputeCxx/Data/Table.h index 28681bd..a68ef51 100644 --- a/Sources/ComputeCxx/Data/Table.h +++ b/Sources/ComputeCxx/Data/Table.h @@ -8,7 +8,7 @@ #include #include -#include "Vector/Vector.h" +#include "Containers/Vector.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h index 71b5fe7..e16a404 100644 --- a/Sources/ComputeCxx/Graph/Context.h +++ b/Sources/ComputeCxx/Graph/Context.h @@ -10,11 +10,16 @@ class Graph::Context { private: Graph *_graph; + uint32_t _value_ref_counter; + public: Graph &graph() const { return *_graph; }; - + uint64_t unique_id(); - + + uint32_t value_ref_counter() { return _value_ref_counter; }; + void call_invalidation(AttributeID attribute); + void call_update(); }; diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 6173931..41dd43b 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -9,6 +9,7 @@ #include "Attribute/Node/Node.h" #include "Attribute/OffsetAttributeID.h" #include "Attribute/WeakAttributeID.h" +#include "Containers/ForwardList.h" #include "Context.h" #include "Errors/Errors.h" #include "KeyTable.h" @@ -245,12 +246,12 @@ void Graph::attribute_modify(data::ptr node, const swift::metadata &metada data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *value) { const AttributeType &type = attribute_type(type_id); - void *effective_value = nullptr; + void *value_source = nullptr; if (type.use_graph_as_initial_value()) { - effective_value = this; + value_source = this; } if (value != nullptr || type.value_metadata().vw_size() != 0) { - effective_value = value; + value_source = value; } void *buffer = nullptr; @@ -304,8 +305,8 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void type.self_metadata().vw_initializeWithCopy((swift::opaque_value *)self_dest, (swift::opaque_value *)body); } - if (effective_value != nullptr) { - value_set_internal(node, *node.get(), effective_value, type.value_metadata()); + if (value_source != nullptr) { + value_set_internal(node, *node.get(), value_source, type.value_metadata()); } else { node->set_state(node->state().with_dirty(true).with_pending(true)); subgraph.add_dirty_flags(node->flags().value3()); @@ -387,8 +388,7 @@ void Graph::update_main_refs(AttributeID attribute) { } else { new_unknown0x20 = std::any_of(node.inputs().begin(), node.inputs().end(), [](auto input_edge) -> bool { - auto resolved = - input_edge.value.resolve(AttributeID::TraversalOptions::EvaluateWeakReferences); + auto resolved = input_edge.value.resolve(TraversalOptions::EvaluateWeakReferences); if (resolved.attribute().is_direct() && resolved.attribute().to_node().flags().value4_unknown0x20()) { return true; @@ -438,7 +438,7 @@ void Graph::remove_node(data::ptr node) { bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, ClosureFunctionAB predicate) const { - auto resolved = attribute.resolve(AttributeID::TraversalOptions::SkipMutableReference); + auto resolved = attribute.resolve(TraversalOptions::SkipMutableReference); if (resolved.attribute().without_kind() == 0) { return false; } @@ -463,8 +463,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, if (options & SearchOptions::SearchInputs) { if (candidate.is_direct()) { for (auto input_edge : candidate.to_node().inputs()) { - auto input = - input_edge.value.resolve(AttributeID::TraversalOptions::SkipMutableReference).attribute(); + auto input = input_edge.value.resolve(TraversalOptions::SkipMutableReference).attribute(); if (seen.contains(input)) { continue; @@ -478,7 +477,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, } else if (candidate.is_indirect()) { // TODO: inputs view on IndirectNode with source? AttributeID source = candidate.to_indirect_node().source().attribute(); - source = source.resolve(AttributeID::TraversalOptions::SkipMutableReference).attribute(); + source = source.resolve(TraversalOptions::SkipMutableReference).attribute(); if (!seen.contains(source)) { if (options & SearchOptions::TraverseGraphContexts || @@ -528,7 +527,7 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui precondition_failure("attribute references can't cross graph namespaces"); } - auto offset_attribute = attribute.resolve(AttributeID::TraversalOptions::SkipMutableReference); + auto offset_attribute = attribute.resolve(TraversalOptions::SkipMutableReference); attribute = offset_attribute.attribute(); if (__builtin_add_overflow(offset, offset_attribute.offset(), &offset) || offset + offset_attribute.offset() > 0x3ffffffe) { @@ -547,6 +546,8 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui if (is_mutable) { auto indirect_node = (data::ptr)subgraph.alloc_bytes(sizeof(MutableIndirectNode), 3); + // TODO: check accessing zone_id directly or through raw_page_seed + // check references of raw_page_seed in ghidra uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); bool traverses_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); @@ -638,7 +639,7 @@ void Graph::indirect_attribute_set(data::ptr attribute, AttributeI foreach_trace([&attribute, &source](Trace &trace) { trace.set_source(attribute, source); }); - OffsetAttributeID resolved_source = source.resolve(AttributeID::TraversalOptions::SkipMutableReference); + OffsetAttributeID resolved_source = source.resolve(TraversalOptions::SkipMutableReference); source = resolved_source.attribute(); uint32_t offset = resolved_source.offset(); @@ -752,6 +753,70 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, #pragma mark - Values +void *Graph::value_ref(AttributeID attribute, bool evaluate_weak_references, const swift::metadata &value_type, + bool *did_update_out) { + + _counter_0x1d8 += 1; + + OffsetAttributeID resolved = attribute.resolve( + TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + (evaluate_weak_references ? TraversalOptions::EvaluateWeakReferences : TraversalOptions::AssertNotNil)); + + if (evaluate_weak_references && (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct())) { + return nullptr; + } + + Node &node = resolved.attribute().to_node(); + const AttributeType &type = attribute_type(node.type_id()); + + if (!type.use_graph_as_initial_value()) { + + bool graph_is_updating = false; + for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { + if (update.get()->graph() == this) { + graph_is_updating = true; + } + } + if (!graph_is_updating) { + _counter_0x1b8 += 1; + } + + uint64_t old_page_seed = 0; + if (evaluate_weak_references) { + old_page_seed = data::table::shared().raw_page_seed(resolved.attribute().page_ptr()); + } + + UpdateStatus status = update_attribute(resolved.attribute(), 0); + if (status != UpdateStatus::Option0) { + *did_update_out = true; + } + + // check new page seed is same as old and zone is not deleted + if ((old_page_seed >> 0x20) & 0xff) { + uint64_t new_page_seed = data::table::shared().raw_page_seed(resolved.attribute().page_ptr()); + if ((new_page_seed >> 0x20) & 0xff) { + if ((old_page_seed & 0x7fffffff) != (new_page_seed & 0xffffffff)) { + return nullptr; + } + } + } + } + + if (resolved.offset() == 0 && (&type.value_metadata() != &value_type)) { + precondition_failure("invalid value type for attribute: %u (saw %s, expected %s)", resolved.attribute(), + type.value_metadata().name(false), value_type.name(false)); + } + if (!node.state().is_value_initialized()) { + precondition_failure("attribute being read has no value: %u", resolved.attribute()); + } + + void *value = node.get_value(); + if (resolved.offset() != 0) { + value = (uint8_t *)value + (resolved.offset() - 1); + } + return value; +} + bool Graph::value_set(data::ptr node, const swift::metadata &value_type, const void *value) { if (!node->inputs().empty() && node->state().is_value_initialized()) { precondition_failure("can only set initial value of computed attributes: %u", node); @@ -770,9 +835,9 @@ bool Graph::value_set(data::ptr node, const swift::metadata &value_type, c return changed; } -bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void *value, +bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void *value_source, const swift::metadata &value_type) { - foreach_trace([&node_ptr, &value](Trace &trace) { trace.set_value(node_ptr, value); }); + foreach_trace([&node_ptr, &value_source](Trace &trace) { trace.set_value(node_ptr, value_source); }); AttributeType &type = *_types[node.type_id()]; if (&type.value_metadata() != &value_type) { @@ -793,7 +858,7 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void ValueLayout layout = type.layout() == ValueLayoutEmpty ? nullptr : type.layout(); // TODO: make void * and rename to dest and source - if (LayoutDescriptor::compare(layout, (const unsigned char *)value_dest, (const unsigned char *)value, + if (LayoutDescriptor::compare(layout, (const unsigned char *)value_dest, (const unsigned char *)value_source, value_type.vw_size(), comparison_options)) { return false; } @@ -801,10 +866,10 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void if (_traces.empty()) { // TODO: finish } else { - mark_changed(AttributeID(node_ptr), &type, value_dest, value, 0); + mark_changed(AttributeID(node_ptr), &type, value_dest, value_source, 0); } - value_type.vw_assignWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); + value_type.vw_assignWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value_source); } else { // not initialized yet node.allocate_value(*this, *AttributeID(node_ptr).subgraph()); @@ -814,7 +879,7 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void mark_changed(node_ptr, nullptr, nullptr, nullptr); void *value_dest = node.get_value(); - value_type.vw_initializeWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value); + value_type.vw_initializeWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value_source); } } @@ -823,7 +888,7 @@ bool Graph::value_exists(data::ptr node) { return node->state().is_value_i AGValueState Graph::value_state(AttributeID attribute) { if (!attribute.is_direct()) { - auto resolved_attribute = attribute.resolve(AttributeID::TraversalOptions::AssertNotNil); + auto resolved_attribute = attribute.resolve(TraversalOptions::AssertNotNil); attribute = resolved_attribute.attribute(); } if (!attribute.is_direct()) { @@ -906,6 +971,128 @@ void Graph::value_mark_all() { } } +void Graph::propagate_dirty(AttributeID attribute) { + if (attribute.is_nil()) { + return; + } + + struct Frame { + ConstOutputEdgeArrayRef outputs; + Node::State state; + }; + + char stack_buffer[0x2000] = {}; + auto heap = util::Heap(stack_buffer, sizeof(stack_buffer), 0); + auto frames = ForwardList(&heap); + + ConstOutputEdgeArrayRef initial_outputs = {}; + Node::State initial_state = Node::State(0); + if (attribute.is_direct()) { + initial_outputs = attribute.to_node().outputs(); + initial_state = attribute.to_node().state(); + } else if (attribute.is_indirect()) { + // TODO: how to make sure indirect is mutable? + initial_outputs = attribute.to_indirect_node().to_mutable().outputs(); + + OffsetAttributeID source = attribute.to_indirect_node().source().attribute().resolve(TraversalOptions::None); + if (source.attribute().is_direct()) { + initial_state = source.attribute().to_node().state(); + } + } + frames.emplace_front(initial_outputs, initial_state); + + while (!frames.empty()) { + auto outputs = frames.front().outputs; + auto state = frames.front().state; + frames.pop_front(); + + for (auto output_edge = outputs.rbegin(), end = outputs.rend(); output_edge != end; ++output_edge) { + AttributeID output = output_edge->value; + + ConstOutputEdgeArrayRef dirty_outputs = {}; + Node::State next_state = state; + + if (output.is_direct()) { + Node &output_node = output.to_node(); + + ArrayRef more_outputs = {nullptr, 0}; + if (state.updates_on_main() && !output_node.state().updates_on_main()) { + output_node.set_state(output_node.state().with_updates_on_main(true)); + dirty_outputs = output_node.outputs(); + } + + next_state = Node::State(output_node.state().data() | state.data()); + + if (!output_node.state().is_dirty()) { + foreach_trace([&output](Trace &trace) { trace.set_dirty(output.to_node_ptr(), true); }); + + output_node.set_state(output_node.state().with_dirty(true)); + if (auto subgraph = output.subgraph()) { + subgraph->add_dirty_flags(output_node.flags().value3()); + } + + dirty_outputs = output_node.outputs(); + + // value4_unknown0x04 could be traverses graph contexts for symmetry with IndirectNode + if (output_node.flags().value4_unknown0x04() && !output_node.outputs().empty()) { + if (auto output_subgraph = output.subgraph()) { + auto context_id = output_subgraph->graph_context_id(); + if (context_id && (attribute.subgraph() == nullptr || + context_id != attribute.subgraph()->graph_context_id())) { + if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { + if (context->value_ref_counter() != context->graph()._counter_0x1d8) { + context->call_invalidation(attribute); + } + } + } + } + } + } + + } else if (output.is_indirect()) { + IndirectNode &output_node = output.to_indirect_node(); + + if (output_node.is_mutable()) { + dirty_outputs = output_node.to_mutable().outputs(); + + if (output_node.traverses_graph_contexts() && !output_node.to_mutable().outputs().empty()) { + if (auto output_subgraph = output.subgraph()) { + auto context_id = output_subgraph->graph_context_id(); + if (context_id && (attribute.subgraph() == nullptr || + context_id != attribute.subgraph()->graph_context_id())) { + if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { + if (context->value_ref_counter() != context->graph()._counter_0x1d8) { + context->call_invalidation(attribute); + } + } + } + } + } + } + } + + if (!dirty_outputs.empty()) { + frames.emplace_front(dirty_outputs, next_state); + } + } + } + + for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { + bool stop = false; + auto frames = update.get()->frames(); + for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { + if (frame->attribute->state().updates_on_main()) { + stop = true; + break; + } + frame->attribute->set_state(frame->attribute->state().with_updates_on_main(true)); + } + if (stop) { + break; + } + } +} + // MARK: Marks void Graph::mark_pending(data::ptr node_ptr, Node *node) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index af7175b..2977827 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -125,6 +125,7 @@ class Graph { uint64_t _counter_0x1b8; uint64_t _update_attribute_count; uint64_t _update_attribute_on_main_count; + uint64_t _counter_0x1d8; public: // MARK: Context @@ -218,7 +219,8 @@ class Graph { // MARK: Values - void *value_ref(AttributeID attribute, const swift::metadata &value_type, const void *value); + void *value_ref(AttributeID attribute, bool evaluate_weak_references, const swift::metadata &value_type, + bool *_Nonnull did_update_out); bool value_set(data::ptr node, const swift::metadata &value_type, const void *value); bool value_set_internal(data::ptr node_ptr, Node &node, const void *value, const swift::metadata &type); diff --git a/Sources/ComputeCxx/Graph/Tree/TreeElement.h b/Sources/ComputeCxx/Graph/Tree/TreeElement.h index 5dd3c33..cac13bf 100644 --- a/Sources/ComputeCxx/Graph/Tree/TreeElement.h +++ b/Sources/ComputeCxx/Graph/Tree/TreeElement.h @@ -2,10 +2,10 @@ #include +#include "Containers/Vector.h" #include "Data/Pointer.h" #include "Graph/Graph.h" #include "Swift/Metadata.h" -#include "Vector/Vector.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Layout/Builder.h b/Sources/ComputeCxx/Layout/Builder.h index 6577fe5..d7955a3 100644 --- a/Sources/ComputeCxx/Layout/Builder.h +++ b/Sources/ComputeCxx/Layout/Builder.h @@ -2,9 +2,9 @@ #include +#include "Containers/Vector.h" #include "LayoutDescriptor.h" #include "Swift/MetadataVisitor.h" -#include "Vector/Vector.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Layout/Compare.h b/Sources/ComputeCxx/Layout/Compare.h index 483b37c..c88d7f1 100644 --- a/Sources/ComputeCxx/Layout/Compare.h +++ b/Sources/ComputeCxx/Layout/Compare.h @@ -2,7 +2,7 @@ #include "LayoutDescriptor.h" -#include "Vector/Vector.h" +#include "Containers/Vector.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 66a4ac9..b848096 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -788,7 +788,7 @@ uint32_t Subgraph::tree_subgraph_child(data::ptr tree_elemen if (owner.without_kind() == 0) { continue; } - OffsetAttributeID resolved = owner.resolve(AttributeID::TraversalOptions::None); + OffsetAttributeID resolved = owner.resolve(TraversalOptions::None); owner = resolved.attribute(); if (owner.is_direct()) { for (auto node_iter = iter; node_iter != nodes.end(); ++node_iter) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index a426e0d..5e6f401 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -6,11 +6,11 @@ #include "Attribute/AttributeID.h" #include "Attribute/Node/Node.h" #include "Closure/ClosureFunction.h" +#include "Containers/IndirectPointerVector.h" #include "Data/Pointer.h" #include "Data/Zone.h" #include "Graph/Graph.h" #include "Private/CFRuntime.h" -#include "Vector/IndirectPointerVector.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Swift/ContextDescriptor.h b/Sources/ComputeCxx/Swift/ContextDescriptor.h index 8f635b0..6c76b5e 100644 --- a/Sources/ComputeCxx/Swift/ContextDescriptor.h +++ b/Sources/ComputeCxx/Swift/ContextDescriptor.h @@ -5,8 +5,8 @@ #include #include +#include "Containers/Vector.h" #include "Metadata.h" -#include "Vector/Vector.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Swift/Metadata.cpp b/Sources/ComputeCxx/Swift/Metadata.cpp index 8c7b33b..fc47e6e 100644 --- a/Sources/ComputeCxx/Swift/Metadata.cpp +++ b/Sources/ComputeCxx/Swift/Metadata.cpp @@ -305,7 +305,8 @@ void metadata::copy_on_write_heap_object(void **object_ref) const { assert(::swift::isHeapMetadataKind(getKind())); auto heap_metadata = reinterpret_cast(this); - ::swift::HeapObject *copy = ::swift::swift_allocObject(heap_metadata, vw_size(), getValueWitnesses()->getAlignmentMask()); + ::swift::HeapObject *copy = + ::swift::swift_allocObject(heap_metadata, vw_size(), getValueWitnesses()->getAlignmentMask()); vw_initializeWithCopy(reinterpret_cast(copy), reinterpret_cast(*object_ref)); ::swift::swift_release(reinterpret_cast<::swift::HeapObject *>(*object_ref)); *object_ref = copy; From cfab6a82f0131bddac9b93e9d9c79a66715ec9ac Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 7 Feb 2025 19:51:47 +1100 Subject: [PATCH 12/74] Implement output methods on Graph --- .../ComputeCxx/Attribute/Node/IndirectNode.h | 18 +-- Sources/ComputeCxx/Attribute/Node/Node.h | 43 ++---- Sources/ComputeCxx/Data/Vector.h | 135 ++++++++++++++++++ Sources/ComputeCxx/Graph/Graph.cpp | 125 +++++++++++++++- Sources/ComputeCxx/Graph/Graph.h | 9 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 4 +- 6 files changed, 279 insertions(+), 55 deletions(-) create mode 100644 Sources/ComputeCxx/Data/Vector.h diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index 049d3bf..252e4f4 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -4,6 +4,7 @@ #include "Attribute/AttributeID.h" #include "Attribute/WeakAttributeID.h" +#include "Data/Vector.h" #include "OutputEdge.h" CF_ASSUME_NONNULL_BEGIN @@ -53,16 +54,8 @@ static_assert(sizeof(IndirectNode) == 0x10); class MutableIndirectNode : public IndirectNode { private: - struct EdgeInfo { - unsigned int flags : 5; - unsigned int num_edges : 11; - unsigned int other_flag : 16; - }; - static_assert(sizeof(EdgeInfo) == 4); - AttributeID _dependency; - EdgeInfo _outputs_info; - data::ptr _outputs; + data::vector _outputs; WeakAttributeID _initial_source; uint32_t _initial_offset; @@ -76,12 +69,7 @@ class MutableIndirectNode : public IndirectNode { WeakAttributeID initial_source() { return _initial_source; }; uint32_t initial_offset() { return _initial_offset; }; - ConstOutputEdgeArrayRef outputs() { - return { - _outputs.get(), - _outputs_info.num_edges, - }; - }; + data::vector outputs() { return _outputs; }; }; static_assert(sizeof(MutableIndirectNode) == 0x28); diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 89981f3..1132d72 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -3,6 +3,7 @@ #include #include "Data/Pointer.h" +#include "Data/Vector.h" #include "InputEdge.h" #include "OutputEdge.h" @@ -31,7 +32,7 @@ class NodeFlags { Unknown0x04 = 1 << 2, // 0x04 Unknown0x08 = 1 << 3, // 0x08 - Unknown0x10 = 1 << 4, // 0x10 + Cacheable = 1 << 4, // 0x10 Unknown0x20 = 1 << 5, // 0x20 - initial value Unknown0x40 = 1 << 6, // 0x40 - didn't call mark_changed @@ -63,9 +64,9 @@ class NodeFlags { void set_value4_unknown0x04(bool value) { _value4 = (_value4 & ~Flags4::Unknown0x04) | (value ? Flags4::Unknown0x04 : 0); }; - bool value4_unknown0x10() { return _value4 & Flags4::Unknown0x10; }; - void set_value4_unknown0x10(bool value) { - _value4 = (_value4 & ~Flags4::Unknown0x10) | (value ? Flags4::Unknown0x10 : 0); + bool cacheable() { return _value4 & Flags4::Cacheable; }; + void set_cacheable(bool value) { + _value4 = (_value4 & ~Flags4::Cacheable) | (value ? Flags4::Cacheable : 0); }; bool value4_unknown0x20() { return _value4 & Flags4::Unknown0x20; }; void set_value4_unknown0x20(bool value) { @@ -109,8 +110,10 @@ class Node { State with_pending(bool value) const { return State((_data & ~Pending) | (value ? Pending : 0)); }; bool updates_on_main() { return _data & UpdatesOnMain; } - State with_updates_on_main(bool value) const { return State((_data & ~UpdatesOnMain) | (value ? UpdatesOnMain : 0)); }; - + State with_updates_on_main(bool value) const { + return State((_data & ~UpdatesOnMain) | (value ? UpdatesOnMain : 0)); + }; + bool is_unknown3() { return _data & Unknown3; } State with_unknown3(bool value) const { return State((_data & ~Unknown3) | (value ? Unknown3 : 0)); }; @@ -138,21 +141,12 @@ class Node { }; static_assert(sizeof(Info) == 4); - struct EdgeInfo { - unsigned int flags : 5; - unsigned int num_edges : 11; - unsigned int other_flag : 16; - }; - static_assert(sizeof(EdgeInfo) == 4); - Info _info; NodeFlags _flags; data::ptr _value; - EdgeInfo _inputs_info; - data::ptr _inputs; - EdgeInfo _outputs_info; - data::ptr _outputs; + data::vector _inputs; + data::vector _outputs; public: Node(State state, uint32_t type_id, uint8_t flags4); @@ -173,19 +167,8 @@ class Node { void destroy(Graph &graph); - ConstInputEdgeArrayRef inputs() { - return { - _inputs.get(), - _inputs_info.num_edges, - }; - }; - - ConstOutputEdgeArrayRef outputs() { - return { - _outputs.get(), - _outputs_info.num_edges, - }; - }; + data::vector inputs() { return _inputs; }; + data::vector outputs() { return _outputs; }; }; static_assert(sizeof(Node) == 0x1c); diff --git a/Sources/ComputeCxx/Data/Vector.h b/Sources/ComputeCxx/Data/Vector.h new file mode 100644 index 0000000..fb587c4 --- /dev/null +++ b/Sources/ComputeCxx/Data/Vector.h @@ -0,0 +1,135 @@ +#pragma once + +#include + +#include "Pointer.h" +#include "Zone.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { +namespace data { + +template class vector { + public: + using value_type = T; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = value_type *_Nonnull; + using const_iterator = const value_type *_Nonnull; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = uint32_t; + + private: + struct Metadata { + // capacity can be calculated via 1 << capacity_exponent + unsigned int capacity_exponent : 5; + unsigned int size : 27; + }; + + Metadata _metadata; + ptr _data; + + void reserve_slow(zone *zone, size_type new_cap) { + + size_type new_capacity_exponent = 32 - std::countl_zero(new_cap - 1); + if (new_cap < 2) { + new_capacity_exponent = 1; + } + + size_type old_capacity = sizeof(T) * capacity(); + + // alignment_mask should be 3 for OutputEdge and 0 for InputEdge, i.e. don't insert padding + size_type alignment_mask = std::has_unique_object_representations_v ? alignof(T) - 1 : 0; + zone->realloc_bytes((ptr *)&_data, old_capacity, (size_type)sizeof(T) << new_capacity_exponent, + alignment_mask); + _metadata.capacity_exponent = new_capacity_exponent; + } + + public: + ~vector() { + for (auto i = 0; i < _metadata.size; ++i) { + _data.get()[i].~T(); + } + } + + // Element access + + reference front() { return *_data.get(); }; + const_reference front() const { return _data.get(); }; + + // Iterators + + iterator begin() { return _data.get(); }; + iterator end() { return _data.get() + _metadata.size; }; + const_iterator cbegin() const { return _data.get(); }; + const_iterator cend() const { return _data.get() + _metadata.size; }; + const_iterator begin() const { return cbegin(); }; + const_iterator end() const { return cend(); }; + + reverse_iterator rbegin() { return std::reverse_iterator(end()); }; + reverse_iterator rend() { return std::reverse_iterator(begin()); }; + const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; + const_reverse_iterator crend() const { return std::reverse_iterator(cbegin()); }; + const_reverse_iterator rbegin() const { return crbegin(); }; + const_reverse_iterator rend() const { return crend(); }; + + // Capacity + + bool empty() const { return _metadata.size == 0; }; + size_type size() const { return _metadata.size; }; + void reserve(zone *zone, size_type new_cap) { + if (new_cap <= capacity()) { + return; + } + reserve_slow(zone, new_cap); + } + size_type capacity() const { + if (_metadata.capacity_exponent == 0) { + return 0; + } + return 1 << _metadata.capacity_exponent; + }; + + // Modifiers + + iterator erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); + } + + iterator erase(iterator first, iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + for (auto iter = first; iter != last; iter++) { + iter->~T(); + } + for (auto iter = last, old_end = end(); iter != old_end; iter++) { + std::swap(*(iter - count), *iter); + } + _metadata.size -= count; + return end(); + } + + void push_back(zone *zone, const T &value) { + reserve(zone, _metadata.size + 1); + new (_data.get()[_metadata.size]) value_type(value); + _metadata.size += 1; + } + + void push_back(zone *zone, T &&value) { + reserve(zone, _metadata.size + 1); + new (&_data.get()[_metadata.size]) value_type(std::move(value)); + _metadata.size += 1; + } +}; + +} // namespace data +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 41dd43b..7febe1a 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -398,11 +398,17 @@ void Graph::update_main_refs(AttributeID attribute) { } if (node.flags().value4_unknown0x20() != new_unknown0x20) { node.flags().set_value4_unknown0x20(new_unknown0x20); - output_edge_arrays.push_back(node.outputs()); + output_edge_arrays.push_back({ + &node.outputs().front(), + node.outputs().size(), + }); } } else if (attribute.is_indirect() && attribute.to_indirect_node().is_mutable()) { MutableIndirectNode &node = attribute.to_indirect_node().to_mutable(); - output_edge_arrays.push_back(node.outputs()); + output_edge_arrays.push_back({ + &node.outputs().front(), + node.outputs().size(), + }); } }; @@ -977,7 +983,7 @@ void Graph::propagate_dirty(AttributeID attribute) { } struct Frame { - ConstOutputEdgeArrayRef outputs; + data::vector outputs; Node::State state; }; @@ -985,7 +991,7 @@ void Graph::propagate_dirty(AttributeID attribute) { auto heap = util::Heap(stack_buffer, sizeof(stack_buffer), 0); auto frames = ForwardList(&heap); - ConstOutputEdgeArrayRef initial_outputs = {}; + data::vector initial_outputs = {}; Node::State initial_state = Node::State(0); if (attribute.is_direct()) { initial_outputs = attribute.to_node().outputs(); @@ -1009,7 +1015,7 @@ void Graph::propagate_dirty(AttributeID attribute) { for (auto output_edge = outputs.rbegin(), end = outputs.rend(); output_edge != end; ++output_edge) { AttributeID output = output_edge->value; - ConstOutputEdgeArrayRef dirty_outputs = {}; + data::vector dirty_outputs = {}; Node::State next_state = state; if (output.is_direct()) { @@ -1093,7 +1099,114 @@ void Graph::propagate_dirty(AttributeID attribute) { } } -// MARK: Marks +#pragma mark - Outputs + +void *Graph::output_value_ref(data::ptr node, const swift::metadata &value_type) { + // Status: Verified + if (!node->state().is_evaluating()) { + precondition_failure("attribute is not evaluating: %u", node); + } + + if (!node->state().is_value_initialized()) { + return nullptr; + } + + const AttributeType &type = attribute_type(node->type_id()); + if (&type.value_metadata() != &value_type) { + precondition_failure("invalid value type for attribute: %u (saw %s, expected %s)", node, + type.value_metadata().name(false), value_type.name(false)); + } + + return node->get_value(); +} + +template <> void Graph::add_output_edge(data::ptr node, AttributeID output) { + node->outputs().push_back(node.page_ptr()->zone, OutputEdge(output)); +} + +template <> void Graph::remove_output_edge(data::ptr node, AttributeID output) { + for (auto iter = node->outputs().begin(), end = node->outputs().end(); iter != end; ++iter) { + if (iter->value == output) { + node->outputs().erase(iter); + break; + } + } + + if (node->outputs().empty() && node->flags().cacheable()) { + AttributeID(node).subgraph()->cache_insert(node); + } +} + +template <> +void Graph::remove_output_edge(data::ptr node, AttributeID output) { + for (auto iter = node->outputs().begin(), end = node->outputs().end(); iter != end; ++iter) { + if (iter->value == output) { + node->outputs().erase(iter); + break; + } + } +} + +bool Graph::remove_removed_output(AttributeID attribute, AttributeID output, bool flag) { + if (output.subgraph()->validation_state() == Subgraph::ValidationState::Invalidated) { + return false; + } + + if (output.is_direct()) { + auto output_node_ptr = output.to_node_ptr(); + + uint32_t index = 0; + for (auto input : output_node_ptr->inputs()) { + if (input.value.traverses(attribute, TraversalOptions::SkipMutableReference)) { + remove_input_edge(output_node_ptr, *output_node_ptr.get(), index); + return true; + } + index += 1; + } + return false; + } + + if (!output.is_indirect()) { + return false; + } + + auto indirect_node = output.to_indirect_node_ptr(); + + if (indirect_node->source().attribute() != attribute) { + + // clear dependency + auto dependency = indirect_node->to_mutable().dependency(); + if (dependency && dependency == attribute) { + foreach_trace( + [&indirect_node](Trace &trace) { trace.set_dependency(indirect_node, AttributeID::make_nil()); }); + indirect_node->to_mutable().set_dependency(AttributeID(0)); // TODO: 0 or nullptr + return true; + } + + return false; + } + + // reset source + auto initial_source = indirect_node->to_mutable().initial_source(); + WeakAttributeID new_source = {AttributeID::make_nil(), 0}; + uint32_t new_offset = 0; + if (initial_source.attribute().without_kind() != 0 && !initial_source.expired()) { + new_source = initial_source; + new_offset = indirect_node->to_mutable().initial_offset(); + } + + foreach_trace( + [&indirect_node, &new_source](Trace &trace) { trace.set_source(indirect_node, new_source.attribute()); }); + indirect_node->modify(new_source, new_offset); + + if (new_source.attribute().without_kind() != 0 && !new_source.expired()) { + add_input_dependencies(output, new_source.attribute()); + } + + return true; +} + +#pragma mark - Marks void Graph::mark_pending(data::ptr node_ptr, Node *node) { if (!node->state().is_pending()) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 2977827..bb776ad 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -45,6 +45,7 @@ class AttributeType; class Subgraph; class Encoder; class Trace; +class MutableIndirectNode; class Graph { public: @@ -263,10 +264,14 @@ class Graph { // MARK: Outputs - void output_value_ref(data::ptr node, const swift::metadata &type); + void *output_value_ref(data::ptr node, const swift::metadata &type); + + template void add_output_edge(data::ptr node, AttributeID output); + template <> void add_output_edge(data::ptr node, AttributeID output); - template void add_output_edge(data::ptr node, AttributeID attribute); template void remove_output_edge(data::ptr node, AttributeID attribute); + template <> void remove_output_edge(data::ptr node, AttributeID attribute); + template <> void remove_output_edge(data::ptr node, AttributeID attribute); bool remove_removed_output(AttributeID attribute, AttributeID source, bool flag); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index b848096..c367260 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -947,7 +947,7 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata item = new NodeCache::Item(item_lookup_key.field_0x00, item_lookup_key.equals_item, node, nullptr, nullptr); - node->flags().set_value4_unknown0x10(true); + node->flags().set_cacheable(true); _cache->items().insert(node, item); } else { @@ -999,7 +999,7 @@ void Subgraph::cache_insert(data::ptr node) { return; } - if (node->flags().value4_unknown0x10() && !node->state().is_evaluating() && node->outputs().empty()) { + if (node->outputs().empty() && node->flags().cacheable() && !node->state().is_evaluating()) { // TODO: one of these flags must indicate it is cached const AttributeType &attribute_type = _graph->attribute_type(node->type_id()); From 39977c20d2876ae5f13c5a49044bdffbc2c4dd54 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 7 Feb 2025 20:52:24 +1100 Subject: [PATCH 13/74] Implement Subgraph() and ~Subgraph() --- Sources/ComputeCxx/Graph/Graph.cpp | 7 +++ Sources/ComputeCxx/Graph/Graph.h | 1 + Sources/ComputeCxx/Subgraph/AGSubgraph.h | 2 + Sources/ComputeCxx/Subgraph/Subgraph.cpp | 65 ++++++++++++++++-------- Sources/ComputeCxx/Subgraph/Subgraph.h | 5 +- 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 7febe1a..0ed222c 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -40,6 +40,13 @@ Graph::without_invalidating::~without_invalidating() { #pragma mark - Subgraphs +void Graph::add_subgraph(Subgraph &subgraph) { + _subgraphs.push_back(&subgraph); + + _num_subgraphs += 1; + _num_subgraphs_created += 1; +} + void Graph::remove_subgraph(Subgraph &subgraph) { auto iter = std::remove(_subgraphs.begin(), _subgraphs.end(), &subgraph); _subgraphs.erase(iter); diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index bb776ad..33f6bc4 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -116,6 +116,7 @@ class Graph { uint64_t _num_nodes; // probably this, not sure uint64_t _num_node_values; // probably this, not sure uint64_t _num_subgraphs; + uint64_t _num_subgraphs_created; bool _needs_update; // 0x199 diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index b3a8081..ea97ff0 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -8,6 +8,8 @@ CF_EXTERN_C_BEGIN typedef struct CF_BRIDGED_TYPE(id) AGSubgraphStorage *AGSubgraphRef CF_SWIFT_NAME(Subgraph); +bool AGSubgraphShouldRecordTree(); + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index c367260..46b7068 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -2,6 +2,7 @@ #include +#include "AGSubgraph.h" #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" @@ -12,6 +13,7 @@ #include "Graph/Graph.h" #include "Graph/Trace.h" #include "Graph/Tree/TreeElement.h" +#include "Graph/UpdateStack.h" #include "NodeCache.h" #include "UniqueID/AGUniqueID.h" #include "Util/CFPointer.h" @@ -31,25 +33,41 @@ void Subgraph::set_current_subgraph(Subgraph *subgraph) { Subgraph::Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID owner) { _object = object; - Graph &graph = context.graph(); - _graph = &graph; + Graph *graph = &context.graph(); + _graph = graph; _graph_context_id = context.unique_id(); - _validation_state = ValidationState::Valid; + // what is this check doing? + if ((uintptr_t)this == 1) { + print(0); + graph = _graph; + } + + graph->add_subgraph(*this); - begin_tree(owner, nullptr, 0); + if (AGSubgraphShouldRecordTree()) { + if (owner.without_kind() == 0) { + auto update = Graph::current_update(); + if (update.tag() == 0 && update.get() != nullptr) { + if (auto top = update.get()->global_top()) { + owner = top->attribute; + } + } + } + begin_tree(owner, nullptr, 0); + } - graph.foreach_trace([*this](Trace &trace) { trace.created(*this); }); + context.graph().foreach_trace([*this](Trace &trace) { trace.created(*this); }); } Subgraph::~Subgraph() { - if (_observers) { + if (observers_vector *vector = observers()) { notify_observers(); - // delete *_observers + delete vector; + } + if (_cache) { + _cache->~NodeCache(); } - // if (_node_cache) { - // _node_cache::~NodeCache(); - // } } #pragma mark - CoreFoundation @@ -874,14 +892,16 @@ void Subgraph::propagate_dirty_flags() { // MARK: - Observers +Subgraph::observers_vector *Subgraph::observers() { return *_observers.get(); } + uint64_t Subgraph::add_observer(ClosureFunctionVV observer) { if (!_observers) { - _observers = (data::ptr)alloc_bytes(sizeof(observers_vector), 7); - *_observers = observers_vector(); + _observers = (data::ptr)alloc_bytes(sizeof(observers_vector *), 7); + *_observers = new observers_vector(); } auto observer_id = AGMakeUniqueID(); - _observers->push_back({ + observers()->push_back({ observer, observer_id, }); @@ -889,24 +909,26 @@ uint64_t Subgraph::add_observer(ClosureFunctionVV observer) { } void Subgraph::remove_observer(uint64_t observer_id) { - if (_observers) { - auto iter = std::remove_if(_observers->begin(), _observers->end(), [&observer_id](auto pair) -> bool { + if (auto vector = observers()) { + auto iter = std::remove_if(vector->begin(), vector->end(), [&observer_id](auto pair) -> bool { if (pair.second == observer_id) { pair.first.release_context(); // TODO: where is retain? return true; } return false; }); - _observers->erase(iter, _observers->end()); + vector->erase(iter, vector->end()); } } void Subgraph::notify_observers() { - while (!_observers->empty()) { - auto observer = _observers->back(); - observer.first(); - observer.first.release_context(); // TODO: where is retain? - _observers->pop_back(); + if (auto vector = observers()) { + while (!vector->empty()) { + auto observer = vector->back(); + observer.first(); + observer.first.release_context(); // TODO: where is retain? + vector->pop_back(); + } } } @@ -916,6 +938,7 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata ClosureFunctionCI closure) { if (_cache == nullptr) { _cache = (data::ptr)alloc_bytes(sizeof(NodeCache), 7); + new (&_cache) NodeCache(); } auto type = _cache->types().lookup(&metadata, nullptr); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 5e6f401..d1a4a9a 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -55,8 +55,6 @@ class Subgraph : public data::zone { bool is_null() const { return value1 == 0 && value2 == 0 and value3 == 0 && value4 == 0; }; }; - struct Observer {}; - enum CacheState : uint8_t { Option1 = 1 << 0, // added to graph._subgraphs_with_cached_nodes, or needs collect? Option2 = 1 << 1, // Is calling cache collect @@ -80,7 +78,7 @@ class Subgraph : public data::zone { vector _children; using observers_vector = vector, uint64_t>, 0, uint64_t>; - data::ptr _observers; + data::ptr _observers; uint32_t _traversal_seed; uint32_t _index; // TODO: get and set @@ -177,6 +175,7 @@ class Subgraph : public data::zone { // MARK: Managing observers + observers_vector *_Nullable observers(); uint64_t add_observer(ClosureFunctionVV observer); void remove_observer(uint64_t observer_id); void notify_observers(); From d2fd0c7e07e2b815c73096e6832223a51a663e8d Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sat, 8 Feb 2025 15:32:27 +1100 Subject: [PATCH 14/74] Implement Graph::UpdateStack --- Sources/ComputeCxx/Attribute/AttributeType.h | 23 +- Sources/ComputeCxx/Attribute/Node/Edge.h | 38 +++ .../ComputeCxx/Attribute/Node/IndirectNode.h | 2 +- Sources/ComputeCxx/Attribute/Node/InputEdge.h | 19 -- Sources/ComputeCxx/Attribute/Node/Node.h | 43 ++- .../ComputeCxx/Attribute/Node/OutputEdge.h | 21 -- Sources/ComputeCxx/Data/Vector.h | 5 + Sources/ComputeCxx/Graph/AGGraph.h | 3 + Sources/ComputeCxx/Graph/Graph.cpp | 45 +-- Sources/ComputeCxx/Graph/Graph.h | 12 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 308 +++++++++++++++--- Sources/ComputeCxx/Graph/UpdateStack.h | 23 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- 13 files changed, 388 insertions(+), 156 deletions(-) create mode 100644 Sources/ComputeCxx/Attribute/Node/Edge.h delete mode 100644 Sources/ComputeCxx/Attribute/Node/InputEdge.h delete mode 100644 Sources/ComputeCxx/Attribute/Node/OutputEdge.h diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index a740874..75cb2dd 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -28,13 +28,13 @@ class AttributeType { enum Flags : uint32_t { ComparisonModeMask = 0x3, - HasDestroySelf = 1 << 2, // 0x04 - InitialValueOfNodeState2And3 = 1 << 3, // 0x08 - UseGraphAsInitialValue = 1 << 4, // 0x10 - Unknown0x20 = 1 << 5, // 0x20 // used in update_main_refs + HasDestroySelf = 1 << 2, // 0x04 + MainThread = 1 << 3, // 0x08 + UseGraphAsInitialValue = 1 << 4, // 0x10 + Unknown0x20 = 1 << 5, // 0x20 // used in update_main_refs }; - using UpdateFunction = void (*)(void *body, AttributeID attribute); + using UpdateFunction = void (*)(void *context, void *body); private: swift::metadata *_self_metadata; @@ -52,10 +52,7 @@ class AttributeType { const swift::metadata &self_metadata() const { return *_self_metadata; }; const swift::metadata &value_metadata() const { return *_value_metadata; }; - uint8_t node_initial_state() const { - uint8_t flag = _flags >> 3 & 1; - return flag << 3 | flag << 2; - }; + bool main_thread() const { return _flags & Flags::MainThread; }; bool use_graph_as_initial_value() const { return _flags & Flags::UseGraphAsInitialValue; }; bool unknown_0x20() const { return _flags & Flags::Unknown0x20; }; @@ -76,16 +73,16 @@ class AttributeType { } }; + void perform_update(void *body) const { _update_function(_update_context, body); }; + // V table methods void vt_destroy_self(void *body) { if (_flags & Flags::HasDestroySelf) { _vtable->destroy_self(this, body); } } - - AttributeVTable::Callback vt_get_update_stack_callback() const { - return _vtable->_update_stack_callback; - } + + AttributeVTable::Callback vt_get_update_stack_callback() const { return _vtable->_update_stack_callback; } }; } // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/Edge.h b/Sources/ComputeCxx/Attribute/Node/Edge.h new file mode 100644 index 0000000..26064f5 --- /dev/null +++ b/Sources/ComputeCxx/Attribute/Node/Edge.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Attribute/AttributeID.h" +#include "Containers/ArrayRef.h" + +namespace AG { + +class Node; + +struct InputEdge { + struct Comparator {}; + enum Flags : uint8_t { + Unknown2 = 1 << 2, + IsPending = 1 << 3, + Unknown4 = 1 << 4, + }; + + AttributeID value; + uint8_t _flags; + + bool is_unknown2() { return _flags & Flags::Unknown2; }; + + bool is_pending() { return _flags & Flags::IsPending; }; + void set_pending(bool value) { _flags = (_flags & ~Flags::IsPending) | (value ? Flags::IsPending : 0); }; + + bool is_unknown4() { return _flags & Flags::Unknown4; }; + void set_unknown4(bool value) { _flags = (_flags & ~Flags::Unknown4) | (value ? Flags::Unknown4 : 0); }; +}; + +using ConstInputEdgeArrayRef = const ArrayRef; + +struct OutputEdge { + AttributeID value; +}; + +using ConstOutputEdgeArrayRef = ArrayRef; + +} // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index 252e4f4..b2a33dc 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -5,7 +5,7 @@ #include "Attribute/AttributeID.h" #include "Attribute/WeakAttributeID.h" #include "Data/Vector.h" -#include "OutputEdge.h" +#include "Edge.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Attribute/Node/InputEdge.h b/Sources/ComputeCxx/Attribute/Node/InputEdge.h deleted file mode 100644 index a6f0f40..0000000 --- a/Sources/ComputeCxx/Attribute/Node/InputEdge.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "Attribute/AttributeID.h" -#include "Containers/ArrayRef.h" - -namespace AG { - -class Node; - -struct InputEdge { - struct Comparator {}; - - AttributeID value; - uint8_t flags; -}; - -using ConstInputEdgeArrayRef = const ArrayRef; - -} // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 1132d72..f3f9b51 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -4,8 +4,7 @@ #include "Data/Pointer.h" #include "Data/Vector.h" -#include "InputEdge.h" -#include "OutputEdge.h" +#include "Edge.h" CF_ASSUME_NONNULL_BEGIN @@ -32,7 +31,7 @@ class NodeFlags { Unknown0x04 = 1 << 2, // 0x04 Unknown0x08 = 1 << 3, // 0x08 - Cacheable = 1 << 4, // 0x10 + Cacheable = 1 << 4, // 0x10 Unknown0x20 = 1 << 5, // 0x20 - initial value Unknown0x40 = 1 << 6, // 0x40 - didn't call mark_changed @@ -65,9 +64,7 @@ class NodeFlags { _value4 = (_value4 & ~Flags4::Unknown0x04) | (value ? Flags4::Unknown0x04 : 0); }; bool cacheable() { return _value4 & Flags4::Cacheable; }; - void set_cacheable(bool value) { - _value4 = (_value4 & ~Flags4::Cacheable) | (value ? Flags4::Cacheable : 0); - }; + void set_cacheable(bool value) { _value4 = (_value4 & ~Flags4::Cacheable) | (value ? Flags4::Cacheable : 0); }; bool value4_unknown0x20() { return _value4 & Flags4::Unknown0x20; }; void set_value4_unknown0x20(bool value) { _value4 = (_value4 & ~Flags4::Unknown0x20) | (value ? Flags4::Unknown0x20 : 0); @@ -83,24 +80,23 @@ class Node { class State { public: enum : uint8_t { - Dirty = 1 << 0, // 0x01 // Unknown0 = 1 << 0, - Pending = 1 << 1, // 0x02 // Unknown1 = 1 << 1, - UpdatesOnMain = 1 << 2, // 0x04 set from attribute type flags & 8 - Unknown3 = 1 << 3, // 0x08 set from attribute type flags & 8 + Dirty = 1 << 0, // 0x01 // Unknown0 = 1 << 0, + Pending = 1 << 1, // 0x02 // Unknown1 = 1 << 1, + MainThread = 1 << 2, // 0x04 set from attribute type flags & 8 + MainThreadOnly = 1 << 3, // 0x08 set from attribute type flags & 8 ValueInitialized = 1 << 4, // 0x10 SelfInitialized = 1 << 5, // 0x20 - InUpdateStack = 1 << 6, // 0x40 - Unknown7 = 1 << 7, // 0x80 + Updating = 1 << 6, // 0x40 + UpdatingCyclic = 1 << 7, // 0x80 - IsEvaluating = InUpdateStack | Unknown7, }; private: uint8_t _data; public: - explicit constexpr State(uint8_t data) : _data(data){}; + explicit constexpr State(uint8_t data = 0) : _data(data){}; uint8_t data() { return _data; }; bool is_dirty() { return _data & Dirty; } @@ -109,13 +105,13 @@ class Node { bool is_pending() { return _data & Pending; } State with_pending(bool value) const { return State((_data & ~Pending) | (value ? Pending : 0)); }; - bool updates_on_main() { return _data & UpdatesOnMain; } - State with_updates_on_main(bool value) const { - return State((_data & ~UpdatesOnMain) | (value ? UpdatesOnMain : 0)); - }; + bool is_main_thread() { return _data & MainThread; } + State with_main_thread(bool value) const { return State((_data & ~MainThread) | (value ? MainThread : 0)); }; - bool is_unknown3() { return _data & Unknown3; } - State with_unknown3(bool value) const { return State((_data & ~Unknown3) | (value ? Unknown3 : 0)); }; + bool is_main_thread_only() { return _data & MainThreadOnly; } + State with_main_thread_only(bool value) const { + return State((_data & ~MainThreadOnly) | (value ? MainThreadOnly : 0)); + }; bool is_value_initialized() { return _data & ValueInitialized; }; State with_value_initialized(bool value) const { @@ -127,11 +123,10 @@ class Node { return State((_data & ~SelfInitialized) | (value ? SelfInitialized : 0)); }; - State with_in_update_stack(bool value) const { - return State((_data & ~InUpdateStack) | (value ? InUpdateStack : 0)); - }; + State with_updating(bool value) const { return State((_data & ~Updating) | (value ? Updating : 0)); }; - bool is_evaluating() { return _data & InUpdateStack || _data & Unknown7; } + bool is_updating() { return _data & (Updating | UpdatingCyclic); } + bool is_updating_cyclic() { return (_data & (Updating | UpdatingCyclic)) == (Updating | UpdatingCyclic); } }; private: diff --git a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h b/Sources/ComputeCxx/Attribute/Node/OutputEdge.h deleted file mode 100644 index 59b22a2..0000000 --- a/Sources/ComputeCxx/Attribute/Node/OutputEdge.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -#include "Attribute/AttributeID.h" -#include "Containers/ArrayRef.h" - -CF_ASSUME_NONNULL_BEGIN - -namespace AG { - -class OutputEdge { - public: - AttributeID value; -}; - -using ConstOutputEdgeArrayRef = ArrayRef; - -} // namespace AG - -CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Data/Vector.h b/Sources/ComputeCxx/Data/Vector.h index fb587c4..81d3e92 100644 --- a/Sources/ComputeCxx/Data/Vector.h +++ b/Sources/ComputeCxx/Data/Vector.h @@ -55,6 +55,9 @@ template class vector { } // Element access + + reference operator[](size_type pos) { return _data.get()[pos]; }; + const_reference operator[](size_type pos) const { return _data.get()[pos]; }; reference front() { return *_data.get(); }; const_reference front() const { return _data.get(); }; @@ -93,6 +96,8 @@ template class vector { }; // Modifiers + + // TODO: check order needs to be preserverd here, UpdateStack manages index... iterator erase(iterator pos) { if (pos == end()) { diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 3b1d278..98e3e3e 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -3,6 +3,7 @@ #include #include "AGSwiftSupport.h" +#include "Swift/AGType.h" CF_ASSUME_NONNULL_BEGIN @@ -10,6 +11,8 @@ CF_EXTERN_C_BEGIN typedef struct CF_BRIDGED_TYPE(id) AGGraphStorage *AGGraphRef AG_SWIFT_NAME(Graph); +void AGGraphSetOutputValue(void *value, AGTypeID type); + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 0ed222c..76e9e97 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -128,7 +128,7 @@ void Graph::reset_update(data::ptr node) { for (auto update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { for (auto frame : update_stack.get()->frames()) { if (frame.attribute == node) { - frame.flags &= 0xf; + frame.num_pushed_inputs = 0; } } } @@ -278,7 +278,8 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void } else { node = (data::ptr)subgraph.alloc_bytes(uint32_t(total_size), uint32_t(alignment_mask | 3)); } - *node = Node(Node::State(type.node_initial_state()), type_id, 0x20); + bool main_thread = type.main_thread(); + *node = Node(Node::State().with_main_thread(main_thread).with_main_thread_only(main_thread), type_id, 0x20); if (type_id >= 0x100000) { precondition_failure("too many node types allocated"); @@ -287,7 +288,7 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void void *self = (uint8_t *)node.get() + type.attribute_offset(); node->set_state(node->state().with_self_initialized(true)); - if (node->state().is_unknown3() && !type.value_metadata().getValueWitnesses()->isPOD()) { + if (node->state().is_main_thread_only() && !type.value_metadata().getValueWitnesses()->isPOD()) { node->flags().set_value4_unknown0x20(!type.unknown_0x20()); // toggle } else { node->flags().set_value4_unknown0x20(false); @@ -332,11 +333,11 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio Node &node = attribute.to_node(); if (node.state().is_value_initialized() && !node.state().is_dirty()) { - return UpdateStatus::Option0; + return UpdateStatus::NoChange; } _update_attribute_count += 1; - if (node.state().updates_on_main()) { + if (node.state().is_main_thread()) { _update_attribute_on_main_count += 1; } @@ -346,7 +347,7 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio trace.begin_update(update_stack, attribute.to_node_ptr(), options); }); - UpdateStatus status = UpdateStatus::Option1; + UpdateStatus status = UpdateStatus::Changed; if (update_stack.push(attribute.to_node_ptr(), node, false, (options & 1) == 0)) { status = update_stack.update(); @@ -390,7 +391,7 @@ void Graph::update_main_refs(AttributeID attribute) { if (type.value_metadata().getValueWitnesses()->isPOD() || type.unknown_0x20()) { new_unknown0x20 = false; } else { - if (node.state().is_unknown3()) { + if (node.state().is_main_thread_only()) { new_unknown0x20 = true; } else { new_unknown0x20 = @@ -433,7 +434,7 @@ void Graph::update_main_refs(AttributeID attribute) { } void Graph::remove_node(data::ptr node) { - if (node->state().is_evaluating()) { + if (node->state().is_updating()) { precondition_failure("deleting updating attribute: %u\n", node); } @@ -800,7 +801,7 @@ void *Graph::value_ref(AttributeID attribute, bool evaluate_weak_references, con } UpdateStatus status = update_attribute(resolved.attribute(), 0); - if (status != UpdateStatus::Option0) { + if (status != UpdateStatus::NoChange) { *did_update_out = true; } @@ -837,7 +838,7 @@ bool Graph::value_set(data::ptr node, const swift::metadata &value_type, c auto update = current_update(); if (update.tag() == 0 && update.get() != nullptr && update.get()->graph() == this && - (!node->outputs().empty() || node->state().is_evaluating())) { + (!node->outputs().empty() || node->state().is_updating())) { precondition_failure("setting value during update: %u", node); } @@ -910,15 +911,15 @@ AGValueState Graph::value_state(AttributeID attribute) { auto node = attribute.to_node(); return (node.state().is_dirty() ? 1 : 0) << 0 | (node.state().is_pending() ? 1 : 0) << 1 | - (node.state().is_evaluating() ? 1 : 0) << 2 | (node.state().is_value_initialized() ? 1 : 0) << 3 | - (node.state().updates_on_main() ? 1 : 0) << 4 | (node.flags().value4_unknown0x20() ? 1 : 0) << 5 | - (node.state().is_unknown3() ? 1 : 0) << 6 | (node.flags().value4_unknown0x40() ? 1 : 0) << 7; + (node.state().is_updating() ? 1 : 0) << 2 | (node.state().is_value_initialized() ? 1 : 0) << 3 | + (node.state().is_main_thread() ? 1 : 0) << 4 | (node.flags().value4_unknown0x20() ? 1 : 0) << 5 | + (node.state().is_main_thread_only() ? 1 : 0) << 6 | (node.flags().value4_unknown0x40() ? 1 : 0) << 7; } void Graph::value_mark(data::ptr node) { - auto update = current_update(); + auto update = current_update(); // TODO: investigate meaning of tags if (update.tag() == 0 && update.get() != nullptr && update.get()->graph() == this && - (!node->outputs().empty() || node->state().is_evaluating())) { + (!node->outputs().empty() || node->state().is_updating())) { precondition_failure("setting value during update: %u", node); } @@ -972,11 +973,11 @@ void Graph::value_mark_all() { auto node = attribute.to_node(); AttributeType &type = *_types[node.type_id()]; if (!type.use_graph_as_initial_value()) { - node.set_state(node.state().with_unknown3(true)); + node.set_state(node.state().with_dirty(true).with_pending(true)); subgraph->add_dirty_flags(node.flags().value3()); } for (auto input_edge : node.inputs()) { - input_edge.flags |= 8; + input_edge.set_unknown4(true); } } } @@ -1029,8 +1030,8 @@ void Graph::propagate_dirty(AttributeID attribute) { Node &output_node = output.to_node(); ArrayRef more_outputs = {nullptr, 0}; - if (state.updates_on_main() && !output_node.state().updates_on_main()) { - output_node.set_state(output_node.state().with_updates_on_main(true)); + if (state.is_main_thread() && !output_node.state().is_main_thread()) { + output_node.set_state(output_node.state().with_main_thread(true)); dirty_outputs = output_node.outputs(); } @@ -1094,11 +1095,11 @@ void Graph::propagate_dirty(AttributeID attribute) { bool stop = false; auto frames = update.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { - if (frame->attribute->state().updates_on_main()) { + if (frame->attribute->state().is_main_thread()) { stop = true; break; } - frame->attribute->set_state(frame->attribute->state().with_updates_on_main(true)); + frame->attribute->set_state(frame->attribute->state().with_main_thread(true)); } if (stop) { break; @@ -1110,7 +1111,7 @@ void Graph::propagate_dirty(AttributeID attribute) { void *Graph::output_value_ref(data::ptr node, const swift::metadata &value_type) { // Status: Verified - if (!node->state().is_evaluating()) { + if (!node->state().is_updating()) { precondition_failure("attribute is not evaluating: %u", node); } diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 33f6bc4..c5e69f1 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -7,8 +7,7 @@ #include #include "Attribute/AttributeID.h" -#include "Attribute/Node/InputEdge.h" -#include "Attribute/Node/OutputEdge.h" +#include "Attribute/Node/Edge.h" #include "Closure/ClosureFunction.h" #include "Util/HashTable.h" #include "Util/Heap.h" @@ -61,8 +60,8 @@ class Graph { }; enum UpdateStatus : uint32_t { - Option0 = 0, - Option1 = 1, + NoChange = 0, + Changed = 1, Option2 = 2, NeedsCallMainHandler = 3, }; @@ -127,6 +126,8 @@ class Graph { uint64_t _counter_0x1b8; uint64_t _update_attribute_count; uint64_t _update_attribute_on_main_count; + uint64_t + _update_stack_frame_counter; // used to detect changes between calling Trace.begin_update/end_update // 0x1d0 uint64_t _counter_0x1d8; public: @@ -272,7 +273,8 @@ class Graph { template void remove_output_edge(data::ptr node, AttributeID attribute); template <> void remove_output_edge(data::ptr node, AttributeID attribute); - template <> void remove_output_edge(data::ptr node, AttributeID attribute); + template <> + void remove_output_edge(data::ptr node, AttributeID attribute); bool remove_removed_output(AttributeID attribute, AttributeID source, bool flag); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 359c30e..76f96d7 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -1,7 +1,12 @@ #include "UpdateStack.h" +#include "AGGraph.h" #include "Attribute/AttributeType.h" +#include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" +#include "Attribute/OffsetAttributeID.h" +#include "Graph/Trace.h" +#include "Subgraph/Subgraph.h" namespace AG { @@ -28,7 +33,7 @@ Graph::UpdateStack::UpdateStack(Graph *graph, uint8_t options) { Graph::UpdateStack::~UpdateStack() { for (auto frame : _frames) { - frame.attribute->set_state(frame.attribute->state().with_in_update_stack(false)); + frame.attribute->set_state(frame.attribute->state().with_updating(false)); } if (_thread != _graph->_current_update_thread) { @@ -53,74 +58,291 @@ Graph::UpdateStack::Frame *Graph::UpdateStack::global_top() { return nullptr; } -bool Graph::UpdateStack::push(data::ptr attribute, Node &node, bool flag1, bool flag2) { - if (!node.state().is_evaluating()) { - node.set_state(node.state().with_in_update_stack(true)); +void Graph::UpdateStack::cancel() { + for (TaggedPointer update_stack = current_update(); update_stack != nullptr; + update_stack = update_stack.get()->previous()) { + auto frames = update_stack.get()->frames(); + for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { + if (frame->cancelled) { + return; + } + frame->cancelled = true; + } + if (update_stack.get()->_options & 2) { + break; + } + } +} - // TODO: check where capacity is being checked - uint32_t frame_flags = 0; - if (node.state().is_pending() || (!node.state().is_value_initialized() && flag2)) { - frame_flags = 1; +bool Graph::UpdateStack::cancelled() { + for (TaggedPointer update_stack = this; update_stack != nullptr; + update_stack = update_stack.get()->previous()) { + if (!update_stack.get()->frames().empty()) { + auto last_frame = update_stack.get()->frames().back(); + return last_frame.cancelled; } - _frames.push_back({attribute, frame_flags}); + } + return false; +} + +bool Graph::UpdateStack::push(data::ptr attribute, Node &node, bool flag1, bool treat_no_value_as_pending) { + if (!node.state().is_updating() && _frames.size() + 1 <= _frames.capacity()) { + node.set_state(node.state().with_updating(true)); + + Graph::UpdateStack::Frame frame = {attribute, 0}; + if (node.state().is_pending() || (!node.state().is_value_initialized() && treat_no_value_as_pending)) { + frame.needs_update = true; + } + _frames.push_back(frame); return true; } - return push_slow(attribute, node, flag1, flag2); + return push_slow(attribute, node, flag1, treat_no_value_as_pending); } -bool Graph::UpdateStack::push_slow(data::ptr attribute, Node &node, bool flag1, bool flag2) { - uint8_t is_evaluating = node.state().is_evaluating(); +bool Graph::UpdateStack::push_slow(data::ptr attribute, Node &node, bool ignore_cycles, + bool treat_no_value_as_pending) { + Node::State old_state = node.state(); - if (is_evaluating) { - // is already updating - if (!flag1) { - Graph::UpdateStack::Frame *top = global_top(); - if (top != nullptr && (top->flags & 2) == 0) { - this->_graph->print_cycle(attribute); - } + if (old_state.is_updating()) { + if (ignore_cycles) { + return false; + } - if (!node.state().is_value_initialized()) { - const AttributeType &attribute_type = _graph->attribute_type(node.type_id()); + Graph::UpdateStack::Frame *top = global_top(); + if (top != nullptr && !top->cyclic) { + // First time we are detecting a cycle for this attribute + this->_graph->print_cycle(attribute); + } - auto callback = attribute_type.vt_get_update_stack_callback(); - if (callback != nullptr) { + if (node.state().is_value_initialized()) { + return false; + } - Graph::UpdateStack::Frame frame = {attribute, 0}; - if (node.state().is_pending() || (!node.state().is_value_initialized() && flag2)) { - frame.flags = 1; - } + const AttributeType &attribute_type = _graph->attribute_type(node.type_id()); + auto callback = attribute_type.vt_get_update_stack_callback(); + if (callback != nullptr) { - _frames.push_back(frame); + Graph::UpdateStack::Frame frame = {attribute, 0}; + if (node.state().is_pending() || (!node.state().is_value_initialized() && treat_no_value_as_pending)) { + frame.needs_update = true; + } - void *self = node.get_self(attribute_type); - callback(&attribute_type, self); + _frames.push_back(frame); - _frames.pop_back(); + void *self = node.get_self(attribute_type); + callback(&attribute_type, self); - if (node.state().is_value_initialized()) { - return false; - } - } - if (is_evaluating == 3) { // both flags set - precondition_failure("cyclic graph: %u", attribute); - } + _frames.pop_back(); + + if (node.state().is_value_initialized()) { + return false; } } + + if (old_state.is_updating_cyclic()) { + precondition_failure("cyclic graph: %u", attribute); + } } - node.set_state(node.state().with_in_update_stack(true)); + node.set_state(node.state().with_updating(true)); Graph::UpdateStack::Frame frame = {attribute, 0}; - if (node.state().is_pending() || (!node.state().is_value_initialized() && flag2)) { - frame.flags = 1; + if (node.state().is_pending() || (!node.state().is_value_initialized() && treat_no_value_as_pending)) { + frame.needs_update = true; } - if (is_evaluating) { - frame.flags |= 0x2; // mark so we can print cycles + if (old_state.is_updating()) { + frame.cyclic = true; } _frames.push_back(frame); return true; } +Graph::UpdateStatus Graph::UpdateStack::update() { + + while (true) { + auto frame = _frames.back(); + Node *node = frame.attribute.get(); + + if (_options >> 2) { + if (!frame.cancelled && _graph->passed_deadline()) { + cancel(); + } + } + + if (frame.cancelled) { + if (_options >> 1) { + return UpdateStatus::Option2; + } + + bool changed = false; + if (!node->state().is_value_initialized()) { + const AttributeType &type = _graph->attribute_type(node->type_id()); + void *self = node->get_self(type); + if (auto callback = type.vt_get_update_stack_callback()) { + callback(&type, self); + changed = true; + } + if (node->state().is_value_initialized()) { + node->set_state(node->state().with_updating(false)); + _frames.pop_back(); + if (_frames.empty()) { + return changed ? UpdateStatus::Changed : UpdateStatus::NoChange; + } + } + } + } + + if (!frame.needs_update && frame.num_pushed_inputs > 0 && + node->inputs()[frame.num_pushed_inputs - 1].is_pending()) { + frame.needs_update = true; + } + + // Push inputs + + for (auto input_index = frame.num_pushed_inputs, num_inputs = node->inputs().size(); input_index != num_inputs; + ++input_index) { + InputEdge &input = node->inputs()[input_index]; + + AttributeID input_attribute = input.value; + while (input_attribute.is_indirect()) { + AttributeID input_attribute = input_attribute.to_indirect_node().source().attribute(); + + if (input_attribute.to_indirect_node().is_mutable()) { + // TDOO: is dependency always direct?? + if (AttributeID dependency = input_attribute.to_indirect_node().to_mutable().dependency()) { + if (dependency.to_node().state().is_dirty() || + !dependency.to_node().state().is_value_initialized()) { + + frame.num_pushed_inputs = input_index; + + push(dependency.to_node_ptr(), dependency.to_node(), false, false); + // go to top regardless + return update(); + } + } + } + } + + if (input_attribute.is_direct()) { + Node &input_node = input_attribute.to_node(); + + if (input.is_pending()) { + frame.needs_update = true; + } + + if (input_node.state().is_dirty() || !input_node.state().is_value_initialized()) { + + if (!input.is_pending() && + input_attribute.subgraph()->validation_state() == Subgraph::ValidationState::Valid) { + + frame.num_pushed_inputs = input_index + 1; + + if (push(input_attribute.to_node_ptr(), input_node, true, true)) { + // go to top + return update(); + } + } + + frame.needs_update = true; + } + } + } + + // Update value + + bool changed = false; + if (frame.needs_update) { + if (_graph->main_handler() != nullptr && node->state().is_main_thread()) { + return Graph::UpdateStatus::NeedsCallMainHandler; + } + + _graph->foreach_trace([&frame](Trace &trace) { trace.begin_update(frame.attribute); }); + uint64_t update_counter = _graph->_update_stack_frame_counter; + + const AttributeType &type = _graph->attribute_type(node->type_id()); + void *self = node->get_self(type); + + type.perform_update(self); + + if (!node->state().is_value_initialized()) { + if (type.value_metadata().vw_size() > 0) { + precondition_failure("attribute failed to set an initial value: %u, %s", frame.attribute, + type.self_metadata().name(false)); + } + + struct { + } value = {}; + AGGraphSetOutputValue(&value, AGTypeID(&type.value_metadata())); + } + + changed = _graph->_update_stack_frame_counter != update_counter; + _graph->foreach_trace([&frame, &changed](Trace &trace) { trace.end_update(frame.attribute, changed); }); + } + + // Reset flags + + bool reset_node_flags = !frame.cancelled; + for (uint32_t input_index = node->inputs().size() - 1; input_index >= 0; --input_index) { + InputEdge &input = node->inputs()[input_index]; + AttributeID input_attribute = input.value; + + bool reset_input_flags = true; + if (frame.flag3 || frame.cancelled) { + input_attribute = input_attribute.resolve(TraversalOptions::None).attribute(); + if (!input_attribute.is_direct() || input_attribute.to_node().state().is_dirty()) { + reset_input_flags = false; + reset_node_flags = false; + } + } + + if (reset_input_flags) { + if (frame.needs_update && !frame.cancelled) { + if (input.is_pending()) { + _graph->foreach_trace([&frame, &input](Trace &trace) { + trace.set_edge_pending(frame.attribute, input.value, false); + }); + input.set_pending(false); + } + } + } + + if (frame.needs_update && !frame.cancelled) { + bool was_unknown4 = input.is_unknown4(); + input.set_unknown4(false); + if (!was_unknown4 && !input.is_unknown2()) { + _graph->remove_input(frame.attribute, input_index); + } + } + } + + node->set_state(node->state().with_updating(false)); + + if (reset_node_flags) { + // only skips if frame.cancelled + if (node->flags().value4_unknown0x40()) { + node->flags().set_value4_unknown0x40(false); + } + if (node->state().is_dirty()) { + _graph->foreach_trace([&frame](Trace &trace) { trace.set_dirty(frame.attribute, false); }); + node->set_state(node->state().with_dirty(false)); + } + if (!node->state().is_main_thread_only()) { + node->set_state(node->state().with_main_thread(false)); + } + } + + if (node->state().is_pending()) { + _graph->foreach_trace([&frame](Trace &trace) { trace.set_pending(frame.attribute, false); }); + node->set_state(node->state().with_pending(false)); + } + + _frames.pop_back(); + if (_frames.empty()) { + return changed ? UpdateStatus::Changed : UpdateStatus::NoChange; + } + } +} + } // namespace AG diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index 7a04cbd..d29f11f 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -10,15 +10,20 @@ namespace AG { class Graph::UpdateStack { public: - struct Frame { - data::ptr attribute; - uint32_t flags; // first four bits are base - }; enum Option : uint8_t { WasNotDeferringInvalidation = 1 << 4, }; private: + struct Frame { + data::ptr attribute; + unsigned int needs_update : 1; + unsigned int cyclic : 1; + unsigned int flag3 : 1; + unsigned int cancelled : 1; + unsigned int num_pushed_inputs : 28; + }; + Graph *_graph; pthread_t _thread; TaggedPointer _previous; @@ -26,6 +31,8 @@ class Graph::UpdateStack { vector _frames; uint8_t _options; + bool push_slow(data::ptr attribute, Node &node, bool ignore_cycles, bool treat_no_value_as_pending); + public: UpdateStack(Graph *graph, uint8_t options); ~UpdateStack(); @@ -37,10 +44,12 @@ class Graph::UpdateStack { Frame *global_top(); - Graph::UpdateStatus update(); + static void cancel(); + bool cancelled(); - bool push(data::ptr attribute, Node &node, bool flag1, bool flag2); - bool push_slow(data::ptr attribute, Node &node, bool flag1, bool flag2); + bool push(data::ptr attribute, Node &node, bool ignore_cycles, bool treat_no_value_as_pending); + + Graph::UpdateStatus update(); }; } // namespace AG diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 46b7068..df9dd1f 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -1022,7 +1022,7 @@ void Subgraph::cache_insert(data::ptr node) { return; } - if (node->outputs().empty() && node->flags().cacheable() && !node->state().is_evaluating()) { + if (node->outputs().empty() && node->flags().cacheable() && !node->state().is_updating()) { // TODO: one of these flags must indicate it is cached const AttributeType &attribute_type = _graph->attribute_type(node->type_id()); From 0e7d048abde9e59824b07b98f25ed001f4f9e655 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 11 Feb 2025 10:29:28 +1100 Subject: [PATCH 15/74] Fix bug in Subgraph::tree_node_at_index --- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index df9dd1f..882c300 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -760,7 +760,7 @@ AttributeID Subgraph::tree_node_at_index(data::ptr tree_elem uint64_t i = index; for (auto node = found; node != nodes.end(); ++node) { - if (found->first != tree_element) { + if (node->first != tree_element) { break; } if (i == 0) { From 4e82cb4b0faf550dd5ffee4ad9ce8fd024dfeb67 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 12:16:13 +1100 Subject: [PATCH 16/74] Implement input methods in Graph --- Sources/ComputeCxx/Attribute/Node/Edge.h | 25 +- .../ComputeCxx/Attribute/Node/IndirectNode.h | 1 + Sources/ComputeCxx/Attribute/Node/Node.h | 24 +- Sources/ComputeCxx/Data/Vector.h | 24 +- Sources/ComputeCxx/Graph/Graph.cpp | 322 +++++++++++++++++- Sources/ComputeCxx/Graph/Graph.h | 33 +- Sources/ComputeCxx/Graph/Trace.h | 4 +- 7 files changed, 399 insertions(+), 34 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/Node/Edge.h b/Sources/ComputeCxx/Attribute/Node/Edge.h index 26064f5..ca22a66 100644 --- a/Sources/ComputeCxx/Attribute/Node/Edge.h +++ b/Sources/ComputeCxx/Attribute/Node/Edge.h @@ -8,23 +8,42 @@ namespace AG { class Node; struct InputEdge { - struct Comparator {}; enum Flags : uint8_t { + Unknown0 = 1 << 0, + Unknown1 = 1 << 1, Unknown2 = 1 << 2, - IsPending = 1 << 3, + IsPending = 1 << 3, // set when node is dirty Unknown4 = 1 << 4, }; + + struct Comparator { + AttributeID attribute; + uint8_t flags_mask; + uint8_t flags; + bool match(InputEdge &input) { return input.value == attribute && (input._flags & flags_mask) == flags; } + }; AttributeID value; uint8_t _flags; + bool is_unknown0() { return _flags & Flags::Unknown0; }; + void set_unknown0(bool value) { _flags = (_flags & ~Flags::Unknown0) | (value ? Flags::Unknown0 : 0); }; + + bool is_unknown1() { return _flags & Flags::Unknown1; }; + void set_unknown1(bool value) { _flags = (_flags & ~Flags::Unknown1) | (value ? Flags::Unknown1 : 0); }; + bool is_unknown2() { return _flags & Flags::Unknown2; }; + void set_unknown2(bool value) { _flags = (_flags & ~Flags::Unknown2) | (value ? Flags::Unknown2 : 0); }; bool is_pending() { return _flags & Flags::IsPending; }; void set_pending(bool value) { _flags = (_flags & ~Flags::IsPending) | (value ? Flags::IsPending : 0); }; - + bool is_unknown4() { return _flags & Flags::Unknown4; }; void set_unknown4(bool value) { _flags = (_flags & ~Flags::Unknown4) | (value ? Flags::Unknown4 : 0); }; + + bool operator<(const InputEdge &other) const noexcept { + return value != other.value ? value < other.value : _flags < other._flags; + }; }; using ConstInputEdgeArrayRef = const ArrayRef; diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index b2a33dc..5524d1a 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -40,6 +40,7 @@ class IndirectNode { bool traverses_graph_contexts() const { return _info.traverses_graph_contexts; }; uint32_t offset() const { return _info.offset; }; + bool has_size() const { return _size != InvalidSize; }; std::optional size() const { return _size != InvalidSize ? std::optional(size_t(_size)) : std::optional(); }; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index f3f9b51..f11ce72 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -29,9 +29,9 @@ class NodeFlags { HasIndirectSelf = 1 << 0, // 0x01 HasIndirectValue = 1 << 1, // 0x02 - Unknown0x04 = 1 << 2, // 0x04 - Unknown0x08 = 1 << 3, // 0x08 - Cacheable = 1 << 4, // 0x10 + InputsTraverseGraphContexts = 1 << 2, // 0x04 + InputsUnsorted = 1 << 3, // 0x08 + Cacheable = 1 << 4, // 0x10 Unknown0x20 = 1 << 5, // 0x20 - initial value Unknown0x40 = 1 << 6, // 0x40 - didn't call mark_changed @@ -59,9 +59,14 @@ class NodeFlags { void set_has_indirect_value(bool value) { _value4 = (_value4 & ~Flags4::HasIndirectValue) | (value ? Flags4::HasIndirectValue : 0); }; - bool value4_unknown0x04() { return _value4 & Flags4::Unknown0x04; }; - void set_value4_unknown0x04(bool value) { - _value4 = (_value4 & ~Flags4::Unknown0x04) | (value ? Flags4::Unknown0x04 : 0); + + bool inputs_traverse_graph_contexts() { return _value4 & Flags4::InputsTraverseGraphContexts; }; + void set_inputs_traverse_graph_contexts(bool value) { + _value4 = (_value4 & ~Flags4::InputsTraverseGraphContexts) | (value ? Flags4::InputsTraverseGraphContexts : 0); + }; + bool inputs_unsorted() { return _value4 & Flags4::InputsUnsorted; }; + void set_inputs_unsorted(bool value) { + _value4 = (_value4 & ~Flags4::InputsUnsorted) | (value ? Flags4::InputsUnsorted : 0); }; bool cacheable() { return _value4 & Flags4::Cacheable; }; void set_cacheable(bool value) { _value4 = (_value4 & ~Flags4::Cacheable) | (value ? Flags4::Cacheable : 0); }; @@ -152,6 +157,13 @@ class Node { NodeFlags &flags() { return _flags; }; + void sort_inputs_if_needed() { + if (_flags.inputs_unsorted()) { + _flags.set_inputs_unsorted(false); + std::sort(_inputs.begin(), _inputs.end()); + } + } + void *get_self(const AttributeType &type); // TODO: inline void update_self(const Graph &graph, void *new_self); void destroy_self(const Graph &graph); diff --git a/Sources/ComputeCxx/Data/Vector.h b/Sources/ComputeCxx/Data/Vector.h index 81d3e92..15dc88c 100644 --- a/Sources/ComputeCxx/Data/Vector.h +++ b/Sources/ComputeCxx/Data/Vector.h @@ -55,7 +55,7 @@ template class vector { } // Element access - + reference operator[](size_type pos) { return _data.get()[pos]; }; const_reference operator[](size_type pos) const { return _data.get()[pos]; }; @@ -96,9 +96,27 @@ template class vector { }; // Modifiers - + // TODO: check order needs to be preserverd here, UpdateStack manages index... + iterator insert(zone *zone, const_iterator pos, const T &value) { + reserve(zone, _metadata.size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (mutable_pos) value_type(value); + _metadata.size += 1; + return end(); + } + + iterator insert(zone *zone, const_iterator pos, T &&value) { + reserve(zone, _metadata.size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (pos) value_type(std::move(value)); + _metadata.size += 1; + return end(); + } + iterator erase(iterator pos) { if (pos == end()) { return end(); @@ -123,7 +141,7 @@ template class vector { void push_back(zone *zone, const T &value) { reserve(zone, _metadata.size + 1); - new (_data.get()[_metadata.size]) value_type(value); + new (&_data.get()[_metadata.size]) value_type(value); _metadata.size += 1; } diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 76e9e97..62e9fb8 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" @@ -977,7 +978,7 @@ void Graph::value_mark_all() { subgraph->add_dirty_flags(node.flags().value3()); } for (auto input_edge : node.inputs()) { - input_edge.set_unknown4(true); + input_edge.set_pending(true); } } } @@ -1047,8 +1048,7 @@ void Graph::propagate_dirty(AttributeID attribute) { dirty_outputs = output_node.outputs(); - // value4_unknown0x04 could be traverses graph contexts for symmetry with IndirectNode - if (output_node.flags().value4_unknown0x04() && !output_node.outputs().empty()) { + if (output_node.flags().inputs_traverse_graph_contexts() && !output_node.outputs().empty()) { if (auto output_subgraph = output.subgraph()) { auto context_id = output_subgraph->graph_context_id(); if (context_id && (attribute.subgraph() == nullptr || @@ -1107,6 +1107,322 @@ void Graph::propagate_dirty(AttributeID attribute) { } } +#pragma mark - Inputs + +void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_attribute, bool evaluate_weak_references, + uint8_t input_flags, const swift::metadata &type, char *arg5, uint32_t index) { + + if ((input_flags >> 1) & 1) { + auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, + InputEdge::Flags::Unknown0 | InputEdge::Flags::Unknown2); + index = index_of_input(*node, comparator); + } + + if (index < 0) { + node.assert_valid(); + if (AttributeID(node).subgraph() == nullptr || AttributeID(node).subgraph()->graph() != this) { + precondition_failure("accessing attribute in a different namespace: %u", node); + } + if (!node->state().is_dirty()) { + auto resolved = input_attribute.resolve( + evaluate_weak_references + ? TraversalOptions::UpdateDependencies | TraversalOptions::EvaluateWeakReferences + : TraversalOptions::UpdateDependencies | TraversalOptions::AssertNotNil); + + if (evaluate_weak_references) { + if (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct()) { + return 0; + } + } + update_attribute(resolved.attribute(), 0); + } + index = add_input(node, input_attribute, evaluate_weak_references, input_flags & 1); + if (index < 0) { + return nullptr; + } + } + + InputEdge &input_edge = node->inputs()[index]; + + input_edge.set_unknown0(input_edge.is_unknown0() || (input_flags & 1) ? true : false); + input_edge.set_unknown4(true); + + OffsetAttributeID resolved = input_edge.value.resolve( + evaluate_weak_references ? TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + TraversalOptions::EvaluateWeakReferences + : TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + TraversalOptions::AssertNotNil); + + if (evaluate_weak_references) { + if (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct()) { + return nullptr; + } + } + + if (!resolved.attribute().to_node().state().is_value_initialized() || + resolved.attribute().to_node().state().is_dirty()) { + if (evaluate_weak_references) { + auto zone_id_before_update = data::table::shared().raw_page_seed(resolved.attribute().page_ptr()); + + update_attribute(resolved.attribute(), 0); + + if (zone_id_before_update & 0xff00000000) { + auto zone_id_after_update = data::table::shared().raw_page_seed(resolved.attribute().page_ptr()); + if (zone_id_after_update & 0xff00000000) { + if ((zone_id_before_update & 0x7fffffff) != (uint32_t)zone_id_after_update) { + return nullptr; + } + } + } + } else { + update_attribute(resolved.attribute(), 0); + } + } + + if (resolved.attribute().to_node().state().is_pending()) { + *arg5 |= 1; + } + if ((input_flags >> 1) & 1 && resolved.attribute().to_node().state().is_self_initialized() && + !type.getValueWitnesses()->isPOD()) { + resolved.attribute().to_node().set_state( + resolved.attribute().to_node().state().with_main_thread_only(true)); // TODO: check + *arg5 |= 2; + } + + if (resolved.offset() == 0) { + auto input_type = attribute_type(resolved.attribute().to_node().type_id()).value_metadata(); + if (&input_type != &type) { + precondition_failure("invalid value type for attribute: %u (saw %s, expected %s)", input_edge.value, + input_type.name(false), type.name(false)); + } + } + if (!resolved.attribute().to_node().state().is_value_initialized()) { + precondition_failure("attribute being read has no value: %u", resolved.attribute()); + } + + void *value = resolved.attribute().to_node().get_value(); + if (resolved.offset() != 0) { + value = (uint8_t *)value + resolved.offset() - 1; + } + return value; +} + +void Graph::input_value_add(data::ptr node, AttributeID input_attribute, uint8_t input_flags) { + input_attribute.to_node_ptr().assert_valid(); + + auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, + InputEdge::Flags::Unknown0 | InputEdge::Flags::Unknown2); + auto index = index_of_input(*node, comparator); + if (index >= 0) { + auto input_edge = node->inputs()[index]; + input_edge.set_unknown4(true); + } +} + +uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_nil, uint8_t input_edge_flags) { + auto resolved = input.resolve(TraversalOptions::EvaluateWeakReferences); + if (resolved.attribute().without_kind() == 0) { + if (allow_nil) { + return -1; + } + precondition_failure("reading from invalid source attribute: %u", input); + } + if (resolved.attribute() == node) { + precondition_failure("cyclic edge: %u -> %u", resolved.attribute(), node); + } + + foreach_trace([&node, &resolved, &input_edge_flags](Trace &trace) { + trace.add_edge(node, resolved.attribute(), input_edge_flags); + }); + + auto subgraph = AttributeID(node).subgraph(); + auto graph_context_id = subgraph ? subgraph->graph_context_id() : 0; + + auto input_subgraph = resolved.attribute().subgraph(); + auto input_graph_context_id = input_subgraph ? input_subgraph->graph_context_id() : 0; + + if (graph_context_id != input_graph_context_id) { + node->flags().set_inputs_traverse_graph_contexts(true); + } + + InputEdge new_input_edge = { + resolved.attribute(), + InputEdge::Flags(input_edge_flags & 5), + }; + if (node->state().is_dirty()) { + new_input_edge.set_pending(true); + } + + uint32_t index = -1; + if (node->flags().inputs_unsorted()) { + node->inputs().push_back(subgraph, new_input_edge); + node->flags().set_inputs_unsorted(true); + index = node->inputs().size() - 1; + } else { + auto pos = std::lower_bound(node->inputs().begin(), node->inputs().end(), new_input_edge); + node->inputs().insert(subgraph, pos, new_input_edge); + index = (uint32_t)(pos - node->inputs().begin()); + } + + add_input_dependencies(node, resolved.attribute()); + + if (node->state().is_updating()) { + reset_update(node); + } + if (node->state().is_dirty()) { + foreach_trace([&node, &index, &input_edge_flags](Trace &trace) { trace.set_edge_pending(node, index, true); }); + } + + return index; +} + +void Graph::remove_input(data::ptr node, uint32_t index) { + remove_input_dependencies(AttributeID(node), node->inputs()[index].value); + remove_input_edge(node, *node, index); +} + +void Graph::remove_all_inputs(data::ptr node) { + for (auto index = node->inputs().size() - 1; index >= 0; --index) { + remove_input(node, index); + } + all_inputs_removed(node); +} + +void Graph::add_input_dependencies(AttributeID attribute, AttributeID input) { + auto resolved = input.resolve(TraversalOptions::SkipMutableReference); + if (resolved.attribute().is_direct()) { + add_output_edge(resolved.attribute().to_node_ptr(), attribute); + } else if (resolved.attribute().is_indirect()) { + add_output_edge(resolved.attribute().to_indirect_node_ptr(), attribute); + } + update_main_refs(attribute); +} + +void Graph::remove_input_dependencies(AttributeID attribute, AttributeID input) { + auto resolved = input.resolve(TraversalOptions::SkipMutableReference); + if (resolved.attribute().is_direct()) { + remove_output_edge(resolved.attribute().to_node_ptr(), attribute); + } else if (resolved.attribute().is_indirect()) { + remove_output_edge(resolved.attribute().to_indirect_node_ptr(), attribute); + } + update_main_refs(attribute); +} + +void Graph::remove_input_edge(data::ptr node_ptr, Node &node, uint32_t index) { + foreach_trace([&node_ptr, &index](Trace &trace) { trace.remove_edge(node_ptr, index); }); + + node.inputs().erase(node.inputs().begin() + index); + if (node.inputs().size() == 0) { + all_inputs_removed(node_ptr); + } + reset_update(node_ptr); +} + +void Graph::remove_removed_input(AttributeID attribute, AttributeID input) { + auto resolved = input.resolve(TraversalOptions::SkipMutableReference | TraversalOptions::EvaluateWeakReferences); + if (resolved.attribute().subgraph()->validation_state() != Subgraph::ValidationState::Invalidated) { + if (resolved.attribute().is_direct()) { + remove_output_edge(resolved.attribute().to_node_ptr(), attribute); + } else if (resolved.attribute().is_indirect()) { + // TODO: to_mutable_indirect_node_ptr + remove_output_edge(resolved.attribute().to_indirect_node_ptr(), attribute); + } + } +} + +bool Graph::any_inputs_changed(data::ptr node, const AttributeID *attributes, uint64_t count) { + for (auto input : node->inputs()) { + input.set_unknown4(true); + if (input.is_pending()) { + const AttributeID *location = (const AttributeID *)wmemchr((const wchar_t *)attributes, input.value, count); + if (location == 0) { + location = attributes + sizeof(AttributeID) * count; + } + if ((location - attributes) / sizeof(AttributeID) == count) { + return true; + } + } + } + return false; +} + +void Graph::all_inputs_removed(data::ptr node) { + node->flags().set_inputs_traverse_graph_contexts(false); + node->flags().set_inputs_unsorted(false); + if (!node->state().is_main_thread_only() && !attribute_type(node->type_id()).main_thread()) { + node->set_state(node->state().with_main_thread_only(false)); + } +} + +uint32_t Graph::index_of_input(Node &node, InputEdge::Comparator comparator) { + if (node.inputs().size() > 8) { + return index_of_input_slow(node, comparator); + } + uint32_t index = 0; + for (auto input : node.inputs()) { + if (comparator.match(input)) { + return index; + } + } + return -1; +} + +uint32_t Graph::index_of_input_slow(Node &node, InputEdge::Comparator comparator) { + node.sort_inputs_if_needed(); + InputEdge *search_start = std::find_if(node.inputs().begin(), node.inputs().end(), [&comparator](InputEdge &input) { + return input.value == comparator.attribute; + }); + uint32_t index = uint32_t(search_start - node.inputs().begin()); + for (auto input = search_start, end = node.inputs().end(); input != end; ++input) { + if (comparator.match(*input)) { + return index; + } + index += 1; + } + return -1; +} + +bool Graph::compare_edge_values(InputEdge input_edge, const AttributeType *type, const void *destination_value, + const void *source_value) { + if (type == nullptr) { + return false; + } + + if (!input_edge.value.is_indirect()) { + return false; + } + + auto indirect_node = input_edge.value.to_indirect_node(); + if (!indirect_node.has_size()) { + return false; + } + + auto size = indirect_node.size().value(); + if (size == 0) { + return true; + } + + auto resolved_offset = input_edge.value.resolve(TraversalOptions::None).offset(); + if (resolved_offset == 0 && type->value_metadata().vw_size() == size) { + return false; + } + + LayoutDescriptor::ComparisonOptions options = + LayoutDescriptor::ComparisonOptions(type->comparison_mode()) | LayoutDescriptor::ComparisonOptions::CopyOnWrite; + + auto layout = type->layout(); + if (layout == 0) { + layout = LayoutDescriptor::fetch(type->value_metadata(), options, 0); + } + if (layout == ValueLayoutEmpty) { + layout = 0; + } + + return LayoutDescriptor::compare_partial(layout, (unsigned char *)destination_value + resolved_offset, + (unsigned char *)source_value + resolved_offset, resolved_offset, size, + options); +} + #pragma mark - Outputs void *Graph::output_value_ref(data::ptr node, const swift::metadata &value_type) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index c5e69f1..bb765ec 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -238,31 +238,30 @@ class Graph { // MARK: Inputs - void input_value_ref(data::ptr node, AttributeID attribute, uint32_t arg3, uint32_t arg4, - const swift::metadata *type, char *arg5, uint64_t arg6); - void input_value_ref_slow(data::ptr node, AttributeID attribute, uint32_t arg3, uint32_t arg4, - const swift::metadata *type, char *arg5, uint64_t arg6); + void *_Nullable input_value_ref_slow(data::ptr node, AttributeID input, bool evaluate_weak_references, + uint8_t input_flags, const swift::metadata &type, char *arg5, uint32_t index); - void add_input(data::ptr node, AttributeID attribute, bool flag, uint32_t option); - void remove_input(data::ptr node, uint64_t arg); - void remove_all_inputs(data::ptr node); + void input_value_add(data::ptr node, AttributeID input, uint8_t input_edge_flags); - bool compare_edge_values(InputEdge input_edge, const AttributeType &type, void const *lhs, void const *rhs); + uint32_t add_input(data::ptr node, AttributeID input, bool allow_nil, uint8_t input_edge_flags); + void remove_input(data::ptr node, uint32_t index); + void remove_all_inputs(data::ptr node); - void input_value_add(data::ptr node, AttributeID attribute, uint32_t arg); + void add_input_dependencies(AttributeID attribute, AttributeID input); + void remove_input_dependencies(AttributeID attribute, AttributeID input); - void any_inputs_changed(data::ptr node, const uint32_t *arg1, uint64_t arg2); - void all_inputs_removed(data::ptr node); + void remove_input_edge(data::ptr node_ptr, Node &node, uint32_t index); - void add_input_dependencies(AttributeID attribute, AttributeID source); - void remove_input_dependencies(AttributeID attribute, AttributeID source); + void remove_removed_input(AttributeID attribute, AttributeID input); - void remove_input_edge(data::ptr node_ptr, Node &node, uint32_t arg); + bool any_inputs_changed(data::ptr node, const AttributeID *attributes, uint64_t count); + void all_inputs_removed(data::ptr node); - void remove_removed_input(AttributeID attribute, AttributeID source); + uint32_t index_of_input(Node &node, InputEdge::Comparator comparator); + uint32_t index_of_input_slow(Node &node, InputEdge::Comparator comparator); - uint32_t index_of_input(data::ptr node, InputEdge::Comparator comparator); - uint32_t index_of_input_slow(data::ptr node, InputEdge::Comparator comparator); + bool compare_edge_values(InputEdge input_edge, const AttributeType *_Nullable type, const void *destination_value, + const void *source_value); // MARK: Outputs diff --git a/Sources/ComputeCxx/Graph/Trace.h b/Sources/ComputeCxx/Graph/Trace.h index 4ff17b2..f2a39a1 100644 --- a/Sources/ComputeCxx/Graph/Trace.h +++ b/Sources/ComputeCxx/Graph/Trace.h @@ -58,9 +58,9 @@ class Trace { virtual void added(data::ptr node); - virtual void add_edge(data::ptr node, AttributeID attribute, uint32_t options); + virtual void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags); virtual void remove_edge(data::ptr node, uint32_t options); - virtual void set_edge_pending(data::ptr node, AttributeID attribute, bool flag); + virtual void set_edge_pending(data::ptr node, uint32_t index, bool flag); virtual void set_dirty(data::ptr node, bool flag); virtual void set_pending(data::ptr node, bool flag); From 58c5f2bf7502764b1e5d3c8c6af558579fcf69e9 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 13:29:08 +1100 Subject: [PATCH 17/74] Implement Graph::mark_changed --- Sources/ComputeCxx/Graph/Graph.cpp | 104 +++++++++++++++++++++++++++++ Sources/ComputeCxx/Graph/Graph.h | 2 +- Sources/ComputeCxx/Graph/Trace.h | 2 +- 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 62e9fb8..4c81280 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1532,6 +1532,110 @@ bool Graph::remove_removed_output(AttributeID attribute, AttributeID output, boo #pragma mark - Marks +void Graph::mark_changed(data::ptr node, AttributeType *_Nullable type, const void *destination_value, + const void *source_value) { + if (!_traces.empty()) { + mark_changed(AttributeID(node), type, destination_value, source_value, 0); + return; + } + + uint32_t output_index = 0; + for (auto output : node->outputs()) { + if (!output.value.is_direct()) { + mark_changed(AttributeID(node), type, destination_value, source_value, output_index); + return; + } + for (auto input : output.value.to_node().inputs()) { + if (input.value.resolve(TraversalOptions::None).attribute() != node) { + continue; + } + if (input.is_pending()) { + continue; + } + if (!input.value.is_direct()) { + if (compare_edge_values(input, type, destination_value, source_value)) { + continue; + } + } + input.set_pending(true); + } + output_index += 1; + } + + _update_stack_frame_counter += 1; +} + +void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, const void *destination_value, + const void *source_value, uint32_t start_output_index) { + if (attribute.is_nil()) { + return; + } + + // TODO: combine logic with propagate_dirty + struct Frame { + ConstOutputEdgeArrayRef outputs; + AttributeID attribute; + }; + + char stack_buffer[0x2000] = {}; + auto heap = util::Heap(stack_buffer, sizeof(stack_buffer), 0); + auto frames = ForwardList(&heap); + + data::vector initial_outputs = {}; + if (attribute.is_direct()) { + initial_outputs = attribute.to_node().outputs(); + } else if (attribute.is_indirect()) { + // TODO: how to make sure indirect is mutable? + initial_outputs = attribute.to_indirect_node().to_mutable().outputs(); + } else { + return; + } + frames.emplace_front(ConstOutputEdgeArrayRef(&initial_outputs.front(), initial_outputs.size()), attribute); + + while (!frames.empty()) { + auto outputs = frames.front().outputs; + auto attribute = frames.front().attribute; + frames.pop_front(); + + for (auto output_edge = outputs.begin() + start_output_index, end = outputs.end(); output_edge != end; + ++output_edge) { + + if (output_edge->value.is_direct()) { + auto inputs = output_edge->value.to_node().inputs(); + for (uint32_t input_index = 0, num_inputs = inputs.size(); input_index < num_inputs; ++input_index) { + auto input_edge = inputs[input_index]; + if (input_edge.value.resolve(TraversalOptions::SkipMutableReference).attribute() != attribute) { + continue; + } + if (input_edge.is_pending()) { + continue; + } + if (!input_edge.value.is_direct()) { + if (compare_edge_values(input_edge, type, destination_value, source_value)) { + continue; + } + } + foreach_trace([&output_edge, &input_index](Trace &trace) { + trace.set_edge_pending(output_edge->value.to_node_ptr(), input_index, true); + }); + input_edge.set_pending(true); + } + } else if (output_edge->value.is_indirect()) { + if (output_edge->value.to_indirect_node().to_mutable().dependency() != attribute) { + auto mutable_node = output_edge->value.to_indirect_node().to_mutable(); + frames.emplace_front( + ConstOutputEdgeArrayRef(&mutable_node.outputs().front(), mutable_node.outputs().size()), + output_edge->value); + } + } + } + + start_output_index = 0; + } + + _update_stack_frame_counter += 1; +} + void Graph::mark_pending(data::ptr node_ptr, Node *node) { if (!node->state().is_pending()) { foreach_trace([&node_ptr](Trace &trace) { trace.set_pending(node_ptr, true); }); diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index bb765ec..16612db 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -282,7 +282,7 @@ class Graph { void mark_changed(data::ptr node, AttributeType *_Nullable type, const void *_Nullable destination_value, const void *_Nullable source_value); void mark_changed(AttributeID attribute, AttributeType *_Nullable type, const void *_Nullable destination_value, - const void *_Nullable source_value, uint64_t option); + const void *_Nullable source_value, uint32_t start_output_index); void mark_pending(data::ptr node_ptr, Node *node); diff --git a/Sources/ComputeCxx/Graph/Trace.h b/Sources/ComputeCxx/Graph/Trace.h index f2a39a1..78eb861 100644 --- a/Sources/ComputeCxx/Graph/Trace.h +++ b/Sources/ComputeCxx/Graph/Trace.h @@ -60,7 +60,7 @@ class Trace { virtual void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags); virtual void remove_edge(data::ptr node, uint32_t options); - virtual void set_edge_pending(data::ptr node, uint32_t index, bool flag); + virtual void set_edge_pending(data::ptr node, uint32_t input_index, bool pending); virtual void set_dirty(data::ptr node, bool flag); virtual void set_pending(data::ptr node, bool flag); From c7a10319887afca5b9fa673224ab3748153a7873 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 16:00:16 +1100 Subject: [PATCH 18/74] Fix linker errors --- Sources/ComputeCxx/Attribute/AttributeID.h | 5 + .../ComputeCxx/Attribute/Node/IndirectNode.h | 15 +- Sources/ComputeCxx/Attribute/Node/Node.h | 16 +- .../Containers/IndirectPointerVector.cpp | 111 ------------- .../Containers/IndirectPointerVector.h | 149 +++++++++++++++--- Sources/ComputeCxx/Data/Table.cpp | 12 +- Sources/ComputeCxx/Data/Table.h | 2 +- Sources/ComputeCxx/Encoder/Encoder.cpp | 17 ++ Sources/ComputeCxx/Graph/AGGraph.cpp | 5 + Sources/ComputeCxx/Graph/Context.cpp | 15 ++ Sources/ComputeCxx/Graph/Context.h | 10 +- Sources/ComputeCxx/Graph/Graph.cpp | 84 ++++++---- Sources/ComputeCxx/Graph/Graph.h | 86 ++++++---- Sources/ComputeCxx/Graph/UpdateStack.cpp | 14 +- Sources/ComputeCxx/Graph/UpdateStack.h | 2 +- Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 6 + Sources/ComputeCxx/Subgraph/Subgraph.cpp | 19 +-- 17 files changed, 340 insertions(+), 228 deletions(-) delete mode 100644 Sources/ComputeCxx/Containers/IndirectPointerVector.cpp create mode 100644 Sources/ComputeCxx/Encoder/Encoder.cpp create mode 100644 Sources/ComputeCxx/Graph/AGGraph.cpp create mode 100644 Sources/ComputeCxx/Graph/Context.cpp create mode 100644 Sources/ComputeCxx/Subgraph/AGSubgraph.cpp diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index 872407e..f600c5d 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -16,6 +16,7 @@ namespace AG { class Subgraph; class Node; class IndirectNode; +class MutableIndirectNode; class OffsetAttributeID; class RelativeAttributeID; @@ -93,6 +94,10 @@ class AttributeID { assert(is_indirect()); return data::ptr(_value & ~KindMask); }; + data::ptr to_mutable_indirect_node_ptr() const { + assert(is_indirect()); // XXX: no assertion that it is mutable + return data::ptr(_value & ~KindMask); + }; Subgraph *_Nullable subgraph() const { return reinterpret_cast(page_ptr()->zone); } diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index 5524d1a..64e6a1b 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -29,7 +29,14 @@ class IndirectNode { uint16_t _relative_offset; // could be relative offset, see Subgraph::insert_attribute public: - IndirectNode(WeakAttributeID source, bool traverses_graph_contexts, uint32_t offset, uint16_t size); + IndirectNode(WeakAttributeID source, bool traverses_graph_contexts, uint32_t offset, uint16_t size) + : _source(source) { + _info.is_mutable = false; + _info.traverses_graph_contexts = traverses_graph_contexts; + _info.offset = offset; + _size = size; + _relative_offset = 0; + } const WeakAttributeID &source() const { return _source; }; @@ -62,7 +69,11 @@ class MutableIndirectNode : public IndirectNode { public: MutableIndirectNode(WeakAttributeID source, bool traverses_graph_contexts, uint32_t offset, uint16_t size, - WeakAttributeID initial_source, uint32_t initial_offset); + WeakAttributeID initial_source, uint32_t initial_offset) + : IndirectNode(source, traverses_graph_contexts, offset, size), _dependency(0), _initial_source(initial_source), + _initial_offset(initial_offset){ + + }; const AttributeID &dependency() const { return _dependency; }; void set_dependency(const AttributeID &dependency) { _dependency = dependency; }; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index f11ce72..ba28877 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -18,19 +18,13 @@ class Graph; class NodeFlags { public: - enum Flags1 : uint8_t { - // TODO: check these - IndirectAttribute = 1 << 0, // 0x1 - NilAttribute = 1 << 1, // 0x2 - }; - enum Flags2 : uint8_t {}; enum Flags3 : uint8_t {}; enum Flags4 : uint8_t { HasIndirectSelf = 1 << 0, // 0x01 HasIndirectValue = 1 << 1, // 0x02 InputsTraverseGraphContexts = 1 << 2, // 0x04 - InputsUnsorted = 1 << 3, // 0x08 + InputsUnsorted = 1 << 3, // 0x08 Cacheable = 1 << 4, // 0x10 Unknown0x20 = 1 << 5, // 0x20 - initial value @@ -43,6 +37,8 @@ class NodeFlags { uint8_t _value4; public: + NodeFlags(uint8_t value4 = 0) : _value4(value4){}; + uint16_t relative_offset() const { return _relative_offset; }; void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; @@ -149,7 +145,11 @@ class Node { data::vector _outputs; public: - Node(State state, uint32_t type_id, uint8_t flags4); + Node(State state, uint32_t type_id, uint8_t flags4) { + _info.state = state.data(); + _info.type_id = type_id; + _flags = NodeFlags(flags4); + }; State state() { return State(_info.state); }; void set_state(State state) { _info.state = state.data(); }; diff --git a/Sources/ComputeCxx/Containers/IndirectPointerVector.cpp b/Sources/ComputeCxx/Containers/IndirectPointerVector.cpp deleted file mode 100644 index ebf9c4f..0000000 --- a/Sources/ComputeCxx/Containers/IndirectPointerVector.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "IndirectPointerVector.h" - -namespace AG { - -template - requires std::unsigned_integral -indirect_pointer_vector::~indirect_pointer_vector() { - clear(); -}; - -template - requires std::unsigned_integral -void indirect_pointer_vector::clear() { - if (this->has_vector()) { - vector_type *vector = this->get_vector(); - if (vector != nullptr) { - delete vector; - } - } - _data = nullptr; -} - -template - requires std::unsigned_integral -indirect_pointer_vector::iterator indirect_pointer_vector::erase(iterator pos) { - if (pos == end()) { - return end(); - } - return erase(pos, pos + 1); -} - -template - requires std::unsigned_integral -indirect_pointer_vector::iterator indirect_pointer_vector::erase(iterator first, - iterator last) { - auto count = last - first; - if (count == 0) { - return last; - } - if (has_vector()) { - return get_vector()->erase(first, last); - } else { - assert(count <= 1); - if (count == 1 && first == begin()) { - _data = nullptr; - } - return end(); - } -} - -template - requires std::unsigned_integral -void indirect_pointer_vector::push_back(const T &value) { - if (this->has_vector()) { - this->get_vector()->push_back(value); - } else { - if (_data == nullptr) { - _data = value; - } else { - vector_type *vector = new vector_type(); - vector->push_back(this->get_element()); - vector->push_back(value); - _data = vector | 1; - } - } -} - -template - requires std::unsigned_integral -void indirect_pointer_vector::push_back(T &&value) { - if (this->has_vector()) { - this->get_vector()->push_back(value); - } else { - if (_data == nullptr) { - _data = std::move(value); - } else { - vector_type *vector = new vector_type(); - vector->push_back(this->get_element()); - vector->push_back(value); - _data = vector | 1; - } - } -} - -template - requires std::unsigned_integral -void indirect_pointer_vector::resize(size_type count) { - if (this->has_element()) { - if (count == 1) { - if (_data == nullptr) { - _data = NullElement; - } - return; - } - if (count == 0) { - if (_data) { - delete _data; - } - _data = nullptr; - return; - } - // put single element into vector - vector_type *vector = new vector_type(); - vector->push_back(this->get_element()); - _data = vector | 1; - } - - this->get_vector()->resize(count); -} - -} // namespace AG diff --git a/Sources/ComputeCxx/Containers/IndirectPointerVector.h b/Sources/ComputeCxx/Containers/IndirectPointerVector.h index a7c811d..4d5c287 100644 --- a/Sources/ComputeCxx/Containers/IndirectPointerVector.h +++ b/Sources/ComputeCxx/Containers/IndirectPointerVector.h @@ -11,20 +11,18 @@ CF_ASSUME_NONNULL_BEGIN namespace AG { /// A vector that efficiently stores a single element as a pointer or stores multiple elements as a vector. -template - requires std::unsigned_integral +template + requires std::unsigned_integral<_size_type> class indirect_pointer_vector { public: - using value_type = T; - using pointer = value_type *_Nonnull; - using const_pointer = const value_type *_Nonnull; + using value_type = T *_Nonnull; using reference = value_type &; using const_reference = const value_type &; - using vector_type = vector; - using iterator = pointer *_Nonnull; - using const_iterator = const pointer *_Nonnull; + using iterator = value_type *_Nonnull; + using const_iterator = const value_type *_Nonnull; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; + using size_type = _size_type; private: uintptr_t _data; @@ -33,6 +31,8 @@ class indirect_pointer_vector { NullElement = 0x2, }; + using vector_type = vector; + bool has_vector() const { return (_data & TagMask) == 1; }; vector_type &get_vector() { assert(has_vector()); @@ -44,36 +44,40 @@ class indirect_pointer_vector { }; public: - indirect_pointer_vector(); + indirect_pointer_vector() = default; ~indirect_pointer_vector(); // Element access - reference front() { return has_vector() ? *get_vector().front() : *reinterpret_cast(_data); }; + reference front() { return has_vector() ? get_vector().front() : reinterpret_cast(_data); }; const_reference front() const { - return has_vector() ? *get_vector().front() : *reinterpret_cast(_data); + if (has_vector()) { + return get_vector().front(); + } else { + return reinterpret_cast(_data); + } }; // Iterators - iterator begin() { return has_vector() ? get_vector().begin() : reinterpret_cast(&_data); }; + iterator begin() { return has_vector() ? get_vector().begin() : reinterpret_cast(&_data); }; iterator end() { if (has_vector()) { return get_vector().end(); } else { - size_type size = reinterpret_cast(_data) == nullptr ? 0 : 1; - return &reinterpret_cast(&_data)[size]; + size_type size = _data == 0 ? 0 : 1; + return &reinterpret_cast(&_data)[size]; } }; const_iterator cbegin() const { - return has_vector() ? get_vector().cbegin() : reinterpret_cast(&_data); + return has_vector() ? get_vector().cbegin() : reinterpret_cast(&_data); }; const_iterator cend() const { if (has_vector()) { return get_vector().cend(); } else { - size_type size = reinterpret_cast(_data) == nullptr ? 0 : 1; - return &reinterpret_cast(&_data)[(T *)_data != nullptr ? 1 : 0]; + size_type size = _data == 0 ? 0 : 1; + return &reinterpret_cast(&_data)[size]; } }; const_iterator begin() const { return cbegin(); }; @@ -88,8 +92,8 @@ class indirect_pointer_vector { // Capacity - bool empty() const { return has_vector() ? get_vector().empty() : reinterpret_cast(_data) == nullptr; }; - size_type size() const { return has_vector() ? get_vector()->size() : _data == nullptr ? 0 : 1; }; + bool empty() const { return has_vector() ? get_vector().empty() : _data == 0; }; + size_type size() const { return has_vector() ? get_vector().size() : _data == 0 ? 0 : 1; }; // Modifiers @@ -98,12 +102,115 @@ class indirect_pointer_vector { iterator erase(iterator pos); iterator erase(iterator first, iterator last); - void push_back(const T &value); - void push_back(T &&value); + void push_back(const value_type &value); + void push_back(value_type &&value); void resize(size_type count); }; +template + requires std::unsigned_integral +indirect_pointer_vector::~indirect_pointer_vector() { + clear(); +}; + +template + requires std::unsigned_integral +void indirect_pointer_vector::clear() { + if (has_vector()) { + vector_type *vector_pointer = reinterpret_cast(_data & ~TagMask); + if (vector_pointer != 0) { + delete vector_pointer; + } + } + _data = 0; +} + +template + requires std::unsigned_integral +indirect_pointer_vector::iterator indirect_pointer_vector::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +indirect_pointer_vector::iterator indirect_pointer_vector::erase(iterator first, + iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + if (has_vector()) { + return get_vector().erase(first, last); + } else { + assert(count <= 1); + if (count == 1 && first == begin()) { + _data = 0; + } + return end(); + } +} + +template + requires std::unsigned_integral +void indirect_pointer_vector::push_back(const value_type &value) { + if (has_vector()) { + get_vector()->push_back(value); + } else { + if (_data == 0) { + _data = value; + } else { + vector_type *vector = new vector_type(); + vector->push_back(this->get_element()); + vector->push_back(value); + _data = vector | 1; + } + } +} + +template + requires std::unsigned_integral +void indirect_pointer_vector::push_back(value_type &&value) { + if (has_vector()) { + get_vector().push_back(value); + } else { + if (_data == 0) { + _data = reinterpret_cast(std::move(value)); + } else { + vector_type *vector = new vector_type(); + vector->push_back(reinterpret_cast(std::move(_data))); + vector->push_back(value); + _data = (uintptr_t)vector | 1; + } + } +} + +template + requires std::unsigned_integral +void indirect_pointer_vector::resize(size_type count) { + if (has_vector()) { + get_vector().resize(count); + return; + } + if (count == 1) { + if (_data == 0) { + _data = NullElement; + } + return; + } + if (count == 0) { + _data = 0; + return; + } + // put single element into vector + vector_type *vector = new vector_type(); + vector->push_back(reinterpret_cast(std::move(_data))); + _data = vector | 1; +} + } // namespace AG CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Data/Table.cpp b/Sources/ComputeCxx/Data/Table.cpp index e5e72fd..81873dd 100644 --- a/Sources/ComputeCxx/Data/Table.cpp +++ b/Sources/ComputeCxx/Data/Table.cpp @@ -246,7 +246,7 @@ void table::make_pages_reusable(uint32_t page_index, bool reusable) { mprotect(mapped_pages_address, mapped_pages_size, protection); } - _num_reusable_pages += reusable ? mapped_pages_size : -mapped_pages_size; + _num_reusable_bytes += reusable ? mapped_pages_size : -mapped_pages_size; } uint64_t table::raw_page_seed(ptr page) { @@ -268,5 +268,15 @@ uint64_t table::raw_page_seed(ptr page) { return result; } +#pragma mark - Printing + +void table::print() { + lock(); + fprintf(stdout, "data::table %p:\n %.2fKB allocated, %.2fKB used, %.2fKB reusable.\n", this, + (_ptr_max_offset - page_size) / 1024.0, (_num_used_pages * page_size) / 1024.0, + _num_reusable_bytes / 1024.0); + unlock(); +} + } // namespace data } // namespace AG diff --git a/Sources/ComputeCxx/Data/Table.h b/Sources/ComputeCxx/Data/Table.h index a68ef51..b1b81cd 100644 --- a/Sources/ComputeCxx/Data/Table.h +++ b/Sources/ComputeCxx/Data/Table.h @@ -37,7 +37,7 @@ class table { uint32_t _ptr_max_offset; uint32_t _num_used_pages = 0; - uint32_t _num_reusable_pages = 0; + uint32_t _num_reusable_bytes = 0; uint32_t _map_search_start = 0; uint32_t _num_zones = 0; diff --git a/Sources/ComputeCxx/Encoder/Encoder.cpp b/Sources/ComputeCxx/Encoder/Encoder.cpp new file mode 100644 index 0000000..2a80337 --- /dev/null +++ b/Sources/ComputeCxx/Encoder/Encoder.cpp @@ -0,0 +1,17 @@ +#include "Encoder.h" + +namespace AG { + +void Encoder::encode_varint(uint64_t value) { + // TODO: not implemented +} + +void Encoder::begin_length_delimited() { + // TODO: not implemented +} + +void Encoder::end_length_delimited() { + // TODO: not implemented +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp new file mode 100644 index 0000000..c02a591 --- /dev/null +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -0,0 +1,5 @@ +#include "AGGraph.h" + +void AGGraphSetOutputValue(void *value, AGTypeID type) { + // TODO: not implemented +} diff --git a/Sources/ComputeCxx/Graph/Context.cpp b/Sources/ComputeCxx/Graph/Context.cpp new file mode 100644 index 0000000..50bc258 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Context.cpp @@ -0,0 +1,15 @@ +#include "Context.h" + +#include "Attribute/AttributeID.h" + +namespace AG { + +void Graph::Context::call_invalidation(AttributeID attribute) { + // TODO: not implemented +} + +void Graph::Context::call_update() { + // TODO: not implemented +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h index e16a404..1e1ee7e 100644 --- a/Sources/ComputeCxx/Graph/Context.h +++ b/Sources/ComputeCxx/Graph/Context.h @@ -2,6 +2,8 @@ #include +#include "Graph.h" + CF_ASSUME_NONNULL_BEGIN namespace AG { @@ -9,15 +11,17 @@ namespace AG { class Graph::Context { private: Graph *_graph; + void *_field_0x08; + uint64_t _unique_id; - uint32_t _value_ref_counter; + uint64_t _graph_version; public: Graph &graph() const { return *_graph; }; - uint64_t unique_id(); + uint64_t unique_id() { return _unique_id; }; - uint32_t value_ref_counter() { return _value_ref_counter; }; + uint64_t graph_version() { return _graph_version; }; void call_invalidation(AttributeID attribute); void call_update(); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 4c81280..59f3881 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -22,17 +22,19 @@ namespace AG { +pthread_key_t Graph::_current_update_key = 0; + #pragma mark - Invalidating Graph::without_invalidating::without_invalidating(Graph *graph) { _graph = graph; - _graph_was_deferring_invalidation = graph->_deferring_invalidation; - graph->_deferring_invalidation = true; + _graph_old_batch_invalidate_subgraphs = graph->_batch_invalidate_subgraphs; + graph->_batch_invalidate_subgraphs = true; } Graph::without_invalidating::~without_invalidating() { - if (_graph && _graph_was_deferring_invalidation == false) { - _graph->_deferring_invalidation = false; + if (_graph && _graph_old_batch_invalidate_subgraphs == false) { + _graph->_batch_invalidate_subgraphs = false; _graph->invalidate_subgraphs(); } } @@ -70,7 +72,7 @@ void Graph::remove_subgraph(Subgraph &subgraph) { } void Graph::invalidate_subgraphs() { - if (_deferring_invalidation) { + if (_batch_invalidate_subgraphs) { return; } @@ -304,7 +306,7 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void } _num_nodes += 1; - _num_node_values += 1; + _num_nodes_created += 1; if (type.self_metadata().vw_size() != 0) { void *self_dest = self; @@ -754,11 +756,11 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, if (old_dependency != dependency) { AttributeID indirect_attribute = AttributeID(attribute).with_kind(AttributeID::Kind::Indirect); if (old_dependency) { - remove_output_edge((data::ptr)old_dependency, indirect_attribute); + remove_output_edge(old_dependency.to_node_ptr(), indirect_attribute); } attribute->to_mutable().set_dependency(dependency); if (dependency) { - add_output_edge((data::ptr)dependency, indirect_attribute); + add_output_edge(dependency.to_node_ptr(), indirect_attribute); if (dependency.to_node().state().is_dirty()) { propagate_dirty(indirect_attribute); } @@ -771,7 +773,7 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, void *Graph::value_ref(AttributeID attribute, bool evaluate_weak_references, const swift::metadata &value_type, bool *did_update_out) { - _counter_0x1d8 += 1; + _version += 1; OffsetAttributeID resolved = attribute.resolve( TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | @@ -786,15 +788,7 @@ void *Graph::value_ref(AttributeID attribute, bool evaluate_weak_references, con if (!type.use_graph_as_initial_value()) { - bool graph_is_updating = false; - for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { - if (update.get()->graph() == this) { - graph_is_updating = true; - } - } - if (!graph_is_updating) { - _counter_0x1b8 += 1; - } + increment_update_count_if_needed(); uint64_t old_page_seed = 0; if (evaluate_weak_references) { @@ -1054,7 +1048,7 @@ void Graph::propagate_dirty(AttributeID attribute) { if (context_id && (attribute.subgraph() == nullptr || context_id != attribute.subgraph()->graph_context_id())) { if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { - if (context->value_ref_counter() != context->graph()._counter_0x1d8) { + if (context->graph_version() != context->graph()._version) { context->call_invalidation(attribute); } } @@ -1075,7 +1069,7 @@ void Graph::propagate_dirty(AttributeID attribute) { if (context_id && (attribute.subgraph() == nullptr || context_id != attribute.subgraph()->graph_context_id())) { if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { - if (context->value_ref_counter() != context->graph()._counter_0x1d8) { + if (context->graph_version() != context->graph()._version) { context->call_invalidation(attribute); } } @@ -1293,7 +1287,8 @@ void Graph::add_input_dependencies(AttributeID attribute, AttributeID input) { if (resolved.attribute().is_direct()) { add_output_edge(resolved.attribute().to_node_ptr(), attribute); } else if (resolved.attribute().is_indirect()) { - add_output_edge(resolved.attribute().to_indirect_node_ptr(), attribute); + assert(resolved.attribute().to_indirect_node().is_mutable()); + add_output_edge(resolved.attribute().to_mutable_indirect_node_ptr(), attribute); } update_main_refs(attribute); } @@ -1303,7 +1298,8 @@ void Graph::remove_input_dependencies(AttributeID attribute, AttributeID input) if (resolved.attribute().is_direct()) { remove_output_edge(resolved.attribute().to_node_ptr(), attribute); } else if (resolved.attribute().is_indirect()) { - remove_output_edge(resolved.attribute().to_indirect_node_ptr(), attribute); + assert(resolved.attribute().to_indirect_node().is_mutable()); + remove_output_edge(resolved.attribute().to_mutable_indirect_node_ptr(), attribute); } update_main_refs(attribute); } @@ -1324,8 +1320,8 @@ void Graph::remove_removed_input(AttributeID attribute, AttributeID input) { if (resolved.attribute().is_direct()) { remove_output_edge(resolved.attribute().to_node_ptr(), attribute); } else if (resolved.attribute().is_indirect()) { - // TODO: to_mutable_indirect_node_ptr - remove_output_edge(resolved.attribute().to_indirect_node_ptr(), attribute); + assert(resolved.attribute().to_indirect_node().is_mutable()); + remove_output_edge(resolved.attribute().to_mutable_indirect_node_ptr(), attribute); } } } @@ -1444,6 +1440,10 @@ void *Graph::output_value_ref(data::ptr node, const swift::metadata &value return node->get_value(); } +template <> void Graph::add_output_edge(data::ptr node, AttributeID output) { + node->outputs().push_back(node.page_ptr()->zone, OutputEdge(output)); +} + template <> void Graph::add_output_edge(data::ptr node, AttributeID output) { node->outputs().push_back(node.page_ptr()->zone, OutputEdge(output)); } @@ -1562,7 +1562,7 @@ void Graph::mark_changed(data::ptr node, AttributeType *_Nullable type, co output_index += 1; } - _update_stack_frame_counter += 1; + _mark_changed_count += 1; } void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, const void *destination_value, @@ -1632,8 +1632,8 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c start_output_index = 0; } - - _update_stack_frame_counter += 1; + + _mark_changed_count += 1; } void Graph::mark_pending(data::ptr node_ptr, Node *node) { @@ -1718,6 +1718,20 @@ uint32_t Graph::intern_type(swift::metadata *metadata, ClosureFunctionVP return type_id; } +#pragma mark - Encoding + +void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { + // TODO: not implemented +} + +void Graph::encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_node) { + // TODO: not implemented +} + +void Graph::encode_tree(Encoder &encoder, data::ptr tree) { + // TODO: not implemented +} + #pragma mark - Tracing void Graph::add_trace(Trace *_Nullable trace) { @@ -1743,6 +1757,18 @@ void Graph::trace_assertion_failure(bool all_stop_tracing, const char *format, . #pragma mark - Printing +void Graph::print() { + // TODO: not implemented +} + +void Graph::print_attribute(data::ptr node) { + // TODO: not implemented +} + +void Graph::print_cycle(data::ptr node) { + // TODO: not implemented +} + void Graph::print_data() { data::table::shared().print(); data::zone::print_header(); @@ -1751,4 +1777,8 @@ void Graph::print_data() { } } +void Graph::print_stack() { + // TODO: not implemented +} + } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 16612db..7560307 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -86,49 +86,60 @@ class Graph { private: static pthread_key_t _current_update_key; - Graph *_prev; + // Graph + Graph *_previous; Graph *_next; util::Heap _heap; + // Attribute types util::UntypedTable _type_ids_by_metadata; vector _types; + // Contexts util::Table _contexts_by_id; + // Traces vector _traces; + // Main thread handler MainHandler _Nullable _main_handler; void *_Nullable _main_handler_context; - size_t _allocated_node_values_size = 0; + // Metrics + uint64_t _num_nodes; + uint64_t _num_nodes_created; + uint64_t _num_subgraphs; + uint64_t _num_subgraphs_created; + size_t _num_node_value_bytes = 0; + + // Profile + + // Trace + // Tree std::unique_ptr> _tree_data_elements_by_subgraph; KeyTable *_Nullable _keys; - // TODO: check field offets + // Subgraphs vector _subgraphs; vector _subgraphs_with_cached_nodes; - vector _invalidated_subgraphs; // TODO: check is 2 stack length - bool _deferring_invalidation; // used to batch invalidate subgraphs + vector _invalidated_subgraphs; + bool _batch_invalidate_subgraphs; - // TODO: check field offets - uint64_t _num_nodes; // probably this, not sure - uint64_t _num_node_values; // probably this, not sure - uint64_t _num_subgraphs; - uint64_t _num_subgraphs_created; - - bool _needs_update; // 0x199 + // Context + bool _needs_update; + uint32_t num_contexts; + // Updates pthread_t _current_update_thread; - uint64_t _deadline; - uint64_t _counter_0x1b8; + // Counters + uint64_t _update_count; // number of times this graph started updating uint64_t _update_attribute_count; uint64_t _update_attribute_on_main_count; - uint64_t - _update_stack_frame_counter; // used to detect changes between calling Trace.begin_update/end_update // 0x1d0 - uint64_t _counter_0x1d8; + uint64_t _mark_changed_count; + uint64_t _version; public: // MARK: Context @@ -141,14 +152,14 @@ class Graph { class without_invalidating { private: Graph *_graph; - bool _graph_was_deferring_invalidation; + bool _graph_old_batch_invalidate_subgraphs; public: without_invalidating(Graph *graph); ~without_invalidating(); }; - vector &subgraphs(); + vector &subgraphs() { return _subgraphs; }; void add_subgraph(Subgraph &subgraph); // called from constructor of Subgraph void remove_subgraph(Subgraph &subgraph); @@ -156,14 +167,12 @@ class Graph { void will_invalidate_subgraph(Subgraph &subgraph) { _invalidated_subgraphs.push_back(&subgraph); }; void invalidate_subgraphs(); - bool deferring_invalidation() { return _deferring_invalidation; }; - void set_deferring_invalidation(bool value) { _deferring_invalidation = value; }; + bool batch_invalidate_subgraphs() { return _batch_invalidate_subgraphs; }; + void set_batch_invalidate_subgraphs(bool value) { _batch_invalidate_subgraphs = value; }; void remove_subgraphs_with_cached_node(Subgraph *subgraph); // overload with iter? void add_subgraphs_with_cached_node(Subgraph *subgraph) { _subgraphs_with_cached_nodes.push_back(subgraph); } - void increment_counter_0x1b8() { _counter_0x1b8 += 1; }; - // MARK: Updates static TaggedPointer current_update(); @@ -185,6 +194,12 @@ class Graph { bool passed_deadline(); bool passed_deadline_slow(); + void increment_update_count_if_needed() { + if (!thread_is_updating()) { + _update_count += 1; + } + }; + // MARK: Attributes const AttributeType &attribute_type(uint32_t type_id) const; @@ -200,14 +215,14 @@ class Graph { void remove_node(data::ptr node); - void did_allocate_node_value(size_t size) { _allocated_node_values_size += size; }; - void did_destroy_node_value(size_t size) { _allocated_node_values_size -= size; }; - - void did_destroy_node(); // decrement counter 0x100 - bool breadth_first_search(AttributeID attribute, SearchOptions options, ClosureFunctionAB predicate) const; + void did_allocate_node_value(size_t size) { _num_node_value_bytes += size; }; + void did_destroy_node_value(size_t size) { _num_node_value_bytes -= size; }; + + void did_destroy_node() { _num_nodes -= 1; }; + // MARK: Indirect attributes void add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, std::optional size, @@ -268,6 +283,7 @@ class Graph { void *output_value_ref(data::ptr node, const swift::metadata &type); template void add_output_edge(data::ptr node, AttributeID output); + template <> void add_output_edge(data::ptr node, AttributeID output); template <> void add_output_edge(data::ptr node, AttributeID output); template void remove_output_edge(data::ptr node, AttributeID attribute); @@ -304,6 +320,13 @@ class Graph { uint32_t intern_type(swift::metadata *metadata, ClosureFunctionVP make_type); + // MARK: Encoding + + void encode_node(Encoder &encoder, const Node &node, bool flag); + void encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_node); + + void encode_tree(Encoder &encoder, data::ptr tree); + // MARK: Tracing void prepare_trace(Trace &trace); @@ -348,13 +371,6 @@ class Graph { static void all_mark_profile(const char *event_name); static void all_reset_profile(); - // MARK: Encoding - - void encode_node(Encoder &encoder, const Node &node, bool flag); - void encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_node); - - void encode_tree(Encoder &encoder, data::ptr tree); - // MARK: Description void description(CFDictionaryRef options); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 76f96d7..5221fa5 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -23,9 +23,9 @@ Graph::UpdateStack::UpdateStack(Graph *graph, uint8_t options) { graph->_current_update_thread = _thread; - if (graph->_deferring_invalidation == false) { - graph->_deferring_invalidation = true; - _options &= Option::WasNotDeferringInvalidation; + if (graph->_batch_invalidate_subgraphs == false) { + graph->_batch_invalidate_subgraphs = true; + _options &= Option::InvalidateSubgraphs; } Graph::set_current_update(TaggedPointer(this, options >> 3 & 1)); @@ -43,8 +43,8 @@ Graph::UpdateStack::~UpdateStack() { _graph->_current_update_thread = _previous_thread; Graph::set_current_update(_previous); - if (_options & Option::WasNotDeferringInvalidation) { - _graph->_deferring_invalidation = false; + if (_options & Option::InvalidateSubgraphs) { + _graph->_batch_invalidate_subgraphs = false; } } @@ -259,7 +259,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { } _graph->foreach_trace([&frame](Trace &trace) { trace.begin_update(frame.attribute); }); - uint64_t update_counter = _graph->_update_stack_frame_counter; + uint64_t mark_changed_count = _graph->_mark_changed_count; const AttributeType &type = _graph->attribute_type(node->type_id()); void *self = node->get_self(type); @@ -277,7 +277,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { AGGraphSetOutputValue(&value, AGTypeID(&type.value_metadata())); } - changed = _graph->_update_stack_frame_counter != update_counter; + changed = _graph->_mark_changed_count != mark_changed_count; _graph->foreach_trace([&frame, &changed](Trace &trace) { trace.end_update(frame.attribute, changed); }); } diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index d29f11f..4291e09 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -11,7 +11,7 @@ namespace AG { class Graph::UpdateStack { public: enum Option : uint8_t { - WasNotDeferringInvalidation = 1 << 4, + InvalidateSubgraphs = 1 << 4, }; private: diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp new file mode 100644 index 0000000..aeb2006 --- /dev/null +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -0,0 +1,6 @@ +#include "AGSubgraph.h" + +bool AGSubgraphShouldRecordTree() { + // TODO: not implemented + return false; +} diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 882c300..0f5abe3 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -104,7 +104,7 @@ void Subgraph::invalidate_and_delete_(bool delete_subgraph) { _parents.clear(); // Check Graph::invalidate_subgraphs - if (_graph->deferring_invalidation() == false && _graph->main_handler() == nullptr) { + if (_graph->batch_invalidate_subgraphs() == false && _graph->main_handler() == nullptr) { invalidate_now(*_graph); _graph->invalidate_subgraphs(); return; @@ -124,7 +124,7 @@ void Subgraph::invalidate_and_delete_(bool delete_subgraph) { void Subgraph::invalidate_now(Graph &graph) { // TODO: double check graph param vs _graph instance var - graph.set_deferring_invalidation(true); + graph.set_batch_invalidate_subgraphs(true); auto removed_subgraphs = vector(); auto stack = std::stack>(); @@ -259,7 +259,7 @@ void Subgraph::invalidate_now(Graph &graph) { free(removed_subgraph); // or delete? } - graph.set_deferring_invalidation(false); + graph.set_batch_invalidate_subgraphs(false); } void Subgraph::graph_destroyed() { @@ -322,7 +322,7 @@ void Subgraph::add_child(Subgraph &child, SubgraphChild::Flags flags) { propagate_dirty_flags(); } - child._parents.push_back(*this); + child._parents.push_back(this); } void Subgraph::remove_child(Subgraph &child, bool without_trace) { @@ -354,7 +354,7 @@ bool Subgraph::ancestor_of(const Subgraph &other) { return true; } - candidate = candidate->_parents.empty() ? nullptr : &candidate->_parents.front(); + candidate = candidate->_parents.empty() ? nullptr : candidate->_parents.front(); for (auto iter = other._parents.begin() + 1, end = other._parents.end(); iter != end; ++iter) { auto parent = *iter; other_parents.push(parent); @@ -588,10 +588,7 @@ void Subgraph::update(uint8_t flags) { for (auto node : nodes_to_update) { if (!thread_is_updating) { - thread_is_updating = _graph->thread_is_updating(); - if (!thread_is_updating) { - _graph->increment_counter_0x1b8(); - } + _graph->increment_update_count_if_needed(); _graph->update_attribute(node, true); if (!subgraph->is_valid()) { @@ -730,12 +727,12 @@ void Subgraph::set_tree_owner(AttributeID owner) { _tree_root->owner = owner; } -void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type, const char *key, uint32_t flags) { +void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type, const char *value, uint32_t flags) { if (!_tree_root) { return; } - auto key_id = graph()->intern_key(key); + auto key_id = graph()->intern_key(value); data::ptr tree_value = (data::ptr)alloc_bytes(sizeof(Graph::TreeValue), 7); tree_value->type = type; From 9e8946c177fb9db696ec35013575b51b95cc5492 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 16:15:24 +1100 Subject: [PATCH 19/74] Move util namespace to its own target --- Sources/ComputeCxx/Containers/ForwardList.h | 118 -------------------- Sources/ComputeCxx/Graph/Graph.cpp | 6 +- Sources/ComputeCxx/Graph/Graph.h | 4 +- Sources/ComputeCxx/Graph/KeyTable.h | 8 +- Sources/ComputeCxx/Subgraph/NodeCache.h | 4 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- 6 files changed, 12 insertions(+), 130 deletions(-) delete mode 100644 Sources/ComputeCxx/Containers/ForwardList.h diff --git a/Sources/ComputeCxx/Containers/ForwardList.h b/Sources/ComputeCxx/Containers/ForwardList.h deleted file mode 100644 index 5b81b09..0000000 --- a/Sources/ComputeCxx/Containers/ForwardList.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include - -#include "Util/Heap.h" - -CF_ASSUME_NONNULL_BEGIN - -namespace AG { - -/// ForwardList is a linked list container that uses util::Heap to allocate nodes, -/// reusing previously removed nodes where possible. -template class ForwardList { - public: - using reference = T &; - using const_reference = const T &; - - private: - struct Node { - Node *_Nullable next; - T value; - }; - - util::Heap *_heap; - Node *_Nullable _front; - Node *_Nullable _spare; - bool _is_heap_owner; - - public: - ForwardList() { - _heap = new util::Heap(nullptr, 0, util::Heap::minimum_increment); - _is_heap_owner = true; - }; - ForwardList(util::Heap *heap) { - _heap = heap; - _is_heap_owner = false; - }; - ~ForwardList() { - if (_is_heap_owner && _heap) { - delete _heap; - } - }; - - // MARK: Element access - - reference front() { - assert(!empty()); - return _front->value; - } - - const_reference front() const { - assert(!empty()); - return _front->value; - } - - // MARK: Capacity - - bool empty() const noexcept { return _front == nullptr; } - - // MARK: Modifiers - - void push_front(const T &value) { - Node *new_node; - if (_spare != nullptr) { - new_node = _spare; - _spare = _spare->previous; - } else { - new_node = _heap->alloc(); - } - new_node->next = _front; - new_node->value = value; - _front = new_node; - } - - void push_front(T &&value) { - Node *new_node; - if (_spare != nullptr) { - new_node = _spare; - _spare = _spare->previous; - } else { - new_node = _heap->alloc(); - } - new_node->next = _front; - new_node->value = std::move(value); - _front = new_node; - } - - template void emplace_front(Args &&...args) { - Node *new_node; - if (_spare != nullptr) { - new_node = _spare; - _spare = _spare->next; - } else { - new_node = _heap->alloc(); - } - new_node->next = _front; - new (&new_node->value) T(args...); - _front = new_node; - } - - void pop_front() { - if (_front == nullptr) { - return; - } - - Node *next = _front->next; - T value = _front->value; - - _front->next = _spare; - _spare = _front; - - _front = next; - } -}; - -} // namespace AG - -CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 59f3881..9b6503c 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -10,7 +10,6 @@ #include "Attribute/Node/Node.h" #include "Attribute/OffsetAttributeID.h" #include "Attribute/WeakAttributeID.h" -#include "Containers/ForwardList.h" #include "Context.h" #include "Errors/Errors.h" #include "KeyTable.h" @@ -19,6 +18,7 @@ #include "Swift/Metadata.h" #include "Trace.h" #include "UpdateStack.h" +#include "Utilities/List.h" namespace AG { @@ -992,7 +992,7 @@ void Graph::propagate_dirty(AttributeID attribute) { char stack_buffer[0x2000] = {}; auto heap = util::Heap(stack_buffer, sizeof(stack_buffer), 0); - auto frames = ForwardList(&heap); + auto frames = util::ForwardList(&heap); data::vector initial_outputs = {}; Node::State initial_state = Node::State(0); @@ -1579,7 +1579,7 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c char stack_buffer[0x2000] = {}; auto heap = util::Heap(stack_buffer, sizeof(stack_buffer), 0); - auto frames = ForwardList(&heap); + auto frames = util::ForwardList(&heap); data::vector initial_outputs = {}; if (attribute.is_direct()) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 7560307..e4a4f7c 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -9,8 +9,8 @@ #include "Attribute/AttributeID.h" #include "Attribute/Node/Edge.h" #include "Closure/ClosureFunction.h" -#include "Util/HashTable.h" -#include "Util/Heap.h" +#include "Utilities/HashTable.h" +#include "Utilities/Heap.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Graph/KeyTable.h b/Sources/ComputeCxx/Graph/KeyTable.h index 3ac7e23..a428283 100644 --- a/Sources/ComputeCxx/Graph/KeyTable.h +++ b/Sources/ComputeCxx/Graph/KeyTable.h @@ -3,7 +3,7 @@ #include #include "Graph.h" -#include "Util/HashTable.h" +#include "Utilities/HashTable.h" CF_ASSUME_NONNULL_BEGIN @@ -16,12 +16,12 @@ class Graph::KeyTable { public: KeyTable(util::Heap *_Nullable heap); - + uint32_t size(); - + uint32_t lookup(const char *key, const char *_Nullable *_Nullable found_key) const; const char *get(uint32_t key_id); - + uint32_t insert(const char *key); }; diff --git a/Sources/ComputeCxx/Subgraph/NodeCache.h b/Sources/ComputeCxx/Subgraph/NodeCache.h index 6f0a93a..f9c8112 100644 --- a/Sources/ComputeCxx/Subgraph/NodeCache.h +++ b/Sources/ComputeCxx/Subgraph/NodeCache.h @@ -4,8 +4,8 @@ #include "Subgraph.h" #include "Swift/Metadata.h" -#include "Util/HashTable.h" -#include "Util/Heap.h" +#include "Utilities/HashTable.h" +#include "Utilities/Heap.h" CF_ASSUME_NONNULL_BEGIN diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 0f5abe3..573421a 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -16,7 +16,7 @@ #include "Graph/UpdateStack.h" #include "NodeCache.h" #include "UniqueID/AGUniqueID.h" -#include "Util/CFPointer.h" +#include "Utilities/CFPointer.h" namespace AG { From 0d1dc51f681c497f6542fa6e8920649beea27aa3 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 16:17:54 +1100 Subject: [PATCH 20/74] Delete ArrayRef::operator new --- Sources/ComputeCxx/Containers/ArrayRef.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Sources/ComputeCxx/Containers/ArrayRef.h b/Sources/ComputeCxx/Containers/ArrayRef.h index fcf815a..1f10a3d 100644 --- a/Sources/ComputeCxx/Containers/ArrayRef.h +++ b/Sources/ComputeCxx/Containers/ArrayRef.h @@ -28,9 +28,6 @@ template class ArrayRef { ArrayRef() = default; ArrayRef(T *_Nullable data, size_t size) : _data(data), _size(size){}; - // TODO: see if this is really needed, or disable operator new in data::table managed objects... - inline void *operator new(std::size_t n, const ArrayRef *_Nonnull ptr) { return (void *)ptr; }; - // Element access const T &operator[](size_t pos) const { From aecfc02c06dffadb5d915c32c6e493ba11c5eae7 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 17:13:29 +1100 Subject: [PATCH 21/74] Implement Graph::Graph and Graph::~Graph() --- Sources/ComputeCxx/Graph/Graph.cpp | 67 ++++++++++++++++++++++++++++++ Sources/ComputeCxx/Graph/Graph.h | 42 +++++++++++-------- Sources/ComputeCxx/Graph/Trace.h | 2 + 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 9b6503c..d10b519 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -22,8 +22,75 @@ namespace AG { +Graph *Graph::_all_graphs = nullptr; +os_unfair_lock Graph::_all_graphs_lock = OS_UNFAIR_LOCK_INIT; + pthread_key_t Graph::_current_update_key = 0; +Graph::Graph() + : _heap(nullptr, 0, 0), _type_ids_by_metadata(nullptr, nullptr, nullptr, nullptr, &_heap), + _contexts_by_id(nullptr, nullptr, nullptr, nullptr, &_heap), _num_contexts(1) { + + data::table::ensure_shared(); + + // what is this check doing? + if ((uintptr_t)this == 1) { + print(); + print_attribute(nullptr); + print_stack(); + print_data(); + write_to_file(nullptr, 0); + } + + static dispatch_once_t make_keys; + dispatch_once_f(&make_keys, nullptr, [](void *context) { + pthread_key_create(&Graph::_current_update_key, 0); + Subgraph::make_current_subgraph_key(); + }); + + _types.push_back(nullptr); // AGAttributeNullType + + // TODO: debug server, trace, profile + + all_lock(); + _next = _all_graphs; + _previous = nullptr; + _all_graphs = this; + if (_next) { + _next->_previous = this; + } + all_unlock(); +} + +Graph::~Graph() { + foreach_trace([this](Trace &trace) { + trace.end_trace(*this); + trace.~Trace(); + }); + + all_lock(); + if (_previous) { + _previous->_next = _next; + if (_next) { + _next->_previous = _previous; + } + } else { + _all_graphs = _next; + if (_next) { + _next->_previous = nullptr; + } + } + all_unlock(); + + for (auto subgraph : _subgraphs) { + subgraph->graph_destroyed(); + } + + if (_keys) { + delete _keys; + } +} + #pragma mark - Invalidating Graph::without_invalidating::without_invalidating(Graph *graph) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index e4a4f7c..8a62ac1 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -84,13 +84,18 @@ class Graph { using MainHandler = void (*)(void (*thunk)(void *), void *thunk_context); // needs AG_SWIFT_CC(swift) ? private: + static Graph *_all_graphs; + static os_unfair_lock _all_graphs_lock; static pthread_key_t _current_update_key; // Graph - Graph *_previous; Graph *_next; + Graph *_previous; util::Heap _heap; + static void all_lock() { os_unfair_lock_lock(&_all_graphs_lock); }; + static void all_unlock() { os_unfair_lock_unlock(&_all_graphs_lock); }; + // Attribute types util::UntypedTable _type_ids_by_metadata; vector _types; @@ -106,10 +111,10 @@ class Graph { void *_Nullable _main_handler_context; // Metrics - uint64_t _num_nodes; - uint64_t _num_nodes_created; - uint64_t _num_subgraphs; - uint64_t _num_subgraphs_created; + uint64_t _num_nodes = 0; + uint64_t _num_nodes_created = 0; + uint64_t _num_subgraphs = 0; + uint64_t _num_subgraphs_created = 0; size_t _num_node_value_bytes = 0; // Profile @@ -126,22 +131,23 @@ class Graph { vector _invalidated_subgraphs; bool _batch_invalidate_subgraphs; - // Context - bool _needs_update; - uint32_t num_contexts; - // Updates - pthread_t _current_update_thread; - uint64_t _deadline; + bool _needs_update; + uint32_t _num_contexts; + pthread_t _current_update_thread = 0; + uint64_t _deadline = UINT64_MAX; // Counters - uint64_t _update_count; // number of times this graph started updating - uint64_t _update_attribute_count; - uint64_t _update_attribute_on_main_count; - uint64_t _mark_changed_count; - uint64_t _version; + uint64_t _update_count = 0; // number of times this graph started updating + uint64_t _update_attribute_count = 0; + uint64_t _update_attribute_on_main_count = 0; + uint64_t _mark_changed_count = 0; + uint64_t _version = 0; public: + Graph(); + ~Graph(); + // MARK: Context bool is_context_updating(uint64_t context_id); @@ -383,7 +389,7 @@ class Graph { vector description_graph(CFDictionaryRef options); - void write_to_file(const char *filename, uint32_t arg); + void write_to_file(const char *_Nullable filename, uint32_t arg); // MARK: Printing @@ -393,7 +399,7 @@ class Graph { void print_cycle(data::ptr node); void print_data(); - void print_stack(); + static void print_stack(); }; } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Trace.h b/Sources/ComputeCxx/Graph/Trace.h index 78eb861..79c450d 100644 --- a/Sources/ComputeCxx/Graph/Trace.h +++ b/Sources/ComputeCxx/Graph/Trace.h @@ -16,6 +16,8 @@ class Trace { public: uint64_t trace_id() { return _trace_id; } + virtual ~Trace(); + // Trace virtual void begin_trace(const Graph &graph); virtual void end_trace(const Graph &graph); From 73bed5942533a7a379068f50b8ef20e5dbb10a0e Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 18:58:56 +1100 Subject: [PATCH 22/74] Implement Graph::Context --- Sources/ComputeCxx/Closure/ClosureFunction.h | 3 + Sources/ComputeCxx/Graph/Context.cpp | 125 ++++++++++++++++++- Sources/ComputeCxx/Graph/Context.h | 30 ++++- Sources/ComputeCxx/Graph/Graph.cpp | 6 +- Sources/ComputeCxx/Graph/Graph.h | 4 + Sources/ComputeCxx/Graph/Trace.h | 4 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 2 +- Sources/ComputeCxx/Graph/UpdateStack.h | 1 + Sources/ComputeCxx/Subgraph/NodeCache.cpp | 2 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- 10 files changed, 166 insertions(+), 13 deletions(-) diff --git a/Sources/ComputeCxx/Closure/ClosureFunction.h b/Sources/ComputeCxx/Closure/ClosureFunction.h index a7a8374..e01066e 100644 --- a/Sources/ComputeCxx/Closure/ClosureFunction.h +++ b/Sources/ComputeCxx/Closure/ClosureFunction.h @@ -17,7 +17,10 @@ template class ClosureFunction { Context _context; public: + operator bool() { return _function != nullptr; } + const ReturnType operator()(Args... args) const noexcept { + // TODO: check _context is first or last parameter return _function(std::forward(args)..., _context); } diff --git a/Sources/ComputeCxx/Graph/Context.cpp b/Sources/ComputeCxx/Graph/Context.cpp index 50bc258..73a4a34 100644 --- a/Sources/ComputeCxx/Graph/Context.cpp +++ b/Sources/ComputeCxx/Graph/Context.cpp @@ -1,15 +1,136 @@ #include "Context.h" #include "Attribute/AttributeID.h" +#include "Subgraph/Subgraph.h" +#include "Trace.h" +#include "UniqueID/AGUniqueID.h" +#include "UpdateStack.h" namespace AG { +Graph::Context::Context(Graph *graph) { + _graph = graph; + _field_0x08 = 0; + _unique_id = AGMakeUniqueID(); + _deadline = UINT64_MAX; + + graph->_num_contexts += 1; + graph->_contexts_by_id.insert(_unique_id, this); + + _graph->foreach_trace([this](Trace &trace) { trace.created(*this); }); +} + +Graph::Context::~Context() { + _graph->foreach_trace([this](Trace &trace) { trace.destroy(*this); }); + + bool removed = _graph->_contexts_by_id.remove(_unique_id); + if (removed && _deadline != UINT64_MAX) { + uint64_t min_deadline = UINT64_MAX; + _graph->_contexts_by_id.for_each( + [](const uint64_t context_id, Context *const context, void *min_deadline_ref) { + if (context->_deadline < *(uint64_t *)min_deadline_ref) { + *(uint64_t *)min_deadline_ref = context->_deadline; + } + }, + &min_deadline); + _graph->set_deadline(min_deadline); + } + + if (_graph->_num_contexts != 1) { + auto batch = without_invalidating(_graph); + for (auto subgraph : _graph->subgraphs()) { + if (subgraph->graph_context_id() == _unique_id) { + subgraph->invalidate_and_delete_(true); + } + } + // batch.~without_invalidating called here + } + + _graph->_num_contexts -= 1; + if (_graph && _graph->_num_contexts == 0) { + _graph->~Graph(); + } + + _invalidation_closure.release_context(); + _update_closure.release_context(); +} + +Graph::Context *Graph::Context::from_cf(AGGraphStorage *storage) { + if (storage->_context._invalidated) { + precondition_failure("invalidated graph"); + } + return &storage->_context; +} + +void Graph::Context::set_deadline(uint64_t deadline) { + if (_deadline == deadline) { + return; + } + _deadline = deadline; + _graph->foreach_trace([this, &deadline](Trace &trace) { trace.set_deadline(deadline); }); + + uint64_t min_deadline = UINT64_MAX; + _graph->_contexts_by_id.for_each( + [](const uint64_t context_id, Context *const context, void *min_deadline_ref) { + if (context->_deadline < *(uint64_t *)min_deadline_ref) { + *(uint64_t *)min_deadline_ref = context->_deadline; + } + }, + &min_deadline); + _graph->set_deadline(min_deadline); +} + +void Graph::Context::set_needs_update() { + if (_needs_update) { + return; + } + _graph->foreach_trace([this](Trace &trace) { trace.needs_update(*this); }); + _needs_update = true; + _graph->set_needs_update(true); +} + +bool Graph::Context::thread_is_updating() { + for (auto update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { + if (update_stack.get()->graph() == _graph) { + return _graph->is_context_updating(_unique_id); + } + } + return false; +} + void Graph::Context::call_invalidation(AttributeID attribute) { - // TODO: not implemented + _graph_version = _graph->_version; + + if (_invalidation_closure) { + auto old_update = current_update(); + if (old_update.value() != 0) { + set_current_update(old_update.with_tag(true)); + } + + _graph->foreach_trace([this, &attribute](Trace &trace) { trace.begin_invalidation(*this, attribute); }); + _invalidation_closure(attribute); + _graph->foreach_trace([this, &attribute](Trace &trace) { trace.end_invalidation(*this, attribute); }); + + set_current_update(old_update); + } } void Graph::Context::call_update() { - // TODO: not implemented + if (!_needs_update) { + return; + } + _needs_update = false; + + if (_update_closure) { + + auto stack = UpdateStack(_graph, UpdateStack::Option::SetTag | UpdateStack::Option::InvalidateSubgraphs); + + _graph->foreach_trace([this](Trace &trace) { trace.begin_update(*this); }); + _update_closure(); + _graph->foreach_trace([this](Trace &trace) { trace.end_update(*this); }); + + // ~UpdateStack() called here + } } } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h index 1e1ee7e..dd9ef42 100644 --- a/Sources/ComputeCxx/Graph/Context.h +++ b/Sources/ComputeCxx/Graph/Context.h @@ -2,10 +2,14 @@ #include +#include "Attribute/AttributeID.h" #include "Graph.h" +#include "Private/CFRuntime.h" CF_ASSUME_NONNULL_BEGIN +struct AGGraphStorage; + namespace AG { class Graph::Context { @@ -14,19 +18,39 @@ class Graph::Context { void *_field_0x08; uint64_t _unique_id; + ClosureFunctionAV _invalidation_closure; + ClosureFunctionVV _update_closure; + + uint64_t _deadline; uint64_t _graph_version; + bool _needs_update; + bool _invalidated; public: - Graph &graph() const { return *_graph; }; + Context(Graph *graph); + ~Context(); - uint64_t unique_id() { return _unique_id; }; + AGGraphStorage *to_cf() { return reinterpret_cast((char *)this - sizeof(CFRuntimeBase)); }; + static Context *from_cf(AGGraphStorage *storage); + Graph &graph() const { return *_graph; }; + uint64_t unique_id() { return _unique_id; }; uint64_t graph_version() { return _graph_version; }; - void call_invalidation(AttributeID attribute); + void set_deadline(uint64_t deadline); + void set_needs_update(); + + bool thread_is_updating(); + + void call_invalidation(AttributeID attribute); void call_update(); }; } // namespace AG +struct AGGraphStorage { + CFRuntimeBase _base; + AG::Graph::Context _context; +}; + CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index d10b519..e739276 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -188,9 +188,9 @@ bool Graph::thread_is_updating() { void Graph::call_update() { while (_needs_update) { _needs_update = false; - _contexts_by_id.for_each([](uint64_t context_id, Context *graph_context, - const void *closure_context) { graph_context->call_update(); }, - nullptr); + _contexts_by_id.for_each( + [](uint64_t context_id, Context *graph_context, void *closure_context) { graph_context->call_update(); }, + nullptr); } } diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 8a62ac1..6c00628 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -30,6 +30,7 @@ template class TaggedPointer { uintptr_t value() { return _value; }; bool tag() { return static_cast(_value & 0x1); }; + TaggedPointer with_tag(bool tag) { return TaggedPointer(get(), tag); }; T *_Nullable get() { return reinterpret_cast(_value & ~0x1); }; @@ -187,6 +188,8 @@ class Graph { bool thread_is_updating(); bool needs_update() { return _needs_update; }; + void set_needs_update(bool needs_update) { _needs_update = needs_update; }; + void call_update(); void reset_update(data::ptr node); @@ -197,6 +200,7 @@ class Graph { MainHandler _Nullable main_handler() { return _main_handler; }; void call_main_handler(void *context, void (*body)(void *context)); + void set_deadline(uint64_t deadline) { _deadline = deadline; }; bool passed_deadline(); bool passed_deadline_slow(); diff --git a/Sources/ComputeCxx/Graph/Trace.h b/Sources/ComputeCxx/Graph/Trace.h index 79c450d..5cdb6b4 100644 --- a/Sources/ComputeCxx/Graph/Trace.h +++ b/Sources/ComputeCxx/Graph/Trace.h @@ -38,8 +38,8 @@ class Trace { virtual void begin_update(const Graph::Context &context); virtual void end_update(const Graph::Context &context); - virtual void begin_invalidation(const Graph::Context &context, data::ptr node); - virtual void end_invalidation(const Graph::Context &context, data::ptr node); + virtual void begin_invalidation(const Graph::Context &context, AttributeID attribute); + virtual void end_invalidation(const Graph::Context &context, AttributeID attribute); virtual void begin_modify(data::ptr node); virtual void end_modify(data::ptr node); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 5221fa5..08c002f 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -28,7 +28,7 @@ Graph::UpdateStack::UpdateStack(Graph *graph, uint8_t options) { _options &= Option::InvalidateSubgraphs; } - Graph::set_current_update(TaggedPointer(this, options >> 3 & 1)); + Graph::set_current_update(TaggedPointer(this, options & Option::SetTag ? 1 : 0)); } Graph::UpdateStack::~UpdateStack() { diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index 4291e09..0986571 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -11,6 +11,7 @@ namespace AG { class Graph::UpdateStack { public: enum Option : uint8_t { + SetTag = 1 << 3, InvalidateSubgraphs = 1 << 4, }; diff --git a/Sources/ComputeCxx/Subgraph/NodeCache.cpp b/Sources/ComputeCxx/Subgraph/NodeCache.cpp index 6b7eb28..7fca02c 100644 --- a/Sources/ComputeCxx/Subgraph/NodeCache.cpp +++ b/Sources/ComputeCxx/Subgraph/NodeCache.cpp @@ -42,7 +42,7 @@ Subgraph::NodeCache::NodeCache() noexcept Subgraph::NodeCache::~NodeCache() noexcept { _items.for_each( - [](data::ptr node, NodeCache::Item *item, const void *context) { + [](data::ptr node, NodeCache::Item *item, void *context) { if (item) { delete item; } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 573421a..2fa31ad 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -1053,7 +1053,7 @@ void Subgraph::cache_collect() { std::pair context = {this, _cache.get()}; if (_cache != nullptr && is_valid()) { _cache->types().for_each( - [](const swift::metadata *metadata, const data::ptr type, const void *context) { + [](const swift::metadata *metadata, const data::ptr type, void *context) { Subgraph *subgraph = reinterpret_cast *>(context)->first; NodeCache *cache = reinterpret_cast *>(context)->second; From 3575138579d9fe5c50c587f2d477b64662eb8a18 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 12 Feb 2025 20:12:46 +1100 Subject: [PATCH 23/74] Implement Graph::is_context_updating and Graph::main_context --- Sources/ComputeCxx/Graph/Graph.cpp | 32 ++++++++++++++++++++++++++++++ Sources/ComputeCxx/Graph/Graph.h | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index e739276..13c5f80 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -91,6 +91,38 @@ Graph::~Graph() { } } +#pragma mark - Context + +bool Graph::is_context_updating(uint64_t context_id) { + for (auto update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { + for (auto frame = update_stack.get()->frames().rbegin(), end = update_stack.get()->frames().rend(); + frame != end; ++frame) { + auto subgraph = AttributeID(frame->attribute).subgraph(); + if (subgraph && subgraph->graph_context_id() == context_id) { + return true; + } + } + } +} + +Graph::Context *Graph::main_context() { + struct Info { + Context *context; + uint64_t context_id; + }; + Info info = {nullptr, UINT64_MAX}; + _contexts_by_id.for_each( + [](const uint64_t context_id, Context *const context, void *info_ref) { + auto typed_info_ref = (std::pair *)info_ref; + if (context_id < ((Info *)info_ref)->context_id) { + ((Info *)info_ref)->context = context; + ((Info *)info_ref)->context_id = context_id; + } + }, + &info); + return info.context; +} + #pragma mark - Invalidating Graph::without_invalidating::without_invalidating(Graph *graph) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 6c00628..2b119ee 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -152,7 +152,7 @@ class Graph { // MARK: Context bool is_context_updating(uint64_t context_id); - void main_context(); + Context *_Nullable main_context(); // MARK: Subgraphs From bec1e3ffa3493de6ec6a3f29c16ffa6395fe48de Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 13 Feb 2025 00:27:05 +1100 Subject: [PATCH 24/74] Implement Encoder --- Sources/ComputeCxx/Containers/Vector.tpp | 4 +- Sources/ComputeCxx/Encoder/Encoder.cpp | 83 +++++++++++++++++++++++- Sources/ComputeCxx/Encoder/Encoder.h | 21 ++++++ 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/Sources/ComputeCxx/Containers/Vector.tpp b/Sources/ComputeCxx/Containers/Vector.tpp index 9c96357..e3e7eaa 100644 --- a/Sources/ComputeCxx/Containers/Vector.tpp +++ b/Sources/ComputeCxx/Containers/Vector.tpp @@ -314,7 +314,7 @@ void vector::resize(size_type count) { } } else if (count > _size) { for (auto i = _size; i < count; i++) { - new (this[i]) value_type(); + new (&this[i]) value_type(); } } _size = count; @@ -330,7 +330,7 @@ void vector::resize(size_type count, const value_type &value) { } } else if (count > _size) { for (auto i = _size; i < count; i++) { - new (this[i]) value_type(value); + new (&this[i]) value_type(value); } } _size = count; diff --git a/Sources/ComputeCxx/Encoder/Encoder.cpp b/Sources/ComputeCxx/Encoder/Encoder.cpp index 2a80337..ee05e57 100644 --- a/Sources/ComputeCxx/Encoder/Encoder.cpp +++ b/Sources/ComputeCxx/Encoder/Encoder.cpp @@ -1,17 +1,94 @@ #include "Encoder.h" +#include "Errors/Errors.h" + namespace AG { +Encoder::Encoder(Delegate *_Nullable delegate, uint64_t flush_interval) + : _delegate(delegate), _flush_interval(flush_interval) { + if (delegate == nullptr && flush_interval != 0) { + precondition_failure("need a delegate if flush interval is non-zero"); + } +} + void Encoder::encode_varint(uint64_t value) { - // TODO: not implemented + uint64_t width = 0; + if (value < 0x80) { + if (_buffer.capacity() > _buffer.size()) { + _buffer.push_back((const char)value); + return; + } + width = 1; + } else { + width = (63 - std::countl_zero(value)) / 7; + } + + uint64_t index = _buffer.size(); + _buffer.resize(_buffer.size() + width); // TODO: how to resize without zeroing memory + + uint64_t remaining_value = value; + while (remaining_value) { + _buffer[index] = ((char)remaining_value & 0x7f) | (0x7f < remaining_value) << 7; + index += 1; + remaining_value = remaining_value >> 7; + }; +} + +void Encoder::encode_fixed64(uint64_t value) { + uint64_t *pointer = (uint64_t *)(_buffer.data() + _buffer.size()); + _buffer.resize(_buffer.size() + sizeof(uint64_t)); + *pointer = value; +} + +void Encoder::encode_data(void *data, size_t length) { + encode_varint(length); + if (length == 0) { + return; + } + void *pointer = (void *)(_buffer.data() + _buffer.size()); + _buffer.resize(_buffer.size() + length); + memcpy(pointer, data, length); } void Encoder::begin_length_delimited() { - // TODO: not implemented + // Reserve one byte for the length and store the position + uint64_t old_length = _buffer.size(); + _buffer.resize(_buffer.size() + 1); + _sections.push_back(old_length); } void Encoder::end_length_delimited() { - // TODO: not implemented + uint64_t index = _sections.back(); + _sections.pop_back(); + + uint64_t length = _buffer.size() - (index + 1); + if (length < 0x80) { + _buffer[index] = length; + } else { + // The length requires more than one byte + uint64_t width = (63 - std::countl_zero(length)) / 7; + _buffer.resize(_buffer.size() + width - 1); + + memmove((void *)(_buffer.data() + index + width), (void *)(_buffer.data() + index + 1), length); + + uint64_t remaining_value = length; + while (remaining_value) { + _buffer[index] = ((char)remaining_value & 0x7f) | (0x7f < remaining_value) << 7; + index += 1; + remaining_value = remaining_value >> 7; + }; + } + + if (_sections.empty() && _flush_interval != 0 && _flush_interval <= _buffer.size()) { + flush(); + } +} + +void Encoder::flush() { + if (!_buffer.empty() && _delegate) { + _delegate->flush(*this); + _buffer.resize(0); + } } } // namespace AG diff --git a/Sources/ComputeCxx/Encoder/Encoder.h b/Sources/ComputeCxx/Encoder/Encoder.h index c94cb7b..7a8b38f 100644 --- a/Sources/ComputeCxx/Encoder/Encoder.h +++ b/Sources/ComputeCxx/Encoder/Encoder.h @@ -2,16 +2,37 @@ #include +#include "Containers/Vector.h" + CF_ASSUME_NONNULL_BEGIN namespace AG { class Encoder { public: + class Delegate { + public: + virtual void flush(Encoder &encoder); + }; + + private: + Delegate *_Nullable _delegate; + uint64_t _flush_interval; + void *_field_0x10; + vector _buffer; + vector _sections; + + public: + Encoder(Delegate *_Nullable delegate, uint64_t flush_interval); + void encode_varint(uint64_t value); + void encode_fixed64(uint64_t value); + void encode_data(void *data, size_t length); void begin_length_delimited(); void end_length_delimited(); + + void flush(); }; } // namespace AG From 94e5e5c9ecbdfe2962a1468e35175e2511769423 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 13 Feb 2025 22:06:01 +1100 Subject: [PATCH 25/74] Add Graph::TraceRecorder and implement some trace methods on Graph --- Sources/ComputeCxx/Attribute/AttributeType.h | 2 + .../ComputeCxx/Attribute/WeakAttributeID.cpp | 2 - .../ComputeCxx/Attribute/WeakAttributeID.h | 6 +- Sources/ComputeCxx/Containers/Vector.h | 9 + Sources/ComputeCxx/Containers/Vector.tpp | 45 + Sources/ComputeCxx/Data/Zone.h | 4 +- Sources/ComputeCxx/Encoder/Encoder.cpp | 2 +- Sources/ComputeCxx/Encoder/Encoder.h | 7 +- Sources/ComputeCxx/Graph/AGGraph.cpp | 4 + Sources/ComputeCxx/Graph/AGGraph.h | 2 + Sources/ComputeCxx/Graph/Context.cpp | 2 +- Sources/ComputeCxx/Graph/Context.h | 4 +- Sources/ComputeCxx/Graph/Graph.cpp | 83 +- Sources/ComputeCxx/Graph/Graph.h | 11 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 1400 +++++++++++++++++ Sources/ComputeCxx/Graph/TraceRecorder.h | 139 ++ Sources/ComputeCxx/Graph/UpdateStack.cpp | 10 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 48 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 6 +- Sources/ComputeCxx/Trace/AGTrace.cpp | 11 + Sources/ComputeCxx/Trace/AGTrace.h | 20 + Sources/ComputeCxx/{Graph => Trace}/Trace.cpp | 0 Sources/ComputeCxx/{Graph => Trace}/Trace.h | 14 +- 23 files changed, 1768 insertions(+), 63 deletions(-) create mode 100644 Sources/ComputeCxx/Graph/TraceRecorder.cpp create mode 100644 Sources/ComputeCxx/Graph/TraceRecorder.h create mode 100644 Sources/ComputeCxx/Trace/AGTrace.cpp create mode 100644 Sources/ComputeCxx/Trace/AGTrace.h rename Sources/ComputeCxx/{Graph => Trace}/Trace.cpp (100%) rename Sources/ComputeCxx/{Graph => Trace}/Trace.h (89%) diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index 75cb2dd..d5393bd 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -52,6 +52,8 @@ class AttributeType { const swift::metadata &self_metadata() const { return *_self_metadata; }; const swift::metadata &value_metadata() const { return *_value_metadata; }; + Flags flags() const { return _flags; }; + bool main_thread() const { return _flags & Flags::MainThread; }; bool use_graph_as_initial_value() const { return _flags & Flags::UseGraphAsInitialValue; }; bool unknown_0x20() const { return _flags & Flags::Unknown0x20; }; diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp b/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp index 34f6d25..cc200a6 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp @@ -17,8 +17,6 @@ bool WeakAttributeID::expired() const { return true; } -const AttributeID &WeakAttributeID::attribute() const { return _attribute; }; - const AttributeID &WeakAttributeID::evaluate() const { return _attribute.without_kind() != 0 && !expired() ? _attribute : AttributeIDNil; }; diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.h b/Sources/ComputeCxx/Attribute/WeakAttributeID.h index 1a3a7a8..ca15953 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.h +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.h @@ -17,9 +17,11 @@ class WeakAttributeID { public: WeakAttributeID(AttributeID attribute, uint32_t zone_id) : _attribute(attribute), _zone_id(zone_id){}; + const AttributeID &attribute() const { return _attribute; }; + uint32_t zone_id() const { return _zone_id; }; + bool expired() const; - const AttributeID &attribute() const; - + /// Returns the attribute it is has not expired, otherwise returns the nil attribute. const AttributeID &evaluate() const; }; diff --git a/Sources/ComputeCxx/Containers/Vector.h b/Sources/ComputeCxx/Containers/Vector.h index 1d3af68..6abff11 100644 --- a/Sources/ComputeCxx/Containers/Vector.h +++ b/Sources/ComputeCxx/Containers/Vector.h @@ -74,6 +74,9 @@ class vector { void clear(); + iterator insert(const_iterator pos, const T &value); + iterator insert(const_iterator pos, T &&value); + iterator erase(iterator pos); iterator erase(iterator first, iterator last); @@ -153,6 +156,9 @@ class vector { // Modifiers void clear(); + + iterator insert(const_iterator pos, const T &value); + iterator insert(const_iterator pos, T &&value); iterator erase(iterator pos); iterator erase(iterator first, iterator last); @@ -230,6 +236,9 @@ class vector, 0, _size_type> { // Modifiers void clear(); + + iterator insert(const_iterator pos, const T &value); + iterator insert(const_iterator pos, T &&value); void push_back(const std::unique_ptr &value) = delete; void push_back(std::unique_ptr &&value); diff --git a/Sources/ComputeCxx/Containers/Vector.tpp b/Sources/ComputeCxx/Containers/Vector.tpp index e3e7eaa..f6225d9 100644 --- a/Sources/ComputeCxx/Containers/Vector.tpp +++ b/Sources/ComputeCxx/Containers/Vector.tpp @@ -97,6 +97,29 @@ void vector::clear() { _size = 0; } +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, + const T &value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (mutable_pos) value_type(value); + _size += 1; + return end(); +} + +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, T &&value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (pos) value_type(std::move(value)); + _size += 1; + return end(); +} + template requires std::unsigned_integral vector::iterator vector::erase(iterator pos) { @@ -254,6 +277,28 @@ void vector::clear() { _size = 0; } +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, const T &value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (mutable_pos) value_type(value); + _size += 1; + return end(); +} + +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, T &&value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (mutable_pos) value_type(std::move(value)); + _size += 1; + return end(); +} + template requires std::unsigned_integral vector::iterator vector::erase(iterator pos) { diff --git a/Sources/ComputeCxx/Data/Zone.h b/Sources/ComputeCxx/Data/Zone.h index e054530..4970ca6 100644 --- a/Sources/ComputeCxx/Data/Zone.h +++ b/Sources/ComputeCxx/Data/Zone.h @@ -48,10 +48,10 @@ class zone { zone(); ~zone(); - info info() { return _info; }; + info info() const { return _info; }; void mark_deleted() { _info = _info.with_deleted(); }; - ptr last_page() { return _last_page; }; + ptr last_page() const { return _last_page; }; void clear(); void realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask); diff --git a/Sources/ComputeCxx/Encoder/Encoder.cpp b/Sources/ComputeCxx/Encoder/Encoder.cpp index ee05e57..94e3120 100644 --- a/Sources/ComputeCxx/Encoder/Encoder.cpp +++ b/Sources/ComputeCxx/Encoder/Encoder.cpp @@ -86,7 +86,7 @@ void Encoder::end_length_delimited() { void Encoder::flush() { if (!_buffer.empty() && _delegate) { - _delegate->flush(*this); + _delegate->flush_encoder(*this); _buffer.resize(0); } } diff --git a/Sources/ComputeCxx/Encoder/Encoder.h b/Sources/ComputeCxx/Encoder/Encoder.h index 7a8b38f..cb2ae71 100644 --- a/Sources/ComputeCxx/Encoder/Encoder.h +++ b/Sources/ComputeCxx/Encoder/Encoder.h @@ -10,9 +10,8 @@ namespace AG { class Encoder { public: - class Delegate { - public: - virtual void flush(Encoder &encoder); + struct Delegate { + virtual int flush_encoder(Encoder &encoder); }; private: @@ -25,6 +24,8 @@ class Encoder { public: Encoder(Delegate *_Nullable delegate, uint64_t flush_interval); + const vector &buffer() const { return _buffer; }; + void encode_varint(uint64_t value); void encode_fixed64(uint64_t value); void encode_data(void *data, size_t length); diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index c02a591..c8cf95a 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -1,5 +1,9 @@ #include "AGGraph.h" +void AGGraphCreate() { + // TODO: not implemented +} + void AGGraphSetOutputValue(void *value, AGTypeID type) { // TODO: not implemented } diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 98e3e3e..4f934e5 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -11,6 +11,8 @@ CF_EXTERN_C_BEGIN typedef struct CF_BRIDGED_TYPE(id) AGGraphStorage *AGGraphRef AG_SWIFT_NAME(Graph); +void AGGraphCreate(); + void AGGraphSetOutputValue(void *value, AGTypeID type); CF_EXTERN_C_END diff --git a/Sources/ComputeCxx/Graph/Context.cpp b/Sources/ComputeCxx/Graph/Context.cpp index 73a4a34..3bcda3a 100644 --- a/Sources/ComputeCxx/Graph/Context.cpp +++ b/Sources/ComputeCxx/Graph/Context.cpp @@ -2,7 +2,7 @@ #include "Attribute/AttributeID.h" #include "Subgraph/Subgraph.h" -#include "Trace.h" +#include "Trace/Trace.h" #include "UniqueID/AGUniqueID.h" #include "UpdateStack.h" diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h index dd9ef42..0deab4b 100644 --- a/Sources/ComputeCxx/Graph/Context.h +++ b/Sources/ComputeCxx/Graph/Context.h @@ -34,8 +34,8 @@ class Graph::Context { static Context *from_cf(AGGraphStorage *storage); Graph &graph() const { return *_graph; }; - uint64_t unique_id() { return _unique_id; }; - uint64_t graph_version() { return _graph_version; }; + uint64_t unique_id() const { return _unique_id; }; + uint64_t graph_version() const { return _graph_version; }; void set_deadline(uint64_t deadline); void set_needs_update(); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 13c5f80..0135e0a 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1,5 +1,6 @@ #include "Graph.h" +#include #include #include #include @@ -16,7 +17,8 @@ #include "Log/Log.h" #include "Subgraph/Subgraph.h" #include "Swift/Metadata.h" -#include "Trace.h" +#include "Trace/Trace.h" +#include "TraceRecorder.h" #include "UpdateStack.h" #include "Utilities/List.h" @@ -1833,6 +1835,10 @@ void Graph::encode_tree(Encoder &encoder, data::ptr tree) { #pragma mark - Tracing +void Graph::prepare_trace(Trace &trace) { + // TODO: not implemented +} + void Graph::add_trace(Trace *_Nullable trace) { if (trace == nullptr) { return; @@ -1850,8 +1856,81 @@ void Graph::remove_trace(uint64_t trace_id) { _traces.erase(iter); } +void Graph::start_tracing(uint32_t arg, std::span span) { + // TODO: not implemented +} + +void Graph::stop_tracing() { + if (_trace_recorder) { + remove_trace(_trace_recorder->unique_id()); + _trace_recorder = nullptr; + } +} + +void Graph::sync_tracing() { + foreach_trace([](Trace &trace) { trace.sync_trace(); }); +} + +CFStringRef Graph::copy_trace_path() { + if (_trace_recorder && _trace_recorder->trace_path()) { + return CFStringCreateWithCString(0, _trace_recorder->trace_path(), kCFStringEncodingUTF8); + } else { + return nullptr; + } +} + +void Graph::all_start_tracing(uint32_t arg, std::span span) { + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + graph->start_tracing(arg, span); + } + all_unlock(); +} + +void Graph::all_stop_tracing() { + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + graph->stop_tracing(); + } + all_unlock(); +} + +void Graph::all_sync_tracing() { + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + graph->sync_tracing(); + } + all_unlock(); +} + +CFStringRef Graph::all_copy_trace_path() { + CFStringRef result = nullptr; + all_lock(); + if (_all_graphs) { + result = _all_graphs->copy_trace_path(); + } + all_unlock(); + return result; +} + void Graph::trace_assertion_failure(bool all_stop_tracing, const char *format, ...) { - // TODO: Not implemented + char *message = nullptr; + + va_list args; + va_start(args, format); + + bool locked = all_try_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + graph->foreach_trace([&format, &args](Trace &trace) { trace.log_message_v(format, args); }); + if (all_stop_tracing) { + graph->stop_tracing(); + } + } + if (locked) { + all_unlock(); + } + + va_end(args); } #pragma mark - Printing diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 2b119ee..37bc120 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -51,6 +51,7 @@ class Graph { public: class Context; class KeyTable; + class TraceRecorder; class UpdateStack; class UpdateStackRef; @@ -95,6 +96,7 @@ class Graph { util::Heap _heap; static void all_lock() { os_unfair_lock_lock(&_all_graphs_lock); }; + static bool all_try_lock() { return os_unfair_lock_trylock(&_all_graphs_lock); }; static void all_unlock() { os_unfair_lock_unlock(&_all_graphs_lock); }; // Attribute types @@ -121,6 +123,7 @@ class Graph { // Profile // Trace + TraceRecorder *_trace_recorder; // Tree std::unique_ptr> _tree_data_elements_by_subgraph; @@ -344,10 +347,10 @@ class Graph { void add_trace(Trace *_Nullable trace); void remove_trace(uint64_t trace_id); - void start_tracing(uint32_t arg, std::span span); + void start_tracing(uint32_t arg, std::span span); void stop_tracing(); void sync_tracing(); - void copy_trace_path(); + CFStringRef copy_trace_path(); template requires std::invocable @@ -357,10 +360,10 @@ class Graph { } }; - static void all_start_tracing(); + static void all_start_tracing(uint32_t arg, std::span span); static void all_stop_tracing(); static void all_sync_tracing(); - static void all_copy_trace_path(); + static CFStringRef all_copy_trace_path(); static void trace_assertion_failure(bool all_stop_tracing, const char *format, ...); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp new file mode 100644 index 0000000..5c149ce --- /dev/null +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -0,0 +1,1400 @@ +#include "TraceRecorder.h" + +#include +#include +#include +#include + +#include "AGGraph.h" +#include "Attribute/AttributeType.h" +#include "Attribute/Node/IndirectNode.h" +#include "Context.h" +#include "KeyTable.h" +#include "Log/Log.h" +#include "Subgraph/Subgraph.h" +#include "Time/Time.h" +#include "Trace/AGTrace.h" +#include "UniqueID/AGUniqueID.h" +#include "UpdateStack.h" + +namespace AG { + +namespace { + +uint64_t uuid_hash(const uuid_t key) { return *key; } + +bool uuid_equal(const uuid_t a, const uuid_t b) { return uuid_compare(a, b) == 0; } + +} // namespace + +Graph::TraceRecorder::TraceRecorder(Graph *graph, uint8_t options, std::span subsystems) + : _graph(graph), _options(options), _heap(_heap_inline_buffer, 256, 0), + _image_offset_cache(uuid_hash, uuid_equal, nullptr, nullptr, &_heap), _encoder(_delegate, 0x10000) { + _unique_id = AGMakeUniqueID(); + + _delegate = this; + + for (auto subsystem : subsystems) { + _named_event_subsystems.push_back(strdup(subsystem)); + } + + void *array[1] = {(void *)&AGGraphCreate}; + image_offset image_offsets[1]; + backtrace_image_offsets(array, image_offsets, 1); + + uuid_copy(_stack_frame_uuid, image_offsets[0].uuid); +} + +Graph::TraceRecorder::~TraceRecorder() { + _encoder.flush(); + + if (_trace_path) { + free((void *)_trace_path); + _trace_path = nullptr; + } + + for (auto iter = _named_event_subsystems.begin(), end = _named_event_subsystems.end(); iter != end; ++iter) { + if (*iter) { + free((void *)*iter); + } + *iter = nullptr; + } + + void *array[1] = {(void *)&AGGraphCreate}; + image_offset image_offsets[1]; + backtrace_image_offsets(array, image_offsets, 1); + + uuid_copy(_stack_frame_uuid, image_offsets[0].uuid); +} + +void Graph::TraceRecorder::encode_types() { + while (_num_encoded_types < _graph->_types.size()) { + auto attribute_type = _graph->attribute_type(_num_encoded_types); + + _encoder.encode_varint(0x1a); + _encoder.begin_length_delimited(); + + if (_num_encoded_types) { + _encoder.encode_varint(8); + _encoder.encode_varint(_num_encoded_types); + } + auto self_metadata_name = attribute_type.self_metadata().name(false); + auto self_metadata_length = strlen(self_metadata_name); + if (self_metadata_length) { + _encoder.encode_varint(0x12); + _encoder.encode_data((void *)self_metadata_name, self_metadata_length); + } + auto value_metadata_name = attribute_type.value_metadata().name(false); + auto value_metadata_length = strlen(value_metadata_name); + if (value_metadata_length) { + _encoder.encode_varint(0x1a); + _encoder.encode_data((void *)value_metadata_name, value_metadata_length); + } + auto self_size = attribute_type.self_metadata().vw_size(); + if (self_size > 0) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(self_size); + } + auto value_size = attribute_type.value_metadata().vw_size(); + if (value_size > 0) { + _encoder.encode_varint(0x28); + _encoder.encode_varint(value_size); + } + auto flags = attribute_type.flags(); + if (flags) { + _encoder.encode_varint(0x30); + _encoder.encode_varint(flags); + } + + _encoder.end_length_delimited(); + + _num_encoded_types += 1; + } +} + +void Graph::TraceRecorder::encode_keys() { + if (_graph->_keys == nullptr) { + return; + } + while (_num_encoded_keys < _graph->_keys->size()) { + if (auto key_name = _graph->key_name(_num_encoded_keys)) { + _encoder.encode_varint(0x22); + _encoder.begin_length_delimited(); + if (_num_encoded_keys) { + _encoder.encode_varint(8); + _encoder.encode_varint(_num_encoded_keys); + } + auto length = strlen(key_name); + if (length) { + _encoder.encode_varint(0x12); + _encoder.encode_data((void *)key_name, length); + } + _encoder.end_length_delimited(); + } + _num_encoded_keys += 1; + } +} + +void Graph::TraceRecorder::encode_stack() { + auto first_update = current_update(); + if (first_update == 0) { + return; + } + + _encoder.encode_varint(0x2a); + _encoder.begin_length_delimited(); + + for (auto update = first_update; update != nullptr; update = update.get()->previous()) { + auto frames = update.get()->frames(); + for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + + if (frame->attribute) { + _encoder.encode_varint(8); + _encoder.encode_varint(frame->attribute); + } + if (frame->needs_update) { + _encoder.encode_varint(0x10); + _encoder.encode_varint(1); + } + if (frame->cyclic) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(1); + } + + _encoder.end_length_delimited(); + } + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::encode_snapshot() { + if (_options & Options::SkipUpdates) { + return; + } + + encode_types(); + encode_keys(); + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x11); + + field_timestamp(_encoder); + + for (auto subgraph : _graph->subgraphs()) { + if (subgraph->is_valid()) { + _encoder.encode_varint(0x12); + _encoder.begin_length_delimited(); + subgraph->encode(_encoder); + _encoder.end_length_delimited(); + } + } + + encode_stack(); + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x12); + field_timestamp(_encoder); + _encoder.end_length_delimited(); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::field_timestamp(Encoder &encoder) { + auto time = current_time(); + if (time != 0.0) { + encoder.encode_varint(0x11); + encoder.encode_fixed64(time); + } +} + +void Graph::TraceRecorder::field_backtrace(Encoder &encoder, uint64_t field_number) { + if ((_options & Options::RecordBacktrace) == 0) { + return; + } + + void *stack_frames_buffer[8]; + auto stack_frames_size = backtrace(stack_frames_buffer, sizeof(stack_frames_buffer)); + + image_offset image_offsets[8]; + backtrace_image_offsets(stack_frames_buffer, image_offsets, stack_frames_size); + + static uint64_t n_stack_frames = []() -> uint64_t { + char *result = getenv("AG_TRACE_STACK_FRAMES"); + if (result) { + return atoi(result); + } + return 8; + }(); + + if (n_stack_frames == 0) { + return; + } + + for (uint64_t frame_index = 0; frame_index < stack_frames_size; ++frame_index) { + image_offset image_offset = image_offsets[frame_index]; + if (image_offset.offset && !uuid_is_null(image_offset.uuid) && + uuid_compare(image_offset.uuid, _stack_frame_uuid)) { + + _encoder.encode_varint(field_number * 8 + 2); + _encoder.begin_length_delimited(); + + const uuid_t cached_uuid = {}; + uint64_t image_offset_id = _image_offset_cache.lookup(image_offset.uuid, &cached_uuid); + if (!cached_uuid) { + uuid_t *key = _heap.alloc(); + uuid_copy(*key, image_offset.uuid); + + image_offset_id = _image_offset_cache.count(); + _image_offset_cache.insert(*key, image_offset_id); + + _encoder.encode_varint(0x1a); + _encoder.begin_length_delimited(); + + uuid_string_t uuid_string = {}; + uuid_unparse(*key, uuid_string); + + _encoder.encode_varint(0xa); + _encoder.encode_data(uuid_string, sizeof(uuid_string_t) - 1); // don't encode trailing NULL character + + Dl_info dl_info; + if (dladdr(stack_frames_buffer[frame_index], &dl_info)) { + if (dl_info.dli_fname) { + auto length = strlen(dl_info.dli_fname); + _encoder.encode_varint(0x12); + _encoder.encode_data((void *)dl_info.dli_fname, length); + } + if (dl_info.dli_fbase) { + _encoder.encode_varint(0x18); + _encoder.encode_varint((uintptr_t)dl_info.dli_fbase); + + // TODO: what is the correct ptrauth key? + mach_vm_address_t address = + (mach_vm_address_t)ptrauth_strip(dl_info.dli_fbase, ptrauth_key_process_independent_code); + mach_vm_size_t size = 0; + vm_region_info_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + mach_port_t object_name = MACH_PORT_NULL; + kern_return_t status = mach_vm_region(mach_task_self(), &address, &size, + VM_REGION_BASIC_INFO_64, info, &info_count, &object_name); + if (object_name) { + mach_port_deallocate(mach_task_self(), object_name); + } + if (status == KERN_SUCCESS) { + if (size) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(size); + } + } + } + } + + _encoder.end_length_delimited(); + } + + if (image_offset_id) { + _encoder.encode_varint(8); + _encoder.encode_varint(image_offset_id); + } + if (image_offset.offset) { + _encoder.encode_varint(0x10); + _encoder.encode_varint(image_offset.offset); + } + + _encoder.end_length_delimited(); + } + } +} + +int Graph::TraceRecorder::flush_encoder(Encoder &encoder) { + int fd = -1; + if (_trace_file_exists) { + fd = open(_trace_path, O_WRONLY | O_APPEND, 0666); + } else { + _trace_file_exists = true; + + const char *trace_file = getenv("AG_TRACE_FILE"); + if (!trace_file) { + trace_file = "trace"; + } + + const char *dir = getenv("TMPDIR"); + if (!dir || !*dir) { + dir = "/tmp"; + } + + const char *separator = dir[strlen(dir) - 1] == '/' ? "" : "/"; + + char *attempted_file_name = nullptr; + for (int attempt = 1; attempt <= 999; ++attempt) { + asprintf(&attempted_file_name, "%s%s%s-%04d.ag-trace", dir, separator, trace_file, attempt); + fd = open(attempted_file_name, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (fd != -1) { + break; + } + if (attempted_file_name) { + free(attempted_file_name); + attempted_file_name = nullptr; + } + if (errno != EEXIST) { + break; + } + } + + const char *old_file_name = _trace_path; + _trace_path = attempted_file_name; + if (old_file_name) { + free((void *)old_file_name); + } + + if (_trace_path) { + os_log(misc_log(), "created trace file %s", _trace_path); + fprintf(stdout, "created trace file \"%s\" (pid %d)\n", _trace_path, getpid()); + } else { + fprintf(stdout, "failed to create trace file: %s%s%s-XXXX.ag-trace\n", dir, separator, trace_file); + } + } + if (fd == -1) { + return; + } + + const char *buffer = encoder.buffer().data(); + size_t remaining = encoder.buffer().size(); + while (remaining > 0) { + ssize_t written = write(fd, buffer, remaining); + if (written < 0) { + if (errno == EINTR) { + // try again on interrupted error + continue; + } + unlink(_trace_path); + break; + } + buffer += written; + remaining -= written; + } + return close(fd); +} + +#pragma mark - Trace methods + +void Graph::TraceRecorder::begin_trace(const Graph &graph) { + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(1); + field_timestamp(_encoder); + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_trace(const Graph &graph) { + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(2); + field_timestamp(_encoder); + _encoder.end_length_delimited(); + + encode_snapshot(); +} + +void Graph::TraceRecorder::sync_trace() { + encode_snapshot(); + _encoder.flush(); +} + +void Graph::TraceRecorder::log_message_v(const char *format, va_list args) { + char *message = nullptr; + vasprintf(&message, format, args); + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x33); + field_timestamp(_encoder); + field_backtrace(_encoder, 8); + + size_t length = strlen(message); + if (length > 0) { + _encoder.encode_varint(0x4a); + _encoder.encode_data(message, length); + } + + _encoder.end_length_delimited(); + + encode_stack(); + + if (message) { + free(message); + } +} + +void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t options) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(3); + field_timestamp(_encoder); + + auto zone_id = subgraph.info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(zone_id); + } + if (options) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(options); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_update(const Subgraph &subgraph) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(4); + field_timestamp(_encoder); + + auto zone_id = subgraph.info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(zone_id); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::begin_update(const Graph::UpdateStack &update_stack, data::ptr node, + uint32_t options) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(5); + field_timestamp(_encoder); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (options) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(options); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_update(const Graph::UpdateStack &update_stack, data::ptr node, + Graph::UpdateStatus update_status) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(6); + field_timestamp(_encoder); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (update_status == Graph::UpdateStatus::Changed) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(Graph::UpdateStatus::Changed); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::begin_update(data::ptr node) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(7); + field_timestamp(_encoder); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_update(data::ptr node, bool changed) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(8); + field_timestamp(_encoder); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (changed) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(true); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::begin_update(const Graph::Context &context) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(9); + field_timestamp(_encoder); + + if (context.unique_id()) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(context.unique_id()); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_update(const Graph::Context &context) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(10); + field_timestamp(_encoder); + + if (context.unique_id()) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(context.unique_id()); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::begin_invalidation(const Graph::Context &context, AttributeID attribute) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0xb); + field_timestamp(_encoder); + + if (attribute) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(attribute); + } + if (context.unique_id()) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(context.unique_id()); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_invalidation(const Graph::Context &context, AttributeID attribute) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0xc); + field_timestamp(_encoder); + + if (attribute) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(attribute); + } + if (context.unique_id()) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(context.unique_id()); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::begin_modify(data::ptr node) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0xd); + field_timestamp(_encoder); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_modify(data::ptr node) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0xe); + field_timestamp(_encoder); + + if (node || _options & Options::SkipUpdates) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(1); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0xf); + field_timestamp(_encoder); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (event) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(event); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x10); + field_timestamp(_encoder); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (event) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(event); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::created(const Graph::Context &context) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x20); + + if (context.unique_id()) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(context.unique_id()); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::destroy(const Graph::Context &context) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x21); + + if (context.unique_id()) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(context.unique_id()); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::needs_update(const Graph::Context &context) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x22); + + if (context.unique_id()) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(context.unique_id()); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::created(const Subgraph &subgraph) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x23); + + auto zone_id = subgraph.info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(zone_id); + } + if (subgraph.graph_context_id()) { + _encoder.encode_varint(0x28); + _encoder.encode_varint(subgraph.graph_context_id()); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::invalidate(const Subgraph &subgraph) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x24); + + auto zone_id = subgraph.info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(zone_id); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); + + _encoder.begin_length_delimited(); + _encoder.encode_varint(0x12); + subgraph.encode(_encoder); + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::destroy(const Subgraph &subgraph) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x35); + + auto zone_id = subgraph.info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(zone_id); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::add_child(const Subgraph &subgraph, const Subgraph &child) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x25); + + auto zone_id = subgraph.info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(zone_id); + } + auto child_zone_id = child.info().zone_id(); + if (child_zone_id) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(child_zone_id); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::remove_child(const Subgraph &subgraph, const Subgraph &child) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x26); + + auto zone_id = subgraph.info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(zone_id); + } + auto child_zone_id = child.info().zone_id(); + if (child_zone_id) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(child_zone_id); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::added(data::ptr node) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x27); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + auto zone_id = AttributeID(node).subgraph()->info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(zone_id); + } + auto type_id = node->type_id(); + if (type_id) { + _encoder.encode_varint(0x28); + _encoder.encode_varint(type_id); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x2f); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (input) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(input); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_index) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x30); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + auto input_edge = node->inputs()[input_index]; + if (input_edge.value) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(input_edge.value); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input_index, bool pending) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x30); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + auto input_edge = node->inputs()[input_index]; + if (input_edge.value) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(input_edge.value); + } + if (pending) { + _encoder.encode_varint(0x28); + _encoder.encode_varint(1); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::set_dirty(data::ptr node, bool dirty) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x28); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (dirty) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(1); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::set_pending(data::ptr node, bool pending) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x29); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + if (pending) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(1); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::set_value(data::ptr node, const void *value) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x2a); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::mark_value(data::ptr node) { + if (_options & Options::SkipUpdates) { + return; + } + if ((_options & Options::RecordInvalidations) == 0) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x2b); + + if (node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(node); + } + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::added(data::ptr indirect_node) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x2c); + + if (indirect_node) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(AttributeID(indirect_node)); + } + auto zone_id = AttributeID(indirect_node).subgraph()->info().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(zone_id); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::set_source(data::ptr indirect_node, AttributeID source) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x2d); + + _encoder.encode_varint(0x18); + _encoder.encode_varint(AttributeID(indirect_node)); + auto source_attribute = indirect_node->source().attribute(); + if (source_attribute) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(source_attribute); + } + auto zone_id = indirect_node->source().zone_id(); + if (zone_id) { + _encoder.encode_varint(0x28); + _encoder.encode_varint(zone_id); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::set_dependency(data::ptr indirect_node, AttributeID dependency) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x2e); + + _encoder.encode_varint(0x18); + _encoder.encode_varint(AttributeID(indirect_node)); + auto dependency_attribute = indirect_node->to_mutable().dependency(); + if (dependency_attribute) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(dependency_attribute); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::set_deadline(uint64_t deadline) { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x37); + + field_timestamp(_encoder); + + if (deadline & 0xffffffff) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(deadline & 0xffffffff); + } + if (deadline >> 32) { + _encoder.encode_varint(0x20); + _encoder.encode_varint(deadline >> 32); + } + + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::passed_deadline() { + if (_options & Options::SkipUpdates) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x38); + + field_timestamp(_encoder); + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); + + encode_stack(); +} + +void Graph::TraceRecorder::mark_profile(const Graph &graph, uint32_t options) { + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x32); + + field_timestamp(_encoder); + if (options) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(options); + } + field_backtrace(_encoder, 8); + + _encoder.end_length_delimited(); +} + +void Graph::TraceRecorder::custom_event(const Graph::Context &context, const char *event_name, const void *value, + const swift::metadata &type) { + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x34); + + field_timestamp(_encoder); + field_backtrace(_encoder, 8); + + auto length = strlen(event_name); + if (length > 0) { + _encoder.encode_varint(0x4a); + _encoder.encode_data((void *)event_name, length); + } + + _encoder.end_length_delimited(); +} + +bool Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, + const uint64_t *event_args, CFDataRef data, uint32_t arg6) { + if (!named_event_enabled(arg2)) { + return; + } + + _encoder.encode_varint(10); + _encoder.begin_length_delimited(); + _encoder.encode_varint(8); + _encoder.encode_varint(0x36); + + if (arg2) { + _encoder.encode_varint(0x50); + _encoder.encode_varint(arg2); + } + + field_timestamp(_encoder); + + if (arg6 != 0) { + if (arg6 & 0x80000000) { + arg6 &= 0x7fffffff; + } + if (arg6) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(arg6); + } + } + + if (num_args > 3) { + num_args = 4; + } + for (auto i = 0; i < num_args; ++i) { + uint64_t event_arg = event_args[i]; + if (event_arg) { + _encoder.encode_varint(0x20 + 8 * i); + _encoder.encode_varint(event_arg); + } + } + + if (data != nullptr) { + void *ptr = (void *)CFDataGetBytePtr(data); + uint64_t length = CFDataGetLength(data); + if (length > 0) { + _encoder.encode_varint(0x4a); + _encoder.encode_data(ptr, length); + } + } + + _encoder.end_length_delimited(); +} + +bool Graph::TraceRecorder::named_event_enabled(uint32_t event_id) { + uint32_t index = 0; + if (!_named_event_infos.empty()) { + auto pos = std::lower_bound( + _named_event_infos.begin(), _named_event_infos.end(), event_id, + [](const NamedEventInfo &info, uint32_t event_id) -> bool { return info.event_id < event_id; }); + if (pos != _named_event_infos.end() && pos->event_id == event_id) { + return pos->enabled; + } + index = (uint32_t)(pos - _named_event_infos.begin()); // TODO: specify difference_type on AG::vector::iterator + } + + const char *event_name = AGGraphGetTraceEventName(event_id); + if (event_name == nullptr) { + precondition_failure("invalid named trace event: %u", event_id); + } + + const char *event_subsystem = AGGraphGetTraceEventSubsystem(event_id); + + bool enabled = false; + if (event_subsystem == nullptr || (_options & Options::Subsystem)) { + enabled = true; + } else { + enabled = (_options & Options::Subsystem) != 0; + for (auto stored_subsystem : _named_event_subsystems) { + if (!strcasecmp(stored_subsystem, event_subsystem)) { + enabled = true; + break; + } + } + } + _named_event_infos.insert(_named_event_infos.begin() + index, {event_id, enabled}); + + if (!enabled) { + return false; + } + + _encoder.encode_varint(0x32); + _encoder.begin_length_delimited(); + if (event_id) { + _encoder.encode_varint(8); + _encoder.encode_varint(event_id); + } + + auto event_name_length = strlen(event_name); + if (event_name_length > 0) { + _encoder.encode_varint(0x12); + _encoder.encode_data((void *)event_name, event_name_length); + } + auto event_subsystem_length = strlen(event_subsystem); + if (event_subsystem_length > 0) { + _encoder.encode_varint(0x1a); + _encoder.encode_data((void *)event_subsystem, event_subsystem_length); + } + + _encoder.end_length_delimited(); + + return true; +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h new file mode 100644 index 0000000..f3dcc37 --- /dev/null +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -0,0 +1,139 @@ +#pragma once + +#include +#include + +#include "Encoder/Encoder.h" +#include "Trace/Trace.h" +#include "Utilities/HashTable.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Graph::TraceRecorder : public Encoder::Delegate, public Trace { + public: + enum Options : uint8_t { + RecordInvalidations = 1 << 1, + RecordBacktrace = 1 << 2, + SkipUpdates = 1 << 4, + Subsystem = 1 << 5, + }; + + private: + uint64_t _unique_id; + + Encoder::Delegate *_Nonnull _delegate; + Graph *_graph; + Encoder _encoder; + uint8_t _options; + + util::Heap _heap; + char _heap_inline_buffer[256]; + + vector _named_event_subsystems; + + util::Table _image_offset_cache; + uuid_t _stack_frame_uuid; + + const char *_Nullable _trace_path = nullptr; + bool _trace_file_exists = false; + + uint32_t _num_encoded_types = 1; // skip AGAttributeNullType + uint32_t _num_encoded_keys = 0; + + struct NamedEventInfo { + uint32_t event_id; + bool enabled; + }; + vector _named_event_infos; + + public: + TraceRecorder(Graph *graph, uint8_t options, std::span subsystems); + ~TraceRecorder(); + + uint64_t unique_id() { return _unique_id; }; + + const char *_Nullable trace_path() const { return _trace_path; }; + + void encode_types(); + void encode_keys(); + void encode_stack(); + void encode_snapshot(); + + void field_timestamp(Encoder &encoder); + void field_backtrace(Encoder &encoder, uint64_t field); + + int flush_encoder(Encoder &encoder); + + // MARK: Trace methods + + void begin_trace(const Graph &graph); + void end_trace(const Graph &graph); + void sync_trace(); + + void log_message_v(const char *format, va_list args); + + void begin_update(const Subgraph &subgraph, uint32_t options); + void end_update(const Subgraph &subgraph); + void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options); + void end_update(const Graph::UpdateStack &update_stack, data::ptr node, Graph::UpdateStatus update_status); + void begin_update(data::ptr node); + void end_update(data::ptr node, bool changed); + void begin_update(const Graph::Context &context); + void end_update(const Graph::Context &context); + + void begin_invalidation(const Graph::Context &context, AttributeID attribute); + void end_invalidation(const Graph::Context &context, AttributeID attribute); + + void begin_modify(data::ptr node); + void end_modify(data::ptr node); + + void begin_event(data::ptr node, uint32_t event); + void end_event(data::ptr node, uint32_t event); + + void created(const Graph::Context &context); + void destroy(const Graph::Context &context); + void needs_update(const Graph::Context &context); + + void created(const Subgraph &subgraph); + void invalidate(const Subgraph &subgraph); + void destroy(const Subgraph &subgraph); + + void add_child(const Subgraph &subgraph, const Subgraph &child); + void remove_child(const Subgraph &subgraph, const Subgraph &child); + + void added(data::ptr node); + + void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags); + void remove_edge(data::ptr node, uint32_t input_index); + void set_edge_pending(data::ptr node, uint32_t input_index, bool pending); + + void set_dirty(data::ptr node, bool dirty); + void set_pending(data::ptr node, bool pending); + + void set_value(data::ptr node, const void *value); + void mark_value(data::ptr node); + + void added(data::ptr indirect_node); + + void set_source(data::ptr indirect_node, AttributeID source); + void set_dependency(data::ptr indirect_node, AttributeID dependency); + + void set_deadline(uint64_t deadline); + void passed_deadline(); + + void mark_profile(const Graph &graph, uint32_t options); + + void custom_event(const Graph::Context &context, const char *event_name, const void *value, + const swift::metadata &type); + bool named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, + CFDataRef data, uint32_t arg6); + bool named_event_enabled(uint32_t event_id); + + // compare_failed not overridden +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 08c002f..2aa3c13 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -5,8 +5,8 @@ #include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" #include "Attribute/OffsetAttributeID.h" -#include "Graph/Trace.h" #include "Subgraph/Subgraph.h" +#include "Trace/Trace.h" namespace AG { @@ -287,16 +287,16 @@ Graph::UpdateStatus Graph::UpdateStack::update() { for (uint32_t input_index = node->inputs().size() - 1; input_index >= 0; --input_index) { InputEdge &input = node->inputs()[input_index]; AttributeID input_attribute = input.value; - + bool reset_input_flags = true; if (frame.flag3 || frame.cancelled) { input_attribute = input_attribute.resolve(TraversalOptions::None).attribute(); if (!input_attribute.is_direct() || input_attribute.to_node().state().is_dirty()) { reset_input_flags = false; - reset_node_flags = false; + reset_node_flags = false; } } - + if (reset_input_flags) { if (frame.needs_update && !frame.cancelled) { if (input.is_pending()) { @@ -307,7 +307,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { } } } - + if (frame.needs_update && !frame.cancelled) { bool was_unknown4 = input.is_unknown4(); input.set_unknown4(false); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 2fa31ad..279336c 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -11,10 +11,10 @@ #include "Errors/Errors.h" #include "Graph/Context.h" #include "Graph/Graph.h" -#include "Graph/Trace.h" #include "Graph/Tree/TreeElement.h" #include "Graph/UpdateStack.h" #include "NodeCache.h" +#include "Trace/Trace.h" #include "UniqueID/AGUniqueID.h" #include "Utilities/CFPointer.h" @@ -1082,8 +1082,7 @@ void Subgraph::cache_collect() { #pragma mark - Encoding -void Subgraph::encode(Encoder &encoder) { - +void Subgraph::encode(Encoder &encoder) const { auto zone_id = info().zone_id(); if (zone_id != 0) { encoder.encode_varint(8); @@ -1113,47 +1112,32 @@ void Subgraph::encode(Encoder &encoder) { if (!is_valid()) { encoder.encode_varint(0x28); - encoder.encode_varint(true); - } - - if (last_page() == nullptr) { - if (_tree_root) { - encoder.encode_varint(0x3a); - encoder.begin_length_delimited(); - _graph->encode_tree(encoder, _tree_root); - encoder.end_length_delimited(); - } - return; + encoder.encode_varint(1); } for (data::ptr page = last_page(); page != nullptr; page = page->previous) { - bool bVar4 = true; - bool bVar3 = true; - do { - bVar3 = bVar4; - - uint16_t relative_offset = bVar3 ? page->relative_offset_1 : page->relative_offset_2; + for (uint32_t iteration = 0; iteration < 2; ++iteration) { + uint16_t relative_offset = iteration == 0 ? page->relative_offset_1 : page->relative_offset_2; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); if (attribute.is_direct()) { - attribute.to_node().flags().relative_offset(); + relative_offset = attribute.to_node().flags().relative_offset(); } else if (attribute.is_indirect()) { - attribute.to_indirect_node().relative_offset(); + relative_offset = attribute.to_indirect_node().relative_offset(); + } else if (attribute.is_nil() || relative_offset == 0) { + break; } else { - if (attribute.is_nil() || relative_offset == 0) { - break; - } continue; } encoder.encode_varint(0x32); encoder.begin_length_delimited(); - if (attribute.to_node_ptr() == nullptr) { + if (attribute == 0) { encoder.encode_varint(0x12); encoder.begin_length_delimited(); - _graph->encode_node(encoder, attribute.to_node(), false); + _graph->encode_node(encoder, attribute.to_node(), false); // TODO: check can handle null attribute encoder.end_length_delimited(); } else { encoder.encode_varint(8); @@ -1174,8 +1158,14 @@ void Subgraph::encode(Encoder &encoder) { encoder.end_length_delimited(); } - bVar4 = false; - } while (bVar3); + } + } + + if (_tree_root) { + encoder.encode_varint(0x3a); + encoder.begin_length_delimited(); + _graph->encode_tree(encoder, _tree_root); + encoder.end_length_delimited(); } } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index d1a4a9a..986bce6 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -107,9 +107,9 @@ class Subgraph : public data::zone { // MARK: Graph Graph *_Nullable graph() const { return _graph; }; - uint64_t graph_context_id() { return _graph_context_id; }; + uint64_t graph_context_id() const { return _graph_context_id; }; - bool is_valid() { return _validation_state == ValidationState::Valid; }; + bool is_valid() const { return _validation_state == ValidationState::Valid; }; ValidationState validation_state() { return _validation_state; }; uint8_t other_state() { return _other_state; }; @@ -189,7 +189,7 @@ class Subgraph : public data::zone { // MARK: Encoding - void encode(Encoder &encoder); + void encode(Encoder &encoder) const; // MARK: Printing diff --git a/Sources/ComputeCxx/Trace/AGTrace.cpp b/Sources/ComputeCxx/Trace/AGTrace.cpp new file mode 100644 index 0000000..4e5c414 --- /dev/null +++ b/Sources/ComputeCxx/Trace/AGTrace.cpp @@ -0,0 +1,11 @@ +#include "AGTrace.h" + +const char *AGGraphGetTraceEventName(uint32_t event_id) { + // TODO: not implemented + return nullptr; +} + +const char *AGGraphGetTraceEventSubsystem(uint32_t event_id) { + // TODO: not implemented + return nullptr; +} diff --git a/Sources/ComputeCxx/Trace/AGTrace.h b/Sources/ComputeCxx/Trace/AGTrace.h new file mode 100644 index 0000000..b6a6ef6 --- /dev/null +++ b/Sources/ComputeCxx/Trace/AGTrace.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +CF_EXPORT +CF_REFINED_FOR_SWIFT +const char *AGGraphGetTraceEventName(uint32_t event_id); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +const char *AGGraphGetTraceEventSubsystem(uint32_t event_id); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Trace.cpp b/Sources/ComputeCxx/Trace/Trace.cpp similarity index 100% rename from Sources/ComputeCxx/Graph/Trace.cpp rename to Sources/ComputeCxx/Trace/Trace.cpp diff --git a/Sources/ComputeCxx/Graph/Trace.h b/Sources/ComputeCxx/Trace/Trace.h similarity index 89% rename from Sources/ComputeCxx/Graph/Trace.h rename to Sources/ComputeCxx/Trace/Trace.h index 5cdb6b4..dd813da 100644 --- a/Sources/ComputeCxx/Graph/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -2,7 +2,7 @@ #include -#include "Graph.h" +#include "Graph/Graph.h" namespace AG { @@ -61,11 +61,11 @@ class Trace { virtual void added(data::ptr node); virtual void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags); - virtual void remove_edge(data::ptr node, uint32_t options); + virtual void remove_edge(data::ptr node, uint32_t input_index); virtual void set_edge_pending(data::ptr node, uint32_t input_index, bool pending); - virtual void set_dirty(data::ptr node, bool flag); - virtual void set_pending(data::ptr node, bool flag); + virtual void set_dirty(data::ptr node, bool dirty); + virtual void set_pending(data::ptr node, bool pending); virtual void set_value(data::ptr node, const void *value); virtual void mark_value(data::ptr node); @@ -82,9 +82,9 @@ class Trace { virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type); - virtual bool named_event(const Graph::Context &context, uint32_t arg2, uint64_t arg3, const uint32_t *arg4, - CFDataRef arg5, uint32_t arg6); - virtual bool named_event_enabled(uint32_t options); + virtual bool named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, + const uint64_t *event_args, CFDataRef data, uint32_t arg6); + virtual bool named_event_enabled(uint32_t event_id); virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t lhs_offset, size_t rhs_offset, const swift::metadata &type); From f0c437e926f72bb604f19db6ba28bb9705d99988 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 14 Feb 2025 09:08:02 +1100 Subject: [PATCH 26/74] Implement Graph::prepare_trace --- Sources/ComputeCxx/Graph/Graph.cpp | 103 ++++++++++++++++++++++- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 19 +++++ Sources/ComputeCxx/Subgraph/Subgraph.h | 1 + 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 0135e0a..14c341c 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1063,6 +1063,8 @@ void Graph::value_mark_all() { relative_offset = attribute.to_node().flags().relative_offset(); } else if (attribute.is_indirect()) { relative_offset = attribute.to_indirect_node().relative_offset(); + } else { + relative_offset = 0; } if (attribute.is_direct()) { @@ -1836,7 +1838,106 @@ void Graph::encode_tree(Encoder &encoder, data::ptr tree) { #pragma mark - Tracing void Graph::prepare_trace(Trace &trace) { - // TODO: not implemented + _contexts_by_id.for_each([](const uint64_t context_id, Context *const context, + void *trace_ref) { ((Trace *)trace_ref)->created(*context); }, + &trace); + + for (auto subgraph : _subgraphs) { + trace.created(*subgraph); + } + for (auto subgraph : _subgraphs) { + for (auto child : subgraph->children()) { + trace.add_child(*subgraph, *child.subgraph()); + } + } + for (auto subgraph : _subgraphs) { + for (uint32_t iteration = 0; iteration < 2; ++iteration) { + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + bool should_break = false; + uint16_t relative_offset = iteration == 0 ? page->relative_offset_2 : page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else if (attribute.is_nil()) { + relative_offset = 0; + should_break = true; + break; + } else { + relative_offset = 0; + } + + if (attribute.is_direct()) { + auto node = attribute.to_node_ptr(); + trace.added(node); + if (node->state().is_dirty()) { + trace.set_dirty(node, true); + } + if (node->state().is_pending()) { + trace.set_pending(node, true); + } + if (node->state().is_value_initialized()) { + void *value = node->get_value(); + trace.set_value(node, value); + } + } else if (attribute.is_indirect()) { + auto indirect_node = attribute.to_indirect_node_ptr(); + trace.added(indirect_node); + } + } + if (should_break) { + break; + } + } + } + } + for (auto subgraph : _subgraphs) { + for (uint32_t iteration = 0; iteration < 2; ++iteration) { + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + bool should_break = false; + uint16_t relative_offset = iteration == 0 ? page->relative_offset_2 : page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else if (attribute.is_nil()) { + relative_offset = 0; + should_break = true; + break; + } else { + relative_offset = 0; + } + + if (attribute.is_direct()) { + auto node = attribute.to_node_ptr(); + uint32_t edge_index = 0; + for (auto input_edge : node->inputs()) { + trace.add_edge(node, input_edge.value, input_edge.is_unknown2()); + if (input_edge.is_pending()) { + trace.set_edge_pending(node, edge_index, true); + } + edge_index += 1; + } + } else if (attribute.is_indirect()) { + auto indirect_node = attribute.to_indirect_node_ptr(); + trace.set_source(indirect_node, indirect_node->source().attribute()); + if (indirect_node->is_mutable() && indirect_node->to_mutable().dependency() != 0) { + trace.set_dependency(indirect_node, indirect_node->to_mutable().dependency()); + } + } + } + if (should_break) { + break; + } + } + } + } } void Graph::add_trace(Trace *_Nullable trace) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 279336c..2e41703 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -216,7 +216,10 @@ void Subgraph::invalidate_now(Graph &graph) { relative_offset = attribute.to_indirect_node().relative_offset(); graph.remove_indirect_node(attribute.to_indirect_node_ptr()); } else if (attribute.is_nil()) { + relative_offset = 0; found_nil_attribute = true; + } else { + relative_offset = 0; } } if (found_nil_attribute) { @@ -238,7 +241,10 @@ void Subgraph::invalidate_now(Graph &graph) { } else if (attribute.is_indirect()) { relative_offset = attribute.to_indirect_node().relative_offset(); } else if (attribute.is_nil()) { + relative_offset = 0; found_nil_attribute = true; + } else { + relative_offset = 0; } if (attribute.is_direct()) { @@ -283,6 +289,8 @@ void Subgraph::graph_destroyed() { relative_offset = attribute.to_node().flags().relative_offset(); } else if (attribute.is_indirect()) { relative_offset = attribute.to_indirect_node().relative_offset(); + } else { + relative_offset = 0; } if (attribute.is_direct()) { @@ -415,6 +423,8 @@ void Subgraph::insert_attribute(AttributeID attribute, bool flag) { if (relative_offset == 0) { break; } + } else { + relative_offset = 0; // TODO: check this line is here } } } @@ -471,6 +481,8 @@ void Subgraph::unlink_attribute(AttributeID attribute) { } else if (next_attribute.is_indirect()) { relative_offset = next_attribute.to_indirect_node().relative_offset(); before_attribute = next_attribute; + } else { + relative_offset = 0; } } @@ -574,6 +586,8 @@ void Subgraph::update(uint8_t flags) { if (flags) { break; } + } else { + relative_offset = 0; } } } @@ -672,6 +686,8 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { if (!flags.is_null()) { break; } + } else { + relative_offset = 0; } } } @@ -1128,6 +1144,7 @@ void Subgraph::encode(Encoder &encoder) const { } else if (attribute.is_nil() || relative_offset == 0) { break; } else { + relative_offset = 0; continue; } @@ -1192,6 +1209,8 @@ void Subgraph::print(uint32_t indent_level) { relative_offset = attribute.to_node().flags().relative_offset(); } else if (attribute.is_indirect()) { relative_offset = attribute.to_indirect_node().relative_offset(); + } else { + relative_offset = 0; } if (attribute.is_direct()) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 986bce6..fa7b78a 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -123,6 +123,7 @@ class Subgraph : public data::zone { void add_child(Subgraph &child, SubgraphChild::Flags flags); void remove_child(Subgraph &child, bool flag); + vector children() { return _children; }; bool ancestor_of(const Subgraph &other); From ff931edcc9dceaba12471d38282c2a31bf32c9ab Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 14 Feb 2025 09:18:29 +1100 Subject: [PATCH 27/74] Implement remaining trace methods on Graph --- Sources/ComputeCxx/Graph/Graph.cpp | 12 ++++++++++-- Sources/ComputeCxx/Graph/Graph.h | 2 +- Sources/ComputeCxx/Graph/TraceRecorder.h | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 14c341c..d22d8ab 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1957,8 +1957,16 @@ void Graph::remove_trace(uint64_t trace_id) { _traces.erase(iter); } -void Graph::start_tracing(uint32_t arg, std::span span) { - // TODO: not implemented +void Graph::start_tracing(uint8_t options, std::span subsystems) { + if (options & TraceRecorder::Options::CreateIfNeeded && _trace_recorder == nullptr) { + _trace_recorder = new TraceRecorder(this, options, subsystems); + if (options & TraceRecorder::Options::PrepareTrace) { + prepare_trace(*_trace_recorder); + } + add_trace(_trace_recorder); + + // TODO: cleanup block + } } void Graph::stop_tracing() { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 37bc120..1ec716f 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -347,7 +347,7 @@ class Graph { void add_trace(Trace *_Nullable trace); void remove_trace(uint64_t trace_id); - void start_tracing(uint32_t arg, std::span span); + void start_tracing(uint8_t options, std::span subsystems); void stop_tracing(); void sync_tracing(); CFStringRef copy_trace_path(); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index f3dcc37..f3ba424 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -14,8 +14,10 @@ namespace AG { class Graph::TraceRecorder : public Encoder::Delegate, public Trace { public: enum Options : uint8_t { + CreateIfNeeded = 1 << 0, RecordInvalidations = 1 << 1, RecordBacktrace = 1 << 2, + PrepareTrace = 1 << 3, SkipUpdates = 1 << 4, Subsystem = 1 << 5, }; From 98f910fcd42cbe06bf34051862b70ca42bf3876f Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 14 Feb 2025 23:16:14 +1100 Subject: [PATCH 28/74] Implement encode methods on Graph --- Sources/ComputeCxx/Attribute/AttributeType.h | 5 +- .../Attribute/Node/IndirectNode.cpp | 5 + .../ComputeCxx/Attribute/Node/IndirectNode.h | 3 +- Sources/ComputeCxx/Attribute/Node/Node.cpp | 4 +- Sources/ComputeCxx/Attribute/Node/Node.h | 19 +- Sources/ComputeCxx/Graph/Graph.cpp | 191 +++++++++++++++++- 6 files changed, 210 insertions(+), 17 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index d5393bd..49fa70a 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -15,11 +15,12 @@ class AttributeType; class AttributeVTable { public: using Callback = void (*)(const AttributeType *attribute_type, void *body); + using Callback2 = CFStringRef _Nonnull (*)(); Callback _unknown_0x00; Callback _unknown_0x08; Callback destroy_self; Callback _unknown_0x18; - Callback _unknown_0x20; + Callback2 _encode_node_callback; Callback _update_stack_callback; // maybe initialize value }; @@ -84,6 +85,8 @@ class AttributeType { } } + AttributeVTable::Callback2 vt_get_encode_node_callback() const { return _vtable->_encode_node_callback; } + AttributeVTable::Callback vt_get_update_stack_callback() const { return _vtable->_update_stack_callback; } }; diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp b/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp index d9cd851..ed31673 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.cpp @@ -9,6 +9,11 @@ MutableIndirectNode &IndirectNode::to_mutable() { return static_cast(*this); } +const MutableIndirectNode &IndirectNode::to_mutable() const { + assert(is_mutable()); + return static_cast(*this); +} + void IndirectNode::modify(WeakAttributeID source, uint32_t offset) { _source = source; _info.offset = offset; diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index 64e6a1b..a0ae007 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -42,6 +42,7 @@ class IndirectNode { bool is_mutable() const { return _info.is_mutable; }; MutableIndirectNode &to_mutable(); + const MutableIndirectNode &to_mutable() const; void set_traverses_graph_contexts(bool value) { _info.traverses_graph_contexts = value; }; bool traverses_graph_contexts() const { return _info.traverses_graph_contexts; }; @@ -81,7 +82,7 @@ class MutableIndirectNode : public IndirectNode { WeakAttributeID initial_source() { return _initial_source; }; uint32_t initial_offset() { return _initial_offset; }; - data::vector outputs() { return _outputs; }; + data::vector outputs() const { return _outputs; }; }; static_assert(sizeof(MutableIndirectNode) == 0x28); diff --git a/Sources/ComputeCxx/Attribute/Node/Node.cpp b/Sources/ComputeCxx/Attribute/Node/Node.cpp index 6f3901f..a3ae575 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.cpp +++ b/Sources/ComputeCxx/Attribute/Node/Node.cpp @@ -8,7 +8,7 @@ namespace AG { -void *Node::get_self(const AttributeType &type) { +void *Node::get_self(const AttributeType &type) const { void *self = ((char *)this + type.attribute_offset()); if (_flags.has_indirect_self()) { self = *(void **)self; @@ -43,7 +43,7 @@ void Node::destroy_self(const Graph &graph) { type.self_metadata().vw_destroy(static_cast(self)); } -void *Node::get_value() { +void *Node::get_value() const { void *value = _value.get(); if (_flags.has_indirect_value()) { value = *(void **)value; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index ba28877..797fba8 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -43,7 +43,7 @@ class NodeFlags { void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; // Flags 3 - uint8_t value3() { return _value3; }; + uint8_t value3() const { return _value3; }; void set_value3(uint8_t value3) { _value3 = value3; }; // Flags 4 @@ -64,13 +64,13 @@ class NodeFlags { void set_inputs_unsorted(bool value) { _value4 = (_value4 & ~Flags4::InputsUnsorted) | (value ? Flags4::InputsUnsorted : 0); }; - bool cacheable() { return _value4 & Flags4::Cacheable; }; + bool cacheable() const { return _value4 & Flags4::Cacheable; }; void set_cacheable(bool value) { _value4 = (_value4 & ~Flags4::Cacheable) | (value ? Flags4::Cacheable : 0); }; - bool value4_unknown0x20() { return _value4 & Flags4::Unknown0x20; }; + bool value4_unknown0x20() const { return _value4 & Flags4::Unknown0x20; }; void set_value4_unknown0x20(bool value) { _value4 = (_value4 & ~Flags4::Unknown0x20) | (value ? Flags4::Unknown0x20 : 0); }; - bool value4_unknown0x40() { return _value4 & Flags4::Unknown0x40; }; + bool value4_unknown0x40() const { return _value4 & Flags4::Unknown0x40; }; void set_value4_unknown0x40(bool value) { _value4 = (_value4 & ~Flags4::Unknown0x40) | (value ? Flags4::Unknown0x40 : 0); }; @@ -151,11 +151,12 @@ class Node { _flags = NodeFlags(flags4); }; - State state() { return State(_info.state); }; + State state() const { return State(_info.state); }; void set_state(State state) { _info.state = state.data(); }; uint32_t type_id() const { return uint32_t(_info.type_id); }; NodeFlags &flags() { return _flags; }; + const NodeFlags &flags() const { return _flags; }; void sort_inputs_if_needed() { if (_flags.inputs_unsorted()) { @@ -164,18 +165,18 @@ class Node { } } - void *get_self(const AttributeType &type); // TODO: inline + void *get_self(const AttributeType &type) const; // TODO: inline void update_self(const Graph &graph, void *new_self); void destroy_self(const Graph &graph); - void *get_value(); + void *get_value() const; void allocate_value(Graph &graph, data::zone &zone); void destroy_value(Graph &graph); void destroy(Graph &graph); - data::vector inputs() { return _inputs; }; - data::vector outputs() { return _outputs; }; + data::vector inputs() const { return _inputs; }; + data::vector outputs() const { return _outputs; }; }; static_assert(sizeof(Node) == 0x1c); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index d22d8ab..006736c 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -19,6 +19,7 @@ #include "Swift/Metadata.h" #include "Trace/Trace.h" #include "TraceRecorder.h" +#include "Tree/TreeElement.h" #include "UpdateStack.h" #include "Utilities/List.h" @@ -1824,15 +1825,197 @@ uint32_t Graph::intern_type(swift::metadata *metadata, ClosureFunctionVP #pragma mark - Encoding void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { - // TODO: not implemented + if (node.type_id()) { + encoder.encode_varint(8); + encoder.encode_varint(node.type_id()); + } + if (flag) { + auto type = attribute_type(node.type_id()); + if (auto callback = type.vt_get_encode_node_callback()) { + void *value = node.get_value(); + CFStringRef str = callback(); + if (str) { + uint64_t length = CFStringGetLength(str); + CFRange range = CFRangeMake(0, length); + uint8_t buffer[1024]; + CFIndex used_buffer_length = 0; + CFStringGetBytes(str, range, kCFStringEncodingUTF8, 0x3f, true, buffer, 1024, &used_buffer_length); + if (used_buffer_length > 0) { + encoder.encode_varint(0x12); + encoder.encode_data(buffer, used_buffer_length); + } + } + } + } + for (auto input_edge : node.inputs()) { + encoder.encode_varint(0x1a); + encoder.begin_length_delimited(); + if (input_edge.value) { + encoder.encode_varint(8); + encoder.encode_varint(input_edge.value); + } + if (input_edge.is_unknown0()) { + encoder.encode_varint(0x10); + encoder.encode_varint(input_edge.is_unknown0()); + } + if (input_edge.is_unknown2()) { + encoder.encode_varint(0x18); + encoder.encode_varint(input_edge.is_unknown2()); + } + if (input_edge.is_pending()) { + encoder.encode_varint(0x20); + encoder.encode_varint(input_edge.is_pending()); + } + if (input_edge.is_unknown4()) { + encoder.encode_varint(0x30); + encoder.encode_varint(input_edge.is_unknown4()); + } + encoder.end_length_delimited(); + } + for (auto output_edge : node.outputs()) { + encoder.encode_varint(0x22); + encoder.begin_length_delimited(); + if (output_edge.value) { + encoder.encode_varint(8); + encoder.encode_varint(output_edge.value); + } + encoder.end_length_delimited(); + } + if (node.state().is_dirty()) { + encoder.encode_varint(0x28); + encoder.encode_varint(true); + } + if (node.state().is_pending()) { + encoder.encode_varint(0x30); + encoder.encode_varint(true); + } + if (node.state().is_updating()) { + encoder.encode_varint(0x38); + encoder.encode_varint(true); + } + if (node.flags().value3()) { + encoder.encode_varint(0x40); + encoder.encode_varint(node.flags().value3()); + } + if (node.state().is_main_thread()) { + encoder.encode_varint(0x48); + encoder.encode_varint(true); + } + if (node.state().is_main_thread_only()) { + encoder.encode_varint(0x50); + encoder.encode_varint(true); + } + if (node.flags().value4_unknown0x20()) { + encoder.encode_varint(0x58); + encoder.encode_varint(node.flags().value4_unknown0x20()); + } + if (node.state().is_value_initialized()) { + encoder.encode_varint(0x60); + encoder.encode_varint(true); + } + if (node.state().is_self_initialized()) { + encoder.encode_varint(0x68); + encoder.encode_varint(true); + } + if (node.flags().cacheable()) { + encoder.encode_varint(0x70); + encoder.encode_varint(true); + } + if (node.flags().value4_unknown0x40()) { + encoder.encode_varint(0x78); + encoder.encode_varint(true); + } } void Graph::encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_node) { - // TODO: not implemented + if (indirect_node.source().attribute()) { + encoder.encode_varint(8); + encoder.encode_varint(indirect_node.source().attribute()); + } + if (indirect_node.source().zone_id()) { + encoder.encode_varint(0x10); + encoder.encode_varint(indirect_node.source().zone_id()); + } + if (indirect_node.offset()) { + encoder.encode_varint(0x18); + encoder.encode_varint(indirect_node.offset()); + } + auto size = indirect_node.size(); + if (size.has_value() && size.value() != 0) { + encoder.encode_varint(0x20); + encoder.encode_varint(size.value()); + } + if (indirect_node.is_mutable()) { + if (indirect_node.to_mutable().dependency()) { + encoder.encode_varint(0x28); + encoder.encode_varint(indirect_node.to_mutable().dependency()); + } + for (auto output_edge : indirect_node.to_mutable().outputs()) { + encoder.encode_varint(0x32); + encoder.begin_length_delimited(); + if (output_edge.value) { + encoder.encode_varint(8); + encoder.encode_varint(output_edge.value); + } + encoder.end_length_delimited(); + } + } } void Graph::encode_tree(Encoder &encoder, data::ptr tree) { - // TODO: not implemented + if (tree->owner.without_kind()) { + encoder.encode_varint(0x10); + encoder.encode_varint(tree->owner); + } + if (tree->flags) { + encoder.encode_varint(0x18); + encoder.encode_varint(tree->flags); + } + if (tree->next) { + encoder.encode_varint(0x22); + encoder.begin_length_delimited(); + encode_tree(encoder, tree->next->old_parent); + encoder.end_length_delimited(); + } + for (auto value = tree->last_value; value != nullptr; value = value->previous_sibling) { + encoder.encode_varint(0x2a); + encoder.begin_length_delimited(); + if (value->value) { + encoder.encode_varint(0x10); + encoder.encode_varint(value->value); + } + if (value->key_id) { + encoder.encode_varint(0x18); + encoder.encode_varint(value->key_id); + } + if (value->flags) { + encoder.encode_varint(0x20); + encoder.encode_varint(value->flags); + } + encoder.end_length_delimited(); + } + + Subgraph *subgraph = reinterpret_cast(tree.page_ptr()->zone); + if (auto map = tree_data_elements()) { + auto tree_data_element = map->find(subgraph); + if (tree_data_element != map->end()) { + tree_data_element->second.sort_nodes(); + + auto nodes = tree_data_element->second.nodes(); + std::pair, data::ptr> *found = std::find_if( + nodes.begin(), nodes.end(), [&tree](auto node) { return node.first == tree; }); + + for (auto node = found; node != nodes.end(); ++node) { + if (node->first != tree) { + break; + } + if (node->second) { + encoder.encode_varint(0x30); + encoder.encode_varint(node->second); + } + } + } + } } #pragma mark - Tracing @@ -1964,7 +2147,7 @@ void Graph::start_tracing(uint8_t options, std::span subsystems) { prepare_trace(*_trace_recorder); } add_trace(_trace_recorder); - + // TODO: cleanup block } } From 52518fc7398a460742f92728ec5a3db5c8ad404f Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 14 Feb 2025 23:16:28 +1100 Subject: [PATCH 29/74] Fix bug in Subgraph::tree_node_at_index --- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 2e41703..8f39443 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -779,7 +779,7 @@ AttributeID Subgraph::tree_node_at_index(data::ptr tree_elem if (i == 0) { return AttributeID(node->second); } - --index; + --i; } } } From 7907b125cba223c608f9f204875415fb53aefd76 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 16 Feb 2025 10:37:24 +1100 Subject: [PATCH 30/74] Implement profiler methods on Graph --- Sources/ComputeCxx/Containers/Vector.h | 24 +- Sources/ComputeCxx/Containers/Vector.tpp | 8 +- Sources/ComputeCxx/Data/Pointer.h | 10 + Sources/ComputeCxx/Debug/DebugServer.h | 16 ++ Sources/ComputeCxx/Graph/Graph.cpp | 217 +++++++++++++++++- Sources/ComputeCxx/Graph/Graph.h | 28 ++- .../ComputeCxx/Graph/Profile/AGAppObserver.h | 9 + .../ComputeCxx/Graph/Profile/AGAppObserver.mm | 37 +++ .../Graph/Profile/ProfileData+JSON.mm | 62 +++++ .../ComputeCxx/Graph/Profile/ProfileData.cpp | 102 ++++++++ .../ComputeCxx/Graph/Profile/ProfileData.h | 103 +++++++++ .../ComputeCxx/Graph/Profile/ProfileTrace.cpp | 20 ++ .../ComputeCxx/Graph/Profile/ProfileTrace.h | 33 +++ Sources/ComputeCxx/Graph/TraceRecorder.cpp | 100 ++++---- Sources/ComputeCxx/Graph/TraceRecorder.h | 10 - Sources/ComputeCxx/Graph/UpdateStack.h | 2 +- 16 files changed, 687 insertions(+), 94 deletions(-) create mode 100644 Sources/ComputeCxx/Debug/DebugServer.h create mode 100644 Sources/ComputeCxx/Graph/Profile/AGAppObserver.h create mode 100644 Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm create mode 100644 Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm create mode 100644 Sources/ComputeCxx/Graph/Profile/ProfileData.cpp create mode 100644 Sources/ComputeCxx/Graph/Profile/ProfileData.h create mode 100644 Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp create mode 100644 Sources/ComputeCxx/Graph/Profile/ProfileTrace.h diff --git a/Sources/ComputeCxx/Containers/Vector.h b/Sources/ComputeCxx/Containers/Vector.h index 6abff11..1a16c74 100644 --- a/Sources/ComputeCxx/Containers/Vector.h +++ b/Sources/ComputeCxx/Containers/Vector.h @@ -156,7 +156,7 @@ class vector { // Modifiers void clear(); - + iterator insert(const_iterator pos, const T &value); iterator insert(const_iterator pos, T &&value); @@ -173,11 +173,11 @@ class vector { // MARK: Specialization for unique_ptr -template +template requires std::unsigned_integral<_size_type> -class vector, 0, _size_type> { +class vector, 0, _size_type> { public: - using value_type = std::unique_ptr; + using value_type = std::unique_ptr; using reference = value_type &; using const_reference = const value_type &; using iterator = value_type *_Nonnull; @@ -187,7 +187,7 @@ class vector, 0, _size_type> { using size_type = _size_type; private: - std::unique_ptr *_Nullable _buffer = nullptr; + std::unique_ptr *_Nonnull _buffer = nullptr; size_type _size = 0; size_type _capacity = 0; @@ -206,8 +206,8 @@ class vector, 0, _size_type> { reference back() { return *&data()[_size - 1]; }; const_reference back() const { return *&data()[_size - 1]; }; - std::unique_ptr *_Nonnull data() { return _buffer; }; - const std::unique_ptr *_Nonnull data() const { return _buffer; }; + std::unique_ptr *_Nonnull data() { return _buffer; }; + const std::unique_ptr *_Nonnull data() const { return _buffer; }; // Iterators @@ -236,12 +236,12 @@ class vector, 0, _size_type> { // Modifiers void clear(); - - iterator insert(const_iterator pos, const T &value); - iterator insert(const_iterator pos, T &&value); - void push_back(const std::unique_ptr &value) = delete; - void push_back(std::unique_ptr &&value); + iterator insert(const_iterator pos, const std::unique_ptr &value); + iterator insert(const_iterator pos, std::unique_ptr &&value); + + void push_back(const std::unique_ptr &value) = delete; + void push_back(std::unique_ptr &&value); void pop_back(); void resize(size_type count); diff --git a/Sources/ComputeCxx/Containers/Vector.tpp b/Sources/ComputeCxx/Containers/Vector.tpp index f6225d9..7001547 100644 --- a/Sources/ComputeCxx/Containers/Vector.tpp +++ b/Sources/ComputeCxx/Containers/Vector.tpp @@ -383,9 +383,9 @@ void vector::resize(size_type count, const value_type &value) { #pragma mark - Specialization for unique_ptr -template +template requires std::unsigned_integral -vector, 0, size_type>::~vector() { +vector, 0, size_type>::~vector() { for (auto i = 0; i < _size; i++) { _buffer[i].reset(); } @@ -394,9 +394,9 @@ vector, 0, size_type>::~vector() { } } -template +template requires std::unsigned_integral -void vector, 0, size_type>::push_back(std::unique_ptr &&value) { +void vector, 0, size_type>::push_back(std::unique_ptr &&value) { reserve(_size + 1); new (&_buffer[_size]) value_type(std::move(value)); _size += 1; diff --git a/Sources/ComputeCxx/Data/Pointer.h b/Sources/ComputeCxx/Data/Pointer.h index 8689ce0..e79733b 100644 --- a/Sources/ComputeCxx/Data/Pointer.h +++ b/Sources/ComputeCxx/Data/Pointer.h @@ -76,4 +76,14 @@ template class ptr { } // namespace data } // namespace AG +namespace std { + +// TODO: see if there's another way to synthesize this +template class hash> { + public: + std::uint64_t operator()(const AG::data::ptr &pointer) const { return pointer.offset(); } +}; + +} // namespace std + CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Debug/DebugServer.h b/Sources/ComputeCxx/Debug/DebugServer.h new file mode 100644 index 0000000..da46fa6 --- /dev/null +++ b/Sources/ComputeCxx/Debug/DebugServer.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class DebugServer { + public: + static void start(uint32_t port); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 006736c..7f1bb8e 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1,9 +1,11 @@ #include "Graph.h" +#include #include #include #include #include +#include #include #include "Attribute/AttributeType.h" @@ -12,15 +14,20 @@ #include "Attribute/OffsetAttributeID.h" #include "Attribute/WeakAttributeID.h" #include "Context.h" +#include "Debug/DebugServer.h" #include "Errors/Errors.h" #include "KeyTable.h" #include "Log/Log.h" +#include "Profile/AGAppObserver.h" +#include "Profile/ProfileData.h" +#include "Profile/ProfileTrace.h" #include "Subgraph/Subgraph.h" #include "Swift/Metadata.h" #include "Trace/Trace.h" #include "TraceRecorder.h" #include "Tree/TreeElement.h" #include "UpdateStack.h" +#include "Utilities/FreeDeleter.h" #include "Utilities/List.h" namespace AG { @@ -53,7 +60,73 @@ Graph::Graph() _types.push_back(nullptr); // AGAttributeNullType - // TODO: debug server, trace, profile + static auto [profiler_flags, trace_flags, trace_subsystems] = + []() -> std::tuple, 0, uint64_t>> { + const char *debug_server = getenv("AG_DEBUG_SERVER"); + if (debug_server) { + uint32_t port = (uint32_t)strtol(debug_server, nullptr, 0); + DebugServer::start(port); + } + + uint32_t profiler_flags = false; + const char *profile_string = getenv("AG_PROFILE"); + if (profile_string) { + profiler_flags = atoi(profile_string) != 0; + } + + vector, 0, uint64_t> trace_subsystems = {}; + + uint32_t trace_flags = 0; + const char *trace_string = getenv("AG_TRACE"); + if (trace_string) { + char *endptr = nullptr; + trace_flags = (uint32_t)strtol(trace_string, &endptr, 0); + + if (endptr) { + const char *c = endptr + strspn(endptr, ", \t\n\f\r"); + while (c) { + size_t option_length = strcspn(c, ", \t\n\f\r"); + + char *option = (char *)malloc(option_length + 1); + memcpy(option, c, option_length); + option[option_length] = 0; + + if (strcasecmp(option, "enabled") == 0) { + trace_flags |= TraceFlags::Enabled; + free(option); + } else if (strcasecmp(option, "full") == 0) { + trace_flags |= TraceFlags::Full; + free(option); + } else if (strcasecmp(option, "backtrace") == 0) { + trace_flags |= TraceFlags::Backtrace; + free(option); + } else if (strcasecmp(option, "prepare") == 0) { + trace_flags |= TraceFlags::Prepare; + free(option); + } else if (strcasecmp(option, "custom") == 0) { + trace_flags |= TraceFlags::Custom; + free(option); + } else if (strcasecmp(option, "all") == 0) { + trace_flags |= TraceFlags::All; + free(option); + } else { + trace_subsystems.push_back(std::unique_ptr(option)); + } + + c += strspn(c + option_length, ", \t\n\f\r"); + } + } + } + + return {profiler_flags, trace_flags, trace_subsystems}; + }(); + + if (trace_flags && !trace_subsystems.empty()) { + start_tracing(trace_flags, std::span((const char **)trace_subsystems.data(), trace_subsystems.size())); + } + if (profiler_flags) { + start_profiling(profiler_flags); + } all_lock(); _next = _all_graphs; @@ -550,9 +623,9 @@ void Graph::remove_node(data::ptr node) { this->remove_removed_output(node, output_edge.value, false); } - // if (_profile_data != nullptr) { - // TODO: _profile_data->remove_node(); - // } + if (_profile_data != nullptr) { + _profile_data->remove_node(node, node->type_id()); + } } bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, @@ -1773,7 +1846,7 @@ uint32_t Graph::intern_key(const char *key) { return key_id; } -const char *Graph::key_name(uint32_t key_id) { +const char *Graph::key_name(uint32_t key_id) const { if (_keys != nullptr && key_id < _keys->size()) { return _keys->get(key_id); } @@ -2002,8 +2075,8 @@ void Graph::encode_tree(Encoder &encoder, data::ptr tree) { tree_data_element->second.sort_nodes(); auto nodes = tree_data_element->second.nodes(); - std::pair, data::ptr> *found = std::find_if( - nodes.begin(), nodes.end(), [&tree](auto node) { return node.first == tree; }); + std::pair, data::ptr> *found = + std::find_if(nodes.begin(), nodes.end(), [&tree](auto node) { return node.first == tree; }); for (auto node = found; node != nodes.end(); ++node) { if (node->first != tree) { @@ -2140,10 +2213,10 @@ void Graph::remove_trace(uint64_t trace_id) { _traces.erase(iter); } -void Graph::start_tracing(uint8_t options, std::span subsystems) { - if (options & TraceRecorder::Options::CreateIfNeeded && _trace_recorder == nullptr) { - _trace_recorder = new TraceRecorder(this, options, subsystems); - if (options & TraceRecorder::Options::PrepareTrace) { +void Graph::start_tracing(uint32_t flags, std::span subsystems) { + if (flags & TraceFlags::Enabled && _trace_recorder == nullptr) { + _trace_recorder = new TraceRecorder(this, flags, subsystems); + if (flags & TraceFlags::Prepare) { prepare_trace(*_trace_recorder); } add_trace(_trace_recorder); @@ -2225,6 +2298,128 @@ void Graph::trace_assertion_failure(bool all_stop_tracing, const char *format, . va_end(args); } +#pragma mark - Profile + +uint64_t Graph::begin_profile_event(data::ptr node, const char *event_name) { + foreach_trace([this, &node, &event_name](Trace &trace) { trace.begin_event(node, intern_key(event_name)); }); + if (_is_profiling) { + return mach_absolute_time(); + } + return 0; +} + +void Graph::end_profile_event(data::ptr node, const char *event_name, uint64_t start_time, bool flag) { + auto event_id = intern_key(event_name); + if (_is_profiling) { + if (_profile_data == nullptr) { + _profile_data.reset(new ProfileData(this)); + } + + auto end_time = mach_absolute_time(); + uint64_t duration = 0; + if (end_time - start_time >= _profile_data->precision()) { + duration = end_time - start_time - _profile_data->precision(); + } + + auto category = _profile_data.get()->categories().try_emplace(event_id).first->second; + category.add_update(node, duration, flag); + + _profile_data->set_has_unmarked_categories(true); + } + foreach_trace([&node, &event_id](Trace &trace) { trace.end_event(node, event_id); }); +} + +void Graph::add_profile_update(data::ptr node, uint64_t time, bool option) { + if (_is_profiling) { + if (_profile_data == nullptr) { + _profile_data.reset(new ProfileData(this)); + } + + uint64_t effective_time = 0; + if (time > _profile_data->precision()) { + effective_time = time - _profile_data->precision(); + } + _profile_data->current_category().add_update(node, effective_time, option); + _profile_data->set_has_unmarked_categories(true); + } +} + +void Graph::start_profiling(uint32_t options) { + _is_profiling = options & 1; + if ((options >> 1) & 1) { + AGAppObserverStartObserving(); + + CFRunLoopRef run_loop = CFRunLoopGetMain(); + if (run_loop) { + CFRunLoopObserverRef observer = CFRunLoopObserverCreate( + 0, kCFRunLoopBeforeWaiting | kCFRunLoopExit, true, 2500000, + [](CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { + all_mark_profile("app/runloop"); + }, + nullptr); + if (observer) { + CFRunLoopAddObserver(run_loop, observer, kCFRunLoopCommonModes); // TODO: check mode + } + } + } + if (_is_profiling && _profile_trace == nullptr) { + _profile_trace = new ProfileTrace(); + add_trace(_profile_trace); + } +} + +void Graph::stop_profiling() { + if (_profile_trace) { + remove_trace(_profile_trace->trace_id()); + _profile_trace = nullptr; + } + _is_profiling = false; +} + +void Graph::mark_profile(uint32_t event_id, uint64_t time) { + foreach_trace([this, &event_id](Trace &trace) { trace.mark_profile(*this, event_id); }); + + if (_profile_data) { + _profile_data->mark(event_id, time); + } +} + +void Graph::reset_profile() { _profile_data.reset(); } + +void Graph::all_start_profiling(uint32_t options) { + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + graph->start_profiling(options); + } + all_unlock(); +} + +void Graph::all_stop_profiling() { + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + graph->stop_profiling(); + } + all_unlock(); +} + +void Graph::all_mark_profile(const char *event_name) { + uint64_t time = mach_absolute_time(); + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + auto event_id = graph->intern_key(event_name); + graph->mark_profile(event_id, time); + } + all_unlock(); +} + +void Graph::all_reset_profile() { + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + graph->reset_profile(); + } + all_unlock(); +} + #pragma mark - Printing void Graph::print() { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 1ec716f..dc2bbcf 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -54,6 +54,8 @@ class Graph { class TraceRecorder; class UpdateStack; class UpdateStackRef; + class ProfileData; + class ProfileTrace; enum SearchOptions : uint32_t { SearchInputs = 1 << 0, @@ -121,6 +123,9 @@ class Graph { size_t _num_node_value_bytes = 0; // Profile + bool _is_profiling; + std::unique_ptr _profile_data; + ProfileTrace *_Nullable _profile_trace; // Trace TraceRecorder *_trace_recorder; @@ -329,7 +334,7 @@ class Graph { // MARK: Intern uint32_t intern_key(const char *key); - const char *key_name(uint32_t key_id); + const char *key_name(uint32_t key_id) const; uint32_t intern_type(swift::metadata *metadata, ClosureFunctionVP make_type); @@ -342,12 +347,21 @@ class Graph { // MARK: Tracing + enum TraceFlags: uint32_t { + Enabled = 1 << 0, + Full = 1 << 1, + Backtrace = 1 << 2, + Prepare = 1 << 3, + Custom = 1 << 4, // skip updates + All = 1 << 5, + }; + void prepare_trace(Trace &trace); void add_trace(Trace *_Nullable trace); void remove_trace(uint64_t trace_id); - void start_tracing(uint8_t options, std::span subsystems); + void start_tracing(uint32_t flags, std::span subsystems); void stop_tracing(); void sync_tracing(); CFStringRef copy_trace_path(); @@ -369,17 +383,19 @@ class Graph { // MARK: Profile - void begin_profile_event(data::ptr node, const char *event_name); + bool is_profiling() const { return _is_profiling; }; + + uint64_t begin_profile_event(data::ptr node, const char *event_name); void end_profile_event(data::ptr node, const char *event_name, uint64_t arg1, bool arg2); void add_profile_update(data::ptr node, uint64_t arg2, bool arg3); - void start_profiling(uint32_t arg); + void start_profiling(uint32_t options); void stop_profiling(); - void mark_profile(uint32_t arg1, uint64_t arg2); + void mark_profile(uint32_t event_id, uint64_t time); void reset_profile(); - static void all_start_profiling(uint32_t arg); + static void all_start_profiling(uint32_t options); static void all_stop_profiling(); static void all_mark_profile(const char *event_name); static void all_reset_profile(); diff --git a/Sources/ComputeCxx/Graph/Profile/AGAppObserver.h b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.h new file mode 100644 index 0000000..23f4cb7 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +CF_ASSUME_NONNULL_BEGIN + +void AGAppObserverStartObserving(); + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm new file mode 100644 index 0000000..e8d0d57 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm @@ -0,0 +1,37 @@ +#include "AGAppObserver.h" + +#include + +#include "Graph/Graph.h" + +@interface AGAppObserver : NSObject + ++ (void)foreground:(AG::Graph *)graph; ++ (void)background:(AG::Graph *)graph; + +@end + +@implementation AGAppObserver + ++ (void)foreground:(AG::Graph *)graph { + graph->all_mark_profile("app/foreground"); +} + ++ (void)background:(AG::Graph *)graph { + graph->all_mark_profile("app/background"); +} + +@end + +void AGAppObserverStartObserving() { + if (NSClassFromString(@"UIApplication")) { + [[NSNotificationCenter defaultCenter] addObserver:[AGAppObserver class] + selector:@selector(foreground:) + name:@"UIApplicationWillEnterForeground" // TODO get real name + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:[AGAppObserver class] + selector:@selector(background:) + name:@"UIApplicationDidEnterBackground" // TODO get real name + object:nil]; + } +} diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm new file mode 100644 index 0000000..d972794 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm @@ -0,0 +1,62 @@ +#include "ProfileData.h" + +#include +#include + +#include "Graph/Graph.h" +#include "Time/Time.h" + +namespace AG { + +CFDictionaryRef Graph::ProfileData::json_data(const Data &data) { + // TODO: figure out real keys + NSMutableDictionary *dict = nil; + if (data.count1) { + if (!dict) { + dict = [NSMutableDictionary dictionary]; + } + dict[@"count1"] = [NSNumber numberWithUnsignedLong:data.count1]; + } + if (data.count2) { + if (!dict) { + dict = [NSMutableDictionary dictionary]; + } + dict[@"count2"] = [NSNumber numberWithUnsignedLong:data.count2]; + } + if (data.time1) { + if (!dict) { + dict = [NSMutableDictionary dictionary]; + } + dict[@"time1"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.time1)]; + } + if (data.time2) { + if (!dict) { + dict = [NSMutableDictionary dictionary]; + } + dict[@"time2"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.time2)]; + } + return (__bridge CFDictionaryRef)dict; +} + +CFDictionaryRef Graph::ProfileData::json_data(const Item &item, const Graph &graph) { + // TODO: figure out real keys + NSMutableDictionary *json = (__bridge NSMutableDictionary *)json_data(item.data()); + if (!item.marks().empty()) { + NSMutableArray *array = [NSMutableArray array]; + for (auto mark : item.marks()) { + NSMutableDictionary *mark_json = (__bridge NSMutableDictionary *)json_data(mark.data); + if (mark_json) { + mark_json[@"event"] = [NSString stringWithUTF8String:graph.key_name(mark.event_id)]; + mark_json[@"time"] = [NSNumber numberWithDouble:absolute_time_to_seconds(mark.time)]; + [array addObject:mark_json]; + } + } + if (!json) { + json = [NSMutableDictionary dictionary]; + } + json[@"marks"] = array; + } + return (__bridge CFDictionaryRef)json; +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp b/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp new file mode 100644 index 0000000..eabd7b6 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp @@ -0,0 +1,102 @@ +#include "ProfileData.h" + +#include + +#include "Graph/Graph.h" + +namespace AG { + +#pragma mark - ProfileData::Item + +void Graph::ProfileData::Item::operator+=(const Item &other) { + _data.count1 += other._data.count1; + _data.count2 += other._data.count2; + _data.time1 += other._data.time1; + _data.time2 += other._data.time2; + + Mark *iter = _marks.begin(); + for (auto other_mark : other._marks) { + bool merged = false; + while (iter != _marks.end() && iter->time <= other_mark.time) { + if (iter == &other_mark) { + iter->data.count1 += other_mark.data.count1; + iter->data.count2 += other_mark.data.count2; + iter->data.time1 += other_mark.data.time1; + iter->data.time2 += other_mark.data.time2; + merged = true; + break; + } + iter += 1; + } + if (merged) { + continue; + } + + _marks.insert(iter, other_mark); + iter += 1; + } +} + +void Graph::ProfileData::Item::mark(uint32_t event_id, uint64_t time) { + if (_data.count1) { + _marks.push_back({ + event_id, + time, + _data, + }); + _data = {0, 0, 0, 0}; + } +} + +#pragma mark - ProfileData::Category + +void Graph::ProfileData::Category::add_update(data::ptr node, uint64_t time, bool flag) { + data().count1 += 1; + data().time1 += time; + if (flag) { + data().count2 += 1; + data().time2 += time; + } + + Item &item = _items_by_attribute.try_emplace(node).first->second; + + item.data().count1 += 1; + item.data().time1 += time; + if (flag) { + item.data().count2 += 1; + item.data().time2 += time; + } +} + +void Graph::ProfileData::Category::mark(uint32_t event_id, uint64_t time) { + Item::mark(event_id, time); + for (auto &entry : _items_by_attribute) { + entry.second.mark(event_id, time); + } + for (auto &entry : _removed_items_by_type_id) { + entry.second.mark(event_id, time); + } +} + +#pragma mark - ProfileData + +Graph::ProfileData::ProfileData(Graph *graph) { + + uint64_t delta = 0; + uint64_t last = mach_absolute_time(); + for (uint32_t i = 16; i; --i) { + uint64_t current = mach_absolute_time(); + delta += current - last; + last = current; + } + _precision = delta / 16; +} + +void Graph::ProfileData::remove_node(data::ptr node, uint32_t type_id) { + _current_category.remove_node(node, type_id); + for (auto &entry : _categories) { + entry.second.remove_node(node, type_id); + } +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData.h b/Sources/ComputeCxx/Graph/Profile/ProfileData.h new file mode 100644 index 0000000..def7870 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include + +#include "Graph/Graph.h" +#include "Utilities/HashTable.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Graph::ProfileData { + public: + struct Data { + uint64_t count1; + uint64_t count2; + uint64_t time1; + uint64_t time2; + }; + + struct Mark { + uint32_t event_id; + uint64_t time; + Data data; + }; + + class Item { + private: + Data _data; + vector _marks; + + public: + Data &data() { return _data; }; + const Data &data() const { return _data; }; + const vector &marks() const { return _marks; }; + + void operator+=(const Item &other); + + void mark(uint32_t event_id, uint64_t time); + }; + + class Category : public Item { + private: + std::unordered_map, Item> _items_by_attribute; + std::unordered_map _removed_items_by_type_id; + + public: + std::unordered_map, Item> &items_by_attribute() { return _items_by_attribute; }; + + void add_update(data::ptr node, uint64_t time, bool flag); + void remove_node(data::ptr node, uint32_t type_id) { + auto found = _items_by_attribute.find(node); + if (found != _items_by_attribute.end()) { + auto &item = _removed_items_by_type_id.try_emplace(type_id).first->second; + item += found->second; + _items_by_attribute.erase(found); + } + }; + + void mark(uint32_t event_id, uint64_t time); + }; + + private: + uint64_t _precision; + Category _current_category; + + std::unordered_map _categories; + bool _has_unmarked_categories; + + public: + ProfileData(Graph *graph); + + uint64_t precision() const { return _precision; }; + + Category ¤t_category() { return _current_category; }; + std::unordered_map &categories() { return _categories; }; + + bool has_unmarked_categories() const { return _has_unmarked_categories; }; + void set_has_unmarked_categories(bool value) { _has_unmarked_categories = value; }; + + void remove_node(data::ptr node, uint32_t type_id); + + void mark(uint32_t event_id, uint64_t time) { + if (_has_unmarked_categories) { + if (time == 0) { + time = mach_absolute_time(); + } + _current_category.mark(event_id, time); + for (auto &entry : _categories) { + entry.second.mark(event_id, time); // TODO: does this modify in place or a copy? + } + _has_unmarked_categories = false; + } + } + + CFDictionaryRef json_data(const Data &data); + CFDictionaryRef json_data(const Item &item, const Graph &graph); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp new file mode 100644 index 0000000..36e53b1 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp @@ -0,0 +1,20 @@ +#include "ProfileTrace.h" + +#include "Graph/UpdateStack.h" + +namespace AG { + +void Graph::ProfileTrace::begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) { + if (update_stack.graph()->is_profiling()) { + + } +} + +void Graph::ProfileTrace::end_update(const Graph::UpdateStack &update_stack, data::ptr node, + Graph::UpdateStatus update_status) {} + +void Graph::ProfileTrace::begin_update(data::ptr node) {} + +void Graph::ProfileTrace::end_update(data::ptr node, bool changed) {} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h new file mode 100644 index 0000000..a60c22c --- /dev/null +++ b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include "Encoder/Encoder.h" +#include "Trace/Trace.h" +#include "Utilities/HashTable.h" + +CF_ASSUME_NONNULL_BEGIN + +namespace AG { + +class Graph::ProfileTrace : public Trace { + private: + struct UpdateData { + int x; + }; + + std::unordered_map _map; + + public: + ~ProfileTrace(); + + void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options); + void end_update(const Graph::UpdateStack &update_stack, data::ptr node, Graph::UpdateStatus update_status); + void begin_update(data::ptr node); + void end_update(data::ptr node, bool changed); +}; + +} // namespace AG + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 5c149ce..487ecc1 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -171,7 +171,7 @@ void Graph::TraceRecorder::encode_stack() { } void Graph::TraceRecorder::encode_snapshot() { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -215,7 +215,7 @@ void Graph::TraceRecorder::field_timestamp(Encoder &encoder) { } void Graph::TraceRecorder::field_backtrace(Encoder &encoder, uint64_t field_number) { - if ((_options & Options::RecordBacktrace) == 0) { + if ((_options & TraceFlags::Backtrace) == 0) { return; } @@ -436,7 +436,7 @@ void Graph::TraceRecorder::log_message_v(const char *format, va_list args) { } void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t options) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -460,7 +460,7 @@ void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t optio } void Graph::TraceRecorder::end_update(const Subgraph &subgraph) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -481,7 +481,7 @@ void Graph::TraceRecorder::end_update(const Subgraph &subgraph) { void Graph::TraceRecorder::begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -505,7 +505,7 @@ void Graph::TraceRecorder::begin_update(const Graph::UpdateStack &update_stack, void Graph::TraceRecorder::end_update(const Graph::UpdateStack &update_stack, data::ptr node, Graph::UpdateStatus update_status) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -528,7 +528,7 @@ void Graph::TraceRecorder::end_update(const Graph::UpdateStack &update_stack, da } void Graph::TraceRecorder::begin_update(data::ptr node) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -547,7 +547,7 @@ void Graph::TraceRecorder::begin_update(data::ptr node) { } void Graph::TraceRecorder::end_update(data::ptr node, bool changed) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -570,7 +570,7 @@ void Graph::TraceRecorder::end_update(data::ptr node, bool changed) { } void Graph::TraceRecorder::begin_update(const Graph::Context &context) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -589,7 +589,7 @@ void Graph::TraceRecorder::begin_update(const Graph::Context &context) { } void Graph::TraceRecorder::end_update(const Graph::Context &context) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -608,10 +608,10 @@ void Graph::TraceRecorder::end_update(const Graph::Context &context) { } void Graph::TraceRecorder::begin_invalidation(const Graph::Context &context, AttributeID attribute) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -634,10 +634,10 @@ void Graph::TraceRecorder::begin_invalidation(const Graph::Context &context, Att } void Graph::TraceRecorder::end_invalidation(const Graph::Context &context, AttributeID attribute) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -660,10 +660,10 @@ void Graph::TraceRecorder::end_invalidation(const Graph::Context &context, Attri } void Graph::TraceRecorder::begin_modify(data::ptr node) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -682,10 +682,10 @@ void Graph::TraceRecorder::begin_modify(data::ptr node) { } void Graph::TraceRecorder::end_modify(data::ptr node) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -695,7 +695,7 @@ void Graph::TraceRecorder::end_modify(data::ptr node) { _encoder.encode_varint(0xe); field_timestamp(_encoder); - if (node || _options & Options::SkipUpdates) { + if (node || _options & TraceFlags::Custom) { _encoder.encode_varint(0x18); _encoder.encode_varint(1); } @@ -704,7 +704,7 @@ void Graph::TraceRecorder::end_modify(data::ptr node) { } void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -727,7 +727,7 @@ void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event) { } void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -750,7 +750,7 @@ void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event) { } void Graph::TraceRecorder::created(const Graph::Context &context) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -770,7 +770,7 @@ void Graph::TraceRecorder::created(const Graph::Context &context) { } void Graph::TraceRecorder::destroy(const Graph::Context &context) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -790,10 +790,10 @@ void Graph::TraceRecorder::destroy(const Graph::Context &context) { } void Graph::TraceRecorder::needs_update(const Graph::Context &context) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -811,7 +811,7 @@ void Graph::TraceRecorder::needs_update(const Graph::Context &context) { } void Graph::TraceRecorder::created(const Subgraph &subgraph) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -836,7 +836,7 @@ void Graph::TraceRecorder::created(const Subgraph &subgraph) { } void Graph::TraceRecorder::invalidate(const Subgraph &subgraph) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -862,7 +862,7 @@ void Graph::TraceRecorder::invalidate(const Subgraph &subgraph) { } void Graph::TraceRecorder::destroy(const Subgraph &subgraph) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -883,7 +883,7 @@ void Graph::TraceRecorder::destroy(const Subgraph &subgraph) { } void Graph::TraceRecorder::add_child(const Subgraph &subgraph, const Subgraph &child) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -907,7 +907,7 @@ void Graph::TraceRecorder::add_child(const Subgraph &subgraph, const Subgraph &c } void Graph::TraceRecorder::remove_child(const Subgraph &subgraph, const Subgraph &child) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -931,7 +931,7 @@ void Graph::TraceRecorder::remove_child(const Subgraph &subgraph, const Subgraph } void Graph::TraceRecorder::added(data::ptr node) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -961,7 +961,7 @@ void Graph::TraceRecorder::added(data::ptr node) { } void Graph::TraceRecorder::add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -985,7 +985,7 @@ void Graph::TraceRecorder::add_edge(data::ptr node, AttributeID input, uin } void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_index) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -1010,10 +1010,10 @@ void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_inde } void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input_index, bool pending) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -1040,10 +1040,10 @@ void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input } void Graph::TraceRecorder::set_dirty(data::ptr node, bool dirty) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -1065,10 +1065,10 @@ void Graph::TraceRecorder::set_dirty(data::ptr node, bool dirty) { } void Graph::TraceRecorder::set_pending(data::ptr node, bool pending) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -1090,10 +1090,10 @@ void Graph::TraceRecorder::set_pending(data::ptr node, bool pending) { } void Graph::TraceRecorder::set_value(data::ptr node, const void *value) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -1111,10 +1111,10 @@ void Graph::TraceRecorder::set_value(data::ptr node, const void *value) { } void Graph::TraceRecorder::mark_value(data::ptr node) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } - if ((_options & Options::RecordInvalidations) == 0) { + if ((_options & TraceFlags::Full) == 0) { return; } @@ -1132,7 +1132,7 @@ void Graph::TraceRecorder::mark_value(data::ptr node) { } void Graph::TraceRecorder::added(data::ptr indirect_node) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -1157,7 +1157,7 @@ void Graph::TraceRecorder::added(data::ptr indirect_node) { } void Graph::TraceRecorder::set_source(data::ptr indirect_node, AttributeID source) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -1185,7 +1185,7 @@ void Graph::TraceRecorder::set_source(data::ptr indirect_node, Att } void Graph::TraceRecorder::set_dependency(data::ptr indirect_node, AttributeID dependency) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -1208,7 +1208,7 @@ void Graph::TraceRecorder::set_dependency(data::ptr indirect_node, } void Graph::TraceRecorder::set_deadline(uint64_t deadline) { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -1234,7 +1234,7 @@ void Graph::TraceRecorder::set_deadline(uint64_t deadline) { } void Graph::TraceRecorder::passed_deadline() { - if (_options & Options::SkipUpdates) { + if (_options & TraceFlags::Custom) { return; } @@ -1357,10 +1357,10 @@ bool Graph::TraceRecorder::named_event_enabled(uint32_t event_id) { const char *event_subsystem = AGGraphGetTraceEventSubsystem(event_id); bool enabled = false; - if (event_subsystem == nullptr || (_options & Options::Subsystem)) { + if (event_subsystem == nullptr || (_options & TraceFlags::All)) { enabled = true; } else { - enabled = (_options & Options::Subsystem) != 0; + enabled = (_options & TraceFlags::All) != 0; for (auto stored_subsystem : _named_event_subsystems) { if (!strcasecmp(stored_subsystem, event_subsystem)) { enabled = true; diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index f3ba424..daeda2f 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -12,16 +12,6 @@ CF_ASSUME_NONNULL_BEGIN namespace AG { class Graph::TraceRecorder : public Encoder::Delegate, public Trace { - public: - enum Options : uint8_t { - CreateIfNeeded = 1 << 0, - RecordInvalidations = 1 << 1, - RecordBacktrace = 1 << 2, - PrepareTrace = 1 << 3, - SkipUpdates = 1 << 4, - Subsystem = 1 << 5, - }; - private: uint64_t _unique_id; diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index 0986571..df1a8e0 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -38,7 +38,7 @@ class Graph::UpdateStack { UpdateStack(Graph *graph, uint8_t options); ~UpdateStack(); - Graph *graph() { return _graph; }; + Graph *graph() const { return _graph; }; TaggedPointer previous() { return _previous; }; vector &frames() { return _frames; }; From 09c2d27658e981f4f75fb592361e3b686754adbb Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 16 Feb 2025 12:35:49 +1100 Subject: [PATCH 31/74] Implement Graph::ProfileTrace --- Sources/ComputeCxx/Graph/Graph.cpp | 10 ++-- Sources/ComputeCxx/Graph/Graph.h | 3 +- .../Graph/Profile/ProfileData+JSON.mm | 16 ++--- .../ComputeCxx/Graph/Profile/ProfileData.cpp | 40 ++++++------- .../ComputeCxx/Graph/Profile/ProfileData.h | 8 +-- .../ComputeCxx/Graph/Profile/ProfileTrace.cpp | 58 +++++++++++++++++-- .../ComputeCxx/Graph/Profile/ProfileTrace.h | 6 +- Sources/ComputeCxx/Graph/UpdateStack.h | 2 +- 8 files changed, 98 insertions(+), 45 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 7f1bb8e..fc99115 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -2329,17 +2329,17 @@ void Graph::end_profile_event(data::ptr node, const char *event_name, uint foreach_trace([&node, &event_id](Trace &trace) { trace.end_event(node, event_id); }); } -void Graph::add_profile_update(data::ptr node, uint64_t time, bool option) { +void Graph::add_profile_update(data::ptr node, uint64_t duration, bool changed) { if (_is_profiling) { if (_profile_data == nullptr) { _profile_data.reset(new ProfileData(this)); } - uint64_t effective_time = 0; - if (time > _profile_data->precision()) { - effective_time = time - _profile_data->precision(); + uint64_t effective_duration = 0; + if (duration > _profile_data->precision()) { + effective_duration = duration - _profile_data->precision(); } - _profile_data->current_category().add_update(node, effective_time, option); + _profile_data->current_category().add_update(node, effective_duration, changed); _profile_data->set_has_unmarked_categories(true); } } diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index dc2bbcf..0efc515 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -33,6 +33,7 @@ template class TaggedPointer { TaggedPointer with_tag(bool tag) { return TaggedPointer(get(), tag); }; T *_Nullable get() { return reinterpret_cast(_value & ~0x1); }; + const T *_Nullable get() const { return reinterpret_cast(_value & ~0x1); }; bool operator==(nullptr_t) const noexcept { return _value == 0; }; bool operator!=(nullptr_t) const noexcept { return _value != 0; }; @@ -388,7 +389,7 @@ class Graph { uint64_t begin_profile_event(data::ptr node, const char *event_name); void end_profile_event(data::ptr node, const char *event_name, uint64_t arg1, bool arg2); - void add_profile_update(data::ptr node, uint64_t arg2, bool arg3); + void add_profile_update(data::ptr node, uint64_t duration, bool changed); void start_profiling(uint32_t options); void stop_profiling(); diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm index d972794..49c2691 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm @@ -11,29 +11,29 @@ CFDictionaryRef Graph::ProfileData::json_data(const Data &data) { // TODO: figure out real keys NSMutableDictionary *dict = nil; - if (data.count1) { + if (data.count) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"count1"] = [NSNumber numberWithUnsignedLong:data.count1]; + dict[@"count1"] = [NSNumber numberWithUnsignedLong:data.count]; } - if (data.count2) { + if (data.changed_count) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"count2"] = [NSNumber numberWithUnsignedLong:data.count2]; + dict[@"count2"] = [NSNumber numberWithUnsignedLong:data.changed_count]; } - if (data.time1) { + if (data.duration) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"time1"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.time1)]; + dict[@"time1"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.duration)]; } - if (data.time2) { + if (data.changed_duration) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"time2"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.time2)]; + dict[@"time2"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.changed_duration)]; } return (__bridge CFDictionaryRef)dict; } diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp b/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp index eabd7b6..be729fd 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp @@ -9,20 +9,20 @@ namespace AG { #pragma mark - ProfileData::Item void Graph::ProfileData::Item::operator+=(const Item &other) { - _data.count1 += other._data.count1; - _data.count2 += other._data.count2; - _data.time1 += other._data.time1; - _data.time2 += other._data.time2; + _data.count += other._data.count; + _data.changed_count += other._data.changed_count; + _data.duration += other._data.duration; + _data.changed_duration += other._data.changed_duration; Mark *iter = _marks.begin(); for (auto other_mark : other._marks) { bool merged = false; while (iter != _marks.end() && iter->time <= other_mark.time) { if (iter == &other_mark) { - iter->data.count1 += other_mark.data.count1; - iter->data.count2 += other_mark.data.count2; - iter->data.time1 += other_mark.data.time1; - iter->data.time2 += other_mark.data.time2; + iter->data.count += other_mark.data.count; + iter->data.changed_count += other_mark.data.changed_count; + iter->data.duration += other_mark.data.duration; + iter->data.changed_duration += other_mark.data.changed_duration; merged = true; break; } @@ -38,7 +38,7 @@ void Graph::ProfileData::Item::operator+=(const Item &other) { } void Graph::ProfileData::Item::mark(uint32_t event_id, uint64_t time) { - if (_data.count1) { + if (_data.count) { _marks.push_back({ event_id, time, @@ -50,21 +50,21 @@ void Graph::ProfileData::Item::mark(uint32_t event_id, uint64_t time) { #pragma mark - ProfileData::Category -void Graph::ProfileData::Category::add_update(data::ptr node, uint64_t time, bool flag) { - data().count1 += 1; - data().time1 += time; - if (flag) { - data().count2 += 1; - data().time2 += time; +void Graph::ProfileData::Category::add_update(data::ptr node, uint64_t duration, bool changed) { + data().count += 1; + data().duration += duration; + if (changed) { + data().changed_count += 1; + data().changed_duration += duration; } Item &item = _items_by_attribute.try_emplace(node).first->second; - item.data().count1 += 1; - item.data().time1 += time; - if (flag) { - item.data().count2 += 1; - item.data().time2 += time; + item.data().count += 1; + item.data().duration += duration; + if (changed) { + item.data().changed_count += 1; + item.data().changed_duration += duration; } } diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData.h b/Sources/ComputeCxx/Graph/Profile/ProfileData.h index def7870..e3ec1c8 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData.h +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData.h @@ -13,10 +13,10 @@ namespace AG { class Graph::ProfileData { public: struct Data { - uint64_t count1; - uint64_t count2; - uint64_t time1; - uint64_t time2; + uint64_t count; + uint64_t changed_count; + uint64_t duration; + uint64_t changed_duration; }; struct Mark { diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp index 36e53b1..ebc6a44 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp +++ b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp @@ -1,20 +1,70 @@ #include "ProfileTrace.h" +#include + #include "Graph/UpdateStack.h" namespace AG { void Graph::ProfileTrace::begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) { if (update_stack.graph()->is_profiling()) { - + uint64_t time = mach_absolute_time(); + UpdateData &data = _map.try_emplace(&update_stack).first->second; + data.start_time = time; + data.child_update_stack_duration = 0; + data.update_node_start_time = 0; } } void Graph::ProfileTrace::end_update(const Graph::UpdateStack &update_stack, data::ptr node, - Graph::UpdateStatus update_status) {} + Graph::UpdateStatus update_status) { + auto found = _map.find(&update_stack); + if (found != _map.end()) { + UpdateData &data = found->second; + _map.erase(found); + + if (auto previous_update_stack = update_stack.previous().get()) { + if (previous_update_stack->graph()->is_profiling()) { + auto found_previous = _map.find(previous_update_stack); + if (found_previous != _map.end()) { + UpdateData &previous_data = found_previous->second; + uint64_t end_time = previous_update_stack->graph()->is_profiling() ? mach_absolute_time() : 0; + previous_data.child_update_stack_duration += end_time - data.start_time; + } + } + } + } +} -void Graph::ProfileTrace::begin_update(data::ptr node) {} +void Graph::ProfileTrace::begin_update(data::ptr node) { + auto update = current_update(); + UpdateStack *update_stack = update.tag() ? nullptr : update.get(); -void Graph::ProfileTrace::end_update(data::ptr node, bool changed) {} + auto found = _map.find(update_stack); + if (found != _map.end()) { + UpdateData &data = found->second; + data.update_node_start_time = update_stack->graph()->is_profiling() ? mach_absolute_time() : 0; + } +} + +void Graph::ProfileTrace::end_update(data::ptr node, bool changed) { + auto update = current_update(); + UpdateStack *update_stack = update.tag() ? nullptr : update.get(); + + auto found = _map.find(update_stack); + if (found != _map.end()) { + UpdateData &data = found->second; + if (data.update_node_start_time != 0) { + uint64_t end_time = update_stack->graph()->is_profiling() ? mach_absolute_time() : 0; + uint64_t duration = 0; + if (end_time - data.update_node_start_time >= data.child_update_stack_duration) { + duration = (end_time - data.update_node_start_time) - data.child_update_stack_duration; + } + data.child_update_stack_duration = 0; + update_stack->graph()->add_profile_update(node, duration, changed); + } + + } +} } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h index a60c22c..bf0e721 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h +++ b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h @@ -14,10 +14,12 @@ namespace AG { class Graph::ProfileTrace : public Trace { private: struct UpdateData { - int x; + uint64_t start_time; + uint64_t child_update_stack_duration; + uint64_t update_node_start_time; }; - std::unordered_map _map; + std::unordered_map _map; public: ~ProfileTrace(); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index df1a8e0..6d0ddde 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -39,7 +39,7 @@ class Graph::UpdateStack { ~UpdateStack(); Graph *graph() const { return _graph; }; - TaggedPointer previous() { return _previous; }; + const TaggedPointer previous() const { return _previous; }; vector &frames() { return _frames; }; From d65ebcfab2a87a2a39002e60adecb5a19dd1eb55 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 16 Feb 2025 13:40:44 +1100 Subject: [PATCH 32/74] Implement print methods on Graph --- Sources/ComputeCxx/Attribute/Node/Node.h | 3 + Sources/ComputeCxx/Graph/AGGraph.h | 2 + Sources/ComputeCxx/Graph/Graph+Description.mm | 134 ++++++++++++++++++ Sources/ComputeCxx/Graph/Graph.cpp | 26 ---- Sources/ComputeCxx/Graph/Graph.h | 29 ++-- 5 files changed, 154 insertions(+), 40 deletions(-) create mode 100644 Sources/ComputeCxx/Graph/Graph+Description.mm diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 797fba8..0a70617 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -128,6 +128,9 @@ class Node { bool is_updating() { return _data & (Updating | UpdatingCyclic); } bool is_updating_cyclic() { return (_data & (Updating | UpdatingCyclic)) == (Updating | UpdatingCyclic); } + uint8_t update_count() const { + return _data >> 6; + } }; private: diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 4f934e5..69d3763 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -15,6 +15,8 @@ void AGGraphCreate(); void AGGraphSetOutputValue(void *value, AGTypeID type); +void AGGraphArchiveJSON(const char *filename); + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm new file mode 100644 index 0000000..896e5c3 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -0,0 +1,134 @@ +#include "Graph.h" + +#include +#include + +#include "AGGraph.h" +#include "Subgraph/Subgraph.h" +#include "Trace/Trace.h" +#include "UpdateStack.h" + +namespace AG { + +void Graph::print() { + NSString *description = (__bridge NSString *)description_graph_dot(nil); + if (description) { + const char *string = [description UTF8String]; + std::cout << string; + } +} + +void Graph::print_attribute(data::ptr node) { + NSString *desc = (__bridge NSString *)description(node); + if (desc) { + const char *string = [desc UTF8String]; + fprintf(stdout, "%s\n", string); + } +} + +void Graph::print_data() { + data::table::shared().print(); + data::zone::print_header(); + for (auto subgraph : _subgraphs) { + subgraph->data::zone::print(); + } +} + +void Graph::print_stack() { + auto update = current_update(); + if (update != 0) { + uint32_t update_stack_count = 0; + for (TaggedPointer update_stack = update; update_stack != nullptr; + update_stack = update_stack.get()->previous()) { + update_stack_count += 1; + } + uint32_t update_stack_index = update_stack_count - 1; + for (TaggedPointer update_stack = update; update_stack != nullptr; + update_stack = update_stack.get()->previous()) { + + for (auto frame_index = update_stack.get()->frames().size() - 1; frame_index >= 0; --frame_index) { + auto frame = update_stack.get()->frames()[frame_index]; + + uint32_t count = frame.attribute->state().update_count(); + uint32_t input_index = frame.num_pushed_inputs; + uint32_t num_inputs = frame.attribute->inputs().size(); + + const char *pending = frame.needs_update ? "P" : ""; + const char *cancelled = frame.cancelled ? "C" : ""; + const char *dirty = frame.attribute->state().is_dirty() ? "D" : ""; + const char *has_value = frame.attribute->state().is_value_initialized() ? "V" : ""; + + fprintf(stdout, "frame %d.%d: attribute %u; count=%d, index=%d/%d %s%s%s%s\n", update_stack_index, + (uint32_t)frame_index, frame.attribute.offset(), count, input_index, num_inputs, pending, + cancelled, dirty, has_value); + } + + update_stack_index -= 1; + } + } +} + +namespace { + +int cycle_verbosity() { + const char *print_cycles = getenv("AG_PRINT_CYCLES"); + if (print_cycles) { + return atoi(print_cycles); + } + return 1; +} + +int trap_cycles() { + const char *trap_cycles = getenv("AG_TRAP_CYCLES"); + if (trap_cycles) { + return atoi(trap_cycles) != 0; + } + return false; +} + +} // namespace + +void Graph::print_cycle(data::ptr node) { + foreach_trace([&node](Trace &trace) { trace.log_message("cycle detected through attribute: %u", node); }); + + static int verbosity = cycle_verbosity(); + if (verbosity >= 1) { + fprintf(stdout, "=== AttributeGraph: cycle detected through attribute %u ===\n", node.offset()); + if (verbosity >= 2) { + if (verbosity >= 3) { + vector, 0, uint64_t> nodes = {}; + collect_stack(nodes); + NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; + for (auto node : nodes) { + [indexSet addIndex:node]; + } + + // TODO: figure out keys and values here + id objects[2] = {indexSet, indexSet}; + id keys[2] = {@"", @"attributes"}; + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys count:2]; + + NSString *desc = (__bridge NSString *)description((__bridge CFMutableDictionaryRef)dict); + if (desc) { + const char *string = [desc UTF8String]; + std::cout << string; + } + std::cout << "=== Evaluation stack ===\n"; + } + + print_stack(); + std::cout << "===\n"; + + if (verbosity >= 4 /* && os_variant_has_internal_diagnostics() */) { + AGGraphArchiveJSON("cycle.ag-json"); + } + } + } + + static bool traps = trap_cycles(); + if (traps) { + precondition_failure("cyclic graph: %u", node); + } +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index fc99115..3faabbd 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -2420,30 +2420,4 @@ void Graph::all_reset_profile() { all_unlock(); } -#pragma mark - Printing - -void Graph::print() { - // TODO: not implemented -} - -void Graph::print_attribute(data::ptr node) { - // TODO: not implemented -} - -void Graph::print_cycle(data::ptr node) { - // TODO: not implemented -} - -void Graph::print_data() { - data::table::shared().print(); - data::zone::print_header(); - for (auto subgraph : _subgraphs) { - subgraph->data::zone::print(); // TODO: make first field.. - } -} - -void Graph::print_stack() { - // TODO: not implemented -} - } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 0efc515..54d20ea 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -348,7 +348,7 @@ class Graph { // MARK: Tracing - enum TraceFlags: uint32_t { + enum TraceFlags : uint32_t { Enabled = 1 << 0, Full = 1 << 1, Backtrace = 1 << 2, @@ -401,12 +401,23 @@ class Graph { static void all_mark_profile(const char *event_name); static void all_reset_profile(); + // MARK: Printing + + void print(); + void print_attribute(data::ptr node); + + void print_cycle(data::ptr node); + + void print_data(); + static void print_stack(); + // MARK: Description - void description(CFDictionaryRef options); - void description(data::ptr node); + CFStringRef description_graph_dot(CFDictionaryRef _Nullable dict); + CFStringRef description(data::ptr node); + + CFStringRef description(CFDictionaryRef options); - char *description_graph_dot(CFDictionaryRef options); char *description_stack(CFDictionaryRef options); char *description_stack_frame(CFDictionaryRef options); char *description_stack_nodes(CFDictionaryRef options); @@ -414,16 +425,6 @@ class Graph { vector description_graph(CFDictionaryRef options); void write_to_file(const char *_Nullable filename, uint32_t arg); - - // MARK: Printing - - void print(); - - void print_attribute(data::ptr node); - void print_cycle(data::ptr node); - - void print_data(); - static void print_stack(); }; } // namespace AG From 9acc13f862dc88f0be2530d71e74ca6b691daa8c Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 25 Feb 2025 19:02:06 +1100 Subject: [PATCH 33/74] Implement Graph print and description methods --- Package.swift | 4 +- Sources/ComputeCxx/Attribute/AttributeID.h | 2 +- Sources/ComputeCxx/Attribute/AttributeType.h | 27 +- Sources/ComputeCxx/Attribute/Node/Edge.h | 18 +- .../ComputeCxx/Attribute/Node/IndirectNode.h | 14 +- Sources/ComputeCxx/Attribute/Node/Node.h | 27 +- Sources/ComputeCxx/Data/Zone.h | 6 +- Sources/ComputeCxx/Graph/Context.cpp | 2 +- Sources/ComputeCxx/Graph/Graph+Description.mm | 1019 ++++++++++++++++- Sources/ComputeCxx/Graph/Graph.cpp | 117 +- Sources/ComputeCxx/Graph/Graph.h | 40 +- .../ComputeCxx/Graph/Profile/ProfileData.h | 3 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 4 +- Sources/ComputeCxx/Graph/Tree/TreeElement.h | 17 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 16 +- Sources/ComputeCxx/Layout/Compare.cpp | 2 +- Sources/ComputeCxx/Subgraph/NodeCache.cpp | 2 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 16 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 10 +- .../{EquatableSupport.h => SwiftShims.h} | 5 + Sources/ComputeCxx/Version/AGVersion.cpp | 3 + Sources/ComputeCxx/Version/AGVersion.h | 9 + .../ComputeCxxSwiftSupport.swift} | 10 +- .../Utilities/include/Utilities/FreeDeleter.h | 1 + 24 files changed, 1231 insertions(+), 143 deletions(-) rename Sources/ComputeCxx/Swift/{EquatableSupport.h => SwiftShims.h} (67%) create mode 100644 Sources/ComputeCxx/Version/AGVersion.cpp create mode 100644 Sources/ComputeCxx/Version/AGVersion.h rename Sources/{EquatableSupport/EquatableSupport.swift => ComputeCxxSwiftSupport/ComputeCxxSwiftSupport.swift} (55%) diff --git a/Package.swift b/Package.swift index 741f074..431bb54 100644 --- a/Package.swift +++ b/Package.swift @@ -69,10 +69,10 @@ let package = Package( ), .swiftRuntimeTarget( name: "ComputeCxx", - dependencies: ["Utilities", "EquatableSupport"], + dependencies: ["Utilities", "ComputeCxxSwiftSupport"], cxxSettings: [.headerSearchPath("")] ), - .target(name: "EquatableSupport"), + .target(name: "ComputeCxxSwiftSupport"), ], cxxLanguageStandard: .cxx20 ) diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index f600c5d..059f978 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -66,7 +66,7 @@ class AttributeID { operator bool() const { return _value == 0; }; - uint32_t value() { return _value; }; + uint32_t value() const { return _value; }; Kind kind() const { return Kind(_value & KindMask); }; AttributeID with_kind(Kind kind) const { return AttributeID((_value & ~KindMask) | kind); }; diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index 49fa70a..6c8d6e5 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -2,6 +2,7 @@ #include +#include "Attribute/Node/Node.h" #include "Layout/LayoutDescriptor.h" #include "Swift/Metadata.h" @@ -15,12 +16,12 @@ class AttributeType; class AttributeVTable { public: using Callback = void (*)(const AttributeType *attribute_type, void *body); - using Callback2 = CFStringRef _Nonnull (*)(); + using DescriptionCallback = CFStringRef _Nonnull (*)(const AttributeType *attribute_type, void *body); Callback _unknown_0x00; Callback _unknown_0x08; Callback destroy_self; - Callback _unknown_0x18; - Callback2 _encode_node_callback; + DescriptionCallback _self_description; + DescriptionCallback _value_description; Callback _update_stack_callback; // maybe initialize value }; @@ -80,13 +81,31 @@ class AttributeType { // V table methods void vt_destroy_self(void *body) { + // TODO: does this check _flags or the callback itself? if (_flags & Flags::HasDestroySelf) { _vtable->destroy_self(this, body); } } - AttributeVTable::Callback2 vt_get_encode_node_callback() const { return _vtable->_encode_node_callback; } + CFStringRef _Nullable vt_self_description(void *self_data) const { + if (auto callback = _vtable->_self_description) { + return callback(this, self_data); + } + return nullptr; + } + CFStringRef _Nullable vt_value_description(void *value) const { + if (auto callback = _vtable->_value_description) { + return callback(this, value); + } + return nullptr; + } + AttributeVTable::DescriptionCallback _Nullable vt_get_self_description_callback() const { + return _vtable->_self_description; + } + AttributeVTable::DescriptionCallback _Nullable vt_get_value_description_callback() const { + return _vtable->_value_description; + } AttributeVTable::Callback vt_get_update_stack_callback() const { return _vtable->_update_stack_callback; } }; diff --git a/Sources/ComputeCxx/Attribute/Node/Edge.h b/Sources/ComputeCxx/Attribute/Node/Edge.h index ca22a66..3b5e20b 100644 --- a/Sources/ComputeCxx/Attribute/Node/Edge.h +++ b/Sources/ComputeCxx/Attribute/Node/Edge.h @@ -9,10 +9,10 @@ class Node; struct InputEdge { enum Flags : uint8_t { - Unknown0 = 1 << 0, + Unprefetched = 1 << 0, Unknown1 = 1 << 1, - Unknown2 = 1 << 2, - IsPending = 1 << 3, // set when node is dirty + AlwaysEnabled = 1 << 2, + Changed = 1 << 3, // set when node is dirty Unknown4 = 1 << 4, }; @@ -26,17 +26,17 @@ struct InputEdge { AttributeID value; uint8_t _flags; - bool is_unknown0() { return _flags & Flags::Unknown0; }; - void set_unknown0(bool value) { _flags = (_flags & ~Flags::Unknown0) | (value ? Flags::Unknown0 : 0); }; + bool is_unprefetched() { return _flags & Flags::Unprefetched; }; + void set_unprefetched(bool value) { _flags = (_flags & ~Flags::Unprefetched) | (value ? Flags::Unprefetched : 0); }; bool is_unknown1() { return _flags & Flags::Unknown1; }; void set_unknown1(bool value) { _flags = (_flags & ~Flags::Unknown1) | (value ? Flags::Unknown1 : 0); }; - bool is_unknown2() { return _flags & Flags::Unknown2; }; - void set_unknown2(bool value) { _flags = (_flags & ~Flags::Unknown2) | (value ? Flags::Unknown2 : 0); }; + bool is_always_enabled() { return _flags & Flags::AlwaysEnabled; }; + void set_always_enabled(bool value) { _flags = (_flags & ~Flags::AlwaysEnabled) | (value ? Flags::AlwaysEnabled : 0); }; - bool is_pending() { return _flags & Flags::IsPending; }; - void set_pending(bool value) { _flags = (_flags & ~Flags::IsPending) | (value ? Flags::IsPending : 0); }; + bool is_changed() { return _flags & Flags::Changed; }; + void set_changed(bool value) { _flags = (_flags & ~Flags::Changed) | (value ? Flags::Changed : 0); }; bool is_unknown4() { return _flags & Flags::Unknown4; }; void set_unknown4(bool value) { _flags = (_flags & ~Flags::Unknown4) | (value ? Flags::Unknown4 : 0); }; diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index a0ae007..e60388d 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -17,7 +17,7 @@ class IndirectNode { private: struct Info { unsigned int is_mutable : 1; - unsigned int traverses_graph_contexts : 1; + unsigned int traverses_contexts : 1; unsigned int offset : 30; }; static_assert(sizeof(Info) == 4); @@ -29,10 +29,10 @@ class IndirectNode { uint16_t _relative_offset; // could be relative offset, see Subgraph::insert_attribute public: - IndirectNode(WeakAttributeID source, bool traverses_graph_contexts, uint32_t offset, uint16_t size) + IndirectNode(WeakAttributeID source, bool traverses_contexts, uint32_t offset, uint16_t size) : _source(source) { _info.is_mutable = false; - _info.traverses_graph_contexts = traverses_graph_contexts; + _info.traverses_contexts = traverses_contexts; _info.offset = offset; _size = size; _relative_offset = 0; @@ -44,8 +44,8 @@ class IndirectNode { MutableIndirectNode &to_mutable(); const MutableIndirectNode &to_mutable() const; - void set_traverses_graph_contexts(bool value) { _info.traverses_graph_contexts = value; }; - bool traverses_graph_contexts() const { return _info.traverses_graph_contexts; }; + void set_traverses_contexts(bool value) { _info.traverses_contexts = value; }; + bool traverses_contexts() const { return _info.traverses_contexts; }; uint32_t offset() const { return _info.offset; }; bool has_size() const { return _size != InvalidSize; }; @@ -69,9 +69,9 @@ class MutableIndirectNode : public IndirectNode { uint32_t _initial_offset; public: - MutableIndirectNode(WeakAttributeID source, bool traverses_graph_contexts, uint32_t offset, uint16_t size, + MutableIndirectNode(WeakAttributeID source, bool traverses_contexts, uint32_t offset, uint16_t size, WeakAttributeID initial_source, uint32_t initial_offset) - : IndirectNode(source, traverses_graph_contexts, offset, size), _dependency(0), _initial_source(initial_source), + : IndirectNode(source, traverses_contexts, offset, size), _dependency(0), _initial_source(initial_source), _initial_offset(initial_offset){ }; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 0a70617..6e9213b 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -18,14 +18,14 @@ class Graph; class NodeFlags { public: - enum Flags3 : uint8_t {}; + enum Flags3 : uint8_t {}; // TODO: subgraph_flags enum Flags4 : uint8_t { HasIndirectSelf = 1 << 0, // 0x01 HasIndirectValue = 1 << 1, // 0x02 - InputsTraverseGraphContexts = 1 << 2, // 0x04 - InputsUnsorted = 1 << 3, // 0x08 - Cacheable = 1 << 4, // 0x10 + InputsTraverseContexts = 1 << 2, // 0x04 + InputsUnsorted = 1 << 3, // 0x08 + Cacheable = 1 << 4, // 0x10 Unknown0x20 = 1 << 5, // 0x20 - initial value Unknown0x40 = 1 << 6, // 0x40 - didn't call mark_changed @@ -56,9 +56,9 @@ class NodeFlags { _value4 = (_value4 & ~Flags4::HasIndirectValue) | (value ? Flags4::HasIndirectValue : 0); }; - bool inputs_traverse_graph_contexts() { return _value4 & Flags4::InputsTraverseGraphContexts; }; - void set_inputs_traverse_graph_contexts(bool value) { - _value4 = (_value4 & ~Flags4::InputsTraverseGraphContexts) | (value ? Flags4::InputsTraverseGraphContexts : 0); + bool inputs_traverse_contexts() { return _value4 & Flags4::InputsTraverseContexts; }; + void set_inputs_traverse_contexts(bool value) { + _value4 = (_value4 & ~Flags4::InputsTraverseContexts) | (value ? Flags4::InputsTraverseContexts : 0); }; bool inputs_unsorted() { return _value4 & Flags4::InputsUnsorted; }; void set_inputs_unsorted(bool value) { @@ -88,7 +88,7 @@ class Node { ValueInitialized = 1 << 4, // 0x10 SelfInitialized = 1 << 5, // 0x20 - Updating = 1 << 6, // 0x40 + Updating = 1 << 6, // 0x40 // TODO: make this and next bit a count 0..<4 UpdatingCyclic = 1 << 7, // 0x80 }; @@ -128,9 +128,7 @@ class Node { bool is_updating() { return _data & (Updating | UpdatingCyclic); } bool is_updating_cyclic() { return (_data & (Updating | UpdatingCyclic)) == (Updating | UpdatingCyclic); } - uint8_t update_count() const { - return _data >> 6; - } + uint8_t update_count() const { return _data >> 6; } }; private: @@ -158,6 +156,13 @@ class Node { void set_state(State state) { _info.state = state.data(); }; uint32_t type_id() const { return uint32_t(_info.type_id); }; + uint8_t value_state() const { + return (state().is_dirty() ? 1 : 0) << 0 | (state().is_pending() ? 1 : 0) << 1 | + (state().is_updating() ? 1 : 0) << 2 | (state().is_value_initialized() ? 1 : 0) << 3 | + (state().is_main_thread() ? 1 : 0) << 4 | (flags().value4_unknown0x20() ? 1 : 0) << 5 | + (state().is_main_thread_only() ? 1 : 0) << 6 | (flags().value4_unknown0x40() ? 1 : 0) << 7; + }; + NodeFlags &flags() { return _flags; }; const NodeFlags &flags() const { return _flags; }; diff --git a/Sources/ComputeCxx/Data/Zone.h b/Sources/ComputeCxx/Data/Zone.h index 4970ca6..b425d08 100644 --- a/Sources/ComputeCxx/Data/Zone.h +++ b/Sources/ComputeCxx/Data/Zone.h @@ -17,15 +17,15 @@ class zone { class info { private: enum { - zone_id_mask = 0x7fffffff, + id_mask = 0x7fffffff, deleted = 0x80000000, }; uint32_t _value; info(uint32_t value) : _value(value){}; public: - uint32_t zone_id() { return _value & zone_id_mask; }; - info with_zone_id(uint32_t zone_id) const { return info((_value & ~zone_id_mask) | (zone_id & zone_id_mask)); }; + uint32_t zone_id() const { return _value & id_mask; }; // TODO: id() + info with_zone_id(uint32_t zone_id) const { return info((_value & ~id_mask) | (zone_id & id_mask)); }; info with_deleted() const { return info(_value | deleted); }; diff --git a/Sources/ComputeCxx/Graph/Context.cpp b/Sources/ComputeCxx/Graph/Context.cpp index 3bcda3a..81193d2 100644 --- a/Sources/ComputeCxx/Graph/Context.cpp +++ b/Sources/ComputeCxx/Graph/Context.cpp @@ -39,7 +39,7 @@ Graph::Context::~Context() { if (_graph->_num_contexts != 1) { auto batch = without_invalidating(_graph); for (auto subgraph : _graph->subgraphs()) { - if (subgraph->graph_context_id() == _unique_id) { + if (subgraph->context_id() == _unique_id) { subgraph->invalidate_and_delete_(true); } } diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 896e5c3..38a879e 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -4,9 +4,35 @@ #include #include "AGGraph.h" +#include "Attribute/AttributeType.h" +#include "Attribute/Node/IndirectNode.h" +#include "Attribute/Node/Node.h" +#include "Attribute/OffsetAttributeID.h" +#include "Graph/Profile/ProfileData.h" #include "Subgraph/Subgraph.h" +#include "Swift/SwiftShims.h" +#include "Time/Time.h" #include "Trace/Trace.h" +#include "Tree/TreeElement.h" #include "UpdateStack.h" +#include "Utilities/FreeDeleter.h" +#include "Version/AGVersion.h" + +NSString *AGDescriptionFormat = @"format"; +NSString *AGDescriptionMaxFrames = @"max-frames"; +NSString *AGDescriptionIncludeValues = @"include-values"; +NSString *AGDescriptionTruncationLimit = @"truncation-limit"; + +namespace { + +NSString *escaped_string(NSString *string, NSUInteger truncation_limit) { + if ([string length] > truncation_limit) { + string = [[string substringToIndex:truncation_limit] stringByAppendingString:@"…"]; + } + return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; +} + +} // namespace namespace AG { @@ -103,12 +129,10 @@ int trap_cycles() { [indexSet addIndex:node]; } - // TODO: figure out keys and values here - id objects[2] = {indexSet, indexSet}; - id keys[2] = {@"", @"attributes"}; - NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys count:2]; + NSMutableDictionary *dict = [NSMutableDictionary + dictionaryWithDictionary:@{AGDescriptionFormat : @"graph/dot", @"attribute-ids" : indexSet}]; - NSString *desc = (__bridge NSString *)description((__bridge CFMutableDictionaryRef)dict); + NSString *desc = (NSString *)description(this, (__bridge CFMutableDictionaryRef)dict); if (desc) { const char *string = [desc UTF8String]; std::cout << string; @@ -131,4 +155,989 @@ int trap_cycles() { } } +#pragma mark - Description + +CFStringRef Graph::description(data::ptr node) {} + +id Graph::description(Graph *graph, CFDictionaryRef options) { + NSString *format = ((__bridge NSDictionary *)options)[AGDescriptionFormat]; + if ([format isEqualToString:@"graph/dict"]) { + return (__bridge id)description_graph(graph, options); + } + if ([format isEqualToString:@"graph/dot"]) { + return (__bridge id)graph->description_graph_dot(options); + } + if ([format hasPrefix:@"stack/"]) { + auto update = current_update(); + if (auto update_stack = update.get()) { + if ([format isEqualToString:@"stack/text"]) { + return (__bridge id)graph->description_stack(options); + } + if ([format isEqualToString:@"stack/nodes"]) { + return (__bridge id)graph->description_stack_nodes(options); + } + if ([format isEqualToString:@"stack/frame"]) { + return (__bridge id)graph->description_stack_frame(options); + } + } + } + return nil; +} + +CFDictionaryRef Graph::description_graph(Graph *graph_param, CFDictionaryRef options) { + NSNumber *include_values_number = ((__bridge NSDictionary *)options)[AGDescriptionIncludeValues]; + bool include_values = false; + if (include_values_number) { + include_values = [include_values_number boolValue]; + } + + NSNumber *truncation_limit_number = ((__bridge NSDictionary *)options)[AGDescriptionTruncationLimit]; + uint64_t truncation_limit = 1024; + if (truncation_limit_number) { + truncation_limit = [truncation_limit_number unsignedLongValue]; + } + + NSMutableArray *graph_dicts = [NSMutableArray array]; + + auto graph_indices_by_id = std::unordered_map(); + auto graph_stack = std::stack>(); + + auto push_graph = [&graph_dicts, &graph_indices_by_id, &graph_stack](Graph *graph) -> uint64_t { + auto found = graph_indices_by_id.find(graph->unique_id()); + if (found != graph_indices_by_id.end()) { + return found->second; + } + uint64_t index = [graph_dicts count]; + [graph_dicts addObject:[NSNull null]]; + graph_indices_by_id.emplace(graph->unique_id(), index); + graph_stack.push(graph); + return index; + }; + + if (graph_param) { + push_graph(graph_param); + } + NSArray *graph_ids = ((__bridge NSDictionary *)options)[@"graph_ids"]; + if (graph_ids && [graph_ids isKindOfClass:[NSArray class]]) { + all_lock(); + for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { + if ([graph_ids containsObject:[NSNumber numberWithUnsignedLong:graph->unique_id()]]) { + push_graph(graph); + } + } + all_unlock(); + } + NSNumber *all_graphs = ((__bridge NSDictionary *)options)[@"all_graphs"]; + if (all_graphs && [all_graphs boolValue]) { + all_lock(); + for (auto other_graph = _all_graphs; other_graph != nullptr; other_graph = other_graph->_next) { + if (other_graph != graph_param) { + push_graph(other_graph); + } + } + all_unlock(); + } + + while (!graph_stack.empty()) { + Graph *graph = graph_stack.top(); + graph_stack.pop(); + + NSMutableArray *node_dicts = [NSMutableArray array]; + NSMutableArray *edge_dicts = [NSMutableArray array]; + + auto type_indices_by_id = std::unordered_map(); + auto type_ids = vector(); + + auto node_indices_by_id = std::unordered_map, uint64_t>(); + + for (auto subgraph : graph->subgraphs()) { + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else { + relative_offset = 0; + } + + if (attribute.is_direct()) { + uint64_t index = node_indices_by_id.size() + 1; + node_indices_by_id.emplace(attribute, index); + + auto node = attribute.to_node(); + + uint32_t type_id = node.type_id(); + uint64_t type_index; + auto found = type_indices_by_id.find(type_id); + if (found != type_indices_by_id.end()) { + type_index = found->second; + } else { + type_index = type_ids.size(); + type_ids.push_back(type_id); + type_indices_by_id.emplace(type_id, index); + } + + NSMutableDictionary *node_dict = [NSMutableDictionary dictionary]; + node_dict[@"type"] = [NSNumber numberWithUnsignedLong:type_index]; + node_dict[@"id"] = [NSNumber numberWithUnsignedLong:attribute]; + + const AttributeType &attribute_type = graph->attribute_type(node.type_id()); + if (node.state().is_self_initialized()) { + void *self = node.get_self(attribute_type); + if (auto desc = attribute_type.vt_self_description(self)) { + node_dict[@"desc"] = escaped_string((__bridge NSString *)desc, truncation_limit); + } + } + if (include_values && node.state().is_value_initialized()) { + void *value = node.get_value(); + if (auto value_desc = attribute_type.vt_value_description(value)) { + node_dict[@"value"] = escaped_string((__bridge NSString *)value_desc, truncation_limit); + } + } + + auto flags = attribute.to_node().value_state(); + if (flags) { + node_dict[@"flags"] = [NSNumber numberWithUnsignedInt:flags]; + } + + auto profile_data = graph->_profile_data.get(); + if (profile_data) { + auto found = + profile_data->current_category().items_by_attribute().find(attribute.to_node_ptr()); + if (found != profile_data->current_category().items_by_attribute().end()) { + CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); + if (item_json) { + node_dict[@"profile"] = (__bridge NSDictionary *)item_json; + } + } + if (profile_data->categories().size()) { + NSMutableDictionary *event_dicts = [NSMutableDictionary dictionary]; + for (auto &entry : profile_data->categories()) { + uint32_t event_id = entry.first; + auto found = entry.second.items_by_attribute().find(attribute.to_node_ptr()); + if (found != entry.second.items_by_attribute().end()) { + CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); + if (item_json) { + NSString *event_name = + [NSString stringWithUTF8String:graph->key_name(event_id)]; + event_dicts[event_name] = (__bridge NSDictionary *)item_json; + } + } + } + if ([event_dicts count]) { + node_dict[@"events"] = event_dicts; + } + } + } + + [node_dicts addObject:node_dict]; + } + } + } + } + + for (auto subgraph : graph->subgraphs()) { + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else { + relative_offset = 0; + } + + if (attribute.is_direct()) { + auto node = attribute.to_node(); + for (auto input_edge : node.inputs()) { + OffsetAttributeID resolved = input_edge.value.resolve(TraversalOptions::None); + if (resolved.attribute().is_direct()) { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + + auto src = node_indices_by_id.find(resolved.attribute().to_node_ptr())->second; + dict[@"src"] = [NSNumber numberWithUnsignedLong:src]; + auto dest = node_indices_by_id.find(attribute.to_node_ptr())->second; + dict[@"dest"] = [NSNumber numberWithUnsignedLong:dest]; + bool indirect = attribute.is_indirect(); + if (indirect) { + if (resolved.offset()) { + dict[@"offset"] = [NSNumber numberWithUnsignedLong:resolved.offset()]; + } + } + if (indirect || input_edge.is_always_enabled() || input_edge.is_changed() || + input_edge.is_unknown4() || input_edge.is_unprefetched()) { + dict[@"flags"] = @YES; // + } + + [edge_dicts addObject:dict]; + } + } + } + } + } + } + + // Add types for any removed nodes + if (graph->_profile_data) { + for (uint32_t type_id = 1; type_id < graph->_types.size(); ++type_id) { + if (type_indices_by_id.contains(type_id)) { + continue; + } + + auto removed_item = graph->_profile_data->current_category().removed_items_by_type_id().find(type_id); + if (removed_item != graph->_profile_data->current_category().removed_items_by_type_id().end()) { + if (!type_indices_by_id.contains(type_id)) { + auto index = type_ids.size(); + type_ids.push_back(type_id); + type_indices_by_id.try_emplace(type_id, index); + } + } else { + for (auto entry : graph->_profile_data->categories()) { + auto category = entry.second; + auto removed_item = category.removed_items_by_type_id().find(type_id); + if (removed_item != category.removed_items_by_type_id().end()) { + if (!type_indices_by_id.contains(type_id)) { + auto index = type_ids.size(); + type_ids.push_back(type_id); + type_indices_by_id.try_emplace(type_id, index); + } + } + } + } + } + } + + NSMutableArray *types = [NSMutableArray array]; + for (auto type_id : type_ids) { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + + const AttributeType &type = graph->attribute_type(type_id); + + dict[@"id"] = [NSNumber numberWithUnsignedInt:type_id]; + NSString *name = [NSString stringWithUTF8String:type.self_metadata().name(false)]; + dict[@"name"] = escaped_string(name, truncation_limit); + NSString *value = [NSString stringWithUTF8String:type.value_metadata().name(false)]; + dict[@"value"] = escaped_string(value, truncation_limit); + dict[@"size"] = + [NSNumber numberWithUnsignedLong:type.self_metadata().vw_size() + type.value_metadata().vw_size()]; + dict[@"flags"] = [NSNumber numberWithUnsignedInt:type.flags()]; + + auto profile_data = graph->_profile_data.get(); + if (profile_data) { + auto found = profile_data->current_category().removed_items_by_type_id().find(type_id); + if (found != profile_data->current_category().removed_items_by_type_id().end()) { + CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); + if (item_json) { + dict[@"profile"] = (__bridge NSDictionary *)item_json; + } + } + if (profile_data->categories().size()) { + NSMutableDictionary *events = [NSMutableDictionary dictionary]; + for (auto &entry : profile_data->categories()) { + uint32_t event_id = entry.first; + auto found = entry.second.removed_items_by_type_id().find(type_id); + if (found != entry.second.removed_items_by_type_id().end()) { + CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); + if (item_json) { + NSString *event_name = [NSString stringWithUTF8String:graph->key_name(event_id)]; + events[event_name] = (__bridge NSDictionary *)item_json; + } + } + } + if ([events count]) { + dict[@"events"] = events; + } + } + } + + [types addObject:dict]; + } + + // Subgraphs + + auto subgraph_indices_by_id = std::unordered_map(); + for (uint32_t index = 0, iter = graph->subgraphs().size(); iter > 0; --iter, ++index) { + subgraph_indices_by_id.try_emplace(graph->subgraphs()[index], index); + } + + NSMutableArray *subgraphs = [NSMutableArray array]; + for (auto subgraph : graph->subgraphs()) { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + dict[@"id"] = [NSNumber numberWithUnsignedInt:subgraph->subgraph_id()]; + dict[@"context_id"] = [NSNumber numberWithUnsignedLong:subgraph->context_id()]; + if (subgraph->validation_state() != Subgraph::ValidationState::Valid) { + dict[@"invalid"] = @YES; + } + + // Parents + NSMutableArray *parent_descriptions = [NSMutableArray array]; + for (auto parent : subgraph->parents()) { + id parent_description; + auto subgraph_index = subgraph_indices_by_id.find(parent); + if (subgraph_index != subgraph_indices_by_id.end()) { + parent_description = [NSNumber numberWithUnsignedLong:subgraph_index->second]; + } else { + uint64_t parent_graph_index; + auto parent_graph = graph_indices_by_id.find(parent->graph()->unique_id()); + if (parent_graph != graph_indices_by_id.end()) { + parent_graph_index = parent_graph->second; + } else { + parent_graph_index = push_graph(parent->graph()); + } + parent_description = @{ + @"graph" : [NSNumber numberWithUnsignedLong:parent_graph_index], + @"subgraph_id" : [NSNumber numberWithUnsignedInt:parent->subgraph_id()] + }; + } + [parent_descriptions addObject:parent_description]; + } + if ([parent_descriptions count]) { + dict[@"parents"] = parent_descriptions; + } + + // Children + NSMutableArray *child_descriptions = [NSMutableArray array]; + for (auto child : subgraph->children()) { + Subgraph *child_subgraph = child.subgraph(); + id child_description; + auto subgraph_index = subgraph_indices_by_id.find(child_subgraph); + if (subgraph_index != subgraph_indices_by_id.end()) { + child_description = [NSNumber numberWithUnsignedLong:subgraph_index->second]; + } else { + uint64_t child_graph_index; + auto child_graph = graph_indices_by_id.find(child_subgraph->graph()->unique_id()); + if (child_graph != graph_indices_by_id.end()) { + child_graph_index = child_graph->second; + } else { + child_graph_index = push_graph(child_subgraph->graph()); + } + child_description = @{ + @"graph" : [NSNumber numberWithUnsignedLong:child_graph_index], + @"subgraph_id" : [NSNumber numberWithUnsignedInt:child_subgraph->subgraph_id()] + }; + } + [child_descriptions addObject:child_description]; + } + if ([child_descriptions count]) { + dict[@"children"] = child_descriptions; + } + + // Nodes + NSMutableArray *nodes = [NSMutableArray array]; + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + if (attribute.is_nil()) { + break; + } + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else { + relative_offset = 0; + } + + if (attribute.is_direct()) { + uint8_t subgraph_flags = attribute.to_node().flags().value3(); + auto found_node_index = node_indices_by_id.find(attribute.to_node_ptr()); + if (found_node_index != node_indices_by_id.end()) { + [nodes addObject:[NSNumber numberWithUnsignedLong:found_node_index->second]]; + if (subgraph_flags) { + NSMutableDictionary *node_dict = nodes[found_node_index->second]; + node_dict[@"subgraph_flags"] = [NSNumber numberWithUnsignedChar:subgraph_flags]; + nodes[found_node_index->second] = node_dict; + } + } + } + } + } + if ([nodes count]) { + dict[@"nodes"] = nodes; + } + + [subgraphs addObject:dict]; + } + + NSMutableArray *tree_dicts = nil; + if (auto map = graph->tree_data_elements()) { + tree_dicts = [NSMutableArray array]; + + auto tree_stack = std::stack, vector, 0, uint64_t>>(); + + auto tree_element_indices = std::unordered_map, uint64_t>(); + auto trees = AG::vector, 0ul, unsigned long>(); + + if (!graph->subgraphs().empty()) { + + for (auto subgraph : graph->subgraphs()) { + data::ptr tree_root = subgraph->tree_root(); + if (tree_root) { + tree_stack.push(tree_root); + } + while (!tree_stack.empty()) { + data::ptr tree = tree_stack.top(); + tree_stack.pop(); + + uint64_t tree_element_index; + auto found_tree_element = tree_element_indices.find(tree); + if (found_tree_element == tree_element_indices.end()) { + auto index = trees.size(); + tree_element_indices.try_emplace(tree, index); + trees.push_back(tree); + + if (tree->next) { + tree_stack.push(tree->next); + } + if (tree->old_parent) { + tree_stack.push(tree->old_parent); + } + } + } + + auto found_tree_data_element = map->find(subgraph); + if (found_tree_data_element != map->end()) { + found_tree_data_element->second.sort_nodes(); + } + } + + for (auto tree : trees) { + NSMutableDictionary *tree_dict = [NSMutableDictionary dictionary]; + + // TODO: what is creator key? + + if (tree->owner.without_kind() != 0 && tree->type != nullptr) { + OffsetAttributeID resolved = tree->owner.resolve(TraversalOptions::ReportIndirectionInOffset); + if (resolved.attribute().is_direct()) { + tree_dict[@"node"] = [NSNumber + numberWithUnsignedLong:node_indices_by_id.find(resolved.attribute().to_node_ptr()) + ->second]; + if (resolved.offset() != 0) { + tree_dict[@"offset"] = [NSNumber numberWithUnsignedLong:resolved.offset() - 1]; // 3 + } + } + + tree_dict[@"desc"] = escaped_string([NSString stringWithUTF8String:tree->type->name(false)], + truncation_limit); // 4 + + if (tree->parent == nullptr) { + tree_dict[@"root"] = @YES; + } + } else if (tree->owner.without_kind() != 0 && tree->type == nullptr) { + tree_dict[@"node"] = [NSNumber + numberWithUnsignedLong:node_indices_by_id.find(tree->owner.to_node_ptr())->second]; // 2 + } else { + if (tree->parent == nullptr) { + tree_dict[@"root"] = @YES; // 1 + } + } + + if (tree->flags) { + tree_dict[@"flags"] = [NSNumber numberWithUnsignedInt:tree->flags]; + } + + if (tree->next) { + NSMutableArray *children = [NSMutableArray array]; + for (data::ptr child = tree->next; child != nullptr; + child = child->old_parent) { + [children + addObject:[NSNumber numberWithUnsignedLong:tree_element_indices.find(child)->second]]; + } + tree_dict[@"children"] = children; + } + + Subgraph *subgraph = TreeElementID(tree).subgraph(); + auto tree_data_element = map->find(subgraph); + if (tree_data_element != map->end()) { + + auto data_nodes = tree_data_element->second.nodes(); + std::pair, data::ptr> *found = std::find_if( + data_nodes.begin(), data_nodes.end(), [&tree](auto node) { return node.first == tree; }); + + if (found != data_nodes.end()) { + NSMutableArray *nodes = [NSMutableArray array]; + for (auto node = found; node != data_nodes.end(); ++node) { + if (node->first != tree) { + break; + } + if (node->second) { + [nodes addObject:[NSNumber + numberWithUnsignedLong:node_indices_by_id.find(node->second) + ->second]]; + } + } + tree_dict[@"nodes"] = nodes; + } + } + + NSMutableDictionary *values = [NSMutableDictionary dictionary]; + for (data::ptr value = tree->last_value; value != nullptr; + value = value->previous_sibling) { + + OffsetAttributeID resolved_value = value->value.resolve(TraversalOptions::None); + if (resolved_value.attribute().is_direct()) { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + dict[@"node"] = + [NSNumber numberWithUnsignedLong:node_indices_by_id + .find(resolved_value.attribute().to_node_ptr()) + ->second]; + if (resolved_value.offset() != 0) { + dict[@"offset"] = [NSNumber numberWithUnsignedLong:resolved_value.offset()]; + } + + values[[NSString stringWithUTF8String:subgraph->graph()->key_name(value->key_id)]] = dict; + } + } + tree_dict[@"values"] = values; + + [tree_dicts addObject:tree_dict]; + } + } + } + + NSMutableDictionary *graph_dict = [NSMutableDictionary dictionary]; + graph_dict[@"id"] = [NSNumber numberWithUnsignedLong:graph->unique_id()]; + graph_dict[@"types"] = types; + graph_dict[@"nodes"] = node_dicts; + graph_dict[@"edges"] = edge_dicts; + graph_dict[@"subgraphs"] = subgraphs; + if (tree_dicts) { + graph_dict[@"trees"] = tree_dicts; + } + + graph_dict[@"transaction_count"] = [NSNumber numberWithUnsignedLong:graph->transaction_count()]; + if (auto profile_data = graph->_profile_data.get()) { + NSDictionary *json = + (__bridge NSDictionary *)profile_data->json_data(profile_data->current_category(), *graph); + if (json) { + [graph_dict addEntriesFromDictionary:json]; + } + if (!profile_data->categories().empty()) { + NSMutableDictionary *events = [NSMutableDictionary dictionary]; + for (auto category : profile_data->categories()) { + NSDictionary *json = (__bridge NSDictionary *)profile_data->json_data(category.second, *graph); + if (json) { + events[[NSString stringWithUTF8String:graph->key_name(category.first)]] = json; + } + } + if ([events count]) { + graph_dict[@"events"] = events; + } + } + } else { + graph_dict[@"update_count"] = [NSNumber numberWithUnsignedLong:graph->update_count()]; + graph_dict[@"change_count"] = [NSNumber numberWithUnsignedLong:graph->change_count()]; + } + + graph_dicts[graph_indices_by_id.find(graph->unique_id())->second] = graph_dict; + } + + NSMutableDictionary *description = [NSMutableDictionary dictionary]; + description[@"version"] = @2; // TODO: check + description[@"graphs"] = graph_dicts; + return (__bridge CFDictionaryRef)description; +} + +CFStringRef Graph::description_graph_dot(CFDictionaryRef _Nullable options) { + NSNumber *include_values_number = ((__bridge NSDictionary *)options)[AGDescriptionIncludeValues]; + bool include_values = false; + if (include_values_number) { + include_values = [include_values_number boolValue]; + } + + id attribute_ids = ((__bridge NSDictionary *)options)[@"attribute-ids"]; + if (![attribute_ids isKindOfClass:[NSIndexSet class]]) { + attribute_ids = nil; + } + + NSNumber *truncation_limit_number = ((__bridge NSDictionary *)options)[AGDescriptionTruncationLimit]; + uint64_t truncation_limit = 40; + if (truncation_limit_number) { + truncation_limit = [truncation_limit_number unsignedLongValue]; + } + + NSMutableString *result = [NSMutableString string]; + + [result appendString:@"digraph {\n"]; + + auto indirect_nodes = std::unordered_map, data::ptr>(); + + if (!subgraphs().empty()) { + for (auto subgraph : subgraphs()) { + for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + uint16_t relative_offset = page->relative_offset_1; + while (relative_offset) { + AttributeID attribute = AttributeID(page + relative_offset); + + if (attribute.is_direct()) { + relative_offset = attribute.to_node().flags().relative_offset(); + } else if (attribute.is_indirect()) { + relative_offset = attribute.to_indirect_node().relative_offset(); + } else if (attribute.is_nil()) { + break; + } else { + break; + } + + if (!attribute.is_direct()) { + continue; + } + + if (!attribute_ids || [attribute_ids containsIndex:attribute]) { + + [result appendFormat:@" _%d[label=\"%d", attribute.value(), attribute.value()]; + + Node &node = attribute.to_node(); + const AttributeType &node_type = attribute_type(node.type_id()); + if (node.state().is_self_initialized()) { + if (auto callback = node_type.vt_get_self_description_callback()) { + void *self = node.get_self(node_type); + if (auto desc = callback(&node_type, self)) { + [result appendString:@": "]; + [result appendString:escaped_string((__bridge NSString *)desc, truncation_limit)]; + } + } + } + if (include_values && node.state().is_value_initialized()) { + if (auto callback = node_type.vt_get_value_description_callback()) { + void *value = node.get_value(); + if (auto value_desc = callback(&node_type, value)) { + [result appendString:@" → "]; + [result + appendString:escaped_string((__bridge NSString *)value_desc, truncation_limit)]; + } + } + } + + double duration_fraction = 0.0; + if (auto profile_data = _profile_data.get()) { + auto items_by_attribute = profile_data->current_category().items_by_attribute(); + auto found_item = items_by_attribute.find(attribute.to_node_ptr()); + if (found_item != items_by_attribute.end()) { + auto item = found_item->second; + if (item.data().count) { + uint64_t count = item.data().count; // count + uint64_t duration = item.data().duration; + uint64_t total_duration = + profile_data->current_category().data().duration; // total duration + double average_update_time = + absolute_time_to_seconds((double)duration / (double)count); + duration_fraction = + ((double)duration / (double)total_duration) * 100.0; // duration fraction + [result appendFormat:@"\\n%.2g%%: %g × %.2fμs", duration_fraction, + (double)item.data().count, average_update_time * 1000000.0]; + } + } + } + [result appendString:@"\""]; + + bool filled = false; + + // heat colors + if (node.state().is_updating()) { + [result appendString:@" fillcolor=cyan"]; + filled = true; + } else if (duration_fraction > 10.0) { + [result appendString:@" fillcolor=orangered"]; + filled = true; + } else if (duration_fraction > 5.0) { + [result appendString:@" fillcolor=orange"]; + filled = true; + } else if (duration_fraction > 1.0) { + [result appendString:@" fillcolor=yellow"]; + filled = true; + } + + if (node.state().is_value_initialized() && !node.inputs().empty() && !node.outputs().empty()) { + if (filled) { + [result appendFormat:@" style=filled"]; + } + } else { + [result appendFormat:node.state().is_value_initialized() ? @" style=\"bold%s\"" + : @" style=\"dashed%s\"", + filled ? ",filled" : ""]; + } + + if (node.state().is_dirty()) { + [result appendString:@" color=red"]; + } + + [result appendString:@"];\n"]; + + for (auto input_edge : node.inputs()) { + + AttributeID resolved_input_attribute = + input_edge.value.resolve(TraversalOptions::None).attribute(); + if (resolved_input_attribute.is_direct() && + (!attribute_ids || [attribute_ids containsIndex:resolved_input_attribute.value()])) { + + [result appendFormat:@" _%d -> _%d[", input_edge.value.without_kind().value(), + attribute.value()]; + + // collect source inputs + AttributeID intermediate = input_edge.value; + while (intermediate.is_indirect()) { + indirect_nodes.try_emplace(intermediate.without_kind().to_indirect_node_ptr(), + intermediate.without_kind().to_indirect_node_ptr()); + + AttributeID source = intermediate.to_indirect_node().source().attribute(); + if (source.is_direct()) { + break; + } + intermediate = source.resolve(TraversalOptions::SkipMutableReference).attribute(); + } + + if (input_edge.is_changed()) { + [result appendString:@" color=red"]; + } + + Subgraph *input_subgraph = resolved_input_attribute.subgraph(); + Subgraph *attribute_subgraph = attribute.subgraph(); + uint64_t input_context_id = input_subgraph ? input_subgraph->context_id() : 0; + uint64_t attribute_context_id = + attribute_subgraph ? attribute_subgraph->context_id() : 0; + if (input_context_id != attribute_context_id) { + [result appendString:@" penwidth=2"]; + } + + if (input_edge.value.is_indirect()) { + uint32_t offset = + input_edge.value.resolve(TraversalOptions::SkipMutableReference).offset(); + [result appendFormat:@" label=\"@%d\"", offset]; + } + + [result appendString:@"];\n"]; + } + } + } + } + } + } + + for (auto entry : indirect_nodes) { + data::ptr indirect_node = entry.first; + + [result appendFormat:@" _%d[label=\"%d\" shape=box];\n", indirect_node.offset(), indirect_node.offset()]; + + OffsetAttributeID resolved_source = + indirect_node->source().attribute().resolve(TraversalOptions::SkipMutableReference); + [result appendFormat:@" _%d -> _%d[label=\"@%d\"];\n", resolved_source.attribute().without_kind().value(), + indirect_node.offset(), resolved_source.offset()]; + + if (indirect_node->is_mutable()) { + if (auto dependency = indirect_node->to_mutable().dependency()) { + [result appendFormat:@" _%d -> _%d[color=blue];\n", dependency.value(), indirect_node.offset()]; + } + } + } + } + + [result appendString:@"}\n"]; + + return (__bridge CFStringRef)result; +} + +CFStringRef Graph::description_stack(CFDictionaryRef options) { + + NSMutableString *description = [NSMutableString string]; + + NSNumber *max_frames_number = [(__bridge NSDictionary *)options objectForKeyedSubscript:AGDescriptionMaxFrames]; + int max_frames = max_frames_number ? [max_frames_number unsignedIntValue] : -1; + + int frame_count = 0; + for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { + auto frames = update.get()->frames(); + for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { + + const AttributeType &type = attribute_type(frame->attribute->type_id()); + [description appendFormat:@" #%d: %u %s -> %s\n", frame_count, frame->attribute.offset(), + type.self_metadata().name(false), type.value_metadata().name(false)]; + + if (frame_count == 0 && frame->attribute->inputs().size() > 0) { + [description appendString:@" -- inputs:\n"]; + for (auto input_edge : frame->attribute->inputs()) { + OffsetAttributeID resolved = input_edge.value.resolve(TraversalOptions::ReportIndirectionInOffset); + [description appendFormat:@" %u", resolved.attribute().value()]; + if (resolved.offset() != 0) { + [description appendFormat:@"[@%d]", resolved.offset() - 1]; + } + if (resolved.attribute().is_direct()) { + const AttributeType &input_type = attribute_type(resolved.attribute().to_node().type_id()); + [description appendFormat:@" %s -> %s", input_type.self_metadata().name(false), + input_type.value_metadata().name(false)]; + } + if (input_edge.is_changed()) { + [description appendString:@", changed"]; + } + if (input_edge.is_always_enabled()) { + [description appendString:@", always-enabled"]; + } + if (input_edge.is_unprefetched()) { + [description appendString:@", unprefetched"]; + } + [description appendString:@"\n"]; + } + [description appendString:@" --\n"]; + } + + frame_count += 1; + if (frame_count >= max_frames) { + return (__bridge CFStringRef)description; + } + } + } + + return (__bridge CFStringRef)description; +} + +CFArrayRef Graph::description_stack_nodes(CFDictionaryRef options) { + NSMutableArray *nodes = [NSMutableArray array]; + + NSNumber *max_frames_number = [(__bridge NSDictionary *)options objectForKeyedSubscript:AGDescriptionMaxFrames]; + int max_frames = max_frames_number ? [max_frames_number unsignedIntValue] : -1; + + int frame_count = 0; + for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { + auto frames = update.get()->frames(); + for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { + + [nodes addObject:[NSNumber numberWithUnsignedInt:frame->attribute]]; + + frame_count += 1; + if (frame_count >= max_frames) { + return (__bridge CFArrayRef)nodes; + } + } + } + + return (__bridge CFArrayRef)nodes; +} + +CFDictionaryRef Graph::description_stack_frame(CFDictionaryRef options) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + + NSNumber *frame_index_number = [(__bridge NSDictionary *)options objectForKeyedSubscript:@"frame_index"]; + int frame_index = frame_index_number ? [frame_index_number unsignedIntValue] : -1; + + NSNumber *frame_node_number = [(__bridge NSDictionary *)options objectForKeyedSubscript:@"frame_node"]; + data::ptr frame_node = frame_node_number ? [frame_node_number unsignedIntValue] : 0; + + for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { + int i = 0; + auto frames = update.get()->frames(); + for (auto frame : frames) { + + if (i == frame_index || frame.attribute == frame_node) { + + dictionary[@"index"] = [NSNumber numberWithUnsignedLong:i]; + dictionary[@"node-id"] = [NSNumber numberWithUnsignedInt:frame.attribute]; + + const AttributeType &type = attribute_type(frame.attribute->type_id()); + AGSetTypeForKey((__bridge CFMutableDictionaryRef)dictionary, (__bridge CFStringRef) @"self-type", + &type.self_metadata()); + AGSetTypeForKey((__bridge CFMutableDictionaryRef)dictionary, (__bridge CFStringRef) @"value-type", + &type.value_metadata()); + + if (!frame.attribute->inputs().empty()) { + NSMutableArray *inputs = [NSMutableArray array]; + + for (auto input_edge : frame.attribute->inputs()) { + NSMutableDictionary *input_dictionary = [NSMutableDictionary dictionary]; + + input_dictionary[@"id"] = [NSNumber numberWithUnsignedInt:input_edge.value]; + + OffsetAttributeID resolved = + input_edge.value.resolve(TraversalOptions::ReportIndirectionInOffset); + + input_dictionary[@"node"] = [NSNumber numberWithUnsignedInt:resolved.attribute()]; + if (resolved.offset() != 0) { + input_dictionary[@"offset"] = [NSNumber numberWithUnsignedLong:resolved.offset() - 1]; + } + + if (resolved.attribute().is_direct()) { + const AttributeType &input_type = attribute_type(resolved.attribute().to_node().type_id()); + AGSetTypeForKey((__bridge CFMutableDictionaryRef)input_dictionary, + (__bridge CFStringRef) @"self-type", &input_type.self_metadata()); + AGSetTypeForKey((__bridge CFMutableDictionaryRef)input_dictionary, + (__bridge CFStringRef) @"value-type", &input_type.value_metadata()); + } + if (input_edge.is_changed()) { + input_dictionary[@"changed"] = @YES; + } + if (input_edge.is_always_enabled()) { + input_dictionary[@"always-enabled"] = @YES; + } + if (input_edge.is_unprefetched()) { + input_dictionary[@"prefetched"] = @NO; + } + } + + dictionary[@"inputs"] = inputs; + } + + return (__bridge CFDictionaryRef)dictionary; + } + + i += 1; + } + } + + return (__bridge CFDictionaryRef)dictionary; +} + +void Graph::write_to_file(Graph *graph, const char *_Nullable filename, bool exclude_values) { + NSDictionary *options = @{ + AGDescriptionFormat : @"graph/dict", + AGDescriptionIncludeValues : @(!exclude_values), + @"all_graphs" : @(graph == nullptr) + }; + NSDictionary *json = description(graph, (__bridge CFDictionaryRef)options); + if (!json) { + return; + } + + if (filename == nullptr) { + filename = "graph.ag-gzon"; + } + + NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; + + NSString *path = [NSString stringWithUTF8String:filename]; + if (*filename != '/') { + path = [NSTemporaryDirectory() stringByAppendingPathComponent:path]; + } + + NSError *error = nil; + if ([[path pathExtension] isEqualToString:@"ag-gzon"]) { + // Disassembly writes compressed data directly using gzwrite instead of creating an intermediate NSData object + data = [data compressedDataUsingAlgorithm:NSDataCompressionAlgorithmZlib error:&error]; + if (!data) { + fprintf(stdout, "Unable to write to \"%s\": %s\n", [path UTF8String], [[error description] UTF8String]); + return; + } + } + + if (![data writeToFile:path options:0 error:&error]) { + fprintf(stdout, "Unable to write to \"%s\": %s\n", [path UTF8String], [[error description] UTF8String]); + return; + } + + fprintf(stdout, "Wrote graph data to \"%s\".\n", [path UTF8String]); +} + } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 3faabbd..64a54bc 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -26,6 +26,7 @@ #include "Trace/Trace.h" #include "TraceRecorder.h" #include "Tree/TreeElement.h" +#include "UniqueID/AGUniqueID.h" #include "UpdateStack.h" #include "Utilities/FreeDeleter.h" #include "Utilities/List.h" @@ -41,6 +42,8 @@ Graph::Graph() : _heap(nullptr, 0, 0), _type_ids_by_metadata(nullptr, nullptr, nullptr, nullptr, &_heap), _contexts_by_id(nullptr, nullptr, nullptr, nullptr, &_heap), _num_contexts(1) { + _unique_id = AGMakeUniqueID(); + data::table::ensure_shared(); // what is this check doing? @@ -49,7 +52,7 @@ Graph::Graph() print_attribute(nullptr); print_stack(); print_data(); - write_to_file(nullptr, 0); + write_to_file(this, nullptr, 0); } static dispatch_once_t make_keys; @@ -174,7 +177,7 @@ bool Graph::is_context_updating(uint64_t context_id) { for (auto frame = update_stack.get()->frames().rbegin(), end = update_stack.get()->frames().rend(); frame != end; ++frame) { auto subgraph = AttributeID(frame->attribute).subgraph(); - if (subgraph && subgraph->graph_context_id() == context_id) { + if (subgraph && subgraph->context_id() == context_id) { return true; } } @@ -514,9 +517,9 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio return UpdateStatus::NoChange; } - _update_attribute_count += 1; + _update_count += 1; if (node.state().is_main_thread()) { - _update_attribute_on_main_count += 1; + _update_on_main_count += 1; } UpdateStack update_stack = UpdateStack(this, options); @@ -539,7 +542,7 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio }); status = context.second; - _update_attribute_on_main_count += 1; + _update_on_main_count += 1; } } @@ -661,7 +664,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, continue; } if (options & SearchOptions::TraverseGraphContexts || - candidate.subgraph()->graph_context_id() == input.subgraph()->graph_context_id()) { + candidate.subgraph()->context_id() == input.subgraph()->context_id()) { seen.insert(input); queue.push_back(input); } @@ -673,7 +676,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, if (!seen.contains(source)) { if (options & SearchOptions::TraverseGraphContexts || - candidate.subgraph()->graph_context_id() == source.subgraph()->graph_context_id()) { + candidate.subgraph()->context_id() == source.subgraph()->context_id()) { seen.insert(source); queue.push_back(source); } @@ -687,7 +690,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, continue; } if (options & SearchOptions::TraverseGraphContexts || - candidate.subgraph()->graph_context_id() == output_edge.value.subgraph()->graph_context_id()) { + candidate.subgraph()->context_id() == output_edge.value.subgraph()->context_id()) { seen.insert(output_edge.value); queue.push_back(output_edge.value); } @@ -699,7 +702,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, continue; } if (options & SearchOptions::TraverseGraphContexts || - candidate.subgraph()->graph_context_id() == output_edge.value.subgraph()->graph_context_id()) { + candidate.subgraph()->context_id() == output_edge.value.subgraph()->context_id()) { seen.insert(output_edge.value); queue.push_back(output_edge.value); } @@ -742,9 +745,9 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui // check references of raw_page_seed in ghidra uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); - bool traverses_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); + bool traverses_contexts = subgraph.context_id() != attribute.subgraph()->context_id(); uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; - *indirect_node = MutableIndirectNode(source, traverses_graph_contexts, offset, node_size, source, offset); + *indirect_node = MutableIndirectNode(source, traverses_contexts, offset, node_size, source, offset); add_input_dependencies(AttributeID(indirect_node).with_kind(AttributeID::Kind::Indirect), attribute); subgraph.add_indirect((data::ptr)indirect_node, true); @@ -753,9 +756,9 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); - bool traverses_graph_contexts = subgraph.graph_context_id() != attribute.subgraph()->graph_context_id(); + bool traverses_contexts = subgraph.context_id() != attribute.subgraph()->context_id(); uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; - *indirect_node = IndirectNode(source, traverses_graph_contexts, offset, node_size); + *indirect_node = IndirectNode(source, traverses_contexts, offset, node_size); subgraph.add_indirect(indirect_node, &subgraph != attribute.subgraph()); } @@ -848,8 +851,8 @@ void Graph::indirect_attribute_set(data::ptr attribute, AttributeI // TODO: check zone id attribute->modify(WeakAttributeID(resolved_source.attribute(), 0), resolved_source.offset()); - attribute->set_traverses_graph_contexts(AttributeID(attribute).subgraph()->graph_context_id() != - resolved_source.attribute().subgraph()->graph_context_id()); + attribute->set_traverses_contexts(AttributeID(attribute).subgraph()->context_id() != + resolved_source.attribute().subgraph()->context_id()); if (resolved_source.attribute() != previous_source) { add_input_dependencies(AttributeID(attribute), resolved_source.attribute()); @@ -888,8 +891,8 @@ bool Graph::indirect_attribute_reset(data::ptr attribute, bool non } attribute->modify(new_source, new_offset); - attribute->set_traverses_graph_contexts(AttributeID(attribute).subgraph()->graph_context_id() != - new_source_or_nil.subgraph()->graph_context_id()); + attribute->set_traverses_contexts(AttributeID(attribute).subgraph()->context_id() != + new_source_or_nil.subgraph()->context_id()); if (old_source_or_nil != new_source_or_nil) { add_input_dependencies(AttributeID(attribute), new_source_or_nil); @@ -963,7 +966,7 @@ void *Graph::value_ref(AttributeID attribute, bool evaluate_weak_references, con if (!type.use_graph_as_initial_value()) { - increment_update_count_if_needed(); + increment_transaction_count_if_needed(); uint64_t old_page_seed = 0; if (evaluate_weak_references) { @@ -1079,11 +1082,7 @@ AGValueState Graph::value_state(AttributeID attribute) { return 0; } - auto node = attribute.to_node(); - return (node.state().is_dirty() ? 1 : 0) << 0 | (node.state().is_pending() ? 1 : 0) << 1 | - (node.state().is_updating() ? 1 : 0) << 2 | (node.state().is_value_initialized() ? 1 : 0) << 3 | - (node.state().is_main_thread() ? 1 : 0) << 4 | (node.flags().value4_unknown0x20() ? 1 : 0) << 5 | - (node.state().is_main_thread_only() ? 1 : 0) << 6 | (node.flags().value4_unknown0x40() ? 1 : 0) << 7; + return attribute.to_node().value_state(); } void Graph::value_mark(data::ptr node) { @@ -1149,7 +1148,7 @@ void Graph::value_mark_all() { subgraph->add_dirty_flags(node.flags().value3()); } for (auto input_edge : node.inputs()) { - input_edge.set_pending(true); + input_edge.set_changed(true); } } } @@ -1219,11 +1218,11 @@ void Graph::propagate_dirty(AttributeID attribute) { dirty_outputs = output_node.outputs(); - if (output_node.flags().inputs_traverse_graph_contexts() && !output_node.outputs().empty()) { + if (output_node.flags().inputs_traverse_contexts() && !output_node.outputs().empty()) { if (auto output_subgraph = output.subgraph()) { - auto context_id = output_subgraph->graph_context_id(); + auto context_id = output_subgraph->context_id(); if (context_id && (attribute.subgraph() == nullptr || - context_id != attribute.subgraph()->graph_context_id())) { + context_id != attribute.subgraph()->context_id())) { if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { if (context->graph_version() != context->graph()._version) { context->call_invalidation(attribute); @@ -1240,11 +1239,11 @@ void Graph::propagate_dirty(AttributeID attribute) { if (output_node.is_mutable()) { dirty_outputs = output_node.to_mutable().outputs(); - if (output_node.traverses_graph_contexts() && !output_node.to_mutable().outputs().empty()) { + if (output_node.traverses_contexts() && !output_node.to_mutable().outputs().empty()) { if (auto output_subgraph = output.subgraph()) { - auto context_id = output_subgraph->graph_context_id(); + auto context_id = output_subgraph->context_id(); if (context_id && (attribute.subgraph() == nullptr || - context_id != attribute.subgraph()->graph_context_id())) { + context_id != attribute.subgraph()->context_id())) { if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { if (context->graph_version() != context->graph()._version) { context->call_invalidation(attribute); @@ -1285,7 +1284,7 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at if ((input_flags >> 1) & 1) { auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, - InputEdge::Flags::Unknown0 | InputEdge::Flags::Unknown2); + InputEdge::Flags::Unprefetched | InputEdge::Flags::AlwaysEnabled); index = index_of_input(*node, comparator); } @@ -1315,7 +1314,7 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at InputEdge &input_edge = node->inputs()[index]; - input_edge.set_unknown0(input_edge.is_unknown0() || (input_flags & 1) ? true : false); + input_edge.set_unprefetched(input_edge.is_unprefetched() || (input_flags & 1) ? true : false); input_edge.set_unknown4(true); OffsetAttributeID resolved = input_edge.value.resolve( @@ -1381,8 +1380,9 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at void Graph::input_value_add(data::ptr node, AttributeID input_attribute, uint8_t input_flags) { input_attribute.to_node_ptr().assert_valid(); + // TODO: check flag and mask are right way around auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, - InputEdge::Flags::Unknown0 | InputEdge::Flags::Unknown2); + InputEdge::Flags::Unprefetched | InputEdge::Flags::AlwaysEnabled); auto index = index_of_input(*node, comparator); if (index >= 0) { auto input_edge = node->inputs()[index]; @@ -1407,13 +1407,13 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni }); auto subgraph = AttributeID(node).subgraph(); - auto graph_context_id = subgraph ? subgraph->graph_context_id() : 0; + auto graph_context_id = subgraph ? subgraph->context_id() : 0; auto input_subgraph = resolved.attribute().subgraph(); - auto input_graph_context_id = input_subgraph ? input_subgraph->graph_context_id() : 0; + auto input_graph_context_id = input_subgraph ? input_subgraph->context_id() : 0; if (graph_context_id != input_graph_context_id) { - node->flags().set_inputs_traverse_graph_contexts(true); + node->flags().set_inputs_traverse_contexts(true); } InputEdge new_input_edge = { @@ -1421,7 +1421,7 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni InputEdge::Flags(input_edge_flags & 5), }; if (node->state().is_dirty()) { - new_input_edge.set_pending(true); + new_input_edge.set_changed(true); } uint32_t index = -1; @@ -1506,7 +1506,7 @@ void Graph::remove_removed_input(AttributeID attribute, AttributeID input) { bool Graph::any_inputs_changed(data::ptr node, const AttributeID *attributes, uint64_t count) { for (auto input : node->inputs()) { input.set_unknown4(true); - if (input.is_pending()) { + if (input.is_changed()) { const AttributeID *location = (const AttributeID *)wmemchr((const wchar_t *)attributes, input.value, count); if (location == 0) { location = attributes + sizeof(AttributeID) * count; @@ -1520,7 +1520,7 @@ bool Graph::any_inputs_changed(data::ptr node, const AttributeID *attribut } void Graph::all_inputs_removed(data::ptr node) { - node->flags().set_inputs_traverse_graph_contexts(false); + node->flags().set_inputs_traverse_contexts(false); node->flags().set_inputs_unsorted(false); if (!node->state().is_main_thread_only() && !attribute_type(node->type_id()).main_thread()) { node->set_state(node->state().with_main_thread_only(false)); @@ -1726,7 +1726,7 @@ void Graph::mark_changed(data::ptr node, AttributeType *_Nullable type, co if (input.value.resolve(TraversalOptions::None).attribute() != node) { continue; } - if (input.is_pending()) { + if (input.is_changed()) { continue; } if (!input.value.is_direct()) { @@ -1734,12 +1734,12 @@ void Graph::mark_changed(data::ptr node, AttributeType *_Nullable type, co continue; } } - input.set_pending(true); + input.set_changed(true); } output_index += 1; } - _mark_changed_count += 1; + _change_count += 1; } void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, const void *destination_value, @@ -1784,7 +1784,7 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c if (input_edge.value.resolve(TraversalOptions::SkipMutableReference).attribute() != attribute) { continue; } - if (input_edge.is_pending()) { + if (input_edge.is_changed()) { continue; } if (!input_edge.value.is_direct()) { @@ -1795,7 +1795,7 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c foreach_trace([&output_edge, &input_index](Trace &trace) { trace.set_edge_pending(output_edge->value.to_node_ptr(), input_index, true); }); - input_edge.set_pending(true); + input_edge.set_changed(true); } } else if (output_edge->value.is_indirect()) { if (output_edge->value.to_indirect_node().to_mutable().dependency() != attribute) { @@ -1810,7 +1810,7 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c start_output_index = 0; } - _mark_changed_count += 1; + _change_count += 1; } void Graph::mark_pending(data::ptr node_ptr, Node *node) { @@ -1904,15 +1904,15 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { } if (flag) { auto type = attribute_type(node.type_id()); - if (auto callback = type.vt_get_encode_node_callback()) { + if (auto callback = type.vt_get_value_description_callback()) { void *value = node.get_value(); - CFStringRef str = callback(); - if (str) { - uint64_t length = CFStringGetLength(str); + CFStringRef description = callback(&type, value); + if (description) { + uint64_t length = CFStringGetLength(description); CFRange range = CFRangeMake(0, length); uint8_t buffer[1024]; CFIndex used_buffer_length = 0; - CFStringGetBytes(str, range, kCFStringEncodingUTF8, 0x3f, true, buffer, 1024, &used_buffer_length); + CFStringGetBytes(description, range, kCFStringEncodingUTF8, 0x3f, true, buffer, 1024, &used_buffer_length); if (used_buffer_length > 0) { encoder.encode_varint(0x12); encoder.encode_data(buffer, used_buffer_length); @@ -1927,17 +1927,17 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { encoder.encode_varint(8); encoder.encode_varint(input_edge.value); } - if (input_edge.is_unknown0()) { + if (input_edge.is_unprefetched()) { encoder.encode_varint(0x10); - encoder.encode_varint(input_edge.is_unknown0()); + encoder.encode_varint(input_edge.is_unprefetched()); } - if (input_edge.is_unknown2()) { + if (input_edge.is_always_enabled()) { encoder.encode_varint(0x18); - encoder.encode_varint(input_edge.is_unknown2()); + encoder.encode_varint(input_edge.is_always_enabled()); } - if (input_edge.is_pending()) { + if (input_edge.is_changed()) { encoder.encode_varint(0x20); - encoder.encode_varint(input_edge.is_pending()); + encoder.encode_varint(input_edge.is_changed()); } if (input_edge.is_unknown4()) { encoder.encode_varint(0x30); @@ -2069,6 +2069,7 @@ void Graph::encode_tree(Encoder &encoder, data::ptr tree) { } Subgraph *subgraph = reinterpret_cast(tree.page_ptr()->zone); + // TODO: can combine with tree_node_at_index? if (auto map = tree_data_elements()) { auto tree_data_element = map->find(subgraph); if (tree_data_element != map->end()) { @@ -2174,8 +2175,8 @@ void Graph::prepare_trace(Trace &trace) { auto node = attribute.to_node_ptr(); uint32_t edge_index = 0; for (auto input_edge : node->inputs()) { - trace.add_edge(node, input_edge.value, input_edge.is_unknown2()); - if (input_edge.is_pending()) { + trace.add_edge(node, input_edge.value, input_edge.is_always_enabled()); // TODO: is last param int or bool? + if (input_edge.is_changed()) { trace.set_edge_pending(node, edge_index, true); } edge_index += 1; diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 54d20ea..9a618a6 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -145,13 +146,15 @@ class Graph { bool _needs_update; uint32_t _num_contexts; pthread_t _current_update_thread = 0; + + uint64_t _unique_id; uint64_t _deadline = UINT64_MAX; // Counters - uint64_t _update_count = 0; // number of times this graph started updating - uint64_t _update_attribute_count = 0; - uint64_t _update_attribute_on_main_count = 0; - uint64_t _mark_changed_count = 0; + uint64_t _transaction_count = 0; + uint64_t _update_count = 0; + uint64_t _update_on_main_count = 0; + uint64_t _change_count = 0; uint64_t _version = 0; public: @@ -213,12 +216,14 @@ class Graph { bool passed_deadline(); bool passed_deadline_slow(); - void increment_update_count_if_needed() { + void increment_transaction_count_if_needed() { if (!thread_is_updating()) { - _update_count += 1; + _transaction_count += 1; } }; + uint64_t unique_id() const { return _unique_id; }; + // MARK: Attributes const AttributeType &attribute_type(uint32_t type_id) const; @@ -346,6 +351,13 @@ class Graph { void encode_tree(Encoder &encoder, data::ptr tree); + // MARK: Counters + + // Counters + uint64_t transaction_count() const { return _transaction_count; }; + uint64_t update_count() const { return _update_count; }; + uint64_t change_count() const { return _change_count; }; + // MARK: Tracing enum TraceFlags : uint32_t { @@ -413,18 +425,16 @@ class Graph { // MARK: Description - CFStringRef description_graph_dot(CFDictionaryRef _Nullable dict); CFStringRef description(data::ptr node); - CFStringRef description(CFDictionaryRef options); - - char *description_stack(CFDictionaryRef options); - char *description_stack_frame(CFDictionaryRef options); - char *description_stack_nodes(CFDictionaryRef options); - - vector description_graph(CFDictionaryRef options); + static id description(Graph *_Nullable graph, CFDictionaryRef options); + static CFDictionaryRef description_graph(Graph *graph, CFDictionaryRef options); + CFStringRef description_graph_dot(CFDictionaryRef _Nullable options); + CFStringRef description_stack(CFDictionaryRef options); + CFArrayRef description_stack_nodes(CFDictionaryRef options); + CFDictionaryRef description_stack_frame(CFDictionaryRef options); - void write_to_file(const char *_Nullable filename, uint32_t arg); + static void write_to_file(Graph *_Nullable graph, const char *_Nullable filename, bool exclude_values); }; } // namespace AG diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData.h b/Sources/ComputeCxx/Graph/Profile/ProfileData.h index e3ec1c8..202b796 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData.h +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData.h @@ -47,6 +47,7 @@ class Graph::ProfileData { public: std::unordered_map, Item> &items_by_attribute() { return _items_by_attribute; }; + std::unordered_map &removed_items_by_type_id() { return _removed_items_by_type_id; }; void add_update(data::ptr node, uint64_t time, bool flag); void remove_node(data::ptr node, uint32_t type_id) { @@ -63,7 +64,7 @@ class Graph::ProfileData { private: uint64_t _precision; - Category _current_category; + Category _current_category; // _all std::unordered_map _categories; bool _has_unmarked_categories; diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 487ecc1..7097436 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -825,9 +825,9 @@ void Graph::TraceRecorder::created(const Subgraph &subgraph) { _encoder.encode_varint(0x18); _encoder.encode_varint(zone_id); } - if (subgraph.graph_context_id()) { + if (subgraph.context_id()) { _encoder.encode_varint(0x28); - _encoder.encode_varint(subgraph.graph_context_id()); + _encoder.encode_varint(subgraph.context_id()); } field_backtrace(_encoder, 8); diff --git a/Sources/ComputeCxx/Graph/Tree/TreeElement.h b/Sources/ComputeCxx/Graph/Tree/TreeElement.h index cac13bf..aab9276 100644 --- a/Sources/ComputeCxx/Graph/Tree/TreeElement.h +++ b/Sources/ComputeCxx/Graph/Tree/TreeElement.h @@ -14,16 +14,27 @@ namespace AG { struct Graph::TreeElement { const swift::metadata *type; - AttributeID owner; + AttributeID owner; // TODO: rename node uint32_t flags; data::ptr parent; - data::ptr next; - data::ptr old_parent; + data::ptr next; // children_front + data::ptr old_parent; // next_child data::ptr last_value; }; static_assert(sizeof(Graph::TreeElement) == 0x20); +class TreeElementID { + private: + uint32_t _value; + + public: + TreeElementID(uint32_t value) : _value(value){}; + + Subgraph *_Nullable subgraph() const { return reinterpret_cast(page_ptr()->zone); } + data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; +}; + struct Graph::TreeValue { const swift::metadata *_Nonnull type; AttributeID value; diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 2aa3c13..18639c7 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -195,7 +195,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { } if (!frame.needs_update && frame.num_pushed_inputs > 0 && - node->inputs()[frame.num_pushed_inputs - 1].is_pending()) { + node->inputs()[frame.num_pushed_inputs - 1].is_changed()) { frame.needs_update = true; } @@ -228,13 +228,13 @@ Graph::UpdateStatus Graph::UpdateStack::update() { if (input_attribute.is_direct()) { Node &input_node = input_attribute.to_node(); - if (input.is_pending()) { + if (input.is_changed()) { frame.needs_update = true; } if (input_node.state().is_dirty() || !input_node.state().is_value_initialized()) { - if (!input.is_pending() && + if (!input.is_changed() && input_attribute.subgraph()->validation_state() == Subgraph::ValidationState::Valid) { frame.num_pushed_inputs = input_index + 1; @@ -259,7 +259,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { } _graph->foreach_trace([&frame](Trace &trace) { trace.begin_update(frame.attribute); }); - uint64_t mark_changed_count = _graph->_mark_changed_count; + uint64_t old_change_count = _graph->_change_count; const AttributeType &type = _graph->attribute_type(node->type_id()); void *self = node->get_self(type); @@ -277,7 +277,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { AGGraphSetOutputValue(&value, AGTypeID(&type.value_metadata())); } - changed = _graph->_mark_changed_count != mark_changed_count; + changed = _graph->_change_count != old_change_count; _graph->foreach_trace([&frame, &changed](Trace &trace) { trace.end_update(frame.attribute, changed); }); } @@ -299,11 +299,11 @@ Graph::UpdateStatus Graph::UpdateStack::update() { if (reset_input_flags) { if (frame.needs_update && !frame.cancelled) { - if (input.is_pending()) { + if (input.is_changed()) { _graph->foreach_trace([&frame, &input](Trace &trace) { trace.set_edge_pending(frame.attribute, input.value, false); }); - input.set_pending(false); + input.set_changed(false); } } } @@ -311,7 +311,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { if (frame.needs_update && !frame.cancelled) { bool was_unknown4 = input.is_unknown4(); input.set_unknown4(false); - if (!was_unknown4 && !input.is_unknown2()) { + if (!was_unknown4 && !input.is_always_enabled()) { _graph->remove_input(frame.attribute, input_index); } } diff --git a/Sources/ComputeCxx/Layout/Compare.cpp b/Sources/ComputeCxx/Layout/Compare.cpp index 412a074..c8ff7df 100644 --- a/Sources/ComputeCxx/Layout/Compare.cpp +++ b/Sources/ComputeCxx/Layout/Compare.cpp @@ -1,8 +1,8 @@ #include "Compare.h" #include "Controls.h" -#include "Swift/EquatableSupport.h" #include "Swift/Metadata.h" +#include "Swift/SwiftShims.h" namespace AG { namespace LayoutDescriptor { diff --git a/Sources/ComputeCxx/Subgraph/NodeCache.cpp b/Sources/ComputeCxx/Subgraph/NodeCache.cpp index 7fca02c..58aa2de 100644 --- a/Sources/ComputeCxx/Subgraph/NodeCache.cpp +++ b/Sources/ComputeCxx/Subgraph/NodeCache.cpp @@ -1,7 +1,7 @@ #include "NodeCache.h" #include "Attribute/AttributeType.h" -#include "Swift/EquatableSupport.h" +#include "Swift/SwiftShims.h" namespace AG { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 8f39443..372c8f4 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -35,7 +35,7 @@ Subgraph::Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID Graph *graph = &context.graph(); _graph = graph; - _graph_context_id = context.unique_id(); + _context_id = context.unique_id(); // what is this check doing? if ((uintptr_t)this == 1) { @@ -152,7 +152,7 @@ void Subgraph::invalidate_now(Graph &graph) { for (auto child : subgraph->_children) { Subgraph *child_subgraph = child.subgraph(); - if (child_subgraph->_graph_context_id == _graph_context_id) { + if (child_subgraph->context_id() == _context_id) { // for each other parent of the child, remove the child from that parent for (auto parent : child_subgraph->_parents) { @@ -602,7 +602,7 @@ void Subgraph::update(uint8_t flags) { for (auto node : nodes_to_update) { if (!thread_is_updating) { - _graph->increment_update_count_if_needed(); + _graph->increment_transaction_count_if_needed(); _graph->update_attribute(node, true); if (!subgraph->is_valid()) { @@ -658,7 +658,7 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { continue; } - if (flags.value4 & 1 || subgraph->_graph_context_id == _graph_context_id) { + if (flags.value4 & 1 || subgraph->context_id() == _context_id) { if (flags.is_null() || (flags.value1 & subgraph->_flags.value1)) { for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { uint16_t relative_offset = page->relative_offset_1; @@ -734,7 +734,7 @@ void Subgraph::end_tree(AttributeID attribute) { } void Subgraph::set_tree_owner(AttributeID owner) { - if (_tree_root) { + if (_tree_root) { // TODO: bug? return; } if (_tree_root->parent) { @@ -1105,9 +1105,9 @@ void Subgraph::encode(Encoder &encoder) const { encoder.encode_varint(zone_id); } - if (_graph_context_id != 0) { + if (_context_id != 0) { encoder.encode_varint(0x10); - encoder.encode_varint(_graph_context_id); + encoder.encode_varint(_context_id); } for (auto parent : _parents) { @@ -1194,7 +1194,7 @@ void Subgraph::print(uint32_t indent_level) { memset(indent_string, ' ', indent_length); indent_string[indent_length] = '\0'; - fprintf(stdout, "%s+ %p: %u in %lu [", indent_string, this, info().zone_id(), (unsigned long)_graph_context_id); + fprintf(stdout, "%s+ %p: %u in %lu [", indent_string, this, info().zone_id(), (unsigned long)_context_id); bool first = true; for (data::ptr page = last_page(); page != nullptr; page = page->previous) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index fa7b78a..4ce03a6 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -72,7 +72,7 @@ class Subgraph : public data::zone { SubgraphObject *_object; Graph *_Nullable _graph; - uint64_t _graph_context_id; + uint64_t _context_id; indirect_pointer_vector _parents; vector _children; @@ -98,6 +98,8 @@ class Subgraph : public data::zone { Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID attribute); ~Subgraph(); + uint32_t subgraph_id() const { return info().zone_id(); }; + // MARK: CoreFoundation static Subgraph *from_cf(SubgraphObject *object); @@ -107,7 +109,7 @@ class Subgraph : public data::zone { // MARK: Graph Graph *_Nullable graph() const { return _graph; }; - uint64_t graph_context_id() const { return _graph_context_id; }; + uint64_t context_id() const { return _context_id; }; bool is_valid() const { return _validation_state == ValidationState::Valid; }; ValidationState validation_state() { return _validation_state; }; @@ -131,6 +133,8 @@ class Subgraph : public data::zone { requires std::invocable && std::same_as, bool> void foreach_ancestor(Callable body); + indirect_pointer_vector parents() { return _parents; }; + // MARK: Attributes void add_node(data::ptr node); @@ -150,6 +154,8 @@ class Subgraph : public data::zone { // MARK: Tree + data::ptr tree_root() { return _tree_root; }; + void begin_tree(AttributeID attribute, const swift::metadata *_Nullable type, uint32_t flags); // TODO: check can be null from Subgraph() void end_tree(AttributeID attribute); diff --git a/Sources/ComputeCxx/Swift/EquatableSupport.h b/Sources/ComputeCxx/Swift/SwiftShims.h similarity index 67% rename from Sources/ComputeCxx/Swift/EquatableSupport.h rename to Sources/ComputeCxx/Swift/SwiftShims.h index 64e7c7f..ee3d494 100644 --- a/Sources/ComputeCxx/Swift/EquatableSupport.h +++ b/Sources/ComputeCxx/Swift/SwiftShims.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include "AGSwiftSupport.h" @@ -13,6 +15,9 @@ AG_SWIFT_CC(swift) bool AGDispatchEquatable(const void *lhs_value, const void *rhs_value, const ::swift::Metadata *type, const ::swift::equatable_support::EquatableWitnessTable *wt); +AG_SWIFT_CC(swift) +bool AGSetTypeForKey(CFMutableDictionaryRef dict, CFStringRef key, const ::swift::Metadata *type); + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Version/AGVersion.cpp b/Sources/ComputeCxx/Version/AGVersion.cpp new file mode 100644 index 0000000..e1f6dc5 --- /dev/null +++ b/Sources/ComputeCxx/Version/AGVersion.cpp @@ -0,0 +1,3 @@ +#include "AGVersion.h" + +const uint32_t AGVersion = 0x2001e; diff --git a/Sources/ComputeCxx/Version/AGVersion.h b/Sources/ComputeCxx/Version/AGVersion.h new file mode 100644 index 0000000..c01c911 --- /dev/null +++ b/Sources/ComputeCxx/Version/AGVersion.h @@ -0,0 +1,9 @@ +#include +#include + +CF_EXTERN_C_BEGIN + +CF_EXPORT +const uint32_t AGVersion; + +CF_EXTERN_C_END diff --git a/Sources/EquatableSupport/EquatableSupport.swift b/Sources/ComputeCxxSwiftSupport/ComputeCxxSwiftSupport.swift similarity index 55% rename from Sources/EquatableSupport/EquatableSupport.swift rename to Sources/ComputeCxxSwiftSupport/ComputeCxxSwiftSupport.swift index adf0a22..8ec810f 100644 --- a/Sources/EquatableSupport/EquatableSupport.swift +++ b/Sources/ComputeCxxSwiftSupport/ComputeCxxSwiftSupport.swift @@ -1,4 +1,3 @@ -// Called from ComputeCxx @_silgen_name("AGDispatchEquatable") public func Equatable_isEqual_indirect( _ lhs: UnsafePointer, @@ -6,3 +5,12 @@ public func Equatable_isEqual_indirect( ) -> Bool { return lhs.pointee == rhs.pointee } + +@_silgen_name("AGSetTypeForKey") +public func setTypeForKey( + _ dict: inout [String: Any], + _ key: String, + _ type: Any.Type +) { + dict[key] = type +} diff --git a/Sources/Utilities/include/Utilities/FreeDeleter.h b/Sources/Utilities/include/Utilities/FreeDeleter.h index 9601b64..ab82bc3 100644 --- a/Sources/Utilities/include/Utilities/FreeDeleter.h +++ b/Sources/Utilities/include/Utilities/FreeDeleter.h @@ -1,6 +1,7 @@ #pragma once #include +#include CF_ASSUME_NONNULL_BEGIN From d6200c3b974d5b10f685367da9d015eb0b5650a3 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 25 Feb 2025 19:11:54 +1100 Subject: [PATCH 34/74] Move tagged pointer to Utilities target --- Sources/ComputeCxx/Graph/Graph+Description.mm | 4 +-- Sources/ComputeCxx/Graph/Graph.cpp | 8 +++--- Sources/ComputeCxx/Graph/Graph.h | 26 +++---------------- Sources/ComputeCxx/Graph/UpdateStack.cpp | 8 +++--- Sources/ComputeCxx/Graph/UpdateStack.h | 4 +-- 5 files changed, 15 insertions(+), 35 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 38a879e..0823387 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -64,12 +64,12 @@ auto update = current_update(); if (update != 0) { uint32_t update_stack_count = 0; - for (TaggedPointer update_stack = update; update_stack != nullptr; + for (util::tagged_ptr update_stack = update; update_stack != nullptr; update_stack = update_stack.get()->previous()) { update_stack_count += 1; } uint32_t update_stack_index = update_stack_count - 1; - for (TaggedPointer update_stack = update; update_stack != nullptr; + for (util::tagged_ptr update_stack = update; update_stack != nullptr; update_stack = update_stack.get()->previous()) { for (auto frame_index = update_stack.get()->frames().size() - 1; frame_index >= 0; --frame_index) { diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 64a54bc..a118c02 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -279,11 +279,11 @@ void Graph::invalidate_subgraphs() { #pragma mark - Updates -TaggedPointer Graph::current_update() { - return TaggedPointer((UpdateStack *)pthread_getspecific(_current_update_key)); +util::tagged_ptr Graph::current_update() { + return util::tagged_ptr((UpdateStack *)pthread_getspecific(_current_update_key)); } -void Graph::set_current_update(TaggedPointer current_update) { +void Graph::set_current_update(util::tagged_ptr current_update) { pthread_setspecific(_current_update_key, (void *)current_update.value()); } @@ -536,7 +536,7 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio std::pair context = {&update_stack, UpdateStatus::NeedsCallMainHandler}; call_main_handler(&context, [](void *void_context) { auto inner_context = reinterpret_cast *>(void_context); - TaggedPointer previous = Graph::current_update(); + util::tagged_ptr previous = Graph::current_update(); inner_context->second = inner_context->first->update(); Graph::set_current_update(previous); }); diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 9a618a6..34fd279 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -12,6 +12,7 @@ #include "Closure/ClosureFunction.h" #include "Utilities/HashTable.h" #include "Utilities/Heap.h" +#include "Utilities/TaggedPointer.h" CF_ASSUME_NONNULL_BEGIN @@ -19,27 +20,6 @@ typedef uint8_t AGValueState; // TODO: move namespace AG { -// TODO: move somewhere else -template class TaggedPointer { - private: - uintptr_t _value; - - public: - TaggedPointer() : _value(0){}; - TaggedPointer(T *_Nullable value) : _value((uintptr_t)value){}; - TaggedPointer(T *_Nullable value, bool tag) : _value(((uintptr_t)value & ~0x1) | (tag ? 1 : 0)){}; - - uintptr_t value() { return _value; }; - bool tag() { return static_cast(_value & 0x1); }; - TaggedPointer with_tag(bool tag) { return TaggedPointer(get(), tag); }; - - T *_Nullable get() { return reinterpret_cast(_value & ~0x1); }; - const T *_Nullable get() const { return reinterpret_cast(_value & ~0x1); }; - - bool operator==(nullptr_t) const noexcept { return _value == 0; }; - bool operator!=(nullptr_t) const noexcept { return _value != 0; }; -}; - namespace swift { class metadata; } @@ -194,8 +174,8 @@ class Graph { // MARK: Updates - static TaggedPointer current_update(); - static void set_current_update(TaggedPointer current_update); + static util::tagged_ptr current_update(); + static void set_current_update(util::tagged_ptr current_update); bool thread_is_updating(); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 18639c7..458d1da 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -28,7 +28,7 @@ Graph::UpdateStack::UpdateStack(Graph *graph, uint8_t options) { _options &= Option::InvalidateSubgraphs; } - Graph::set_current_update(TaggedPointer(this, options & Option::SetTag ? 1 : 0)); + Graph::set_current_update(util::tagged_ptr(this, options & Option::SetTag ? 1 : 0)); } Graph::UpdateStack::~UpdateStack() { @@ -49,7 +49,7 @@ Graph::UpdateStack::~UpdateStack() { } Graph::UpdateStack::Frame *Graph::UpdateStack::global_top() { - for (TaggedPointer update_stack = this; update_stack != nullptr; + for (util::tagged_ptr update_stack = this; update_stack != nullptr; update_stack = update_stack.get()->previous()) { if (!update_stack.get()->frames().empty()) { return &update_stack.get()->frames().back(); @@ -59,7 +59,7 @@ Graph::UpdateStack::Frame *Graph::UpdateStack::global_top() { } void Graph::UpdateStack::cancel() { - for (TaggedPointer update_stack = current_update(); update_stack != nullptr; + for (util::tagged_ptr update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { auto frames = update_stack.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { @@ -75,7 +75,7 @@ void Graph::UpdateStack::cancel() { } bool Graph::UpdateStack::cancelled() { - for (TaggedPointer update_stack = this; update_stack != nullptr; + for (util::tagged_ptr update_stack = this; update_stack != nullptr; update_stack = update_stack.get()->previous()) { if (!update_stack.get()->frames().empty()) { auto last_frame = update_stack.get()->frames().back(); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index 6d0ddde..0151d94 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -27,7 +27,7 @@ class Graph::UpdateStack { Graph *_graph; pthread_t _thread; - TaggedPointer _previous; + util::tagged_ptr _previous; pthread_t _previous_thread; vector _frames; uint8_t _options; @@ -39,7 +39,7 @@ class Graph::UpdateStack { ~UpdateStack(); Graph *graph() const { return _graph; }; - const TaggedPointer previous() const { return _previous; }; + const util::tagged_ptr previous() const { return _previous; }; vector &frames() { return _frames; }; From 87b5d8635a36806c17cac0e63a957b7952f8fe52 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 25 Feb 2025 19:30:28 +1100 Subject: [PATCH 35/74] Fix linker errors --- Sources/ComputeCxx/Containers/Vector.tpp | 18 ++++ Sources/ComputeCxx/Debug/DebugServer.cpp | 7 ++ Sources/ComputeCxx/Encoder/Encoder.h | 2 +- Sources/ComputeCxx/Graph/AGGraph.cpp | 4 + .../ComputeCxx/Graph/Profile/ProfileTrace.h | 2 - Sources/ComputeCxx/Trace/Trace.h | 90 +++++++++---------- 6 files changed, 75 insertions(+), 48 deletions(-) create mode 100644 Sources/ComputeCxx/Debug/DebugServer.cpp diff --git a/Sources/ComputeCxx/Containers/Vector.tpp b/Sources/ComputeCxx/Containers/Vector.tpp index 7001547..86c8421 100644 --- a/Sources/ComputeCxx/Containers/Vector.tpp +++ b/Sources/ComputeCxx/Containers/Vector.tpp @@ -394,6 +394,24 @@ vector, 0, size_type>::~vector() { } } +template + requires std::unsigned_integral +void vector, 0, size_type>::reserve_slow(size_type new_cap) { + size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); + _buffer = reinterpret_cast *>( + details::realloc_vector)>(_buffer, &_capacity, + effective_new_cap)); +} + +template + requires std::unsigned_integral +void vector, 0, size_type>::reserve(size_type new_cap) { + if (new_cap <= capacity()) { + return; + } + reserve_slow(new_cap); +} + template requires std::unsigned_integral void vector, 0, size_type>::push_back(std::unique_ptr &&value) { diff --git a/Sources/ComputeCxx/Debug/DebugServer.cpp b/Sources/ComputeCxx/Debug/DebugServer.cpp new file mode 100644 index 0000000..e999773 --- /dev/null +++ b/Sources/ComputeCxx/Debug/DebugServer.cpp @@ -0,0 +1,7 @@ +#include "DebugServer.h" + +namespace AG { + +void DebugServer::start(uint32_t port) {} + +} // namespace AG diff --git a/Sources/ComputeCxx/Encoder/Encoder.h b/Sources/ComputeCxx/Encoder/Encoder.h index cb2ae71..66a1dfe 100644 --- a/Sources/ComputeCxx/Encoder/Encoder.h +++ b/Sources/ComputeCxx/Encoder/Encoder.h @@ -11,7 +11,7 @@ namespace AG { class Encoder { public: struct Delegate { - virtual int flush_encoder(Encoder &encoder); + virtual int flush_encoder(Encoder &encoder){}; }; private: diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index c8cf95a..5fc30d2 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -7,3 +7,7 @@ void AGGraphCreate() { void AGGraphSetOutputValue(void *value, AGTypeID type) { // TODO: not implemented } + +void AGGraphArchiveJSON(const char *filename) { + // TODO: not implemented +} diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h index bf0e721..278ac2b 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h +++ b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.h @@ -22,8 +22,6 @@ class Graph::ProfileTrace : public Trace { std::unordered_map _map; public: - ~ProfileTrace(); - void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options); void end_update(const Graph::UpdateStack &update_stack, data::ptr node, Graph::UpdateStatus update_status); void begin_update(data::ptr node); diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index dd813da..aa363dd 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -16,78 +16,78 @@ class Trace { public: uint64_t trace_id() { return _trace_id; } - virtual ~Trace(); + virtual ~Trace(){}; // Trace - virtual void begin_trace(const Graph &graph); - virtual void end_trace(const Graph &graph); - virtual void sync_trace(); + virtual void begin_trace(const Graph &graph){}; + virtual void end_trace(const Graph &graph){}; + virtual void sync_trace(){}; // Log - virtual void log_message_v(const char *format, va_list args); + virtual void log_message_v(const char *format, va_list args){}; void log_message(const char *format, ...); // Updates - virtual void begin_update(const Subgraph &subgraph, uint32_t options); - virtual void end_update(const Subgraph &subgraph); - virtual void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options); + virtual void begin_update(const Subgraph &subgraph, uint32_t options){}; + virtual void end_update(const Subgraph &subgraph){}; + virtual void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options){}; virtual void end_update(const Graph::UpdateStack &update_stack, data::ptr node, - Graph::UpdateStatus update_status); - virtual void begin_update(data::ptr node); - virtual void end_update(data::ptr node, bool changed); - virtual void begin_update(const Graph::Context &context); - virtual void end_update(const Graph::Context &context); + Graph::UpdateStatus update_status){}; + virtual void begin_update(data::ptr node){}; + virtual void end_update(data::ptr node, bool changed){}; + virtual void begin_update(const Graph::Context &context){}; + virtual void end_update(const Graph::Context &context){}; - virtual void begin_invalidation(const Graph::Context &context, AttributeID attribute); - virtual void end_invalidation(const Graph::Context &context, AttributeID attribute); + virtual void begin_invalidation(const Graph::Context &context, AttributeID attribute){}; + virtual void end_invalidation(const Graph::Context &context, AttributeID attribute){}; - virtual void begin_modify(data::ptr node); - virtual void end_modify(data::ptr node); + virtual void begin_modify(data::ptr node){}; + virtual void end_modify(data::ptr node){}; - virtual void begin_event(data::ptr node, uint32_t event); - virtual void end_event(data::ptr node, uint32_t event); + virtual void begin_event(data::ptr node, uint32_t event){}; + virtual void end_event(data::ptr node, uint32_t event){}; - virtual void created(const Graph::Context &context); - virtual void destroy(const Graph::Context &context); - virtual void needs_update(const Graph::Context &context); + virtual void created(const Graph::Context &context){}; + virtual void destroy(const Graph::Context &context){}; + virtual void needs_update(const Graph::Context &context){}; - virtual void created(const Subgraph &subgraph); - virtual void invalidate(const Subgraph &subgraph); - virtual void destroy(const Subgraph &subgraph); + virtual void created(const Subgraph &subgraph){}; + virtual void invalidate(const Subgraph &subgraph){}; + virtual void destroy(const Subgraph &subgraph){}; - virtual void add_child(const Subgraph &subgraph, const Subgraph &child); - virtual void remove_child(const Subgraph &subgraph, const Subgraph &child); + virtual void add_child(const Subgraph &subgraph, const Subgraph &child){}; + virtual void remove_child(const Subgraph &subgraph, const Subgraph &child){}; - virtual void added(data::ptr node); + virtual void added(data::ptr node){}; - virtual void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags); - virtual void remove_edge(data::ptr node, uint32_t input_index); - virtual void set_edge_pending(data::ptr node, uint32_t input_index, bool pending); + virtual void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags){}; + virtual void remove_edge(data::ptr node, uint32_t input_index){}; + virtual void set_edge_pending(data::ptr node, uint32_t input_index, bool pending){}; - virtual void set_dirty(data::ptr node, bool dirty); - virtual void set_pending(data::ptr node, bool pending); + virtual void set_dirty(data::ptr node, bool dirty){}; + virtual void set_pending(data::ptr node, bool pending){}; - virtual void set_value(data::ptr node, const void *value); - virtual void mark_value(data::ptr node); + virtual void set_value(data::ptr node, const void *value){}; + virtual void mark_value(data::ptr node){}; - virtual void added(data::ptr indirect_node); + virtual void added(data::ptr indirect_node){}; - virtual void set_source(data::ptr indirect_node, AttributeID source); - virtual void set_dependency(data::ptr indirect_node, AttributeID dependency); + virtual void set_source(data::ptr indirect_node, AttributeID source){}; + virtual void set_dependency(data::ptr indirect_node, AttributeID dependency){}; - virtual void set_deadline(uint64_t deadline); - virtual void passed_deadline(); + virtual void set_deadline(uint64_t deadline){}; + virtual void passed_deadline(){}; - virtual void mark_profile(const Graph &graph, uint32_t options); + virtual void mark_profile(const Graph &graph, uint32_t options){}; virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, - const swift::metadata &type); + const swift::metadata &type){}; virtual bool named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, - const uint64_t *event_args, CFDataRef data, uint32_t arg6); - virtual bool named_event_enabled(uint32_t event_id); + const uint64_t *event_args, CFDataRef data, uint32_t arg6){}; + virtual bool named_event_enabled(uint32_t event_id) { return false; }; virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t lhs_offset, - size_t rhs_offset, const swift::metadata &type); + size_t rhs_offset, const swift::metadata &type){}; }; } // namespace AG From fed4ebf0a829c278c9a3f1d38de11b6fa7ad7300 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 15:29:46 +1100 Subject: [PATCH 36/74] Implement DebugServer class --- Sources/ComputeCxx/Containers/Vector.h | 3 + Sources/ComputeCxx/Containers/Vector.tpp | 28 ++ Sources/ComputeCxx/Debug/Connection.cpp | 137 +++++++++ Sources/ComputeCxx/Debug/DebugServer.cpp | 7 - Sources/ComputeCxx/Debug/DebugServer.h | 54 +++- Sources/ComputeCxx/Debug/DebugServer.mm | 285 +++++++++++++++++++ Sources/ComputeCxx/Graph/Graph+Description.h | 12 + Sources/ComputeCxx/Graph/Graph.cpp | 38 +-- Sources/ComputeCxx/Graph/Graph.h | 12 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 104 +++---- Sources/ComputeCxx/Graph/TraceRecorder.h | 4 +- 11 files changed, 597 insertions(+), 87 deletions(-) create mode 100644 Sources/ComputeCxx/Debug/Connection.cpp delete mode 100644 Sources/ComputeCxx/Debug/DebugServer.cpp create mode 100644 Sources/ComputeCxx/Debug/DebugServer.mm create mode 100644 Sources/ComputeCxx/Graph/Graph+Description.h diff --git a/Sources/ComputeCxx/Containers/Vector.h b/Sources/ComputeCxx/Containers/Vector.h index 1a16c74..90cbb36 100644 --- a/Sources/ComputeCxx/Containers/Vector.h +++ b/Sources/ComputeCxx/Containers/Vector.h @@ -239,6 +239,9 @@ class vector, 0, _size_type> { iterator insert(const_iterator pos, const std::unique_ptr &value); iterator insert(const_iterator pos, std::unique_ptr &&value); + + iterator erase(iterator pos); + iterator erase(iterator first, iterator last); void push_back(const std::unique_ptr &value) = delete; void push_back(std::unique_ptr &&value); diff --git a/Sources/ComputeCxx/Containers/Vector.tpp b/Sources/ComputeCxx/Containers/Vector.tpp index 86c8421..a98ad4d 100644 --- a/Sources/ComputeCxx/Containers/Vector.tpp +++ b/Sources/ComputeCxx/Containers/Vector.tpp @@ -412,6 +412,34 @@ void vector, 0, size_type>::reserve(size_type n reserve_slow(new_cap); } +template + requires std::unsigned_integral +vector, 0, size_type>::iterator +vector, 0, size_type>::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +vector, 0, size_type>::iterator +vector, 0, size_type>::erase(iterator first, iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + for (auto iter = first; iter != last; iter++) { + iter->reset(); + } + for (auto iter = last, old_end = end(); iter != old_end; iter++) { + std::swap(*(iter - count), *iter); + } + _size -= count; + return end(); +} + template requires std::unsigned_integral void vector, 0, size_type>::push_back(std::unique_ptr &&value) { diff --git a/Sources/ComputeCxx/Debug/Connection.cpp b/Sources/ComputeCxx/Debug/Connection.cpp new file mode 100644 index 0000000..90053eb --- /dev/null +++ b/Sources/ComputeCxx/Debug/Connection.cpp @@ -0,0 +1,137 @@ +#include "DebugServer.h" + +#include + +namespace AG { + +DebugServer::Connection::Connection(DebugServer *server, int socket) { + _server = server; + _socket = socket; + + _dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket, 0, dispatch_get_main_queue()); + dispatch_set_context(_dispatch_source, this); + dispatch_source_set_event_handler_f(_dispatch_source, handler); + dispatch_resume(_dispatch_source); +} + +DebugServer::Connection::~Connection() { + dispatch_source_set_event_handler(_dispatch_source, nullptr); + dispatch_set_context(_dispatch_source, nullptr); + _dispatch_source = nullptr; + + close(_socket); +} + +namespace { + +bool blocking_read(int socket_fd, void *buffer, size_t size) { + if (size == 0) { + return true; + } + + size_t total_read = 0; + char *bytes = static_cast(buffer); + + while (total_read < size) { + ssize_t bytes_read = read(socket_fd, bytes + total_read, size - total_read); + + if (bytes_read > 0) { + total_read += bytes_read; + } else if (bytes_read == 0) { + // Socket closed + return false; + } else { + if (errno == EINTR) { + continue; // Interrupted, retry + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + // Non-blocking mode: No data available, retry or handle accordingly + continue; + } else { + perror("AGDebugServer: read"); + return false; + } + } + } + + return true; +} + +bool blocking_write(int socket_fd, const void *buffer, size_t size) { + if (size == 0) { + return true; + } + + size_t total_written = 0; + const char *bytes = static_cast(buffer); + + while (total_written < size) { + ssize_t bytes_written = write(socket_fd, bytes + total_written, size - total_written); + + if (bytes_written > 0) { + total_written += bytes_written; + } else if (bytes_written == 0) { + return false; // Unexpected write failure + } else { + if (errno == EINTR) { + continue; // Interrupted, retry + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; // Non-blocking mode: Retry or handle accordingly + } else { + perror("AGDebugServer: write"); + return false; + } + } + } + + return true; +} + +} // namespace + +void DebugServer::Connection::handler(void *context) { + Connection *connection = (Connection *)context; + + uint32_t header[4]; + if (!blocking_read(connection->_socket, header, sizeof(header))) { + connection->_server->close_connection(connection); + return; + } + + if (header[0] != connection->_server->_token) { + connection->_server->close_connection(connection); + return; + } + + CFIndex length = header[2]; + CFMutableDataRef request_data = CFDataCreateMutable(kCFAllocatorDefault, length); + if (!request_data) { + connection->_server->close_connection(connection); + return; + } + + CFDataSetLength(request_data, length); + void *request_bytes = CFDataGetMutableBytePtr(request_data); + + if (blocking_read(connection->_socket, request_bytes, length)) { + CFDataRef inout_data = request_data; + DebugServer::receive(&inout_data); + if (inout_data) { + CFIndex response_length = CFDataGetLength(inout_data); + if (response_length >> 32 == 0) { + header[2] = (uint32_t)response_length; + if (blocking_write(connection->_socket, (const unsigned char *)header, sizeof(header))) { + const unsigned char *response_bytes = CFDataGetBytePtr(inout_data); + if (blocking_write(connection->_socket, response_bytes, response_length)) { + connection = nullptr; // do not close connection + } + } + } + } + } + + if (connection) { + connection->_server->close_connection(connection); + } +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Debug/DebugServer.cpp b/Sources/ComputeCxx/Debug/DebugServer.cpp deleted file mode 100644 index e999773..0000000 --- a/Sources/ComputeCxx/Debug/DebugServer.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "DebugServer.h" - -namespace AG { - -void DebugServer::start(uint32_t port) {} - -} // namespace AG diff --git a/Sources/ComputeCxx/Debug/DebugServer.h b/Sources/ComputeCxx/Debug/DebugServer.h index da46fa6..4cf7269 100644 --- a/Sources/ComputeCxx/Debug/DebugServer.h +++ b/Sources/ComputeCxx/Debug/DebugServer.h @@ -1,14 +1,66 @@ #pragma once #include +#include +#include +#include + +#include "Containers/Vector.h" CF_ASSUME_NONNULL_BEGIN +struct AGDebugServerMessageHeader {}; + namespace AG { class DebugServer { public: - static void start(uint32_t port); + enum Options : uint32_t { + + }; + + class Connection { + private: + DebugServer *_server; + int _socket; + dispatch_source_t _dispatch_source; + + public: + Connection(DebugServer *server, int socket); + ~Connection(); + + int socket() const { return _socket; }; + + static void handler(void *context); + }; + + private: + int _socket; + uint32_t _ip_address; + uint16_t _port; + uint16_t _padding; + uint32_t _token; + dispatch_source_t _dispatch_source; + vector, 0, uint64_t> _clients; + + static DebugServer *_shared_server; + + public: + static void start(uint32_t options); + static void stop(); + + static void accept_handler(void *context); + static void receive(CFDataRef _Nonnull *_Nonnull body); + + DebugServer(uint32_t options); + ~DebugServer(); + + void run(int timeout); + void shutdown(); + + void close_connection(Connection *connection); + + CFURLRef copy_url(); }; } // namespace AG diff --git a/Sources/ComputeCxx/Debug/DebugServer.mm b/Sources/ComputeCxx/Debug/DebugServer.mm new file mode 100644 index 0000000..c3903bf --- /dev/null +++ b/Sources/ComputeCxx/Debug/DebugServer.mm @@ -0,0 +1,285 @@ +#include "DebugServer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Graph/Graph+Description.h" +#include "Graph/Graph.h" +#include "Log/Log.h" +#include "Utilities/FreeDeleter.h" + +namespace AG { + +constexpr int backlog = 5; + +DebugServer *DebugServer::_shared_server = nullptr; + +void DebugServer::start(uint32_t options) { + if (options & 1 && !_shared_server) { + if (true /* && os_variant_has_internal_diagnostics() */) { + _shared_server = new DebugServer(options); + } + } + return _shared_server; +} + +void DebugServer::stop() { + if (_shared_server) { + delete _shared_server; + _shared_server = nullptr; + } + return _shared_server; +} + +DebugServer::DebugServer(uint32_t options) { + _socket = -1; + _ip_address = 0; + _port = 0; + + _token = arc4random(); + _dispatch_source = nullptr; + + _socket = socket(AF_INET, SOCK_STREAM, 0); + if (_socket < 0) { + perror("AGDebugServer: socket"); + return; + } + + fcntl(_socket, F_SETFD, FD_CLOEXEC); + + int option_value = 1; + setsockopt(_socket, SOL_SOCKET, SO_NOSIGPIPE, &option_value, sizeof(option_value)); + + sockaddr_in address = {}; + address.sin_family = AF_INET; + address.sin_port = 0; + address.sin_addr.s_addr = (options & 2) ? INADDR_ANY : htonl(INADDR_LOOPBACK); + + if (bind(_socket, (struct sockaddr *)&address, sizeof(address)) < 0) { + perror("AGDebugServer: bind"); + shutdown(); + return; + } + + socklen_t length = sizeof(address); + if (getsockname(_socket, (struct sockaddr *)&address, &length) < 0) { + perror("AGDebugServer: getsockname"); + shutdown(); + return; + } + + _ip_address = ntohl(address.sin_addr.s_addr); + _port = ntohs(address.sin_port); + + if (options & 2) { + struct ifaddrs *ifaddr = nullptr; + if (!getifaddrs(&ifaddr)) { + for (auto *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *sa = (struct sockaddr_in *)ifa->ifa_addr; + if (ntohl(sa->sin_addr.s_addr) != INADDR_LOOPBACK) { + _ip_address = ntohl(sa->sin_addr.s_addr); + break; + } + } + } + freeifaddrs(ifaddr); + } + } + + if (listen(_socket, backlog) < 0) { + perror("AGDebugServer: listen"); + shutdown(); + return this; + } + + _dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, _socket, 0, dispatch_get_main_queue()); + dispatch_set_context(_dispatch_source, this); + dispatch_source_set_event_handler_f(_dispatch_source, accept_handler); + dispatch_resume(_dispatch_source); + + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &_ip_address, ip_str, sizeof(ip_str)); + + os_log(misc_log(), "debug server graph://%s:%d/?token=%u", ip_str, _port, _token); + fprintf(stdout, "debug server graph://%s:%d/?token=%u\n", ip_str, _port, _token); +} + +DebugServer::~DebugServer() { shutdown(); } + +void DebugServer::shutdown() { + if (auto dispatch_source = _dispatch_source) { + dispatch_source_set_event_handler(dispatch_source, nullptr); + dispatch_set_context(dispatch_source, nullptr); + _dispatch_source = nullptr; + } + if (_socket >= 0) { + close(_socket); + _socket = -1; + } +} + +void DebugServer::accept_handler(void *context) { + DebugServer *server = (DebugServer *)context; + + struct sockaddr address = {}; + socklen_t length = sizeof(address); + int connection_socket = accept(server->_socket, &address, &length); + if (connection_socket < 0) { + perror("AGDebugServer: accept"); + return; + } + + fcntl(server->_socket, F_SETFD, FD_CLOEXEC); + + server->_clients.push_back(std::make_unique(server, connection_socket)); +} + +void DebugServer::run(int timeout) { + fd_set write_fds; + struct timeval tv; + + bool accepted = false; + while (!accepted || !_clients.empty()) { + FD_ZERO(&write_fds); + FD_SET(_socket, &write_fds); + + int max_socket = _socket; + for (auto &connection : _clients) { + FD_SET(connection->socket(), &write_fds); + if (connection->socket() > max_socket) { + max_socket = connection->socket(); + } + } + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + int activity = select(max_socket + 1, nullptr, &write_fds, nullptr, &tv); + if (activity <= 0) { + if (errno != EAGAIN) { + perror("AGDebugServer: select"); + return; + } + } else { + if (FD_ISSET(_socket, &write_fds)) { + accept_handler(this); + accepted = true; + } + + for (uint64_t i = 0; i < _clients.size(); ++i) { + Connection *connection = _clients[i].get(); + if (FD_ISSET(connection->socket(), &write_fds)) { + FD_CLR(connection->socket(), &write_fds); + connection->handler(connection); + + // Restart loop to handle possible mutations to clients + i = 0; + } + } + } + } +} + +void DebugServer::receive(CFDataRef *body) { + @autoreleasepool { + id body_json = [NSJSONSerialization JSONObjectWithData:(__bridge NSData *)*body options:0 error:nullptr]; + if (!body_json) { + body = nullptr; + return; + } + + if (![body_json isKindOfClass:[NSDictionary class]]) { + body = nullptr; + return; + } + NSDictionary *body_dict = (NSDictionary *)body_json; + + NSString *command = body_dict[@"command"]; + + if ([command isEqual:@"graph/description"]) { + NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:body_dict]; + options[AGDescriptionFormat] = @"graph/dict"; + + id desc = Graph::description(nullptr, (__bridge CFDictionaryRef)options); + if (!desc) { + *body = nullptr; + return; + } + + NSData *new_data = [NSJSONSerialization dataWithJSONObject:desc options:0 error:nil]; + + *body = (__bridge CFDataRef)new_data; + } else if ([command isEqual:@"profiler/start"]) { + id profiler_flags_json = body_dict[@"flags"]; + uint32_t profiler_flags = + ([profiler_flags_json isKindOfClass:[NSNumber class]] ? [profiler_flags_json unsignedIntValue] : 0) | 1; + Graph::all_start_profiling(profiler_flags); + } else if ([command isEqual:@"profiler/stop"]) { + Graph::all_stop_profiling(); + } else if ([command isEqual:@"profiler/reset"]) { + Graph::all_reset_profile(); + } else if ([command isEqual:@"profiler/mark"]) { + id name_json = body_dict[@"name"]; + if ([name_json isKindOfClass:[NSString class]]) { + Graph::all_mark_profile([name_json UTF8String]); + } + } else if ([command isEqual:@"tracing/start"]) { + id tracing_flags_json = body_dict[@"flags"]; + uint32_t tracing_flags = + ([tracing_flags_json isKindOfClass:[NSNumber class]] ? [tracing_flags_json unsignedIntValue] : 0) | 1; + + auto subsystems = std::span(); + auto subsystems_vector = vector, 0, uint64_t>(); + id trace_subsystems_json = body_dict[@"subsystems"]; + if ([trace_subsystems_json isKindOfClass:[NSArray class]]) { + for (id trace_subsystem_json in trace_subsystems_json) { + if ([trace_subsystem_json isKindOfClass:[NSString class]]) { + const char *str = [trace_subsystem_json UTF8String]; + subsystems_vector.push_back(std::unique_ptr(str)); + } + } + subsystems = std::span((const char **)subsystems_vector.data(), subsystems_vector.size()); + } + + Graph::all_start_tracing(tracing_flags, subsystems); + } else if ([command isEqual:@"tracing/stop"]) { + Graph::all_stop_tracing(); + } else if ([command isEqual:@"tracing/sync"]) { + AG::Graph::all_sync_tracing(); + } + + *body = nullptr; + return; + } +} + +void DebugServer::close_connection(Connection *connection) { + auto iter = std::remove_if(_clients.begin(), _clients.end(), + [&connection](auto &candidate) -> bool { return candidate.get() == connection; }); + _clients.erase(iter); +} + +CFURLRef DebugServer::copy_url() { + if (_socket < 0) { + return nullptr; + } + + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &_ip_address, ip_str, sizeof(ip_str)); + + char bytes[0x100]; + snprintf_l(bytes, 0x100, nullptr, "graph://%s:%d/?token=%u", ip_str, _port, _token); + + CFIndex length = strlen(bytes); + return CFURLCreateWithBytes(kCFAllocatorDefault, (const unsigned char *)bytes, length, kCFStringEncodingUTF8, + nullptr); +} + +} // namespace AG diff --git a/Sources/ComputeCxx/Graph/Graph+Description.h b/Sources/ComputeCxx/Graph/Graph+Description.h new file mode 100644 index 0000000..c9d96ac --- /dev/null +++ b/Sources/ComputeCxx/Graph/Graph+Description.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *AGDescriptionFormat; +extern NSString *AGDescriptionMaxFrames; +extern NSString *AGDescriptionIncludeValues; +extern NSString *AGDescriptionTruncationLimit; + +NS_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index a118c02..028aa38 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -95,22 +95,22 @@ Graph::Graph() option[option_length] = 0; if (strcasecmp(option, "enabled") == 0) { - trace_flags |= TraceFlags::Enabled; + trace_flags |= TracingFlags::Enabled; free(option); } else if (strcasecmp(option, "full") == 0) { - trace_flags |= TraceFlags::Full; + trace_flags |= TracingFlags::Full; free(option); } else if (strcasecmp(option, "backtrace") == 0) { - trace_flags |= TraceFlags::Backtrace; + trace_flags |= TracingFlags::Backtrace; free(option); } else if (strcasecmp(option, "prepare") == 0) { - trace_flags |= TraceFlags::Prepare; + trace_flags |= TracingFlags::Prepare; free(option); } else if (strcasecmp(option, "custom") == 0) { - trace_flags |= TraceFlags::Custom; + trace_flags |= TracingFlags::Custom; free(option); } else if (strcasecmp(option, "all") == 0) { - trace_flags |= TraceFlags::All; + trace_flags |= TracingFlags::All; free(option); } else { trace_subsystems.push_back(std::unique_ptr(option)); @@ -2214,10 +2214,10 @@ void Graph::remove_trace(uint64_t trace_id) { _traces.erase(iter); } -void Graph::start_tracing(uint32_t flags, std::span subsystems) { - if (flags & TraceFlags::Enabled && _trace_recorder == nullptr) { - _trace_recorder = new TraceRecorder(this, flags, subsystems); - if (flags & TraceFlags::Prepare) { +void Graph::start_tracing(uint32_t tracing_flags, std::span subsystems) { + if (tracing_flags & TracingFlags::Enabled && _trace_recorder == nullptr) { + _trace_recorder = new TraceRecorder(this, tracing_flags, subsystems); + if (tracing_flags & TracingFlags::Prepare) { prepare_trace(*_trace_recorder); } add_trace(_trace_recorder); @@ -2245,10 +2245,10 @@ CFStringRef Graph::copy_trace_path() { } } -void Graph::all_start_tracing(uint32_t arg, std::span span) { +void Graph::all_start_tracing(uint32_t tracing_flags, std::span span) { all_lock(); for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { - graph->start_tracing(arg, span); + graph->start_tracing(tracing_flags, span); } all_unlock(); } @@ -2345,9 +2345,9 @@ void Graph::add_profile_update(data::ptr node, uint64_t duration, bool cha } } -void Graph::start_profiling(uint32_t options) { - _is_profiling = options & 1; - if ((options >> 1) & 1) { +void Graph::start_profiling(uint32_t profiler_flags) { + _is_profiling = profiler_flags & 1; + if ((profiler_flags >> 1) & 1) { AGAppObserverStartObserving(); CFRunLoopRef run_loop = CFRunLoopGetMain(); @@ -2387,10 +2387,10 @@ void Graph::mark_profile(uint32_t event_id, uint64_t time) { void Graph::reset_profile() { _profile_data.reset(); } -void Graph::all_start_profiling(uint32_t options) { +void Graph::all_start_profiling(uint32_t profiler_flags) { all_lock(); for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { - graph->start_profiling(options); + graph->start_profiling(profiler_flags); } all_unlock(); } @@ -2403,11 +2403,11 @@ void Graph::all_stop_profiling() { all_unlock(); } -void Graph::all_mark_profile(const char *event_name) { +void Graph::all_mark_profile(const char *name) { uint64_t time = mach_absolute_time(); all_lock(); for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { - auto event_id = graph->intern_key(event_name); + auto event_id = graph->intern_key(name); graph->mark_profile(event_id, time); } all_unlock(); diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 34fd279..5771843 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -340,7 +340,7 @@ class Graph { // MARK: Tracing - enum TraceFlags : uint32_t { + enum TracingFlags : uint32_t { Enabled = 1 << 0, Full = 1 << 1, Backtrace = 1 << 2, @@ -354,7 +354,7 @@ class Graph { void add_trace(Trace *_Nullable trace); void remove_trace(uint64_t trace_id); - void start_tracing(uint32_t flags, std::span subsystems); + void start_tracing(uint32_t tracing_flags, std::span subsystems); void stop_tracing(); void sync_tracing(); CFStringRef copy_trace_path(); @@ -367,7 +367,7 @@ class Graph { } }; - static void all_start_tracing(uint32_t arg, std::span span); + static void all_start_tracing(uint32_t tracing_flags, std::span span); static void all_stop_tracing(); static void all_sync_tracing(); static CFStringRef all_copy_trace_path(); @@ -383,14 +383,14 @@ class Graph { void add_profile_update(data::ptr node, uint64_t duration, bool changed); - void start_profiling(uint32_t options); + void start_profiling(uint32_t profiler_flags); void stop_profiling(); void mark_profile(uint32_t event_id, uint64_t time); void reset_profile(); - static void all_start_profiling(uint32_t options); + static void all_start_profiling(uint32_t profiler_flags); static void all_stop_profiling(); - static void all_mark_profile(const char *event_name); + static void all_mark_profile(const char *name); static void all_reset_profile(); // MARK: Printing diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 7097436..dddf695 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -27,8 +27,8 @@ bool uuid_equal(const uuid_t a, const uuid_t b) { return uuid_compare(a, b) == 0 } // namespace -Graph::TraceRecorder::TraceRecorder(Graph *graph, uint8_t options, std::span subsystems) - : _graph(graph), _options(options), _heap(_heap_inline_buffer, 256, 0), +Graph::TraceRecorder::TraceRecorder(Graph *graph, uint8_t tracing_flags, std::span subsystems) + : _graph(graph), _tracing_flags(tracing_flags), _heap(_heap_inline_buffer, 256, 0), _image_offset_cache(uuid_hash, uuid_equal, nullptr, nullptr, &_heap), _encoder(_delegate, 0x10000) { _unique_id = AGMakeUniqueID(); @@ -171,7 +171,7 @@ void Graph::TraceRecorder::encode_stack() { } void Graph::TraceRecorder::encode_snapshot() { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -215,7 +215,7 @@ void Graph::TraceRecorder::field_timestamp(Encoder &encoder) { } void Graph::TraceRecorder::field_backtrace(Encoder &encoder, uint64_t field_number) { - if ((_options & TraceFlags::Backtrace) == 0) { + if ((_tracing_flags & TracingFlags::Backtrace) == 0) { return; } @@ -436,7 +436,7 @@ void Graph::TraceRecorder::log_message_v(const char *format, va_list args) { } void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t options) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -460,7 +460,7 @@ void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t optio } void Graph::TraceRecorder::end_update(const Subgraph &subgraph) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -481,7 +481,7 @@ void Graph::TraceRecorder::end_update(const Subgraph &subgraph) { void Graph::TraceRecorder::begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -505,7 +505,7 @@ void Graph::TraceRecorder::begin_update(const Graph::UpdateStack &update_stack, void Graph::TraceRecorder::end_update(const Graph::UpdateStack &update_stack, data::ptr node, Graph::UpdateStatus update_status) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -528,7 +528,7 @@ void Graph::TraceRecorder::end_update(const Graph::UpdateStack &update_stack, da } void Graph::TraceRecorder::begin_update(data::ptr node) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -547,7 +547,7 @@ void Graph::TraceRecorder::begin_update(data::ptr node) { } void Graph::TraceRecorder::end_update(data::ptr node, bool changed) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -570,7 +570,7 @@ void Graph::TraceRecorder::end_update(data::ptr node, bool changed) { } void Graph::TraceRecorder::begin_update(const Graph::Context &context) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -589,7 +589,7 @@ void Graph::TraceRecorder::begin_update(const Graph::Context &context) { } void Graph::TraceRecorder::end_update(const Graph::Context &context) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -608,10 +608,10 @@ void Graph::TraceRecorder::end_update(const Graph::Context &context) { } void Graph::TraceRecorder::begin_invalidation(const Graph::Context &context, AttributeID attribute) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -634,10 +634,10 @@ void Graph::TraceRecorder::begin_invalidation(const Graph::Context &context, Att } void Graph::TraceRecorder::end_invalidation(const Graph::Context &context, AttributeID attribute) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -660,10 +660,10 @@ void Graph::TraceRecorder::end_invalidation(const Graph::Context &context, Attri } void Graph::TraceRecorder::begin_modify(data::ptr node) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -682,10 +682,10 @@ void Graph::TraceRecorder::begin_modify(data::ptr node) { } void Graph::TraceRecorder::end_modify(data::ptr node) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -695,7 +695,7 @@ void Graph::TraceRecorder::end_modify(data::ptr node) { _encoder.encode_varint(0xe); field_timestamp(_encoder); - if (node || _options & TraceFlags::Custom) { + if (node || _tracing_flags & TracingFlags::Custom) { _encoder.encode_varint(0x18); _encoder.encode_varint(1); } @@ -704,7 +704,7 @@ void Graph::TraceRecorder::end_modify(data::ptr node) { } void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -727,7 +727,7 @@ void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event) { } void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -750,7 +750,7 @@ void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event) { } void Graph::TraceRecorder::created(const Graph::Context &context) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -770,7 +770,7 @@ void Graph::TraceRecorder::created(const Graph::Context &context) { } void Graph::TraceRecorder::destroy(const Graph::Context &context) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -790,10 +790,10 @@ void Graph::TraceRecorder::destroy(const Graph::Context &context) { } void Graph::TraceRecorder::needs_update(const Graph::Context &context) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -811,7 +811,7 @@ void Graph::TraceRecorder::needs_update(const Graph::Context &context) { } void Graph::TraceRecorder::created(const Subgraph &subgraph) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -836,7 +836,7 @@ void Graph::TraceRecorder::created(const Subgraph &subgraph) { } void Graph::TraceRecorder::invalidate(const Subgraph &subgraph) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -862,7 +862,7 @@ void Graph::TraceRecorder::invalidate(const Subgraph &subgraph) { } void Graph::TraceRecorder::destroy(const Subgraph &subgraph) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -883,7 +883,7 @@ void Graph::TraceRecorder::destroy(const Subgraph &subgraph) { } void Graph::TraceRecorder::add_child(const Subgraph &subgraph, const Subgraph &child) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -907,7 +907,7 @@ void Graph::TraceRecorder::add_child(const Subgraph &subgraph, const Subgraph &c } void Graph::TraceRecorder::remove_child(const Subgraph &subgraph, const Subgraph &child) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -931,7 +931,7 @@ void Graph::TraceRecorder::remove_child(const Subgraph &subgraph, const Subgraph } void Graph::TraceRecorder::added(data::ptr node) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -961,7 +961,7 @@ void Graph::TraceRecorder::added(data::ptr node) { } void Graph::TraceRecorder::add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -985,7 +985,7 @@ void Graph::TraceRecorder::add_edge(data::ptr node, AttributeID input, uin } void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_index) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -1010,10 +1010,10 @@ void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_inde } void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input_index, bool pending) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -1040,10 +1040,10 @@ void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input } void Graph::TraceRecorder::set_dirty(data::ptr node, bool dirty) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -1065,10 +1065,10 @@ void Graph::TraceRecorder::set_dirty(data::ptr node, bool dirty) { } void Graph::TraceRecorder::set_pending(data::ptr node, bool pending) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -1090,10 +1090,10 @@ void Graph::TraceRecorder::set_pending(data::ptr node, bool pending) { } void Graph::TraceRecorder::set_value(data::ptr node, const void *value) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -1111,10 +1111,10 @@ void Graph::TraceRecorder::set_value(data::ptr node, const void *value) { } void Graph::TraceRecorder::mark_value(data::ptr node) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } - if ((_options & TraceFlags::Full) == 0) { + if ((_tracing_flags & TracingFlags::Full) == 0) { return; } @@ -1132,7 +1132,7 @@ void Graph::TraceRecorder::mark_value(data::ptr node) { } void Graph::TraceRecorder::added(data::ptr indirect_node) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -1157,7 +1157,7 @@ void Graph::TraceRecorder::added(data::ptr indirect_node) { } void Graph::TraceRecorder::set_source(data::ptr indirect_node, AttributeID source) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -1185,7 +1185,7 @@ void Graph::TraceRecorder::set_source(data::ptr indirect_node, Att } void Graph::TraceRecorder::set_dependency(data::ptr indirect_node, AttributeID dependency) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -1208,7 +1208,7 @@ void Graph::TraceRecorder::set_dependency(data::ptr indirect_node, } void Graph::TraceRecorder::set_deadline(uint64_t deadline) { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -1234,7 +1234,7 @@ void Graph::TraceRecorder::set_deadline(uint64_t deadline) { } void Graph::TraceRecorder::passed_deadline() { - if (_options & TraceFlags::Custom) { + if (_tracing_flags & TracingFlags::Custom) { return; } @@ -1357,10 +1357,10 @@ bool Graph::TraceRecorder::named_event_enabled(uint32_t event_id) { const char *event_subsystem = AGGraphGetTraceEventSubsystem(event_id); bool enabled = false; - if (event_subsystem == nullptr || (_options & TraceFlags::All)) { + if (event_subsystem == nullptr || (_tracing_flags & TracingFlags::All)) { enabled = true; } else { - enabled = (_options & TraceFlags::All) != 0; + enabled = (_tracing_flags & TracingFlags::All) != 0; for (auto stored_subsystem : _named_event_subsystems) { if (!strcasecmp(stored_subsystem, event_subsystem)) { enabled = true; diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index daeda2f..c56d434 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -18,7 +18,7 @@ class Graph::TraceRecorder : public Encoder::Delegate, public Trace { Encoder::Delegate *_Nonnull _delegate; Graph *_graph; Encoder _encoder; - uint8_t _options; + uint8_t _tracing_flags; util::Heap _heap; char _heap_inline_buffer[256]; @@ -41,7 +41,7 @@ class Graph::TraceRecorder : public Encoder::Delegate, public Trace { vector _named_event_infos; public: - TraceRecorder(Graph *graph, uint8_t options, std::span subsystems); + TraceRecorder(Graph *graph, uint8_t tracing_flags, std::span subsystems); ~TraceRecorder(); uint64_t unique_id() { return _unique_id; }; From 9125e1d7fe79b892a27b0dfeaba48b3b4be8267f Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 16:48:51 +1100 Subject: [PATCH 37/74] Add InlineHeap class --- Sources/ComputeCxx/Graph/Graph.cpp | 24 +++++++++++----------- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 2 +- Sources/ComputeCxx/Graph/TraceRecorder.h | 3 +-- Sources/Utilities/include/Utilities/Heap.h | 8 ++++++++ 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 028aa38..9dca627 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -852,7 +852,7 @@ void Graph::indirect_attribute_set(data::ptr attribute, AttributeI // TODO: check zone id attribute->modify(WeakAttributeID(resolved_source.attribute(), 0), resolved_source.offset()); attribute->set_traverses_contexts(AttributeID(attribute).subgraph()->context_id() != - resolved_source.attribute().subgraph()->context_id()); + resolved_source.attribute().subgraph()->context_id()); if (resolved_source.attribute() != previous_source) { add_input_dependencies(AttributeID(attribute), resolved_source.attribute()); @@ -892,7 +892,7 @@ bool Graph::indirect_attribute_reset(data::ptr attribute, bool non attribute->modify(new_source, new_offset); attribute->set_traverses_contexts(AttributeID(attribute).subgraph()->context_id() != - new_source_or_nil.subgraph()->context_id()); + new_source_or_nil.subgraph()->context_id()); if (old_source_or_nil != new_source_or_nil) { add_input_dependencies(AttributeID(attribute), new_source_or_nil); @@ -1166,8 +1166,7 @@ void Graph::propagate_dirty(AttributeID attribute) { Node::State state; }; - char stack_buffer[0x2000] = {}; - auto heap = util::Heap(stack_buffer, sizeof(stack_buffer), 0); + auto heap = util::InlineHeap<0x2000>(); auto frames = util::ForwardList(&heap); data::vector initial_outputs = {}; @@ -1221,8 +1220,8 @@ void Graph::propagate_dirty(AttributeID attribute) { if (output_node.flags().inputs_traverse_contexts() && !output_node.outputs().empty()) { if (auto output_subgraph = output.subgraph()) { auto context_id = output_subgraph->context_id(); - if (context_id && (attribute.subgraph() == nullptr || - context_id != attribute.subgraph()->context_id())) { + if (context_id && + (attribute.subgraph() == nullptr || context_id != attribute.subgraph()->context_id())) { if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { if (context->graph_version() != context->graph()._version) { context->call_invalidation(attribute); @@ -1242,8 +1241,8 @@ void Graph::propagate_dirty(AttributeID attribute) { if (output_node.traverses_contexts() && !output_node.to_mutable().outputs().empty()) { if (auto output_subgraph = output.subgraph()) { auto context_id = output_subgraph->context_id(); - if (context_id && (attribute.subgraph() == nullptr || - context_id != attribute.subgraph()->context_id())) { + if (context_id && + (attribute.subgraph() == nullptr || context_id != attribute.subgraph()->context_id())) { if (auto context = _contexts_by_id.lookup(context_id, nullptr)) { if (context->graph_version() != context->graph()._version) { context->call_invalidation(attribute); @@ -1754,8 +1753,7 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c AttributeID attribute; }; - char stack_buffer[0x2000] = {}; - auto heap = util::Heap(stack_buffer, sizeof(stack_buffer), 0); + auto heap = util::InlineHeap<0x2000>(); auto frames = util::ForwardList(&heap); data::vector initial_outputs = {}; @@ -1912,7 +1910,8 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { CFRange range = CFRangeMake(0, length); uint8_t buffer[1024]; CFIndex used_buffer_length = 0; - CFStringGetBytes(description, range, kCFStringEncodingUTF8, 0x3f, true, buffer, 1024, &used_buffer_length); + CFStringGetBytes(description, range, kCFStringEncodingUTF8, 0x3f, true, buffer, 1024, + &used_buffer_length); if (used_buffer_length > 0) { encoder.encode_varint(0x12); encoder.encode_data(buffer, used_buffer_length); @@ -2175,7 +2174,8 @@ void Graph::prepare_trace(Trace &trace) { auto node = attribute.to_node_ptr(); uint32_t edge_index = 0; for (auto input_edge : node->inputs()) { - trace.add_edge(node, input_edge.value, input_edge.is_always_enabled()); // TODO: is last param int or bool? + trace.add_edge(node, input_edge.value, + input_edge.is_always_enabled()); // TODO: is last param int or bool? if (input_edge.is_changed()) { trace.set_edge_pending(node, edge_index, true); } diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index dddf695..e2180af 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -28,7 +28,7 @@ bool uuid_equal(const uuid_t a, const uuid_t b) { return uuid_compare(a, b) == 0 } // namespace Graph::TraceRecorder::TraceRecorder(Graph *graph, uint8_t tracing_flags, std::span subsystems) - : _graph(graph), _tracing_flags(tracing_flags), _heap(_heap_inline_buffer, 256, 0), + : _graph(graph), _tracing_flags(tracing_flags), _heap(), _image_offset_cache(uuid_hash, uuid_equal, nullptr, nullptr, &_heap), _encoder(_delegate, 0x10000) { _unique_id = AGMakeUniqueID(); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index c56d434..2c3e15d 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -20,8 +20,7 @@ class Graph::TraceRecorder : public Encoder::Delegate, public Trace { Encoder _encoder; uint8_t _tracing_flags; - util::Heap _heap; - char _heap_inline_buffer[256]; + util::InlineHeap<256> _heap; vector _named_event_subsystems; diff --git a/Sources/Utilities/include/Utilities/Heap.h b/Sources/Utilities/include/Utilities/Heap.h index 0c33fb7..5c99d7d 100644 --- a/Sources/Utilities/include/Utilities/Heap.h +++ b/Sources/Utilities/include/Utilities/Heap.h @@ -65,6 +65,14 @@ template class InlineHeap : public Heap { InlineHeap() : Heap(_inline_buffer, _inline_size, 0) {} }; +template class InlineHeap : public Heap { + private: + char _inline_buffer[_inline_size] = {}; + + public: + InlineHeap() : Heap(_inline_buffer, _inline_size, 0) {} +}; + } // namespace util CF_ASSUME_NONNULL_END From 8620512c296fae3f7dbfba7c440e8e344693204f Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 19:01:46 +1100 Subject: [PATCH 38/74] Add ExternalTrace class --- Sources/ComputeCxx/External/ExternalTrace.cpp | 312 ++++++++++++++++++ Sources/ComputeCxx/External/ExternalTrace.h | 146 ++++++++ Sources/ComputeCxx/Graph/Context.h | 2 +- Sources/ComputeCxx/Graph/Graph.cpp | 2 +- Sources/ComputeCxx/Graph/Graph.h | 2 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 14 +- Sources/ComputeCxx/Graph/TraceRecorder.h | 6 +- .../ComputeCxx/Layout/AGComparison-Private.h | 16 + Sources/ComputeCxx/Layout/AGComparison.cpp | 8 +- .../ComputeCxx/Layout/LayoutDescriptor.cpp | 11 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 2 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 2 +- Sources/ComputeCxx/Trace/Trace.h | 14 +- 13 files changed, 509 insertions(+), 28 deletions(-) create mode 100644 Sources/ComputeCxx/External/ExternalTrace.cpp create mode 100644 Sources/ComputeCxx/External/ExternalTrace.h create mode 100644 Sources/ComputeCxx/Layout/AGComparison-Private.h diff --git a/Sources/ComputeCxx/External/ExternalTrace.cpp b/Sources/ComputeCxx/External/ExternalTrace.cpp new file mode 100644 index 0000000..24a5380 --- /dev/null +++ b/Sources/ComputeCxx/External/ExternalTrace.cpp @@ -0,0 +1,312 @@ +#include "ExternalTrace.h" + +#include "Graph/Context.h" +#include "Graph/Graph.h" +#include "Layout/AGComparison-Private.h" + +void ExternalTrace::begin_trace(const AG::Graph &graph) { + auto cf_graph = graph.main_context()->to_cf(); + if (auto callback = _interface.begin_trace) { + callback(_trace, cf_graph); + } +} + +void ExternalTrace::end_trace(const AG::Graph &graph) { + auto cf_graph = graph.main_context()->to_cf(); + if (auto callback = _interface.end_trace) { + callback(_trace, cf_graph); + } +} + +void ExternalTrace::begin_update(const AG::Subgraph &subgraph, uint32_t options) { + auto cf_subgraph = subgraph.to_cf(); + if (auto callback = _interface.begin_update_subgraph) { + callback(_trace, cf_subgraph); + } +} + +void ExternalTrace::end_update(const AG::Subgraph &subgraph) { + auto cf_subgraph = subgraph.to_cf(); + if (auto callback = _interface.end_update_subgraph) { + callback(_trace, cf_subgraph); + } +} + +void ExternalTrace::begin_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, + uint32_t options) { + if (auto callback = _interface.begin_update_stack) { + callback(_trace, node); + } +} + +void ExternalTrace::end_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, + AG::Graph::UpdateStatus update_status) { + if (auto callback = _interface.end_update_stack) { + callback(_trace, update_status == AG::Graph::UpdateStatus::Changed); + } +} + +void ExternalTrace::begin_update(AG::data::ptr node) { + if (auto callback = _interface.begin_update_node) { + callback(_trace); + } +} + +void ExternalTrace::end_update(AG::data::ptr node, bool changed) { + if (auto callback = _interface.end_update_node) { + callback(_trace); + } +} + +void ExternalTrace::begin_update(const AG::Graph::Context &context) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.begin_update_context) { + callback(_trace, cf_context); + } +} + +void ExternalTrace::end_update(const AG::Graph::Context &context) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.end_update_context) { + callback(_trace, cf_context); + } +} + +void ExternalTrace::begin_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.begin_invalidation) { + callback(_trace, cf_context, attribute); + } +} + +void ExternalTrace::end_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.end_invalidation) { + callback(_trace, cf_context, attribute); + } +} + +void ExternalTrace::begin_modify(AG::data::ptr node) { + if (auto callback = _interface.begin_modify) { + callback(_trace); + } +} + +void ExternalTrace::end_modify(AG::data::ptr node) { + if (auto callback = _interface.end_modify) { + callback(_trace); + } +} + +void ExternalTrace::begin_event(AG::data::ptr node, uint32_t event_id) { + if (auto callback = _interface.begin_event) { + if (auto subgraph = AG::AttributeID(node).subgraph()) { + const char *event_name = subgraph->graph()->key_name(event_id); + callback(_trace, node, event_name); + } + } +} + +void ExternalTrace::end_event(AG::data::ptr node, uint32_t event_id) { + if (auto callback = _interface.end_event) { + if (auto subgraph = AG::AttributeID(node).subgraph()) { + const char *event_name = subgraph->graph()->key_name(event_id); + callback(_trace, node, event_name); + } + } +} + +void ExternalTrace::created(const AG::Graph::Context &context) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.created_context) { + callback(_trace, cf_context); + } +} + +void ExternalTrace::destroy(const AG::Graph::Context &context) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.destroy_context) { + callback(_trace, cf_context); + } +} + +void ExternalTrace::needs_update(const AG::Graph::Context &context) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.needs_update_context) { + callback(_trace, cf_context); + } +} + +void ExternalTrace::created(const AG::Subgraph &subgraph) { + auto cf_subgraph = subgraph.to_cf(); + if (auto callback = _interface.created_subgraph) { + callback(_trace, cf_subgraph); + } +} + +void ExternalTrace::invalidate(const AG::Subgraph &subgraph) { + auto cf_subgraph = subgraph.to_cf(); + if (auto callback = _interface.invalidate_subgraph) { + callback(_trace, cf_subgraph); + } +} + +void ExternalTrace::destroy(const AG::Subgraph &subgraph) { + // no method in binary +} + +void ExternalTrace::add_child(const AG::Subgraph &subgraph, const AG::Subgraph &child) { + auto cf_subgraph = subgraph.to_cf(); + auto cf_child = subgraph.to_cf(); + if (auto callback = _interface.add_child_subgraph) { + callback(_trace, cf_subgraph, cf_child); + } +} + +void ExternalTrace::remove_child(const AG::Subgraph &subgraph, const AG::Subgraph &child) { + auto cf_subgraph = subgraph.to_cf(); + auto cf_child = subgraph.to_cf(); + if (auto callback = _interface.remove_child_subgraph) { + callback(_trace, cf_subgraph, cf_child); + } +} + +void ExternalTrace::added(AG::data::ptr node) { + if (auto callback = _interface.added_node) { + callback(_trace); + } +} + +void ExternalTrace::add_edge(AG::data::ptr node, AG::AttributeID input, uint8_t input_edge_flags) { + if (auto callback = _interface.add_edge) { + callback(_trace); + } +} + +void ExternalTrace::remove_edge(AG::data::ptr node, uint32_t input_index) { + if (auto callback = _interface.remove_edge) { + if (AG::AttributeID(node).subgraph()) { + callback(_trace); + } + } +} + +void ExternalTrace::set_edge_pending(AG::data::ptr node, uint32_t input_index, bool pending) { + if (auto callback = _interface.set_edge_pending) { + if (AG::AttributeID(node).subgraph()) { + callback(_trace); + } + } +} + +void ExternalTrace::set_dirty(AG::data::ptr node, bool dirty) { + if (auto callback = _interface.set_dirty) { + callback(_trace); + } +} + +void ExternalTrace::set_pending(AG::data::ptr node, bool pending) { + if (auto callback = _interface.set_pending) { + callback(_trace); + } +} + +void ExternalTrace::set_value(AG::data::ptr node, const void *value) { + if (auto callback = _interface.set_value) { + callback(_trace); + } +} + +void ExternalTrace::mark_value(AG::data::ptr node) { + if (auto callback = _interface.mark_value) { + callback(_trace); + } +} + +void ExternalTrace::added(AG::data::ptr indirect_node) { + if (auto callback = _interface.added_indirect_node) { + callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind + } +} + +void ExternalTrace::set_source(AG::data::ptr indirect_node, AG::AttributeID source) { + if (auto callback = _interface.set_source) { + callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind + } +} + +void ExternalTrace::set_dependency(AG::data::ptr indirect_node, AG::AttributeID dependency) { + if (auto callback = _interface.set_dependency) { + callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind + } +} + +void ExternalTrace::mark_profile(const AG::Graph &graph, uint32_t event_id) { + if (auto callback = _interface.mark_profile) { + const char *event_name = graph.key_name(event_id); + callback(_trace, event_name); + } +} + +// void ExternalTrace::destroy(const AG::Subgraph &subgraph) { +// auto cf_subgraph = subgraph.to_cf(); +// if (auto callback = _interface.destroy_subgraph) { +// callback(_trace, cf_subgraph); +// } +// } + +void ExternalTrace::custom_event(const AG::Graph::Context &context, const char *event_name, const void *value, + const AG::swift::metadata &type) { + if (_interface.options != 0) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.custom_event) { + callback(_trace, cf_context, event_name, value, AGTypeID(&type)); + } + } +} + +void ExternalTrace::named_event(const AG::Graph::Context &context, uint32_t arg2, uint32_t num_args, + const uint64_t *event_args, CFDataRef data, uint32_t arg6) { + if (_interface.options > 1) { + auto cf_context = context.to_cf(); + if (auto callback = _interface.named_event) { + callback(_trace, cf_context, arg2, num_args, event_args, data, arg6); + } + } +} + +bool ExternalTrace::named_event_enabled(uint32_t event_id) { + if (_interface.options < 2) { + return false; + } + if (auto callback = _interface.named_event_enabled) { + return callback(_trace); + } + return _interface.named_event != nullptr; +} + +void ExternalTrace::set_deadline(uint64_t deadline) { + if (_interface.options > 2) { + if (auto callback = _interface.set_deadline) { + callback(_trace); + } + } +} + +void ExternalTrace::passed_deadline() { + if (_interface.options > 2) { + if (auto callback = _interface.passed_deadline) { + callback(_trace); + } + } +} + +void ExternalTrace::compare_failed(AG::data::ptr node, const void *lhs, const void *rhs, size_t range_offset, + size_t range_size, const AG::swift::metadata *_Nullable type) { + if (_interface.options > 3) { + AGComparisonStateStorage storage = {lhs, rhs, range_offset, range_size, AGTypeID(&type)}; + if (auto callback = _interface.compare_failed) { + callback(_trace, node, &storage); + } + } +} diff --git a/Sources/ComputeCxx/External/ExternalTrace.h b/Sources/ComputeCxx/External/ExternalTrace.h new file mode 100644 index 0000000..7906e0a --- /dev/null +++ b/Sources/ComputeCxx/External/ExternalTrace.h @@ -0,0 +1,146 @@ +#pragma once + +#include + +#include "Attribute/AGAttribute.h" +#include "Graph/Context.h" +#include "Layout/AGComparison.h" +#include "Subgraph/Subgraph.h" +#include "Swift/AGType.h" +#include "Trace/Trace.h" + +CF_ASSUME_NONNULL_BEGIN + +class ExternalTrace : public AG::Trace { + public: + struct Interface { + uint64_t options; + + void (*_Nullable begin_trace)(void *trace, AGGraphStorage *graph); + void (*_Nullable end_trace)(void *trace, AGGraphStorage *graph); + + void (*_Nullable begin_update_subgraph)(void *trace, AG::SubgraphObject *subgraph); + void (*_Nullable end_update_subgraph)(void *trace, AG::SubgraphObject *subgraph); + void (*_Nullable begin_update_stack)(void *trace, AGAttribute attribute); + void (*_Nullable end_update_stack)(void *trace, bool changed); + void (*_Nullable begin_update_node)(void *trace); + void (*_Nullable end_update_node)(void *trace); + void (*_Nullable begin_update_context)(void *trace, AGGraphStorage *graph); + void (*_Nullable end_update_context)(void *trace, AGGraphStorage *graph); + + void (*_Nullable begin_invalidation)(void *trace, AGGraphStorage *graph, AGAttribute attribute); + void (*_Nullable end_invalidation)(void *trace, AGGraphStorage *graph, AGAttribute attribute); + + void (*_Nullable begin_modify)(void *trace); + void (*_Nullable end_modify)(void *trace); + + void (*_Nullable begin_event)(void *trace, AGAttribute attribute, const char *event_name); + void (*_Nullable end_event)(void *trace, AGAttribute attribute, const char *event_name); + + void (*_Nullable created_context)(void *trace, AGGraphStorage *graph); + void (*_Nullable destroy_context)(void *trace, AGGraphStorage *graph); + void (*_Nullable needs_update_context)(void *trace, AGGraphStorage *graph); + + void (*_Nullable created_subgraph)(void *trace, AG::SubgraphObject *subgraph); + void (*_Nullable invalidate_subgraph)(void *trace, AG::SubgraphObject *subgraph); + void (*_Nullable add_child_subgraph)(void *trace, AG::SubgraphObject *subgraph, AG::SubgraphObject *child); + void (*_Nullable remove_child_subgraph)(void *trace, AG::SubgraphObject *subgraph, AG::SubgraphObject *child); + + void (*_Nullable added_node)(void *trace); + void (*_Nullable add_edge)(void *trace); + void (*_Nullable remove_edge)(void *trace); + void (*_Nullable set_edge_pending)(void *trace); + + void (*_Nullable set_dirty)(void *trace); + void (*_Nullable set_pending)(void *trace); + void (*_Nullable set_value)(void *trace); + void (*_Nullable mark_value)(void *trace); + + void (*_Nullable added_indirect_node)(void *trace, AGAttribute attribute); + void (*_Nullable set_source)(void *trace, AGAttribute attribute); + void (*_Nullable set_dependency)(void *trace, AGAttribute attribute); + + void (*_Nullable mark_profile)(void *trace, const char *event_name); + + void (*_Nullable custom_event)(void *trace, AGGraphStorage *graph, const char *event_name, const void *value, + AGTypeID type); + void (*_Nullable named_event)(void *trace, AGGraphStorage *graph, uint32_t arg2, uint32_t num_args, + const uint64_t *event_args, CFDataRef data, uint32_t arg6); + bool (*_Nullable named_event_enabled)(void *trace); + void (*_Nullable set_deadline)(void *trace); + void (*_Nullable passed_deadline)(void *trace); + + void (*_Nullable compare_failed)(void *trace, AGAttribute attribute, AGComparisonState comparison_state); + }; + + private: + Interface _interface; + void *_trace; + + public: + void begin_trace(const AG::Graph &graph); + void end_trace(const AG::Graph &graph); + + void begin_update(const AG::Subgraph &subgraph, uint32_t options); + void end_update(const AG::Subgraph &subgraph); + + void begin_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, uint32_t options); + void end_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, + AG::Graph::UpdateStatus update_status); + void begin_update(AG::data::ptr node); + void end_update(AG::data::ptr node, bool changed); + void begin_update(const AG::Graph::Context &context); + void end_update(const AG::Graph::Context &context); + + void begin_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute); + void end_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute); + + void begin_modify(AG::data::ptr node); + void end_modify(AG::data::ptr node); + + void begin_event(AG::data::ptr node, uint32_t event_id); + void end_event(AG::data::ptr node, uint32_t event_id); + + void created(const AG::Graph::Context &context); + void destroy(const AG::Graph::Context &context); + void needs_update(const AG::Graph::Context &context); + + void created(const AG::Subgraph &subgraph); + void invalidate(const AG::Subgraph &subgraph); + void destroy(const AG::Subgraph &subgraph); + + void add_child(const AG::Subgraph &subgraph, const AG::Subgraph &child); + void remove_child(const AG::Subgraph &subgraph, const AG::Subgraph &child); + + void added(AG::data::ptr node); + + void add_edge(AG::data::ptr node, AG::AttributeID input, uint8_t input_edge_flags); + void remove_edge(AG::data::ptr node, uint32_t input_index); + void set_edge_pending(AG::data::ptr node, uint32_t input_index, bool pending); + + void set_dirty(AG::data::ptr node, bool dirty); + void set_pending(AG::data::ptr node, bool pending); + void set_value(AG::data::ptr node, const void *value); + void mark_value(AG::data::ptr node); + + void added(AG::data::ptr indirect_node); + + void set_source(AG::data::ptr indirect_node, AG::AttributeID source); + void set_dependency(AG::data::ptr indirect_node, AG::AttributeID dependency); + + void mark_profile(const AG::Graph &graph, uint32_t event_id); // TODO: change options to event_id everywhere + + void custom_event(const AG::Graph::Context &context, const char *event_name, const void *value, + const AG::swift::metadata &type); + void named_event(const AG::Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, + CFDataRef data, uint32_t arg6); // TODO: what are these args? + bool named_event_enabled(uint32_t event_id); + + void set_deadline(uint64_t deadline); + void passed_deadline(); + + void compare_failed(AG::data::ptr node, const void *lhs, const void *rhs, size_t range_offset, + size_t range_size, const AG::swift::metadata *_Nullable type); +}; + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h index 0deab4b..d97c302 100644 --- a/Sources/ComputeCxx/Graph/Context.h +++ b/Sources/ComputeCxx/Graph/Context.h @@ -30,7 +30,7 @@ class Graph::Context { Context(Graph *graph); ~Context(); - AGGraphStorage *to_cf() { return reinterpret_cast((char *)this - sizeof(CFRuntimeBase)); }; + AGGraphStorage *to_cf() const { return reinterpret_cast((char *)this - sizeof(CFRuntimeBase)); }; static Context *from_cf(AGGraphStorage *storage); Graph &graph() const { return *_graph; }; diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 9dca627..3b29f4d 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -184,7 +184,7 @@ bool Graph::is_context_updating(uint64_t context_id) { } } -Graph::Context *Graph::main_context() { +Graph::Context *Graph::main_context() const { struct Info { Context *context; uint64_t context_id; diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 5771843..10f6214 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -144,7 +144,7 @@ class Graph { // MARK: Context bool is_context_updating(uint64_t context_id); - Context *_Nullable main_context(); + Context *_Nullable main_context() const; // MARK: Subgraphs diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index e2180af..792b704 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -703,7 +703,7 @@ void Graph::TraceRecorder::end_modify(data::ptr node) { _encoder.end_length_delimited(); } -void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event) { +void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event_id) { if (_tracing_flags & TracingFlags::Custom) { return; } @@ -718,15 +718,15 @@ void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event) { _encoder.encode_varint(0x18); _encoder.encode_varint(node); } - if (event) { + if (event_id) { _encoder.encode_varint(0x20); - _encoder.encode_varint(event); + _encoder.encode_varint(event_id); } _encoder.end_length_delimited(); } -void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event) { +void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event_id) { if (_tracing_flags & TracingFlags::Custom) { return; } @@ -741,9 +741,9 @@ void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event) { _encoder.encode_varint(0x18); _encoder.encode_varint(node); } - if (event) { + if (event_id) { _encoder.encode_varint(0x20); - _encoder.encode_varint(event); + _encoder.encode_varint(event_id); } _encoder.end_length_delimited(); @@ -1286,7 +1286,7 @@ void Graph::TraceRecorder::custom_event(const Graph::Context &context, const cha _encoder.end_length_delimited(); } -bool Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, +void Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6) { if (!named_event_enabled(arg2)) { return; diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index 2c3e15d..04e8daa 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -80,8 +80,8 @@ class Graph::TraceRecorder : public Encoder::Delegate, public Trace { void begin_modify(data::ptr node); void end_modify(data::ptr node); - void begin_event(data::ptr node, uint32_t event); - void end_event(data::ptr node, uint32_t event); + void begin_event(data::ptr node, uint32_t event_id); + void end_event(data::ptr node, uint32_t event_id); void created(const Graph::Context &context); void destroy(const Graph::Context &context); @@ -118,7 +118,7 @@ class Graph::TraceRecorder : public Encoder::Delegate, public Trace { void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type); - bool named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, + void named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6); bool named_event_enabled(uint32_t event_id); diff --git a/Sources/ComputeCxx/Layout/AGComparison-Private.h b/Sources/ComputeCxx/Layout/AGComparison-Private.h new file mode 100644 index 0000000..f5e074c --- /dev/null +++ b/Sources/ComputeCxx/Layout/AGComparison-Private.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "AGComparison.h" + +CF_ASSUME_NONNULL_BEGIN + +typedef struct AGComparisonStateStorage { + const void *destination; + const void *source; + AGFieldRange field_range; + AGTypeID field_type; +} AGComparisonStateStorage; + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Layout/AGComparison.cpp b/Sources/ComputeCxx/Layout/AGComparison.cpp index 19bf20a..5201e8b 100644 --- a/Sources/ComputeCxx/Layout/AGComparison.cpp +++ b/Sources/ComputeCxx/Layout/AGComparison.cpp @@ -1,16 +1,10 @@ #include "AGComparison.h" +#include "AGComparison-Private.h" #include "Layout/LayoutDescriptor.h" #include "Swift/ContextDescriptor.h" #include "Swift/Metadata.h" -typedef struct AGComparisonStateStorage { - const void *destination; - const void *source; - AGFieldRange field_range; - AGTypeID field_type; -} AGComparisonStateStorage; - const void *AGComparisonStateGetDestination(AGComparisonState state) { return state->destination; } const void *AGComparisonStateGetSource(AGComparisonState state) { return state->source; } diff --git a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp index 69531ad..3ebea13 100644 --- a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp +++ b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp @@ -6,8 +6,11 @@ #include "Builder.h" #include "Compare.h" #include "Controls.h" +#include "Graph/Graph.h" +#include "Graph/UpdateStack.h" #include "Swift/Metadata.h" #include "Time/Time.h" +#include "Trace/Trace.h" #include "Utilities/HashTable.h" namespace AG { @@ -433,7 +436,13 @@ bool compare_bytes_top_level(const unsigned char *lhs, const unsigned char *rhs, size_t failure_location = 0; bool result = compare_bytes(lhs, rhs, size, &failure_location); if (options.report_failures() && !result) { - // TODO: tracing + for (auto update = Graph::current_update(); update != nullptr; update = update.get()->previous()) { + auto graph = update.get()->graph(); + auto attribute = update.get()->frames().back().attribute; + graph->foreach_trace([&attribute, &lhs, &rhs, &failure_location](Trace &trace) { + trace.compare_failed(attribute, lhs, rhs, failure_location, 1, nullptr); + }); + } } return result; } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 372c8f4..7325290 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -74,7 +74,7 @@ Subgraph::~Subgraph() { Subgraph *Subgraph::from_cf(SubgraphObject *object) { return object->subgraph(); } -SubgraphObject *Subgraph::to_cf() { return _object; } +SubgraphObject *Subgraph::to_cf() const { return _object; } void Subgraph::clear_object() { auto object = _object; diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 4ce03a6..23c6ee0 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -103,7 +103,7 @@ class Subgraph : public data::zone { // MARK: CoreFoundation static Subgraph *from_cf(SubgraphObject *object); - SubgraphObject *to_cf(); + SubgraphObject *to_cf() const; void clear_object(); // MARK: Graph diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index aa363dd..4c2d914 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -4,6 +4,8 @@ #include "Graph/Graph.h" +CF_ASSUME_NONNULL_BEGIN + namespace AG { class Node; @@ -44,8 +46,8 @@ class Trace { virtual void begin_modify(data::ptr node){}; virtual void end_modify(data::ptr node){}; - virtual void begin_event(data::ptr node, uint32_t event){}; - virtual void end_event(data::ptr node, uint32_t event){}; + virtual void begin_event(data::ptr node, uint32_t event_id){}; + virtual void end_event(data::ptr node, uint32_t event_id){}; virtual void created(const Graph::Context &context){}; virtual void destroy(const Graph::Context &context){}; @@ -82,12 +84,14 @@ class Trace { virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type){}; - virtual bool named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, + virtual void named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6){}; virtual bool named_event_enabled(uint32_t event_id) { return false; }; - virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t lhs_offset, - size_t rhs_offset, const swift::metadata &type){}; + virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t range_offset, + size_t range_size, const swift::metadata *_Nullable type){}; }; } // namespace AG + +CF_ASSUME_NONNULL_END From 2a831ff64d55a8c41e0dc5283dce301bec408270 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 19:58:07 +1100 Subject: [PATCH 39/74] Add util::objc_ptr class --- Sources/ComputeCxx/Debug/DebugServer.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/ComputeCxx/Debug/DebugServer.h b/Sources/ComputeCxx/Debug/DebugServer.h index 4cf7269..1cc924a 100644 --- a/Sources/ComputeCxx/Debug/DebugServer.h +++ b/Sources/ComputeCxx/Debug/DebugServer.h @@ -6,6 +6,7 @@ #include #include "Containers/Vector.h" +#include "Utilities/ObjCPointer.h" CF_ASSUME_NONNULL_BEGIN @@ -23,7 +24,7 @@ class DebugServer { private: DebugServer *_server; int _socket; - dispatch_source_t _dispatch_source; + util::objc_ptr _dispatch_source; public: Connection(DebugServer *server, int socket); @@ -40,7 +41,7 @@ class DebugServer { uint16_t _port; uint16_t _padding; uint32_t _token; - dispatch_source_t _dispatch_source; + util::objc_ptr _dispatch_source; vector, 0, uint64_t> _clients; static DebugServer *_shared_server; From 025c3f5b95290b0ce15abe8c4a1d8f1ace0732b4 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 20:13:37 +1100 Subject: [PATCH 40/74] Rename flags --- Sources/ComputeCxx/Attribute/Node/Node.h | 8 +++--- Sources/ComputeCxx/Graph/Graph+Description.mm | 2 +- Sources/ComputeCxx/Graph/Graph.cpp | 20 ++++++------- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 28 +++++++++---------- Sources/ComputeCxx/Subgraph/Subgraph.h | 2 +- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 6e9213b..835561f 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -18,7 +18,7 @@ class Graph; class NodeFlags { public: - enum Flags3 : uint8_t {}; // TODO: subgraph_flags + enum SubgraphFlags : uint8_t {}; enum Flags4 : uint8_t { HasIndirectSelf = 1 << 0, // 0x01 HasIndirectValue = 1 << 1, // 0x02 @@ -33,7 +33,7 @@ class NodeFlags { private: uint16_t _relative_offset; - uint8_t _value3; + uint8_t _subgraph_flags; uint8_t _value4; public: @@ -43,8 +43,8 @@ class NodeFlags { void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; // Flags 3 - uint8_t value3() const { return _value3; }; - void set_value3(uint8_t value3) { _value3 = value3; }; + uint8_t subgraph_flags() const { return _subgraph_flags; }; + void set_subgraph_flags(uint8_t subgraph_flags) { _subgraph_flags = subgraph_flags; }; // Flags 4 bool has_indirect_self() const { return _value4 & Flags4::HasIndirectSelf; } diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 0823387..e515b1e 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -554,7 +554,7 @@ int trap_cycles() { } if (attribute.is_direct()) { - uint8_t subgraph_flags = attribute.to_node().flags().value3(); + uint8_t subgraph_flags = attribute.to_node().flags().subgraph_flags(); auto found_node_index = node_indices_by_id.find(attribute.to_node_ptr()); if (found_node_index != node_indices_by_id.end()) { [nodes addObject:[NSNumber numberWithUnsignedLong:found_node_index->second]]; diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 3b29f4d..7452302 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -498,7 +498,7 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void value_set_internal(node, *node.get(), value_source, type.value_metadata()); } else { node->set_state(node->state().with_dirty(true).with_pending(true)); - subgraph.add_dirty_flags(node->flags().value3()); + subgraph.add_dirty_flags(node->flags().subgraph_flags()); } subgraph.add_node(node); @@ -1108,9 +1108,9 @@ void Graph::value_mark(data::ptr node) { foreach_trace([&node](Trace &trace) { trace.set_pending(node, true); }); node->set_state(node->state().with_pending(true)); } - if (node->flags().value3()) { + if (node->flags().subgraph_flags()) { Subgraph *subgraph = AttributeID(node).subgraph(); - subgraph->add_dirty_flags(node->flags().value3()); + subgraph->add_dirty_flags(node->flags().subgraph_flags()); } } @@ -1145,7 +1145,7 @@ void Graph::value_mark_all() { AttributeType &type = *_types[node.type_id()]; if (!type.use_graph_as_initial_value()) { node.set_state(node.state().with_dirty(true).with_pending(true)); - subgraph->add_dirty_flags(node.flags().value3()); + subgraph->add_dirty_flags(node.flags().subgraph_flags()); } for (auto input_edge : node.inputs()) { input_edge.set_changed(true); @@ -1212,7 +1212,7 @@ void Graph::propagate_dirty(AttributeID attribute) { output_node.set_state(output_node.state().with_dirty(true)); if (auto subgraph = output.subgraph()) { - subgraph->add_dirty_flags(output_node.flags().value3()); + subgraph->add_dirty_flags(output_node.flags().subgraph_flags()); } dirty_outputs = output_node.outputs(); @@ -1820,10 +1820,10 @@ void Graph::mark_pending(data::ptr node_ptr, Node *node) { foreach_trace([&node_ptr](Trace &trace) { trace.set_dirty(node_ptr, true); }); node->set_state(node->state().with_dirty(true)); - uint8_t dirty_flags = node->flags().value3(); + uint8_t subgraph_flags = node->flags().subgraph_flags(); Subgraph *subgraph = AttributeID(node_ptr).subgraph(); - if (dirty_flags && subgraph != nullptr) { - subgraph->add_dirty_flags(dirty_flags); + if (subgraph_flags && subgraph != nullptr) { + subgraph->add_dirty_flags(subgraph_flags); } propagate_dirty(node_ptr); @@ -1965,9 +1965,9 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { encoder.encode_varint(0x38); encoder.encode_varint(true); } - if (node.flags().value3()) { + if (node.flags().subgraph_flags()) { encoder.encode_varint(0x40); - encoder.encode_varint(node.flags().value3()); + encoder.encode_varint(node.flags().subgraph_flags()); } if (node.state().is_main_thread()) { encoder.encode_varint(0x48); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 7325290..50e8753 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -385,7 +385,7 @@ void Subgraph::foreach_ancestor(Callable body) { #pragma mark - Attributes void Subgraph::add_node(data::ptr node) { - node->flags().set_value3(0); + node->flags().set_subgraph_flags(0); insert_attribute(AttributeID(node), true); if (_tree_root) { @@ -409,13 +409,13 @@ void Subgraph::insert_attribute(AttributeID attribute, bool flag) { AttributeID source = AttributeID::make_nil(); if (flag) { - if (!attribute.is_direct() || attribute.to_node().flags().value3() == 0) { + if (!attribute.is_direct() || attribute.to_node().flags().subgraph_flags() == 0) { uint16_t relative_offset = attribute.page_ptr()->relative_offset_1; while (relative_offset) { AttributeID next_attribute = AttributeID(attribute.page_ptr().offset() + relative_offset); if (next_attribute.is_direct()) { auto node = next_attribute.to_node(); - if (node.flags().value3() == 0) { + if (node.flags().subgraph_flags() == 0) { break; } source = next_attribute; @@ -571,10 +571,10 @@ void Subgraph::update(uint8_t flags) { relative_offset = attribute.to_node().flags().relative_offset(); auto node = attribute.to_node(); if (flags) { - if (node.flags().value3() == 0) { + if (node.flags().subgraph_flags() == 0) { break; } - if ((node.flags().value3() & flags) == 0) { + if ((node.flags().subgraph_flags() & flags) == 0) { continue; } } @@ -672,10 +672,10 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { relative_offset = attribute.to_node().flags().relative_offset(); if (!flags.is_null()) { - if (attribute.to_node().flags().value3() == 0) { + if (attribute.to_node().flags().subgraph_flags() == 0) { break; } - if (flags.value3 & (attribute.to_node().flags().value3() == 0)) { + if (flags.value3 & (attribute.to_node().flags().subgraph_flags() == 0)) { continue; } } @@ -845,16 +845,16 @@ uint32_t Subgraph::tree_subgraph_child(data::ptr tree_elemen // MARK: - Flags -void Subgraph::set_flags(data::ptr node, NodeFlags::Flags3 flags) { - if (node->flags().value3() == flags) { +void Subgraph::set_flags(data::ptr node, NodeFlags::SubgraphFlags flags) { + if (node->flags().subgraph_flags() == flags) { return; } - if (node->flags().value3() == 0 || flags == 0) { + if (node->flags().subgraph_flags() == 0 || flags == 0) { unlink_attribute(node); - node->flags().set_value3(flags); + node->flags().set_subgraph_flags(flags); insert_attribute(node, true); } else { - node->flags().set_value3(flags); + node->flags().set_subgraph_flags(flags); } add_flags(flags); @@ -1215,8 +1215,8 @@ void Subgraph::print(uint32_t indent_level) { if (attribute.is_direct()) { fprintf(stdout, "%s%u", first ? "" : " ", attribute.value()); - if (attribute.to_node().flags().value3()) { - fprintf(stdout, "(%u)", attribute.to_node().flags().value3()); + if (attribute.to_node().flags().subgraph_flags()) { + fprintf(stdout, "(%u)", attribute.to_node().flags().subgraph_flags()); } first = false; } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 23c6ee0..f03e6c4 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -172,7 +172,7 @@ class Subgraph : public data::zone { // flags 1 and 3 are the values themselvs // flags 3 and 4 are the dirty subset of 1 and 2 - void set_flags(data::ptr node, NodeFlags::Flags3 flags3); + void set_flags(data::ptr node, NodeFlags::SubgraphFlags flags3); void add_flags(uint8_t flags); void add_dirty_flags(uint8_t dirty_flags); From cfaba659194fa09fb29352b99aabd263e82976b9 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 20:19:48 +1100 Subject: [PATCH 41/74] Use actual notification names --- Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm index e8d0d57..df79302 100644 --- a/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm +++ b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm @@ -27,11 +27,11 @@ void AGAppObserverStartObserving() { if (NSClassFromString(@"UIApplication")) { [[NSNotificationCenter defaultCenter] addObserver:[AGAppObserver class] selector:@selector(foreground:) - name:@"UIApplicationWillEnterForeground" // TODO get real name + name:@"UIApplicationWillEnterForegroundNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:[AGAppObserver class] selector:@selector(background:) - name:@"UIApplicationDidEnterBackground" // TODO get real name + name:@"UIApplicationDidEnterBackgroundNotification" object:nil]; } } From 9451f1c4408b94b853a8da4a31f56831ae365030 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 20:20:01 +1100 Subject: [PATCH 42/74] Remove comment --- Sources/ComputeCxx/External/ExternalTrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/External/ExternalTrace.h b/Sources/ComputeCxx/External/ExternalTrace.h index 7906e0a..fa70cd0 100644 --- a/Sources/ComputeCxx/External/ExternalTrace.h +++ b/Sources/ComputeCxx/External/ExternalTrace.h @@ -128,7 +128,7 @@ class ExternalTrace : public AG::Trace { void set_source(AG::data::ptr indirect_node, AG::AttributeID source); void set_dependency(AG::data::ptr indirect_node, AG::AttributeID dependency); - void mark_profile(const AG::Graph &graph, uint32_t event_id); // TODO: change options to event_id everywhere + void mark_profile(const AG::Graph &graph, uint32_t event_id); void custom_event(const AG::Graph::Context &context, const char *event_name, const void *value, const AG::swift::metadata &type); From 9e380dc6f087297144d46c12db88577b96e098cd Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 20:29:34 +1100 Subject: [PATCH 43/74] Rename profile fields --- Sources/ComputeCxx/Graph/Graph+Description.mm | 33 ++++++++-------- Sources/ComputeCxx/Graph/Graph.cpp | 2 +- .../Graph/Profile/ProfileData+JSON.mm | 22 +++++------ .../ComputeCxx/Graph/Profile/ProfileData.cpp | 38 +++++++++---------- .../ComputeCxx/Graph/Profile/ProfileData.h | 16 ++++---- 5 files changed, 53 insertions(+), 58 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index e515b1e..8065787 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -309,9 +309,8 @@ int trap_cycles() { auto profile_data = graph->_profile_data.get(); if (profile_data) { - auto found = - profile_data->current_category().items_by_attribute().find(attribute.to_node_ptr()); - if (found != profile_data->current_category().items_by_attribute().end()) { + auto found = profile_data->all_events().items_by_attribute().find(attribute.to_node_ptr()); + if (found != profile_data->all_events().items_by_attribute().end()) { CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); if (item_json) { node_dict[@"profile"] = (__bridge NSDictionary *)item_json; @@ -397,8 +396,8 @@ int trap_cycles() { continue; } - auto removed_item = graph->_profile_data->current_category().removed_items_by_type_id().find(type_id); - if (removed_item != graph->_profile_data->current_category().removed_items_by_type_id().end()) { + auto removed_item = graph->_profile_data->all_events().removed_items_by_type_id().find(type_id); + if (removed_item != graph->_profile_data->all_events().removed_items_by_type_id().end()) { if (!type_indices_by_id.contains(type_id)) { auto index = type_ids.size(); type_ids.push_back(type_id); @@ -437,8 +436,8 @@ int trap_cycles() { auto profile_data = graph->_profile_data.get(); if (profile_data) { - auto found = profile_data->current_category().removed_items_by_type_id().find(type_id); - if (found != profile_data->current_category().removed_items_by_type_id().end()) { + auto found = profile_data->all_events().removed_items_by_type_id().find(type_id); + if (found != profile_data->all_events().removed_items_by_type_id().end()) { CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); if (item_json) { dict[@"profile"] = (__bridge NSDictionary *)item_json; @@ -722,8 +721,7 @@ int trap_cycles() { graph_dict[@"transaction_count"] = [NSNumber numberWithUnsignedLong:graph->transaction_count()]; if (auto profile_data = graph->_profile_data.get()) { - NSDictionary *json = - (__bridge NSDictionary *)profile_data->json_data(profile_data->current_category(), *graph); + NSDictionary *json = (__bridge NSDictionary *)profile_data->json_data(profile_data->all_events(), *graph); if (json) { [graph_dict addEntriesFromDictionary:json]; } @@ -826,21 +824,20 @@ int trap_cycles() { double duration_fraction = 0.0; if (auto profile_data = _profile_data.get()) { - auto items_by_attribute = profile_data->current_category().items_by_attribute(); + auto items_by_attribute = profile_data->all_events().items_by_attribute(); auto found_item = items_by_attribute.find(attribute.to_node_ptr()); if (found_item != items_by_attribute.end()) { auto item = found_item->second; - if (item.data().count) { - uint64_t count = item.data().count; // count - uint64_t duration = item.data().duration; - uint64_t total_duration = - profile_data->current_category().data().duration; // total duration + if (item.data().update_count) { + uint64_t update_count = item.data().update_count; + uint64_t update_total = item.data().update_total; + uint64_t all_update_total = profile_data->all_events().data().update_total; double average_update_time = - absolute_time_to_seconds((double)duration / (double)count); + absolute_time_to_seconds((double)update_total / (double)update_count); duration_fraction = - ((double)duration / (double)total_duration) * 100.0; // duration fraction + ((double)update_total / (double)all_update_total) * 100.0; // duration fraction [result appendFormat:@"\\n%.2g%%: %g × %.2fμs", duration_fraction, - (double)item.data().count, average_update_time * 1000000.0]; + (double)update_count, average_update_time * 1000000.0]; } } } diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 7452302..6cd33ec 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -2340,7 +2340,7 @@ void Graph::add_profile_update(data::ptr node, uint64_t duration, bool cha if (duration > _profile_data->precision()) { effective_duration = duration - _profile_data->precision(); } - _profile_data->current_category().add_update(node, effective_duration, changed); + _profile_data->all_events().add_update(node, effective_duration, changed); _profile_data->set_has_unmarked_categories(true); } } diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm index 49c2691..a90558c 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm @@ -9,45 +9,43 @@ namespace AG { CFDictionaryRef Graph::ProfileData::json_data(const Data &data) { - // TODO: figure out real keys NSMutableDictionary *dict = nil; - if (data.count) { + if (data.update_count) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"count1"] = [NSNumber numberWithUnsignedLong:data.count]; + dict[@"update_count"] = [NSNumber numberWithUnsignedLong:data.update_count]; } - if (data.changed_count) { + if (data.change_count) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"count2"] = [NSNumber numberWithUnsignedLong:data.changed_count]; + dict[@"change_count"] = [NSNumber numberWithUnsignedLong:data.change_count]; } - if (data.duration) { + if (data.update_total) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"time1"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.duration)]; + dict[@"update_total"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.update_total)]; } - if (data.changed_duration) { + if (data.changed_total) { if (!dict) { dict = [NSMutableDictionary dictionary]; } - dict[@"time2"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.changed_duration)]; + dict[@"changed_total"] = [NSNumber numberWithDouble:absolute_time_to_seconds(data.changed_total)]; } return (__bridge CFDictionaryRef)dict; } CFDictionaryRef Graph::ProfileData::json_data(const Item &item, const Graph &graph) { - // TODO: figure out real keys NSMutableDictionary *json = (__bridge NSMutableDictionary *)json_data(item.data()); if (!item.marks().empty()) { NSMutableArray *array = [NSMutableArray array]; for (auto mark : item.marks()) { NSMutableDictionary *mark_json = (__bridge NSMutableDictionary *)json_data(mark.data); if (mark_json) { - mark_json[@"event"] = [NSString stringWithUTF8String:graph.key_name(mark.event_id)]; - mark_json[@"time"] = [NSNumber numberWithDouble:absolute_time_to_seconds(mark.time)]; + mark_json[@"name"] = [NSString stringWithUTF8String:graph.key_name(mark.event_id)]; + mark_json[@"timestamp"] = [NSNumber numberWithDouble:absolute_time_to_seconds(mark.timestamp)]; [array addObject:mark_json]; } } diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp b/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp index be729fd..38e7e8f 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData.cpp @@ -9,20 +9,20 @@ namespace AG { #pragma mark - ProfileData::Item void Graph::ProfileData::Item::operator+=(const Item &other) { - _data.count += other._data.count; - _data.changed_count += other._data.changed_count; - _data.duration += other._data.duration; - _data.changed_duration += other._data.changed_duration; + _data.update_count += other._data.update_count; + _data.change_count += other._data.change_count; + _data.update_total += other._data.update_total; + _data.changed_total += other._data.changed_total; Mark *iter = _marks.begin(); for (auto other_mark : other._marks) { bool merged = false; - while (iter != _marks.end() && iter->time <= other_mark.time) { + while (iter != _marks.end() && iter->timestamp <= other_mark.timestamp) { if (iter == &other_mark) { - iter->data.count += other_mark.data.count; - iter->data.changed_count += other_mark.data.changed_count; - iter->data.duration += other_mark.data.duration; - iter->data.changed_duration += other_mark.data.changed_duration; + iter->data.update_count += other_mark.data.update_count; + iter->data.change_count += other_mark.data.change_count; + iter->data.update_total += other_mark.data.update_total; + iter->data.changed_total += other_mark.data.changed_total; merged = true; break; } @@ -38,7 +38,7 @@ void Graph::ProfileData::Item::operator+=(const Item &other) { } void Graph::ProfileData::Item::mark(uint32_t event_id, uint64_t time) { - if (_data.count) { + if (_data.update_count) { _marks.push_back({ event_id, time, @@ -51,20 +51,20 @@ void Graph::ProfileData::Item::mark(uint32_t event_id, uint64_t time) { #pragma mark - ProfileData::Category void Graph::ProfileData::Category::add_update(data::ptr node, uint64_t duration, bool changed) { - data().count += 1; - data().duration += duration; + data().update_count += 1; + data().update_total += duration; if (changed) { - data().changed_count += 1; - data().changed_duration += duration; + data().change_count += 1; + data().changed_total += duration; } Item &item = _items_by_attribute.try_emplace(node).first->second; - item.data().count += 1; - item.data().duration += duration; + item.data().update_count += 1; + item.data().update_total += duration; if (changed) { - item.data().changed_count += 1; - item.data().changed_duration += duration; + item.data().change_count += 1; + item.data().changed_total += duration; } } @@ -93,7 +93,7 @@ Graph::ProfileData::ProfileData(Graph *graph) { } void Graph::ProfileData::remove_node(data::ptr node, uint32_t type_id) { - _current_category.remove_node(node, type_id); + _all_events.remove_node(node, type_id); for (auto &entry : _categories) { entry.second.remove_node(node, type_id); } diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData.h b/Sources/ComputeCxx/Graph/Profile/ProfileData.h index 202b796..1e60a09 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData.h +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData.h @@ -13,15 +13,15 @@ namespace AG { class Graph::ProfileData { public: struct Data { - uint64_t count; - uint64_t changed_count; - uint64_t duration; - uint64_t changed_duration; + uint64_t update_count; + uint64_t change_count; + uint64_t update_total; + uint64_t changed_total; }; struct Mark { uint32_t event_id; - uint64_t time; + uint64_t timestamp; Data data; }; @@ -64,7 +64,7 @@ class Graph::ProfileData { private: uint64_t _precision; - Category _current_category; // _all + Category _all_events; std::unordered_map _categories; bool _has_unmarked_categories; @@ -74,7 +74,7 @@ class Graph::ProfileData { uint64_t precision() const { return _precision; }; - Category ¤t_category() { return _current_category; }; + Category &all_events() { return _all_events; }; std::unordered_map &categories() { return _categories; }; bool has_unmarked_categories() const { return _has_unmarked_categories; }; @@ -87,7 +87,7 @@ class Graph::ProfileData { if (time == 0) { time = mach_absolute_time(); } - _current_category.mark(event_id, time); + _all_events.mark(event_id, time); for (auto &entry : _categories) { entry.second.mark(event_id, time); // TODO: does this modify in place or a copy? } From 6a4fa345232ae1d1391fbbb69f11c30731adc654 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 21:00:09 +1100 Subject: [PATCH 44/74] Move AGValueState --- Sources/ComputeCxx/Graph/AGValue.h | 13 +++++++++++++ Sources/ComputeCxx/Graph/Graph.h | 3 +-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 Sources/ComputeCxx/Graph/AGValue.h diff --git a/Sources/ComputeCxx/Graph/AGValue.h b/Sources/ComputeCxx/Graph/AGValue.h new file mode 100644 index 0000000..ef15e5e --- /dev/null +++ b/Sources/ComputeCxx/Graph/AGValue.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +typedef uint8_t AGValueState; + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 10f6214..1c5dc7a 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -7,6 +7,7 @@ #include #include +#include "AGValue.h" #include "Attribute/AttributeID.h" #include "Attribute/Node/Edge.h" #include "Closure/ClosureFunction.h" @@ -16,8 +17,6 @@ CF_ASSUME_NONNULL_BEGIN -typedef uint8_t AGValueState; // TODO: move - namespace AG { namespace swift { From 86fda99a34f80ae55b274e92728ca01d305f18c6 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 26 Feb 2025 21:00:54 +1100 Subject: [PATCH 45/74] Implement remainder of Graph::value_set_internal --- Sources/ComputeCxx/Graph/Graph.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 6cd33ec..18208d2 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1050,11 +1050,8 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void return false; } - if (_traces.empty()) { - // TODO: finish - } else { - mark_changed(AttributeID(node_ptr), &type, value_dest, value_source, 0); - } + // TODO: make this inline? + mark_changed(node_ptr, &type, value_dest, value_source); value_type.vw_assignWithCopy((swift::opaque_value *)value_dest, (swift::opaque_value *)value_source); } else { @@ -1734,6 +1731,7 @@ void Graph::mark_changed(data::ptr node, AttributeType *_Nullable type, co } } input.set_changed(true); + break; } output_index += 1; } From 9c80c8f27a0b795cce56282d9c654b99aa0543c5 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 27 Feb 2025 11:15:56 +1100 Subject: [PATCH 46/74] Add AGType enum functions --- .../ComputeCxx/Layout/LayoutDescriptor.cpp | 2 +- Sources/ComputeCxx/Swift/AGType.cpp | 120 +++++++++++++++++- Sources/ComputeCxx/Swift/AGType.h | 8 +- 3 files changed, 125 insertions(+), 5 deletions(-) diff --git a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp index 3ebea13..945b226 100644 --- a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp +++ b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp @@ -1216,7 +1216,7 @@ bool Builder::visit_case(const swift::metadata &type, const swift::field_record add_field(sizeof(void *)); result = true; } else { - auto mangled_name = field.FieldName.get(); + auto mangled_name = field.FieldName.get(); // TODO: check this is FieldName of MangledTypeName auto field_type = mangled_name != nullptr ? type.mangled_type_name_ref(mangled_name, false, nullptr) : nullptr; if (field_type == nullptr) { // bail out if we can't get a type for the enum case payload diff --git a/Sources/ComputeCxx/Swift/AGType.cpp b/Sources/ComputeCxx/Swift/AGType.cpp index 2fedcfa..90b0a82 100644 --- a/Sources/ComputeCxx/Swift/AGType.cpp +++ b/Sources/ComputeCxx/Swift/AGType.cpp @@ -135,7 +135,7 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, bool visit_case(const AG::swift::metadata &type, const AG::swift::field_record &field, uint32_t index) const override { auto mangled_name = field.MangledTypeName.get(); - auto field_type = type.mangled_type_name_ref(mangled_name, true, nullptr); + auto field_type = type.mangled_type_name_ref(mangled_name, true, nullptr); // TODO: _cached or not? if (!field_type) { return unknown_result(); } @@ -174,3 +174,121 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, return false; } } + +void AGTypeApplyEnumData(AGTypeID typeID, void *value, + void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)) { + auto type = reinterpret_cast(typeID); + auto value_witness = type->getValueWitnesses(); + if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { + AG::precondition_failure("not an enum type: %s", type->name(false)); + } + + auto enum_value_witness = value_witness->_asEVWT(); + uint32_t tag = enum_value_witness->getEnumTag(reinterpret_cast(value), type); + + if (auto descriptor = type->nominal_descriptor()) { + auto enum_descriptor = reinterpret_cast(descriptor); + if (auto fields = enum_descriptor->Fields.get()) { + auto num_payload_cases = enum_descriptor->getNumPayloadCases(); + if (tag < num_payload_cases) { + auto &field = fields->getFields()[tag]; + if (auto mangled_name = field.MangledTypeName.get()) { + auto field_type = type->mangled_type_name_ref_cached(mangled_name, nullptr); + if (field_type) { + enum_value_witness->destructiveProjectEnumData( + reinterpret_cast(value), type); + + void *field_value = value; + if (field.isIndirectCase()) { + size_t alignment_mask = field_type->getValueWitnesses()->getAlignmentMask(); + size_t offset = (sizeof(::swift::HeapObject) + alignment_mask) & ~alignment_mask; + field_value = *(char **)value + offset; + } + body(tag, AGTypeID(field_type), field_value); + + enum_value_witness->destructiveInjectEnumTag(reinterpret_cast(value), + tag, type); + return true; + } + } + } + } + } + return false; +} + +void AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, + void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)) { + auto type = reinterpret_cast(typeID); + auto value_witness = type->getValueWitnesses(); + if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { + AG::precondition_failure("not an enum type: %s", type->name(false)); + } + + auto enum_value_witness = value_witness->_asEVWT(); + uint32_t tag = enum_value_witness->getEnumTag(reinterpret_cast(value), type); + + if (auto descriptor = type->nominal_descriptor()) { + auto enum_descriptor = reinterpret_cast(descriptor); + if (auto fields = enum_descriptor->Fields.get()) { + auto num_payload_cases = enum_descriptor->getNumPayloadCases(); + if (tag < num_payload_cases) { + auto &field = fields->getFields()[tag]; + if (auto mangled_name = field.MangledTypeName.get()) { + auto field_type = type->mangled_type_name_ref_cached(mangled_name, nullptr); + if (field_type) { + enum_value_witness->destructiveProjectEnumData( + reinterpret_cast(value), type); + + void *field_value = value; + if (field.isIndirectCase()) { + field_type->copy_on_write_heap_object((void **)value); + size_t alignment_mask = field_type->getValueWitnesses()->getAlignmentMask(); + size_t offset = (sizeof(::swift::HeapObject) + alignment_mask) & ~alignment_mask; + field_value = *(char **)value + offset; + } + body(tag, AGTypeID(field_type), field_value); + + enum_value_witness->destructiveInjectEnumTag(reinterpret_cast(value), + tag, type); + return true; + } + } + } + } + } + return false; +} + +uint64_t AGTypeGetEnumTag(AGTypeID typeID, void *value) { + auto type = reinterpret_cast(typeID); + auto value_witness = type->getValueWitnesses(); + if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { + AG::precondition_failure("not an enum type: %s", type->name(false)); + } + + auto enum_value_witness = value_witness->_asEVWT(); + return enum_value_witness->getEnumTag(reinterpret_cast(value), type); +} + +void AGTypeProjectEnumData(AGTypeID typeID, void *value) { + auto type = reinterpret_cast(typeID); + auto value_witness = type->getValueWitnesses(); + if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { + AG::precondition_failure("not an enum type: %s", type->name(false)); + } + + auto enum_value_witness = value_witness->_asEVWT(); + enum_value_witness->destructiveProjectEnumData(reinterpret_cast(value), type); +} + +void AGTypeInjectEnumTag(AGTypeID typeID, void *value, uint32_t tag) { + auto type = reinterpret_cast(typeID); + auto value_witness = type->getValueWitnesses(); + if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { + AG::precondition_failure("not an enum type: %s", type->name(false)); + } + + auto enum_value_witness = value_witness->_asEVWT(); + enum_value_witness->destructiveInjectEnumTag(reinterpret_cast(value), tag, type); +} diff --git a/Sources/ComputeCxx/Swift/AGType.h b/Sources/ComputeCxx/Swift/AGType.h index 0c25349..11aa6d0 100644 --- a/Sources/ComputeCxx/Swift/AGType.h +++ b/Sources/ComputeCxx/Swift/AGType.h @@ -76,15 +76,17 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, CF_EXPORT CF_REFINED_FOR_SWIFT -void AGTypeApplyEnumData(AGTypeID typeID); +void AGTypeApplyEnumData(AGTypeID typeID, void *value, + void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGTypeApplyMutableEnumData(AGTypeID typeID); +void AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, + void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)); CF_EXPORT CF_REFINED_FOR_SWIFT -uint64_t AGTypeGetEnumTag(AGTypeID typeID); +uint64_t AGTypeGetEnumTag(AGTypeID typeID, void *value); CF_EXPORT CF_REFINED_FOR_SWIFT From c4724c6b70b49c135e1ec44a4841a1ea0f0f7122 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 20 Mar 2025 18:19:44 +1100 Subject: [PATCH 47/74] Add AGGraph... functions --- Sources/ComputeCxx/Attribute/AGAttribute.cpp | 5 + Sources/ComputeCxx/Attribute/AGAttribute.h | 5 + .../ComputeCxx/Attribute/AGWeakAttribute.cpp | 20 + .../ComputeCxx/Attribute/AGWeakAttribute.h | 25 + .../ComputeCxx/Attribute/Node/IndirectNode.h | 7 +- Sources/ComputeCxx/Attribute/Node/Node.h | 10 +- .../ComputeCxx/Attribute/WeakAttributeID.h | 6 + Sources/ComputeCxx/Closure/AGClosure.cpp | 17 + Sources/ComputeCxx/Closure/AGClosure.h | 23 + Sources/ComputeCxx/Closure/ClosureFunction.h | 12 +- Sources/ComputeCxx/Debug/AGDebugServer.cpp | 22 + Sources/ComputeCxx/Debug/AGDebugServer.h | 24 + Sources/ComputeCxx/Debug/DebugServer.h | 4 +- Sources/ComputeCxx/Debug/DebugServer.mm | 2 +- Sources/ComputeCxx/External/ExternalTrace.cpp | 102 +- Sources/ComputeCxx/External/ExternalTrace.h | 25 +- Sources/ComputeCxx/Graph/AGGraph-Private.h | 16 + Sources/ComputeCxx/Graph/AGGraph.cpp | 1045 ++++++++++++++++- Sources/ComputeCxx/Graph/AGGraph.h | 473 +++++++- Sources/ComputeCxx/Graph/Context.cpp | 18 +- Sources/ComputeCxx/Graph/Context.h | 32 +- Sources/ComputeCxx/Graph/Graph.cpp | 173 ++- Sources/ComputeCxx/Graph/Graph.h | 66 +- .../ComputeCxx/Graph/Profile/ProfileTrace.cpp | 10 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 30 +- Sources/ComputeCxx/Graph/TraceRecorder.h | 2 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 10 +- Sources/ComputeCxx/Graph/UpdateStack.h | 2 + Sources/ComputeCxx/Layout/AGComparison.cpp | 4 +- Sources/ComputeCxx/Layout/AGComparison.h | 17 + Sources/ComputeCxx/Layout/Controls.h | 2 + .../ComputeCxx/Subgraph/AGSubgraph-Private.h | 16 + Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 38 +- Sources/ComputeCxx/Subgraph/AGSubgraph.h | 3 + Sources/ComputeCxx/Subgraph/Subgraph.cpp | 59 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 16 +- Sources/ComputeCxx/Swift/AGTuple.cpp | 208 ++++ Sources/ComputeCxx/Swift/AGTuple.h | 91 ++ Sources/ComputeCxx/Trace/AGTrace.cpp | 11 - Sources/ComputeCxx/Trace/AGTrace.h | 20 - Sources/ComputeCxx/Trace/Trace.h | 6 +- 41 files changed, 2411 insertions(+), 266 deletions(-) create mode 100644 Sources/ComputeCxx/Attribute/AGAttribute.cpp create mode 100644 Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp create mode 100644 Sources/ComputeCxx/Attribute/AGWeakAttribute.h create mode 100644 Sources/ComputeCxx/Closure/AGClosure.cpp create mode 100644 Sources/ComputeCxx/Closure/AGClosure.h create mode 100644 Sources/ComputeCxx/Debug/AGDebugServer.cpp create mode 100644 Sources/ComputeCxx/Debug/AGDebugServer.h create mode 100644 Sources/ComputeCxx/Graph/AGGraph-Private.h create mode 100644 Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h create mode 100644 Sources/ComputeCxx/Swift/AGTuple.cpp create mode 100644 Sources/ComputeCxx/Swift/AGTuple.h delete mode 100644 Sources/ComputeCxx/Trace/AGTrace.cpp delete mode 100644 Sources/ComputeCxx/Trace/AGTrace.h diff --git a/Sources/ComputeCxx/Attribute/AGAttribute.cpp b/Sources/ComputeCxx/Attribute/AGAttribute.cpp new file mode 100644 index 0000000..a477087 --- /dev/null +++ b/Sources/ComputeCxx/Attribute/AGAttribute.cpp @@ -0,0 +1,5 @@ +#include "AGAttribute.h" + +#include "AttributeID.h" + +const AGAttribute AGAttributeNil = AGAttribute(AG::AttributeID::Kind::NilAttribute); diff --git a/Sources/ComputeCxx/Attribute/AGAttribute.h b/Sources/ComputeCxx/Attribute/AGAttribute.h index 5a6b5a6..42bb46f 100644 --- a/Sources/ComputeCxx/Attribute/AGAttribute.h +++ b/Sources/ComputeCxx/Attribute/AGAttribute.h @@ -11,6 +11,11 @@ CF_EXTERN_C_BEGIN typedef uint32_t AGAttribute AG_SWIFT_STRUCT AG_SWIFT_NAME(AnyAttribute); +CF_EXPORT +const AGAttribute AGAttributeNil; + + + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp b/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp new file mode 100644 index 0000000..605b1fd --- /dev/null +++ b/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp @@ -0,0 +1,20 @@ +#include "AGWeakAttribute.h" + +#include "Attribute/AttributeID.h" +#include "Attribute/WeakAttributeID.h" +#include "Data/Zone.h" + +AGWeakAttribute AGCreateWeakAttribute(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + if (attribute_id.without_kind() == 0) { + return AG::WeakAttributeID(attribute_id, 0).to_opaque_value(); + } + + auto zone_id = attribute_id.to_node_ptr().page_ptr()->zone->info().zone_id(); + return AG::WeakAttributeID(attribute_id, zone_id).to_opaque_value(); +} + +AGAttribute AGWeakAttributeGetAttribute(AGWeakAttribute attribute) { + auto attribute_id = AG::WeakAttributeID(attribute); + return attribute_id.evaluate(); +} diff --git a/Sources/ComputeCxx/Attribute/AGWeakAttribute.h b/Sources/ComputeCxx/Attribute/AGWeakAttribute.h new file mode 100644 index 0000000..388b5ee --- /dev/null +++ b/Sources/ComputeCxx/Attribute/AGWeakAttribute.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include "AGAttribute.h" +#include "AGSwiftSupport.h" + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +typedef uint64_t AGWeakAttribute AG_SWIFT_STRUCT AG_SWIFT_NAME(AnyWeakAttribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGWeakAttribute AGCreateWeakAttribute(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGWeakAttributeGetAttribute(AGWeakAttribute attribute); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index e60388d..71a1411 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -14,6 +14,10 @@ namespace AG { class MutableIndirectNode; class IndirectNode { + public: + // TODO: is there special treatment of lowest bit? + static constexpr uint16_t MaximumOffset = 0x3ffffffe; // 30 bits - 1 + private: struct Info { unsigned int is_mutable : 1; @@ -29,8 +33,7 @@ class IndirectNode { uint16_t _relative_offset; // could be relative offset, see Subgraph::insert_attribute public: - IndirectNode(WeakAttributeID source, bool traverses_contexts, uint32_t offset, uint16_t size) - : _source(source) { + IndirectNode(WeakAttributeID source, bool traverses_contexts, uint32_t offset, uint16_t size) : _source(source) { _info.is_mutable = false; _info.traverses_contexts = traverses_contexts; _info.offset = offset; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 835561f..43bc228 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -28,7 +28,7 @@ class NodeFlags { Cacheable = 1 << 4, // 0x10 Unknown0x20 = 1 << 5, // 0x20 - initial value - Unknown0x40 = 1 << 6, // 0x40 - didn't call mark_changed + SelfModified = 1 << 6, // 0x40 }; private: @@ -70,9 +70,9 @@ class NodeFlags { void set_value4_unknown0x20(bool value) { _value4 = (_value4 & ~Flags4::Unknown0x20) | (value ? Flags4::Unknown0x20 : 0); }; - bool value4_unknown0x40() const { return _value4 & Flags4::Unknown0x40; }; - void set_value4_unknown0x40(bool value) { - _value4 = (_value4 & ~Flags4::Unknown0x40) | (value ? Flags4::Unknown0x40 : 0); + bool self_modified() const { return _value4 & Flags4::SelfModified; }; + void set_self_modified(bool value) { + _value4 = (_value4 & ~Flags4::SelfModified) | (value ? Flags4::SelfModified : 0); }; }; @@ -160,7 +160,7 @@ class Node { return (state().is_dirty() ? 1 : 0) << 0 | (state().is_pending() ? 1 : 0) << 1 | (state().is_updating() ? 1 : 0) << 2 | (state().is_value_initialized() ? 1 : 0) << 3 | (state().is_main_thread() ? 1 : 0) << 4 | (flags().value4_unknown0x20() ? 1 : 0) << 5 | - (state().is_main_thread_only() ? 1 : 0) << 6 | (flags().value4_unknown0x40() ? 1 : 0) << 7; + (state().is_main_thread_only() ? 1 : 0) << 6 | (flags().self_modified() ? 1 : 0) << 7; }; NodeFlags &flags() { return _flags; }; diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.h b/Sources/ComputeCxx/Attribute/WeakAttributeID.h index ca15953..8940750 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.h +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.h @@ -3,6 +3,7 @@ #include #include +#include "AGWeakAttribute.h" #include "AttributeID.h" CF_ASSUME_NONNULL_BEGIN @@ -15,8 +16,13 @@ class WeakAttributeID { uint32_t _zone_id; public: + WeakAttributeID(AGWeakAttribute opaque_value) + : _attribute(opaque_value & 0xffffffff), _zone_id(opaque_value >> 0x20){}; WeakAttributeID(AttributeID attribute, uint32_t zone_id) : _attribute(attribute), _zone_id(zone_id){}; + // TODO: rename + uint64_t to_opaque_value() const { return _attribute.value() | ((uint64_t)_zone_id << 0x20); }; + const AttributeID &attribute() const { return _attribute; }; uint32_t zone_id() const { return _zone_id; }; diff --git a/Sources/ComputeCxx/Closure/AGClosure.cpp b/Sources/ComputeCxx/Closure/AGClosure.cpp new file mode 100644 index 0000000..0a5b192 --- /dev/null +++ b/Sources/ComputeCxx/Closure/AGClosure.cpp @@ -0,0 +1,17 @@ +#include "AGClosure.h" + +#include + +struct AGClosureStorage { + const void * _Nullable function; + const void * _Nullable context; +}; + +AGClosureRef AGRetainClosure(AGClosureRef closure) { + ::swift::swift_retain((::swift::HeapObject *)closure->context); + return closure; +} + +void AGReleaseClosure(AGClosureRef closure) { + ::swift::swift_release((::swift::HeapObject *)closure->context); +} diff --git a/Sources/ComputeCxx/Closure/AGClosure.h b/Sources/ComputeCxx/Closure/AGClosure.h new file mode 100644 index 0000000..e7b9d49 --- /dev/null +++ b/Sources/ComputeCxx/Closure/AGClosure.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "AGSwiftSupport.h" + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +typedef struct CF_BRIDGED_TYPE(id) AGClosureStorage *AGClosureRef AG_SWIFT_NAME(Closure); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGClosureRef AGRetainClosure(AGClosureRef closure); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGReleaseClosure(AGClosureRef closure); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Closure/ClosureFunction.h b/Sources/ComputeCxx/Closure/ClosureFunction.h index e01066e..3bc4093 100644 --- a/Sources/ComputeCxx/Closure/ClosureFunction.h +++ b/Sources/ComputeCxx/Closure/ClosureFunction.h @@ -10,21 +10,25 @@ namespace AG { template class ClosureFunction { public: using Context = const void *_Nullable; - using Callable = AG_SWIFT_CC(swift) const ReturnType (*_Nullable)(Args..., Context AG_SWIFT_CONTEXT); + using Callable = AG_SWIFT_CC(swift) ReturnType (*_Nullable)(Args..., Context AG_SWIFT_CONTEXT); private: Callable _function; Context _context; public: + inline ClosureFunction(Callable function, Context context) : _function(function), _context(context) { + ::swift::swift_retain((::swift::HeapObject *)_context); + } + inline ClosureFunction(std::nullptr_t = nullptr) : _function(nullptr), _context(nullptr) {} + inline ~ClosureFunction() { ::swift::swift_release((::swift::HeapObject *)_context); } + operator bool() { return _function != nullptr; } - + const ReturnType operator()(Args... args) const noexcept { // TODO: check _context is first or last parameter return _function(std::forward(args)..., _context); } - - void release_context() { ::swift::swift_release((::swift::HeapObject *)_context); }; }; // void * diff --git a/Sources/ComputeCxx/Debug/AGDebugServer.cpp b/Sources/ComputeCxx/Debug/AGDebugServer.cpp new file mode 100644 index 0000000..ae15243 --- /dev/null +++ b/Sources/ComputeCxx/Debug/AGDebugServer.cpp @@ -0,0 +1,22 @@ +#include "AGDebugServer.h" + +#include "DebugServer.h" + +CFURLRef AGDebugServerCopyURL() { + if (AG::DebugServer::shared() != nullptr) { + return AG::DebugServer::shared()->copy_url(); + } + return nullptr; +} + +void AGDebugServerRun(uint32_t timeout) { + if (AG::DebugServer::shared() != nullptr) { + AG::DebugServer::shared()->run(timeout); + } +} + +void AGDebugServerStart(AGDebugServerRef server, uint32_t options) { + reinterpret_cast(server)->start(options); +} + +void AGDebugServerStop(AGDebugServerRef server) { reinterpret_cast(server)->stop(); } diff --git a/Sources/ComputeCxx/Debug/AGDebugServer.h b/Sources/ComputeCxx/Debug/AGDebugServer.h new file mode 100644 index 0000000..dc32a3f --- /dev/null +++ b/Sources/ComputeCxx/Debug/AGDebugServer.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include "AGSwiftSupport.h" + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +typedef void *AGDebugServerRef AG_SWIFT_NAME(DebugServer); + +CFURLRef AGDebugServerCopyURL(); + +void AGDebugServerRun(uint32_t timeout); + +void AGDebugServerStart(AGDebugServerRef server, uint32_t options); + +void AGDebugServerStop(AGDebugServerRef server); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Debug/DebugServer.h b/Sources/ComputeCxx/Debug/DebugServer.h index 1cc924a..2189e52 100644 --- a/Sources/ComputeCxx/Debug/DebugServer.h +++ b/Sources/ComputeCxx/Debug/DebugServer.h @@ -47,6 +47,8 @@ class DebugServer { static DebugServer *_shared_server; public: + static DebugServer *shared() { return _shared_server; } + static void start(uint32_t options); static void stop(); @@ -56,7 +58,7 @@ class DebugServer { DebugServer(uint32_t options); ~DebugServer(); - void run(int timeout); + void run(uint32_t timeout); void shutdown(); void close_connection(Connection *connection); diff --git a/Sources/ComputeCxx/Debug/DebugServer.mm b/Sources/ComputeCxx/Debug/DebugServer.mm index c3903bf..2cf8ba8 100644 --- a/Sources/ComputeCxx/Debug/DebugServer.mm +++ b/Sources/ComputeCxx/Debug/DebugServer.mm @@ -141,7 +141,7 @@ server->_clients.push_back(std::make_unique(server, connection_socket)); } -void DebugServer::run(int timeout) { +void DebugServer::run(uint32_t timeout) { fd_set write_fds; struct timeval tv; diff --git a/Sources/ComputeCxx/External/ExternalTrace.cpp b/Sources/ComputeCxx/External/ExternalTrace.cpp index 24a5380..69d4e0e 100644 --- a/Sources/ComputeCxx/External/ExternalTrace.cpp +++ b/Sources/ComputeCxx/External/ExternalTrace.cpp @@ -6,100 +6,100 @@ void ExternalTrace::begin_trace(const AG::Graph &graph) { auto cf_graph = graph.main_context()->to_cf(); - if (auto callback = _interface.begin_trace) { + if (auto callback = _interface->begin_trace) { callback(_trace, cf_graph); } } void ExternalTrace::end_trace(const AG::Graph &graph) { auto cf_graph = graph.main_context()->to_cf(); - if (auto callback = _interface.end_trace) { + if (auto callback = _interface->end_trace) { callback(_trace, cf_graph); } } void ExternalTrace::begin_update(const AG::Subgraph &subgraph, uint32_t options) { auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _interface.begin_update_subgraph) { + if (auto callback = _interface->begin_update_subgraph) { callback(_trace, cf_subgraph); } } void ExternalTrace::end_update(const AG::Subgraph &subgraph) { auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _interface.end_update_subgraph) { + if (auto callback = _interface->end_update_subgraph) { callback(_trace, cf_subgraph); } } void ExternalTrace::begin_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, uint32_t options) { - if (auto callback = _interface.begin_update_stack) { + if (auto callback = _interface->begin_update_stack) { callback(_trace, node); } } void ExternalTrace::end_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, AG::Graph::UpdateStatus update_status) { - if (auto callback = _interface.end_update_stack) { + if (auto callback = _interface->end_update_stack) { callback(_trace, update_status == AG::Graph::UpdateStatus::Changed); } } void ExternalTrace::begin_update(AG::data::ptr node) { - if (auto callback = _interface.begin_update_node) { + if (auto callback = _interface->begin_update_node) { callback(_trace); } } void ExternalTrace::end_update(AG::data::ptr node, bool changed) { - if (auto callback = _interface.end_update_node) { + if (auto callback = _interface->end_update_node) { callback(_trace); } } void ExternalTrace::begin_update(const AG::Graph::Context &context) { auto cf_context = context.to_cf(); - if (auto callback = _interface.begin_update_context) { + if (auto callback = _interface->begin_update_context) { callback(_trace, cf_context); } } void ExternalTrace::end_update(const AG::Graph::Context &context) { auto cf_context = context.to_cf(); - if (auto callback = _interface.end_update_context) { + if (auto callback = _interface->end_update_context) { callback(_trace, cf_context); } } void ExternalTrace::begin_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute) { auto cf_context = context.to_cf(); - if (auto callback = _interface.begin_invalidation) { + if (auto callback = _interface->begin_invalidation) { callback(_trace, cf_context, attribute); } } void ExternalTrace::end_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute) { auto cf_context = context.to_cf(); - if (auto callback = _interface.end_invalidation) { + if (auto callback = _interface->end_invalidation) { callback(_trace, cf_context, attribute); } } void ExternalTrace::begin_modify(AG::data::ptr node) { - if (auto callback = _interface.begin_modify) { + if (auto callback = _interface->begin_modify) { callback(_trace); } } void ExternalTrace::end_modify(AG::data::ptr node) { - if (auto callback = _interface.end_modify) { + if (auto callback = _interface->end_modify) { callback(_trace); } } void ExternalTrace::begin_event(AG::data::ptr node, uint32_t event_id) { - if (auto callback = _interface.begin_event) { + if (auto callback = _interface->begin_event) { if (auto subgraph = AG::AttributeID(node).subgraph()) { const char *event_name = subgraph->graph()->key_name(event_id); callback(_trace, node, event_name); @@ -108,7 +108,7 @@ void ExternalTrace::begin_event(AG::data::ptr node, uint32_t event_id) } void ExternalTrace::end_event(AG::data::ptr node, uint32_t event_id) { - if (auto callback = _interface.end_event) { + if (auto callback = _interface->end_event) { if (auto subgraph = AG::AttributeID(node).subgraph()) { const char *event_name = subgraph->graph()->key_name(event_id); callback(_trace, node, event_name); @@ -118,35 +118,35 @@ void ExternalTrace::end_event(AG::data::ptr node, uint32_t event_id) { void ExternalTrace::created(const AG::Graph::Context &context) { auto cf_context = context.to_cf(); - if (auto callback = _interface.created_context) { + if (auto callback = _interface->created_context) { callback(_trace, cf_context); } } void ExternalTrace::destroy(const AG::Graph::Context &context) { auto cf_context = context.to_cf(); - if (auto callback = _interface.destroy_context) { + if (auto callback = _interface->destroy_context) { callback(_trace, cf_context); } } void ExternalTrace::needs_update(const AG::Graph::Context &context) { auto cf_context = context.to_cf(); - if (auto callback = _interface.needs_update_context) { + if (auto callback = _interface->needs_update_context) { callback(_trace, cf_context); } } void ExternalTrace::created(const AG::Subgraph &subgraph) { auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _interface.created_subgraph) { + if (auto callback = _interface->created_subgraph) { callback(_trace, cf_subgraph); } } void ExternalTrace::invalidate(const AG::Subgraph &subgraph) { auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _interface.invalidate_subgraph) { + if (auto callback = _interface->invalidate_subgraph) { callback(_trace, cf_subgraph); } } @@ -158,7 +158,7 @@ void ExternalTrace::destroy(const AG::Subgraph &subgraph) { void ExternalTrace::add_child(const AG::Subgraph &subgraph, const AG::Subgraph &child) { auto cf_subgraph = subgraph.to_cf(); auto cf_child = subgraph.to_cf(); - if (auto callback = _interface.add_child_subgraph) { + if (auto callback = _interface->add_child_subgraph) { callback(_trace, cf_subgraph, cf_child); } } @@ -166,25 +166,25 @@ void ExternalTrace::add_child(const AG::Subgraph &subgraph, const AG::Subgraph & void ExternalTrace::remove_child(const AG::Subgraph &subgraph, const AG::Subgraph &child) { auto cf_subgraph = subgraph.to_cf(); auto cf_child = subgraph.to_cf(); - if (auto callback = _interface.remove_child_subgraph) { + if (auto callback = _interface->remove_child_subgraph) { callback(_trace, cf_subgraph, cf_child); } } void ExternalTrace::added(AG::data::ptr node) { - if (auto callback = _interface.added_node) { + if (auto callback = _interface->added_node) { callback(_trace); } } void ExternalTrace::add_edge(AG::data::ptr node, AG::AttributeID input, uint8_t input_edge_flags) { - if (auto callback = _interface.add_edge) { + if (auto callback = _interface->add_edge) { callback(_trace); } } void ExternalTrace::remove_edge(AG::data::ptr node, uint32_t input_index) { - if (auto callback = _interface.remove_edge) { + if (auto callback = _interface->remove_edge) { if (AG::AttributeID(node).subgraph()) { callback(_trace); } @@ -192,7 +192,7 @@ void ExternalTrace::remove_edge(AG::data::ptr node, uint32_t input_ind } void ExternalTrace::set_edge_pending(AG::data::ptr node, uint32_t input_index, bool pending) { - if (auto callback = _interface.set_edge_pending) { + if (auto callback = _interface->set_edge_pending) { if (AG::AttributeID(node).subgraph()) { callback(_trace); } @@ -200,49 +200,49 @@ void ExternalTrace::set_edge_pending(AG::data::ptr node, uint32_t inpu } void ExternalTrace::set_dirty(AG::data::ptr node, bool dirty) { - if (auto callback = _interface.set_dirty) { + if (auto callback = _interface->set_dirty) { callback(_trace); } } void ExternalTrace::set_pending(AG::data::ptr node, bool pending) { - if (auto callback = _interface.set_pending) { + if (auto callback = _interface->set_pending) { callback(_trace); } } void ExternalTrace::set_value(AG::data::ptr node, const void *value) { - if (auto callback = _interface.set_value) { + if (auto callback = _interface->set_value) { callback(_trace); } } void ExternalTrace::mark_value(AG::data::ptr node) { - if (auto callback = _interface.mark_value) { + if (auto callback = _interface->mark_value) { callback(_trace); } } void ExternalTrace::added(AG::data::ptr indirect_node) { - if (auto callback = _interface.added_indirect_node) { + if (auto callback = _interface->added_indirect_node) { callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind } } void ExternalTrace::set_source(AG::data::ptr indirect_node, AG::AttributeID source) { - if (auto callback = _interface.set_source) { + if (auto callback = _interface->set_source) { callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind } } void ExternalTrace::set_dependency(AG::data::ptr indirect_node, AG::AttributeID dependency) { - if (auto callback = _interface.set_dependency) { + if (auto callback = _interface->set_dependency) { callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind } } void ExternalTrace::mark_profile(const AG::Graph &graph, uint32_t event_id) { - if (auto callback = _interface.mark_profile) { + if (auto callback = _interface->mark_profile) { const char *event_name = graph.key_name(event_id); callback(_trace, event_name); } @@ -250,52 +250,52 @@ void ExternalTrace::mark_profile(const AG::Graph &graph, uint32_t event_id) { // void ExternalTrace::destroy(const AG::Subgraph &subgraph) { // auto cf_subgraph = subgraph.to_cf(); -// if (auto callback = _interface.destroy_subgraph) { +// if (auto callback = _interface->destroy_subgraph) { // callback(_trace, cf_subgraph); // } // } void ExternalTrace::custom_event(const AG::Graph::Context &context, const char *event_name, const void *value, const AG::swift::metadata &type) { - if (_interface.options != 0) { + if (_interface->options != 0) { auto cf_context = context.to_cf(); - if (auto callback = _interface.custom_event) { + if (auto callback = _interface->custom_event) { callback(_trace, cf_context, event_name, value, AGTypeID(&type)); } } } -void ExternalTrace::named_event(const AG::Graph::Context &context, uint32_t arg2, uint32_t num_args, +void ExternalTrace::named_event(const AG::Graph::Context &context, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6) { - if (_interface.options > 1) { + if (_interface->options > 1) { auto cf_context = context.to_cf(); - if (auto callback = _interface.named_event) { - callback(_trace, cf_context, arg2, num_args, event_args, data, arg6); + if (auto callback = _interface->named_event) { + callback(_trace, cf_context, event_id, num_event_args, event_args, data, arg6); } } } bool ExternalTrace::named_event_enabled(uint32_t event_id) { - if (_interface.options < 2) { + if (_interface->options < 2) { return false; } - if (auto callback = _interface.named_event_enabled) { + if (auto callback = _interface->named_event_enabled) { return callback(_trace); } - return _interface.named_event != nullptr; + return _interface->named_event != nullptr; } void ExternalTrace::set_deadline(uint64_t deadline) { - if (_interface.options > 2) { - if (auto callback = _interface.set_deadline) { + if (_interface->options > 2) { + if (auto callback = _interface->set_deadline) { callback(_trace); } } } void ExternalTrace::passed_deadline() { - if (_interface.options > 2) { - if (auto callback = _interface.passed_deadline) { + if (_interface->options > 2) { + if (auto callback = _interface->passed_deadline) { callback(_trace); } } @@ -303,9 +303,9 @@ void ExternalTrace::passed_deadline() { void ExternalTrace::compare_failed(AG::data::ptr node, const void *lhs, const void *rhs, size_t range_offset, size_t range_size, const AG::swift::metadata *_Nullable type) { - if (_interface.options > 3) { + if (_interface->options > 3) { AGComparisonStateStorage storage = {lhs, rhs, range_offset, range_size, AGTypeID(&type)}; - if (auto callback = _interface.compare_failed) { + if (auto callback = _interface->compare_failed) { callback(_trace, node, &storage); } } diff --git a/Sources/ComputeCxx/External/ExternalTrace.h b/Sources/ComputeCxx/External/ExternalTrace.h index fa70cd0..40c7392 100644 --- a/Sources/ComputeCxx/External/ExternalTrace.h +++ b/Sources/ComputeCxx/External/ExternalTrace.h @@ -19,8 +19,8 @@ class ExternalTrace : public AG::Trace { void (*_Nullable begin_trace)(void *trace, AGGraphStorage *graph); void (*_Nullable end_trace)(void *trace, AGGraphStorage *graph); - void (*_Nullable begin_update_subgraph)(void *trace, AG::SubgraphObject *subgraph); - void (*_Nullable end_update_subgraph)(void *trace, AG::SubgraphObject *subgraph); + void (*_Nullable begin_update_subgraph)(void *trace, AGSubgraphStorage *subgraph); + void (*_Nullable end_update_subgraph)(void *trace, AGSubgraphStorage *subgraph); void (*_Nullable begin_update_stack)(void *trace, AGAttribute attribute); void (*_Nullable end_update_stack)(void *trace, bool changed); void (*_Nullable begin_update_node)(void *trace); @@ -41,10 +41,10 @@ class ExternalTrace : public AG::Trace { void (*_Nullable destroy_context)(void *trace, AGGraphStorage *graph); void (*_Nullable needs_update_context)(void *trace, AGGraphStorage *graph); - void (*_Nullable created_subgraph)(void *trace, AG::SubgraphObject *subgraph); - void (*_Nullable invalidate_subgraph)(void *trace, AG::SubgraphObject *subgraph); - void (*_Nullable add_child_subgraph)(void *trace, AG::SubgraphObject *subgraph, AG::SubgraphObject *child); - void (*_Nullable remove_child_subgraph)(void *trace, AG::SubgraphObject *subgraph, AG::SubgraphObject *child); + void (*_Nullable created_subgraph)(void *trace, AGSubgraphStorage *subgraph); + void (*_Nullable invalidate_subgraph)(void *trace, AGSubgraphStorage *subgraph); + void (*_Nullable add_child_subgraph)(void *trace, AGSubgraphStorage *subgraph, AGSubgraphStorage *child); + void (*_Nullable remove_child_subgraph)(void *trace, AGSubgraphStorage *subgraph, AGSubgraphStorage *child); void (*_Nullable added_node)(void *trace); void (*_Nullable add_edge)(void *trace); @@ -64,7 +64,7 @@ class ExternalTrace : public AG::Trace { void (*_Nullable custom_event)(void *trace, AGGraphStorage *graph, const char *event_name, const void *value, AGTypeID type); - void (*_Nullable named_event)(void *trace, AGGraphStorage *graph, uint32_t arg2, uint32_t num_args, + void (*_Nullable named_event)(void *trace, AGGraphStorage *graph, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6); bool (*_Nullable named_event_enabled)(void *trace); void (*_Nullable set_deadline)(void *trace); @@ -74,10 +74,15 @@ class ExternalTrace : public AG::Trace { }; private: - Interface _interface; + Interface *_interface; void *_trace; public: + ExternalTrace(Interface *interface, void *trace) : _interface(interface), _trace(trace){}; + ExternalTrace(uint64_t trace_id, Interface *interface, void *trace) : _interface(interface), _trace(trace){ + _trace_id = trace_id; + }; + void begin_trace(const AG::Graph &graph); void end_trace(const AG::Graph &graph); @@ -132,8 +137,8 @@ class ExternalTrace : public AG::Trace { void custom_event(const AG::Graph::Context &context, const char *event_name, const void *value, const AG::swift::metadata &type); - void named_event(const AG::Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, - CFDataRef data, uint32_t arg6); // TODO: what are these args? + void named_event(const AG::Graph::Context &context, uint32_t event_id, uint32_t num_event_args, + const uint64_t *event_args, CFDataRef data, uint32_t arg6); // TODO: what are these args? bool named_event_enabled(uint32_t event_id); void set_deadline(uint64_t deadline); diff --git a/Sources/ComputeCxx/Graph/AGGraph-Private.h b/Sources/ComputeCxx/Graph/AGGraph-Private.h new file mode 100644 index 0000000..dad8e42 --- /dev/null +++ b/Sources/ComputeCxx/Graph/AGGraph-Private.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "AGGraph.h" +#include "Graph/Context.h" +#include "Private/CFRuntime.h" + +CF_ASSUME_NONNULL_BEGIN + +struct AGGraphStorage { + CFRuntimeBase base; + AG::Graph::Context context; +}; + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 5fc30d2..fb3d31c 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -1,13 +1,1052 @@ -#include "AGGraph.h" +#include "AGGraph-Private.h" -void AGGraphCreate() { +#include "Attribute/AttributeID.h" +#include "Attribute/AttributeType.h" +#include "Attribute/Node/IndirectNode.h" +#include "Attribute/OffsetAttributeID.h" +#include "Attribute/WeakAttributeID.h" +#include "Context.h" +#include "External/ExternalTrace.h" +#include "Graph.h" +#include "Private/CFRuntime.h" +#include "Trace/Trace.h" +#include "UpdateStack.h" + +namespace { + +CFRuntimeClass &graph_type_id() { + static auto finalize = [](CFTypeRef graph_ref) { + AGGraphStorage *storage = (AGGraphRef)graph_ref; + if (!storage->context.invalidated()) { + storage->context.AG::Graph::Context::~Context(); + } + }; + static CFRuntimeClass klass = { + 0, // version + "AGGraph", // className + NULL, // init + NULL, // copy, + finalize, + NULL, // equal + NULL, // hash + NULL, // copyFormattingDesc + NULL, // copyDebugDesc, + 0 // ?? + }; + return klass; +} + +} // namespace + +CFTypeID AGGraphGetTypeID() { + static CFTypeID type = _CFRuntimeRegisterClass(&graph_type_id()); + return type; +} + +AGGraphRef AGGraphCreate() { return AGGraphCreateShared(nullptr); }; + +AGGraphRef AGGraphCreateShared(AGGraphRef original) { + CFIndex extra_bytes = sizeof(struct AGGraphStorage) - sizeof(CFRuntimeBase); + struct AGGraphStorage *instance = + (struct AGGraphStorage *)_CFRuntimeCreateInstance(kCFAllocatorDefault, AGGraphGetTypeID(), extra_bytes, NULL); + if (!instance) { + AG::precondition_failure("memory allocation failure."); + } + + AG::Graph *graph; + if (original) { + if (original->context.invalidated()) { + AG::precondition_failure("invalidated graph"); + } + graph = &original->context.graph(); + AG::Graph::will_add_to_context(graph); + } else { + graph = new AG::Graph(); + } + + new (&instance->context) AG::Graph::Context(graph); + + AG::Graph::did_remove_from_context(graph); + + instance->context.set_invalidated(false); + + return instance; +}; + +AGGraphContextRef AGGraphGetGraphContext(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); // TODO: inline + return reinterpret_cast(context); +} + +AGGraphRef AGGraphContextGetGraph(AGGraphContextRef context) { + return reinterpret_cast(reinterpret_cast(context) - sizeof(CFRuntimeBase)); +} + +const void *AGGraphGetContext(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); + return context->context_info(); +} + +void AGGraphSetContext(AGGraphRef graph, const void *context_info) { + auto context = AG::Graph::Context::from_cf(graph); + context->set_context_info(context_info); +} + +#pragma mark - Counters + +uint64_t AGGraphGetCounter(AGGraphRef graph, AGGraphCounterQuery query) { + auto context = AG::Graph::Context::from_cf(graph); + switch (query) { + case AGGraphCounterQueryNodeCount: + return context->graph().num_nodes(); + case AGGraphCounterQueryTransactionCount: + return context->graph().transaction_count(); + case AGGraphCounterQueryUpdateCount: + return context->graph().update_count(); + case AGGraphCounterQueryChangeCount: + return context->graph().change_count(); + case AGGraphCounterQueryContextID: + return context->unique_id(); + case AGGraphCounterQueryGraphID: + return context->graph().unique_id(); + case AGGraphCounterQueryContextThreadUpdating: + return context->thread_is_updating(); + case AGGraphCounterQueryThreadUpdating: + return context->graph().thread_is_updating(); + case AGGraphCounterQueryContextNeedsUpdate: + return context->needs_update(); + case AGGraphCounterQueryNeedsUpdate: + return context->graph().needs_update(); + case AGGraphCounterQueryMainThreadUpdateCount: + return context->graph().update_on_main_count(); + case AGGraphCounterQueryNodeTotalCount: + return context->graph().num_nodes_created(); + case AGGraphCounterQuerySubgraphCount: + return context->graph().num_subgraphs(); + case AGGraphCounterQuerySubgraphTotalCount: + return context->graph().num_subgraphs_created(); + default: + return 0; + } +} + +#pragma mark - Subgraph + +bool AGGraphBeginDeferringSubgraphInvalidation(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); + bool old_value = context->graph().is_deferring_subgraph_invalidation(); + context->graph().set_deferring_subgraph_invalidation(true); + return old_value; +} + +void AGGraphEndDeferringSubgraphInvalidation(AGGraphRef graph, bool was_deferring_subgraph_invalidation) { + auto context = AG::Graph::Context::from_cf(graph); + if (!was_deferring_subgraph_invalidation) { + context->graph().set_deferring_subgraph_invalidation(false); + context->graph().invalidate_subgraphs(); + } +} + +#pragma mark - Attribute types + +// TODO: is this AGGraphRef or AG::Graph ? +uint32_t AGGraphInternAttributeType(AGGraphRef graph, AGTypeID type, + void *(*intern)(const void *context AG_SWIFT_CONTEXT)AG_SWIFT_CC(swift), + const void *context) { + auto metadata = reinterpret_cast(type); + return graph->context.graph().intern_type(metadata, AG::ClosureFunctionVP(intern, context)); +} + +void AGGraphVerifyType(AGAttribute attribute, AGTypeID type) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + if (attribute_id.is_direct()) { + auto metadata = reinterpret_cast(type); + auto attribute_type = subgraph->graph()->attribute_type(attribute_id.to_node().type_id()); + if (&attribute_type.value_metadata() != metadata) { + AG::precondition_failure("type check failed: %u, expected %s, got %s", attribute, metadata->name(false), + attribute_type.value_metadata().name(false)); + } + } +} + +#pragma mark - Attributes + +AGAttribute AGGraphCreateAttribute(uint32_t type_id, void *body, void *value) { + auto current_subgraph = AG::Subgraph::current_subgraph(); + if (!current_subgraph) { + AG::precondition_failure("no subgraph active while adding attribute"); + } + return current_subgraph->graph()->add_attribute(*current_subgraph, type_id, body, value); +} + +AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + if (auto subgraph = attribute_id.subgraph()) { + auto context_id = subgraph->context_id(); + if (context_id != 0) { + if (auto context = subgraph->graph()->context_with_id(context_id)) { + return context->to_cf(); + } + } + } + AG::precondition_failure("no graph: %u", attribute); +} + +AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute) { + auto subgraph = AGGraphGetAttributeSubgraph2(attribute); + if (subgraph == nullptr) { + AG::precondition_failure("no subgraph"); + } + + return subgraph; +} + +AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (subgraph == nullptr) { + AG::precondition_failure("internal error"); + } + + return subgraph->to_cf(); +} + +AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + const void *body = nullptr; + auto type = subgraph->graph()->attribute_ref(attribute_id.to_node_ptr(), &body); + return AGAttributeInfo(type, body); +} + +uint8_t AGGraphGetFlags(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + return attribute_id.to_node().flags().subgraph_flags(); +} + +void AGGraphSetFlags(AGAttribute attribute, uint8_t flags) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.subgraph()->set_flags(attribute_id.to_node_ptr(), AG::NodeFlags::SubgraphFlags(flags)); +} + +void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool flag, + void (*modify)(void *body, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + subgraph->graph()->attribute_modify(attribute_id.to_node_ptr(), + *reinterpret_cast(type), + AG::ClosureFunctionPV(modify, context), flag); +} + +bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, + bool (*predicate)(uint32_t attribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + return subgraph->graph()->breadth_first_search(attribute_id, AG::Graph::SearchOptions(options), + AG::ClosureFunctionAB(predicate, context)); +} + +#pragma mark - Cached attributes + +namespace { + +void read_cached_attribute(unsigned long, AGSwiftMetadata const *, void const *, AGSwiftMetadata const *, unsigned int, + unsigned int, unsigned char &, + AG::ClosureFunctionCI) {} + +} // namespace + +void AGGraphReadCachedAttribute() { // TODO: not implemented } +void AGGraphReadCachedAttributeIfExists() { + // TODO: not implemented +} + +#pragma mark - Current attribute + +AGAttribute AGGraphGetCurrentAttribute() { + auto update = AG::Graph::current_update(); + if (update.tag() == 0) { + if (auto update_stack = update.get()) { + auto frame = update_stack->frames().back(); + if (frame.attribute) { + return frame.attribute; + } + } + } + return AGAttributeNil; +} + +bool AGGraphCurrentAttributeWasModified() { + auto update = AG::Graph::current_update(); + if (update.tag() == 0) { + if (auto update_stack = update.get()) { + auto frame = update_stack->frames().back(); + if (frame.attribute) { + return frame.attribute->flags().self_modified(); + } + } + } + return false; +} + +#pragma mark - Indirect attributes + +namespace { + +AG::AttributeID create_indirect_attribute(AG::AttributeID attribute_id, std::optional size) { + auto current_subgraph = AG::Subgraph::current_subgraph(); + if (current_subgraph == nullptr) { + AG::precondition_failure("no subgraph active while making indirection"); + } + + AG::data::ptr indirect_node = + current_subgraph->graph()->add_indirect_attribute(*current_subgraph, attribute_id, 0, size, true); + return AG::AttributeID(indirect_node); // TODO: check adds kind +} + +} // namespace + +AGAttribute AGGraphCreateIndirectAttribute(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + return create_indirect_attribute(attribute_id, std::optional()); +} + +AGAttribute AGGraphCreateIndirectAttribute2(AGAttribute attribute, size_t size) { + auto attribute_id = AG::AttributeID(attribute); + return create_indirect_attribute(attribute_id, std::optional(size)); +} + +AGAttribute AGGraphGetIndirectAttribute(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_indirect()) { + return attribute_id; + } + return attribute_id.to_indirect_node().source().attribute(); +} + +void AGGraphSetIndirectAttribute(AGAttribute attribute, AGAttribute source) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { + AG::precondition_failure("invalid indirect attribute: %u", attribute); + } + + auto source_id = AG::AttributeID(source); + if (source_id.is_nil()) { + attribute_id.subgraph()->graph()->indirect_attribute_reset(attribute_id.to_indirect_node_ptr(), false); + } else { + attribute_id.subgraph()->graph()->indirect_attribute_set(attribute_id.to_indirect_node_ptr(), source_id); + } +} + +void AGGraphResetIndirectAttribute(AGAttribute attribute, bool non_nil) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { + AG::precondition_failure("invalid indirect attribute: %u", attribute); + } + + attribute_id.subgraph()->graph()->indirect_attribute_reset(attribute_id.to_indirect_node_ptr(), non_nil); +} + +AGAttribute AGGraphGetIndirectDependency(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { + AG::precondition_failure("invalid indirect attribute: %u", attribute); + } + + return attribute_id.subgraph()->graph()->indirect_attribute_dependency(attribute_id.to_indirect_node_ptr()); +} + +void AGGraphSetIndirectDependency(AGAttribute attribute, AGAttribute dependency) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { + AG::precondition_failure("invalid indirect attribute: %u", attribute); + } + + return attribute_id.subgraph()->graph()->indirect_attribute_set_dependency(attribute_id.to_indirect_node_ptr(), + AG::AttributeID(dependency)); +} + +void AGGraphRegisterDependency(AGAttribute dependency, uint8_t input_edge_flags) { + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.tag() != 0 || update_stack_ptr.get() == nullptr) { + AG::precondition_failure("no attribute updating"); + } + + auto update_stack = update_stack_ptr.get(); + auto graph = update_stack->graph(); + auto frame = update_stack->frames().back(); + + graph->input_value_add(frame.attribute, AG::AttributeID(dependency), input_edge_flags); +} + +#pragma mark - Offset attributes + +namespace { + +AG::AttributeID create_offset_attribute(AG::AttributeID attribute_id, uint32_t offset, std::optional size) { + if (offset == 0) { + if (size.has_value() && attribute_id.is_indirect()) { + auto calculated_size = attribute_id.size(); + if (calculated_size.has_value() && calculated_size.value() == size.value()) { + return attribute_id; + } + } + } else if (offset > AG::IndirectNode::MaximumOffset) { + AG::precondition_failure("invalid offset: %u, %lu", offset, size.value()); + } + + auto current_subgraph = AG::Subgraph::current_subgraph(); + if (current_subgraph == nullptr) { + AG::precondition_failure("no subgraph active while adding attribute"); + } + + AG::data::ptr indirect_node = + current_subgraph->graph()->add_indirect_attribute(*current_subgraph, attribute_id, offset, size, false); + return AG::AttributeID(indirect_node); // TODO: check adds kind +} + +} // namespace + +AGAttribute AGGraphCreateOffsetAttribute(AGAttribute attribute, uint32_t offset) { + auto attribute_id = AG::AttributeID(attribute); + return create_offset_attribute(attribute_id, offset, std::optional()); +} + +AGAttribute AGGraphCreateOffsetAttribute2(AGAttribute attribute, uint32_t offset, size_t size) { + auto attribute_id = AG::AttributeID(attribute); + return create_offset_attribute(attribute_id, offset, std::optional(size)); +} + +#pragma mark - Updates + +void AGGraphInvalidate(AGGraphRef graph) { + if (graph->context.invalidated()) { + return; + } + graph->context.~Context(); // TODO: check this goes here + graph->context.set_invalidated(true); +} + +void AGGraphInvalidateAllValues(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().value_mark_all(); +} + +void AGGraphInvalidateValue(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + subgraph->graph()->value_mark(attribute_id.to_node_ptr()); +} + +void AGGraphSetInvalidationCallback(AGGraphRef graph, + void (*callback)(AGAttribute, const void *context AG_SWIFT_CONTEXT) + AG_SWIFT_CC(swift), + const void *callback_context) { + auto context = AG::Graph::Context::from_cf(graph); + context->set_invalidation_callback(AG::ClosureFunctionAV(callback, callback_context)); +} + +void AGGraphUpdateValue(AGAttribute attribute, uint8_t options) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + subgraph->graph()->update_attribute(attribute_id, options); +} + +void AGGraphCancelUpdate() { + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.get() == nullptr) { + AG::precondition_failure("no attribute updating"); + } + + update_stack_ptr.get()->cancel(); +} + +bool AGGraphCancelUpdateIfNeeded() { + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.get() == nullptr) { + AG::precondition_failure("no attribute updating"); + } + + if (update_stack_ptr.get()->cancelled()) { + return true; + } + + if (update_stack_ptr.get()->graph()->passed_deadline()) { + update_stack_ptr.get()->cancel(); + return true; + } + + return false; +} + +bool AGGraphUpdateWasCancelled() { + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.tag() != 0 || update_stack_ptr.get() == nullptr) { + AG::precondition_failure("no attribute updating"); + } + + return update_stack_ptr.get()->cancelled(); +} + +void AGGraphSetUpdate(const void *update) { + AG::Graph::set_current_update(util::tagged_ptr((AG::Graph::UpdateStack *)update)); +} + +void AGGraphSetUpdateCallback(AGGraphRef graph, + void (*callback)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *callback_context) { + auto context = AG::Graph::Context::from_cf(graph); + context->set_update_callback(AG::ClosureFunctionVV(callback, callback_context)); +} + +void AGGraphClearUpdate() { + auto current_update = AG::Graph::current_update(); + if (current_update != nullptr && current_update.tag() == 0) { + AG::Graph::set_current_update(current_update.with_tag(true)); // TODO: tag is cleared... + } +} + +uint64_t AGGraphGetDeadline(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); + return context->deadline(); +} + +void AGGraphSetDeadline(AGGraphRef graph, uint64_t deadline) { + auto context = AG::Graph::Context::from_cf(graph); + context->set_deadline(deadline); +} + +bool AGGraphHasDeadlinePassed() { + auto current_update = AG::Graph::current_update(); + if (current_update != nullptr) { + return current_update.get()->graph()->passed_deadline(); + } + return false; +} + +void AGGraphSetNeedsUpdate(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); + context->set_needs_update(); +} + +void AGGraphWithUpdate(AGAttribute attribute, void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context) { + auto attribute_id = AG::AttributeID(attribute); + if (attribute_id.is_nil()) { + // TODO: AGGraphWithoutUpdate + auto update = AG::Graph::current_update(); + AG::Graph::set_current_update(update != nullptr ? update.with_tag(true) : nullptr); + function(context); + AG::Graph::set_current_update(update); + return; + } + + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + subgraph->graph()->with_update(attribute_id.to_node_ptr(), AG::ClosureFunctionVV(function, context)); +} + +void AGGraphWithoutUpdate(void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context) { + AG::Graph::without_update(AG::ClosureFunctionVV(function, context)); +} + +void AGGraphWithMainThreadHandler(AGGraphRef graph, + void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *body_context, + void (*main_thread_handler)(void (*thunk)(void *), + const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *main_thread_handler_context) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().with_main_handler(AG::ClosureFunctionVV(function, body_context), main_thread_handler, + main_thread_handler_context); +} + +#pragma mark - Values + +namespace { + +// TODO: inline +AGValue get_value(AG::AttributeID attribute_id, uint32_t zone_id, AGValueOptions options, + const AG::swift::metadata &metadata) { + if (!(options & AGValueOptions0x04)) { + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.tag() == 0 && update_stack_ptr.get() != nullptr) { + auto update_stack = update_stack_ptr.get(); + + auto graph = update_stack->graph(); + auto frame = update_stack->frames().back(); + + uint8_t state = 0; + void *value = graph->input_value_ref(frame.attribute, attribute_id, zone_id, options & 3, metadata, &state); + + // TODO: check if this is state or changed + return {value, (state & 1) == 1}; + } + } + + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute_id); + } + + bool changed = false; + void *value = subgraph->graph()->value_ref(attribute_id, zone_id, metadata, &changed); + + return {value, changed}; +} + +} // namespace + +bool AGGraphHasValue(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + return subgraph->graph()->value_exists(attribute_id.to_node_ptr()); +} + +AGValue AGGraphGetValue(AGAttribute attribute, AGValueOptions options, AGTypeID type) { + auto attribute_id = AG::AttributeID(attribute); + auto metadata = reinterpret_cast(type); + return get_value(attribute_id, 0, options, *metadata); +} + +AGValue AGGraphGetWeakValue(AGWeakAttribute attribute, AGValueOptions options, AGTypeID type) { + auto weak_attribute_id = AG::WeakAttributeID(attribute); + auto attribute_id = weak_attribute_id.evaluate(); + if (attribute_id.is_nil()) { + return {nullptr, false}; + } + + auto metadata = reinterpret_cast(type); + return get_value(attribute_id, weak_attribute_id.zone_id(), options, *metadata); +} + +bool AGGraphSetValue(AGAttribute attribute, void *value, AGTypeID type) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + auto metadata = reinterpret_cast(type); + return subgraph->graph()->value_set(attribute_id.to_node_ptr(), *metadata, value); +} + +AGGraphUpdateStatus AGGraphPrefetchValue(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr() + .assert_valid(); // TODO: make assert_value on AttributeID, don't care about kind at this point + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + if (subgraph->graph()->passed_deadline()) { + return AGGraphUpdateStatusChanged; + } + + auto resolved = attribute_id.resolve(AG::TraversalOptions::AssertNotNil); + + // TODO: typed options + return subgraph->graph()->update_attribute(resolved.attribute(), 6); +} + +#pragma mark - Inputs + +AGValue AGGraphGetInputValue(AGAttribute attribute, AGAttribute input_attribute, AGValueOptions options, + AGTypeID type) { + auto attribute_id = AG::AttributeID(attribute); + if (options & AGValueOptions0x04 || attribute_id.is_nil()) { + return AGGraphGetValue(input_attribute, options, type); + } + + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + auto input_attribute_id = AG::AttributeID(input_attribute); + auto metadata = reinterpret_cast(type); + + uint8_t state = 0; + void *value = subgraph->graph()->input_value_ref(attribute, input_attribute_id, 0, options & 3, *metadata, &state); + + // TODO: check if this is state or changed + return {value, (state & 1) == 1}; +} + +uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, uint8_t input_edge_flags) { + auto attribute_id = AG::AttributeID(attribute); + if (!attribute_id.is_direct()) { + AG::precondition_failure("non-direct attribute id: %u", attribute); + } + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + auto input_attribute_id = AG::AttributeID(input); + input_attribute_id.to_node_ptr().assert_valid(); + + if (input_attribute_id.subgraph() != nullptr && input_attribute_id.subgraph()->graph() != subgraph->graph()) { + AG::precondition_failure("accessing attribute in a different namespace: %u", input); + } + + return subgraph->graph()->add_input(attribute_id.to_node_ptr(), input_attribute_id, false, input_edge_flags); +} + +bool AGGraphAnyInputsChanged(const AGAttribute *exclude_attributes, uint64_t exclude_attributes_count) { + // TODO: tag must be whether frames is empty or not + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.tag() != 0 || update_stack_ptr.get() == nullptr) { + AG::precondition_failure("no attribute updating"); + } + + auto update_stack = update_stack_ptr.get(); + auto frame = update_stack->frames().back(); + + return update_stack->graph()->any_inputs_changed( + frame.attribute, reinterpret_cast(exclude_attributes), exclude_attributes_count); +} + +#pragma mark - Outputs + +void *AGGraphGetOutputValue(AGTypeID type) { + // TODO: tag must be whether frames is empty or not + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.tag() != 0 || update_stack_ptr.get() == nullptr) { + AG::precondition_failure("no attribute updating"); + } + + auto update_stack = update_stack_ptr.get(); + auto frame = update_stack->frames().back(); + + auto graph = update_stack->graph(); + auto metadata = reinterpret_cast(type); + return graph->output_value_ref(frame.attribute, *metadata); +} + void AGGraphSetOutputValue(void *value, AGTypeID type) { + // TODO: tag must be whether frames is empty or not + auto update_stack_ptr = AG::Graph::current_update(); + if (update_stack_ptr.tag() != 0 || update_stack_ptr.get() == nullptr) { + AG::precondition_failure("no attribute updating"); + } + + auto update_stack = update_stack_ptr.get(); + auto frame = update_stack->frames().back(); + + if (!frame.attribute->state().is_updating()) { + // TODO: change to is_evaluating() + AG::precondition_failure("writing attribute that is not evaluating: %", frame.attribute); + } + + auto graph = update_stack->graph(); + auto metadata = reinterpret_cast(type); + graph->value_set_internal(frame.attribute, *frame.attribute.get(), value, *metadata); +} + +#pragma mark - Tracing + +bool AGGraphIsTracingActive(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); + return context->graph().is_tracing_active(); +} + +void AGGraphPrepareTrace(AGGraphRef graph, void *trace_vtable, void *trace) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().remove_trace(0); + + auto external_trace = new ExternalTrace(reinterpret_cast(trace_vtable), trace); + context->graph().prepare_trace(*external_trace); +} + +uint64_t AGGraphAddTrace(AGGraphRef graph, void *interface, void *trace_info) { + auto context = AG::Graph::Context::from_cf(graph); + auto trace = new ExternalTrace(reinterpret_cast(interface), trace_info); + context->graph().add_trace(trace); + return trace->trace_id(); +} + +void AGGraphRemoveTrace(AGGraphRef graph, uint64_t trace_id) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().remove_trace(trace_id); +} + +void AGGraphStartTracing(AGGraphRef graph, uint32_t tracing_flags) { AGGraphStartTracing2(graph, tracing_flags, 0); } + +void AGGraphStartTracing2(AGGraphRef graph, uint32_t tracing_flags, uint32_t unknown) { + if (!graph) { + AG::Graph::all_start_tracing(tracing_flags, {}); + return; + } + auto context = AG::Graph::Context::from_cf(graph); + context->graph().start_tracing(tracing_flags, {}); +} + +void AGGraphStopTracing(AGGraphRef graph) { + if (!graph) { + AG::Graph::all_stop_tracing(); + return; + } + auto context = AG::Graph::Context::from_cf(graph); + context->graph().stop_tracing(); +} + +void AGGraphSyncTracing(AGGraphRef graph) { + if (!graph) { + AG::Graph::all_sync_tracing(); + return; + } + auto context = AG::Graph::Context::from_cf(graph); + context->graph().sync_tracing(); +} + +CFStringRef AGGraphCopyTracePath(AGGraphRef graph) { + if (graph == nullptr) { + return AG::Graph::all_copy_trace_path(); + } + auto context = AG::Graph::Context::from_cf(graph); + return context->graph().copy_trace_path(); +} + +void AGGraphSetTrace(AGGraphRef graph, void *trace_vtable, void *trace) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().remove_trace(0); + + auto external_trace = new ExternalTrace(0, reinterpret_cast(trace_vtable), trace); + context->graph().add_trace(external_trace); +} + +void AGGraphResetTrace(AGGraphRef graph) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().remove_trace(0); +} + +bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id) { + auto context = AG::Graph::Context::from_cf(graph); + for (auto trace : context->graph().traces()) { + if (trace->named_event_enabled(event_id)) { + return true; + } + } + return false; +} + +void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, void *value, AGTypeID type) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().foreach_trace([&context, &event_name, &value, &type](AG::Trace &trace) { + trace.custom_event(*context, event_name, value, *reinterpret_cast(type)); + }); +} + +void AGGraphAddNamedTraceEvent(AGGraphRef graph, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, + CFDataRef data, uint32_t arg6) { + auto context = AG::Graph::Context::from_cf(graph); + context->graph().foreach_trace([&context, &event_id, &num_event_args, &event_args, &data, &arg6](AG::Trace &trace) { + trace.named_event(*context, event_id, num_event_args, event_args, data, arg6); + }); +} + +const char *AGGraphGetTraceEventName(uint32_t event_id) { // TODO: not implemented + return nullptr; } -void AGGraphArchiveJSON(const char *filename) { +const char *AGGraphGetTraceEventSubsystem(uint32_t event_id) { // TODO: not implemented + return nullptr; +} + +void AGGraphRegisterNamedTraceEvent() { + // TODO: not implemented +} + +#pragma mark - Profiler + +bool AGGraphIsProfilingEnabled(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + return subgraph->graph()->is_profiling_enabled(); +} + +uint64_t AGGraphBeginProfileEvent(AGAttribute attribute, const char *event_name) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + auto resolved = attribute_id.resolve(AG::TraversalOptions::AssertNotNil); + return subgraph->graph()->begin_profile_event(resolved.attribute().to_node_ptr(), event_name); +} + +void AGGraphEndProfileEvent(AGAttribute attribute, const char *event_name, uint64_t start_time, bool changed) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + auto resolved = attribute_id.resolve(AG::TraversalOptions::AssertNotNil); + subgraph->graph()->end_profile_event(resolved.attribute().to_node_ptr(), event_name, start_time, changed); +} + +void AGGraphStartProfiling(AGGraphRef graph) { + if (!graph) { + AG::Graph::all_start_profiling(1); + return; + } + auto context = AG::Graph::Context::from_cf(graph); + context->graph().start_profiling(1); +} + +void AGGraphStopProfiling(AGGraphRef graph) { + if (!graph) { + AG::Graph::all_stop_profiling(); + return; + } + auto context = AG::Graph::Context::from_cf(graph); + context->graph().stop_profiling(); +} + +void AGGraphMarkProfile(AGGraphRef graph, const char *name, uint64_t time) { + if (!graph) { + AG::Graph::all_mark_profile(name); + return; + } + auto context = AG::Graph::Context::from_cf(graph); + uint32_t event_id = context->graph().intern_key(name); + context->graph().mark_profile(event_id, time); +} + +void AGGraphResetProfile(AGGraphRef graph) { + if (!graph) { + AG::Graph::all_reset_profile(); + return; + } + auto context = AG::Graph::Context::from_cf(graph); + context->graph().reset_profile(); +} + +#pragma mark - Description + +void AGGraphDescription(AGGraphRef graph, CFDictionaryRef options) { + if (graph == nullptr) { + return AG::Graph::description(nullptr, options); + } + auto context = AG::Graph::Context::from_cf(graph); + return AG::Graph::description(&context->graph(), options); +} + +void AGGraphArchiveJSON(const char *filename) { AG::Graph::write_to_file(nullptr, filename, false); } + +void AGGraphArchiveJSON2(const char *filename, bool exclude_values) { + AG::Graph::write_to_file(nullptr, filename, exclude_values); } diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 69d3763..54cafd5 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -1,22 +1,493 @@ #pragma once #include +#include #include "AGSwiftSupport.h" +#include "Attribute/AGAttribute.h" +#include "Attribute/AGWeakAttribute.h" +#include "Subgraph/AGSubgraph.h" #include "Swift/AGType.h" CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN +// MARK: CFType + typedef struct CF_BRIDGED_TYPE(id) AGGraphStorage *AGGraphRef AG_SWIFT_NAME(Graph); +typedef struct CF_BRIDGED_TYPE(id) AGGraphContextStorage *AGGraphContextRef AG_SWIFT_NAME(GraphContext); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +CFTypeID AGGraphGetTypeID(); + +// MARK: Graph Context + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGGraphRef AGGraphCreate(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGGraphRef AGGraphCreateShared(AGGraphRef _Nullable graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGGraphContextRef AGGraphGetGraphContext(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGGraphRef AGGraphContextGetGraph(AGGraphContextRef context); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void *AGGraphGetContext(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetContext(); + +// MARK: Counter + +typedef CF_ENUM(uint32_t, AGGraphCounterQuery) { + AGGraphCounterQueryNodeCount, + AGGraphCounterQueryTransactionCount, + AGGraphCounterQueryUpdateCount, + AGGraphCounterQueryChangeCount, + AGGraphCounterQueryContextID, + AGGraphCounterQueryGraphID, + AGGraphCounterQueryContextThreadUpdating, + AGGraphCounterQueryThreadUpdating, + AGGraphCounterQueryContextNeedsUpdate, + AGGraphCounterQueryNeedsUpdate, + AGGraphCounterQueryMainThreadUpdateCount, + AGGraphCounterQueryNodeTotalCount, + AGGraphCounterQuerySubgraphCount, + AGGraphCounterQuerySubgraphTotalCount, +}; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint64_t AGGraphGetCounter(AGGraphRef graph, AGGraphCounterQuery query); + +// MARK: Subgraphs + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphBeginDeferringSubgraphInvalidation(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphEndDeferringSubgraphInvalidation(AGGraphRef graph); + +// MARK: Attribute types + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint32_t AGGraphInternAttributeType(AGGraphRef graph, AGTypeID type, + void *_Nonnull (*_Nonnull intern)(const void *context AG_SWIFT_CONTEXT) + AG_SWIFT_CC(swift), + const void *context); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphVerifyType(AGAttribute attribute, AGTypeID type); + +// MARK: Attributes + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphCreateAttribute(uint32_t type_id, void *body, void *value); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute); + +// TODO: need this? +// typedef struct AGAttributeType { +// AGTypeID typeID; +// AGTypeID valueTypeID; +// AGClosureStorage update; +// AGAttributeVTable vTable; +// AGAttributeTypeFlags flags; +//} AGAttributeType; + +typedef struct AGAttributeInfo { + const void *type; + const void *body; +} AGAttributeInfo; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint8_t AGGraphGetFlags(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetFlags(AGAttribute attribute, uint8_t flags); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool flag, + const void (*modify)(void *body, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context); + +typedef CF_OPTIONS(uint32_t, AGSearchOptions) { + AGSearchOptionsSearchInputs = 1 << 0, + AGSearchOptionsSearchOutputs = 1 << 1, + AGSearchOptionsTraverseGraphContexts = 1 << 2, +}; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, + bool (*predicate)(AGAttribute attribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context); + +// MARK: Cached attributes + +CF_EXPORT +void AGGraphReadCachedAttribute(); + +CF_EXPORT +void AGGraphReadCachedAttributeIfExists(); + +// MARK: Current attribute + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphGetCurrentAttribute(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphCurrentAttributeWasModified(); + +// MARK: Indirect attributes + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphCreateIndirectAttribute(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphCreateIndirectAttribute2(AGAttribute attribute, size_t size); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphGetIndirectAttribute(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetIndirectAttribute(AGAttribute attribute, AGAttribute source); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphResetIndirectAttribute(AGAttribute attribute, bool non_nil); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphGetIndirectDependency(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetIndirectDependency(AGAttribute attribute, AGAttribute dependency); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphRegisterDependency(AGAttribute dependency, uint8_t input_edge_flags); + +// MARK: Offset attributes + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphCreateOffsetAttribute(AGAttribute attribute, uint32_t offset); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGGraphCreateOffsetAttribute2(AGAttribute attribute, uint32_t offset, size_t size); + +// MARK: Updates + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphInvalidate(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphInvalidateAllValues(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphInvalidateValue(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetInvalidationCallback(AGGraphRef graph, + void (*callback)(AGAttribute, const void *context AG_SWIFT_CONTEXT) + AG_SWIFT_CC(swift), + const void *callback_context); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphUpdateValue(AGAttribute attribute, uint8_t options); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphCancelUpdate(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphCancelUpdateIfNeeded(); -void AGGraphCreate(); +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphUpdateWasCancelled(); +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetUpdate(void *update); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetUpdateCallback(AGGraphRef graph, + void (*callback)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *callback_context); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphClearUpdate(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint64_t AGGraphGetDeadline(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetDeadline(AGGraphRef graph, uint64_t deadline); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphHasDeadlinePassed(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetNeedsUpdate(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphWithUpdate(AGAttribute attribute, void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphWithoutUpdate(void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *context); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphWithMainThreadHandler(AGGraphRef graph, + void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *body_context, + void (*main_thread_handler)(void (*thunk)(void *), + const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *main_thread_handler_context); + +// MARK: Values + +typedef struct AGValue { + void *value; + bool changed; +} AGValue; + +typedef CF_OPTIONS(uint32_t, AGValueOptions) { + // options & 3 == AG::InputEdge::Flags + AGValueOptionsInputEdgeFlagsUnprefetched = 1 << 0, + AGValueOptionsInputEdgeFlagsUnknown1 = 1 << 1, + + AGValueOptions0x04 = 1 << 2, + + // AGValueOptionsUnknown1 = 1 << 0, + // AGValueOptionsValidateDirect = 1 << 1, + // AGValueOptionsUnknown3 = 1 << 2, + // AGValueOptionsResolveIndirect = 1 << 3, + // AGValueOptionsResolveWeak = 1 << 4, +}; + +typedef CF_OPTIONS(uint32_t, AGGraphUpdateStatus) { + AGGraphUpdateStatusNoChange = 0, + AGGraphUpdateStatusChanged = 1, + AGGraphUpdateStatusOption2 = 2, + AGGraphUpdateStatusNeedsCallMainHandler = 3, +}; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphHasValue(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGValue AGGraphGetValue(AGAttribute attribute, AGValueOptions options, AGTypeID type); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGValue AGGraphGetWeakValue(AGWeakAttribute attribute, AGValueOptions options, AGTypeID type); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphSetValue(AGAttribute attribute, void *value, AGTypeID type); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGGraphUpdateStatus AGGraphPrefetchValue(AGAttribute attribute); + +// MARK: Inputs + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGValue AGGraphGetInputValue(AGAttribute attribute, AGValueOptions options, AGTypeID type); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, uint8_t input_edge_flags); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphAnyInputsChanged(const AGAttribute *exclude_attributes, uint64_t exclude_attributes_count); + +// MARK: Outputs + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void *AGGraphGetOutputValue(AGTypeID type); + +CF_EXPORT +CF_REFINED_FOR_SWIFT void AGGraphSetOutputValue(void *value, AGTypeID type); +// MARK: Tracing + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphIsTracingActive(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphPrepareTrace(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint64_t AGGraphAddTrace(AGGraphRef graph, void *interface, void *trace_info); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphRemoveTrace(AGGraphRef graph, uint64_t trace_id); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphStartTracing(AGGraphRef graph, uint32_t tracing_flags); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphStartTracing2(AGGraphRef graph, uint32_t tracing_flags, uint32_t unknown); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphStopTracing(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSyncTracing(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +CFStringRef AGGraphCopyTracePath(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphSetTrace(AGGraphRef graph, void *trace_vtable, void *trace); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphResetTrace(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, void *value, AGTypeID type); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphAddNamedTraceEvent(AGGraphRef graph, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, + CFDataRef data, uint32_t arg6); + +CF_EXPORT +const char *AGGraphGetTraceEventName(uint32_t event_id); + +CF_EXPORT +const char *AGGraphGetTraceEventSubsystem(uint32_t event_id); + +CF_EXPORT +void AGGraphRegisterNamedTraceEvent(); + +// MARK: Profiler + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGGraphIsProfilingEnabled(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint64_t AGGraphBeginProfileEvent(AGAttribute attribute, const char *event_name); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphEndProfileEvent(AGAttribute attribute, const char *event_name, uint64_t start_time, bool changed); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphStartProfiling(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphStopProfiling(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphMarkProfile(AGGraphRef graph, const char *event_name, uint64_t time); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphResetProfile(AGGraphRef graph); + +// MARK: Description + +CF_EXPORT +CF_REFINED_FOR_SWIFT +CFStringRef AGGraphDescription(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT void AGGraphArchiveJSON(const char *filename); +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGGraphArchiveJSON2(const char *filename, bool exclude_values); + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Context.cpp b/Sources/ComputeCxx/Graph/Context.cpp index 81193d2..37ca61e 100644 --- a/Sources/ComputeCxx/Graph/Context.cpp +++ b/Sources/ComputeCxx/Graph/Context.cpp @@ -1,5 +1,6 @@ #include "Context.h" +#include "AGGraph-Private.h" #include "Attribute/AttributeID.h" #include "Subgraph/Subgraph.h" #include "Trace/Trace.h" @@ -10,7 +11,7 @@ namespace AG { Graph::Context::Context(Graph *graph) { _graph = graph; - _field_0x08 = 0; + _context_info = nullptr; _unique_id = AGMakeUniqueID(); _deadline = UINT64_MAX; @@ -50,16 +51,13 @@ Graph::Context::~Context() { if (_graph && _graph->_num_contexts == 0) { _graph->~Graph(); } - - _invalidation_closure.release_context(); - _update_closure.release_context(); } Graph::Context *Graph::Context::from_cf(AGGraphStorage *storage) { - if (storage->_context._invalidated) { + if (storage->context._invalidated) { precondition_failure("invalidated graph"); } - return &storage->_context; + return &storage->context; } void Graph::Context::set_deadline(uint64_t deadline) { @@ -101,14 +99,14 @@ bool Graph::Context::thread_is_updating() { void Graph::Context::call_invalidation(AttributeID attribute) { _graph_version = _graph->_version; - if (_invalidation_closure) { + if (_invalidation_callback) { auto old_update = current_update(); if (old_update.value() != 0) { set_current_update(old_update.with_tag(true)); } _graph->foreach_trace([this, &attribute](Trace &trace) { trace.begin_invalidation(*this, attribute); }); - _invalidation_closure(attribute); + _invalidation_callback(attribute); _graph->foreach_trace([this, &attribute](Trace &trace) { trace.end_invalidation(*this, attribute); }); set_current_update(old_update); @@ -121,12 +119,12 @@ void Graph::Context::call_update() { } _needs_update = false; - if (_update_closure) { + if (_update_callback) { auto stack = UpdateStack(_graph, UpdateStack::Option::SetTag | UpdateStack::Option::InvalidateSubgraphs); _graph->foreach_trace([this](Trace &trace) { trace.begin_update(*this); }); - _update_closure(); + _update_callback(); _graph->foreach_trace([this](Trace &trace) { trace.end_update(*this); }); // ~UpdateStack() called here diff --git a/Sources/ComputeCxx/Graph/Context.h b/Sources/ComputeCxx/Graph/Context.h index d97c302..7b3d4dc 100644 --- a/Sources/ComputeCxx/Graph/Context.h +++ b/Sources/ComputeCxx/Graph/Context.h @@ -2,6 +2,7 @@ #include +#include "Attribute/AGAttribute.h" #include "Attribute/AttributeID.h" #include "Graph.h" #include "Private/CFRuntime.h" @@ -15,16 +16,16 @@ namespace AG { class Graph::Context { private: Graph *_graph; - void *_field_0x08; + const void *_context_info; uint64_t _unique_id; - ClosureFunctionAV _invalidation_closure; - ClosureFunctionVV _update_closure; + ClosureFunctionAV _invalidation_callback; + ClosureFunctionVV _update_callback; uint64_t _deadline; uint64_t _graph_version; bool _needs_update; - bool _invalidated; + bool _invalidated; // set to true after destru public: Context(Graph *graph); @@ -34,12 +35,28 @@ class Graph::Context { static Context *from_cf(AGGraphStorage *storage); Graph &graph() const { return *_graph; }; + + const void *context_info() const { return _context_info; }; + void set_context_info(const void *context_info) { _context_info = context_info; }; + uint64_t unique_id() const { return _unique_id; }; - uint64_t graph_version() const { return _graph_version; }; + void set_invalidation_callback(AG::ClosureFunctionAV callback) { + _invalidation_callback = callback; + } + void set_update_callback(AG::ClosureFunctionVV callback) { _update_callback = callback; } + + uint64_t deadline() const { return _deadline; }; void set_deadline(uint64_t deadline); + + uint64_t graph_version() const { return _graph_version; }; + + bool needs_update() const { return _needs_update; }; void set_needs_update(); + bool invalidated() const { return _invalidated; }; + void set_invalidated(bool invalidated) { _invalidated = invalidated; }; + bool thread_is_updating(); void call_invalidation(AttributeID attribute); @@ -48,9 +65,4 @@ class Graph::Context { } // namespace AG -struct AGGraphStorage { - CFRuntimeBase _base; - AG::Graph::Context _context; -}; - CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 18208d2..9e9d192 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -206,13 +206,13 @@ Graph::Context *Graph::main_context() const { Graph::without_invalidating::without_invalidating(Graph *graph) { _graph = graph; - _graph_old_batch_invalidate_subgraphs = graph->_batch_invalidate_subgraphs; - graph->_batch_invalidate_subgraphs = true; + _graph_old_batch_invalidate_subgraphs = graph->_deferring_subgraph_invalidation; + graph->_deferring_subgraph_invalidation = true; } Graph::without_invalidating::~without_invalidating() { if (_graph && _graph_old_batch_invalidate_subgraphs == false) { - _graph->_batch_invalidate_subgraphs = false; + _graph->_deferring_subgraph_invalidation = false; _graph->invalidate_subgraphs(); } } @@ -250,7 +250,7 @@ void Graph::remove_subgraph(Subgraph &subgraph) { } void Graph::invalidate_subgraphs() { - if (_batch_invalidate_subgraphs) { + if (_deferring_subgraph_invalidation) { return; } @@ -341,6 +341,31 @@ void Graph::with_update(data::ptr node, ClosureFunctionVV body) // ~scoped_update called } +void Graph::without_update(ClosureFunctionVV body) { + // TODO: use RAII pattern here too? + // TODO: is tag here update not active or is paused? + + auto previous_update = current_update(); + AG::Graph::set_current_update(previous_update != nullptr ? previous_update.with_tag(true) : nullptr); + body(); + AG::Graph::set_current_update(previous_update); +} + +void Graph::with_main_handler(ClosureFunctionVV body, MainHandler _Nullable main_handler, + const void *main_handler_context) { + + auto old_main_handler = _main_handler; + auto old_main_handler_context = _main_handler_context; + + _main_handler = main_handler; + _main_handler_context = main_handler_context; + + body(); + + _main_handler = old_main_handler; + _main_handler_context = old_main_handler_context; +} + void Graph::call_main_handler(void *context, void (*body)(void *)) { assert(_main_handler); @@ -399,7 +424,7 @@ bool Graph::passed_deadline_slow() { const AttributeType &Graph::attribute_type(uint32_t type_id) const { return *_types[type_id]; } -const AttributeType &Graph::attribute_ref(data::ptr node, const void *_Nullable *_Nullable self_out) const { +const AttributeType *Graph::attribute_ref(data::ptr node, const void *_Nullable *_Nullable self_out) const { auto &type = attribute_type(node->type_id()); if (self_out) { *self_out = node->get_self(type); @@ -408,7 +433,7 @@ const AttributeType &Graph::attribute_ref(data::ptr node, const void *_Nul } void Graph::attribute_modify(data::ptr node, const swift::metadata &metadata, - ClosureFunctionPV modify, bool flag) { + ClosureFunctionPV modify, bool update_flags) { if (!node->state().is_self_initialized()) { precondition_failure("no self data: %u", node); } @@ -425,8 +450,8 @@ void Graph::attribute_modify(data::ptr node, const swift::metadata &metada foreach_trace([&node](Trace &trace) { trace.end_modify(node); }); - if (flag) { - node->flags().set_value4_unknown0x40(true); + if (update_flags) { + node->flags().set_self_modified(true); mark_pending(node, node.get()); } } @@ -716,7 +741,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, #pragma mark - Indirect attributes -void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, +data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, std::optional size, bool is_mutable) { if (subgraph.graph() != attribute.subgraph()->graph()) { precondition_failure("attribute references can't cross graph namespaces"); @@ -751,6 +776,7 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui add_input_dependencies(AttributeID(indirect_node).with_kind(AttributeID::Kind::Indirect), attribute); subgraph.add_indirect((data::ptr)indirect_node, true); + return indirect_node; } else { auto indirect_node = (data::ptr)subgraph.alloc_bytes_recycle(sizeof(Node), 3); @@ -761,6 +787,7 @@ void Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, ui *indirect_node = IndirectNode(source, traverses_contexts, offset, node_size); subgraph.add_indirect(indirect_node, &subgraph != attribute.subgraph()); + return indirect_node; } } @@ -948,16 +975,15 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, #pragma mark - Values -void *Graph::value_ref(AttributeID attribute, bool evaluate_weak_references, const swift::metadata &value_type, - bool *did_update_out) { +void *Graph::value_ref(AttributeID attribute, uint32_t zone_id, const swift::metadata &value_type, bool *changed_out) { _version += 1; - OffsetAttributeID resolved = attribute.resolve( - TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | - (evaluate_weak_references ? TraversalOptions::EvaluateWeakReferences : TraversalOptions::AssertNotNil)); + OffsetAttributeID resolved = + attribute.resolve(TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + (zone_id != 0 ? TraversalOptions::EvaluateWeakReferences : TraversalOptions::AssertNotNil)); - if (evaluate_weak_references && (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct())) { + if (zone_id != 0 && (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct())) { return nullptr; } @@ -969,13 +995,13 @@ void *Graph::value_ref(AttributeID attribute, bool evaluate_weak_references, con increment_transaction_count_if_needed(); uint64_t old_page_seed = 0; - if (evaluate_weak_references) { + if (zone_id != 0) { old_page_seed = data::table::shared().raw_page_seed(resolved.attribute().page_ptr()); } UpdateStatus status = update_attribute(resolved.attribute(), 0); if (status != UpdateStatus::NoChange) { - *did_update_out = true; + *changed_out = true; } // check new page seed is same as old and zone is not deleted @@ -1095,7 +1121,7 @@ void Graph::value_mark(data::ptr node) { if (type.use_graph_as_initial_value()) { mark_changed(node, nullptr, nullptr, nullptr); } else { - node->flags().set_value4_unknown0x40(true); + node->flags().set_self_modified(true); if (!node->state().is_dirty()) { foreach_trace([&node](Trace &trace) { trace.set_dirty(node, true); }); @@ -1275,8 +1301,45 @@ void Graph::propagate_dirty(AttributeID attribute) { #pragma mark - Inputs -void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_attribute, bool evaluate_weak_references, - uint8_t input_flags, const swift::metadata &type, char *arg5, uint32_t index) { +// TODO: inline +void *Graph::input_value_ref(data::ptr node, AttributeID input_attribute, uint32_t zone_id, + uint8_t input_flags, const swift::metadata &type, uint8_t *state_out) { + + // TODO: double check this is input_attribute and not node + auto comparator = InputEdge::Comparator( + input_attribute, InputEdge::Flags::Unprefetched | InputEdge::Flags::Unknown1 | InputEdge::Flags::AlwaysEnabled, + input_flags); + + // TODO: index_of_input<0x120> + uint32_t index = index_of_input(*node.get(), comparator); + + if (index < 0) { + // TODO: AVGalueOptions is same as InputEdge::Flags ? + return input_value_ref_slow(frame.attribute, attribute_id, zone_id, options, metadata, state_out, index); + } + + AG::OffsetAttributeID resolved = + attribute_id.resolve(AG::TraversalOptions::UpdateDependencies | AG::TraversalOptions::AssertNotNil); + if (resolved.attribute().to_node().state().is_value_initialized() && + !resolved.attribute().to_node().state().is_dirty()) { + auto input_edge = node->inputs()[index]; + bool changed = input_edge.is_changed(); + input_edge.set_unknown4(true); // TODO: does this set by reference? + void *value = resolved.attribute().to_node().get_value(); + value = (uint8_t *)value + resolved.offset(); + + state_out |= changed; + return value; + } + + // TODO: combine with block above, make index signed first though + // TODO: AVGalueOptions is same as InputEdge::Flags ? + return graph->input_value_ref_slow(frame.attribute, attribute_id, zone_id, options, metadata, state_out, index); +} + +void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_attribute, uint32_t zone_id, + uint8_t input_flags, const swift::metadata &type, uint8_t *state_out, + uint32_t index) { if ((input_flags >> 1) & 1) { auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, @@ -1291,18 +1354,17 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at } if (!node->state().is_dirty()) { auto resolved = input_attribute.resolve( - evaluate_weak_references - ? TraversalOptions::UpdateDependencies | TraversalOptions::EvaluateWeakReferences - : TraversalOptions::UpdateDependencies | TraversalOptions::AssertNotNil); + zone_id != 0 ? TraversalOptions::UpdateDependencies | TraversalOptions::EvaluateWeakReferences + : TraversalOptions::UpdateDependencies | TraversalOptions::AssertNotNil); - if (evaluate_weak_references) { + if (zone_id != 0) { if (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct()) { return 0; } } update_attribute(resolved.attribute(), 0); } - index = add_input(node, input_attribute, evaluate_weak_references, input_flags & 1); + index = add_input(node, input_attribute, zone_id != 0, input_flags & 1); if (index < 0) { return nullptr; } @@ -1314,12 +1376,12 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at input_edge.set_unknown4(true); OffsetAttributeID resolved = input_edge.value.resolve( - evaluate_weak_references ? TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | - TraversalOptions::EvaluateWeakReferences - : TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | - TraversalOptions::AssertNotNil); + zone_id != 0 ? TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + TraversalOptions::EvaluateWeakReferences + : TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + TraversalOptions::AssertNotNil); - if (evaluate_weak_references) { + if (zone_id != 0) { if (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct()) { return nullptr; } @@ -1327,7 +1389,7 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at if (!resolved.attribute().to_node().state().is_value_initialized() || resolved.attribute().to_node().state().is_dirty()) { - if (evaluate_weak_references) { + if (zone_id != 0) { auto zone_id_before_update = data::table::shared().raw_page_seed(resolved.attribute().page_ptr()); update_attribute(resolved.attribute(), 0); @@ -1346,13 +1408,13 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at } if (resolved.attribute().to_node().state().is_pending()) { - *arg5 |= 1; + *state_out |= 1; } if ((input_flags >> 1) & 1 && resolved.attribute().to_node().state().is_self_initialized() && !type.getValueWitnesses()->isPOD()) { resolved.attribute().to_node().set_state( resolved.attribute().to_node().state().with_main_thread_only(true)); // TODO: check - *arg5 |= 2; + *state_out |= 2; } if (resolved.offset() == 0) { @@ -1499,15 +1561,27 @@ void Graph::remove_removed_input(AttributeID attribute, AttributeID input) { } } -bool Graph::any_inputs_changed(data::ptr node, const AttributeID *attributes, uint64_t count) { +namespace { + +// TODO: inline, or add to ArrayRef +size_t find_attribute(const AttributeID *attributes, AttributeID search, uint64_t count) { + static_assert(sizeof(wchar_t) == sizeof(AttributeID)); // TODO: check + + const AttributeID *location = (const AttributeID *)wmemchr((const wchar_t *)attributes, search, count); + if (location == nullptr) { + return count; + } + return (location - attributes) / sizeof(AttributeID); +} + +} // namespace + +bool Graph::any_inputs_changed(data::ptr node, const AttributeID *exclude_attributes, + uint64_t exclude_attributes_count) { for (auto input : node->inputs()) { input.set_unknown4(true); if (input.is_changed()) { - const AttributeID *location = (const AttributeID *)wmemchr((const wchar_t *)attributes, input.value, count); - if (location == 0) { - location = attributes + sizeof(AttributeID) * count; - } - if ((location - attributes) / sizeof(AttributeID) == count) { + if (find_attribute(exclude_attributes, input.value, exclude_attributes_count) == exclude_attributes_count) { return true; } } @@ -1523,6 +1597,7 @@ void Graph::all_inputs_removed(data::ptr node) { } } +// TODO: make threshold a template uint32_t Graph::index_of_input(Node &node, InputEdge::Comparator comparator) { if (node.inputs().size() > 8) { return index_of_input_slow(node, comparator); @@ -1849,7 +1924,7 @@ const char *Graph::key_name(uint32_t key_id) const { AG::precondition_failure("invalid string key id: %u", key_id); } -uint32_t Graph::intern_type(swift::metadata *metadata, ClosureFunctionVP make_type) { +uint32_t Graph::intern_type(const swift::metadata *metadata, ClosureFunctionVP make_type) { uint32_t type_id = uint32_t(reinterpret_cast(_type_ids_by_metadata.lookup(metadata, nullptr))); if (type_id) { return type_id; @@ -1991,7 +2066,7 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { encoder.encode_varint(0x70); encoder.encode_varint(true); } - if (node.flags().value4_unknown0x40()) { + if (node.flags().self_modified()) { encoder.encode_varint(0x78); encoder.encode_varint(true); } @@ -2301,15 +2376,15 @@ void Graph::trace_assertion_failure(bool all_stop_tracing, const char *format, . uint64_t Graph::begin_profile_event(data::ptr node, const char *event_name) { foreach_trace([this, &node, &event_name](Trace &trace) { trace.begin_event(node, intern_key(event_name)); }); - if (_is_profiling) { + if (_is_profiling_enabled) { return mach_absolute_time(); } return 0; } -void Graph::end_profile_event(data::ptr node, const char *event_name, uint64_t start_time, bool flag) { +void Graph::end_profile_event(data::ptr node, const char *event_name, uint64_t start_time, bool changed) { auto event_id = intern_key(event_name); - if (_is_profiling) { + if (_is_profiling_enabled) { if (_profile_data == nullptr) { _profile_data.reset(new ProfileData(this)); } @@ -2321,7 +2396,7 @@ void Graph::end_profile_event(data::ptr node, const char *event_name, uint } auto category = _profile_data.get()->categories().try_emplace(event_id).first->second; - category.add_update(node, duration, flag); + category.add_update(node, duration, changed); _profile_data->set_has_unmarked_categories(true); } @@ -2329,7 +2404,7 @@ void Graph::end_profile_event(data::ptr node, const char *event_name, uint } void Graph::add_profile_update(data::ptr node, uint64_t duration, bool changed) { - if (_is_profiling) { + if (_is_profiling_enabled) { if (_profile_data == nullptr) { _profile_data.reset(new ProfileData(this)); } @@ -2344,7 +2419,7 @@ void Graph::add_profile_update(data::ptr node, uint64_t duration, bool cha } void Graph::start_profiling(uint32_t profiler_flags) { - _is_profiling = profiler_flags & 1; + _is_profiling_enabled = profiler_flags & 1; // TODO: Make Graph::ProfilerFlags if ((profiler_flags >> 1) & 1) { AGAppObserverStartObserving(); @@ -2361,7 +2436,7 @@ void Graph::start_profiling(uint32_t profiler_flags) { } } } - if (_is_profiling && _profile_trace == nullptr) { + if (_is_profiling_enabled && _profile_trace == nullptr) { _profile_trace = new ProfileTrace(); add_trace(_profile_trace); } @@ -2372,7 +2447,7 @@ void Graph::stop_profiling() { remove_trace(_profile_trace->trace_id()); _profile_trace = nullptr; } - _is_profiling = false; + _is_profiling_enabled = false; } void Graph::mark_profile(uint32_t event_id, uint64_t time) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 1c5dc7a..35eedd1 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -7,6 +7,7 @@ #include #include +#include "AGSwiftSupport.h" #include "AGValue.h" #include "Attribute/AttributeID.h" #include "Attribute/Node/Edge.h" @@ -66,7 +67,7 @@ class Graph { void push_back(TreeElementNodePair pair) { _nodes.push_back(pair); }; }; - using MainHandler = void (*)(void (*thunk)(void *), void *thunk_context); // needs AG_SWIFT_CC(swift) ? + typedef void (*MainHandler)(void (*thunk)(void *), const void *thunk_context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift); private: static Graph *_all_graphs; @@ -94,7 +95,7 @@ class Graph { // Main thread handler MainHandler _Nullable _main_handler; - void *_Nullable _main_handler_context; + const void *_Nullable _main_handler_context; // Metrics uint64_t _num_nodes = 0; @@ -104,7 +105,7 @@ class Graph { size_t _num_node_value_bytes = 0; // Profile - bool _is_profiling; + bool _is_profiling_enabled; std::unique_ptr _profile_data; ProfileTrace *_Nullable _profile_trace; @@ -119,7 +120,7 @@ class Graph { vector _subgraphs; vector _subgraphs_with_cached_nodes; vector _invalidated_subgraphs; - bool _batch_invalidate_subgraphs; + bool _deferring_subgraph_invalidation; // Updates bool _needs_update; @@ -145,6 +146,16 @@ class Graph { bool is_context_updating(uint64_t context_id); Context *_Nullable main_context() const; + static void will_add_to_context(Graph *graph) { graph->_num_contexts += 1; }; + static void did_remove_from_context(Graph *graph) { + graph->_num_contexts -= 1; + if (graph->_num_contexts == 0) { + delete graph; + } + }; + + Context *context_with_id(uint64_t context_id) const { return _contexts_by_id.lookup(context_id, nullptr); } + // MARK: Subgraphs class without_invalidating { @@ -165,8 +176,8 @@ class Graph { void will_invalidate_subgraph(Subgraph &subgraph) { _invalidated_subgraphs.push_back(&subgraph); }; void invalidate_subgraphs(); - bool batch_invalidate_subgraphs() { return _batch_invalidate_subgraphs; }; - void set_batch_invalidate_subgraphs(bool value) { _batch_invalidate_subgraphs = value; }; + bool is_deferring_subgraph_invalidation() { return _deferring_subgraph_invalidation; }; + void set_deferring_subgraph_invalidation(bool value) { _deferring_subgraph_invalidation = value; }; void remove_subgraphs_with_cached_node(Subgraph *subgraph); // overload with iter? void add_subgraphs_with_cached_node(Subgraph *subgraph) { _subgraphs_with_cached_nodes.push_back(subgraph); } @@ -187,8 +198,11 @@ class Graph { void collect_stack(vector, 0, uint64_t> &nodes); void with_update(data::ptr node, ClosureFunctionVV body); + static void without_update(ClosureFunctionVV body); MainHandler _Nullable main_handler() { return _main_handler; }; + void with_main_handler(ClosureFunctionVV body, MainHandler _Nullable main_handler, + const void *_Nullable main_handler_context); void call_main_handler(void *context, void (*body)(void *context)); void set_deadline(uint64_t deadline) { _deadline = deadline; }; @@ -206,10 +220,10 @@ class Graph { // MARK: Attributes const AttributeType &attribute_type(uint32_t type_id) const; - const AttributeType &attribute_ref(data::ptr node, const void *_Nullable *_Nullable self_out) const; + const AttributeType *attribute_ref(data::ptr node, const void *_Nullable *_Nullable self_out) const; void attribute_modify(data::ptr node, const swift::metadata &type, ClosureFunctionPV closure, - bool flag); + bool update_flags); data::ptr add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *_Nullable value); @@ -228,8 +242,8 @@ class Graph { // MARK: Indirect attributes - void add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, std::optional size, - bool is_mutable); + data::ptr add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, + std::optional size, bool is_mutable); void remove_indirect_node(data::ptr node); void indirect_attribute_set(data::ptr, AttributeID source); @@ -240,8 +254,8 @@ class Graph { // MARK: Values - void *value_ref(AttributeID attribute, bool evaluate_weak_references, const swift::metadata &value_type, - bool *_Nonnull did_update_out); + void *value_ref(AttributeID attribute, uint32_t zone_id, const swift::metadata &value_type, + bool *_Nonnull changed_out); bool value_set(data::ptr node, const swift::metadata &value_type, const void *value); bool value_set_internal(data::ptr node_ptr, Node &node, const void *value, const swift::metadata &type); @@ -256,8 +270,12 @@ class Graph { // MARK: Inputs - void *_Nullable input_value_ref_slow(data::ptr node, AttributeID input, bool evaluate_weak_references, - uint8_t input_flags, const swift::metadata &type, char *arg5, uint32_t index); + void *_Nullable input_value_ref(data::ptr node, AttributeID input, uint32_t zone_id, uint8_t input_flags, + const swift::metadata &type, uint8_t *state_out); + + void *_Nullable input_value_ref_slow(data::ptr node, AttributeID input, uint32_t zone_id, + uint8_t input_flags, const swift::metadata &type, uint8_t *state_out, + uint32_t index); void input_value_add(data::ptr node, AttributeID input, uint8_t input_edge_flags); @@ -272,7 +290,8 @@ class Graph { void remove_removed_input(AttributeID attribute, AttributeID input); - bool any_inputs_changed(data::ptr node, const AttributeID *attributes, uint64_t count); + bool any_inputs_changed(data::ptr node, const AttributeID *exclude_attributes, + uint64_t exclude_attributes_count); void all_inputs_removed(data::ptr node); uint32_t index_of_input(Node &node, InputEdge::Comparator comparator); @@ -321,7 +340,7 @@ class Graph { uint32_t intern_key(const char *key); const char *key_name(uint32_t key_id) const; - uint32_t intern_type(swift::metadata *metadata, ClosureFunctionVP make_type); + uint32_t intern_type(const swift::metadata *metadata, ClosureFunctionVP make_type); // MARK: Encoding @@ -332,9 +351,14 @@ class Graph { // MARK: Counters - // Counters + uint64_t num_nodes() const { return _num_nodes; }; + uint64_t num_nodes_created() const { return _num_nodes_created; }; + uint64_t num_subgraphs() const { return _num_subgraphs; }; + uint64_t num_subgraphs_created() const { return _num_subgraphs_created; }; + uint64_t transaction_count() const { return _transaction_count; }; uint64_t update_count() const { return _update_count; }; + uint64_t update_on_main_count() const { return _update_on_main_count; }; uint64_t change_count() const { return _change_count; }; // MARK: Tracing @@ -348,6 +372,8 @@ class Graph { All = 1 << 5, }; + bool is_tracing_active() const { return !_traces.empty(); }; + void prepare_trace(Trace &trace); void add_trace(Trace *_Nullable trace); @@ -358,6 +384,8 @@ class Graph { void sync_tracing(); CFStringRef copy_trace_path(); + vector traces() const { return _traces; }; + template requires std::invocable void foreach_trace(T body) { @@ -375,10 +403,10 @@ class Graph { // MARK: Profile - bool is_profiling() const { return _is_profiling; }; + bool is_profiling_enabled() const { return _is_profiling_enabled; }; uint64_t begin_profile_event(data::ptr node, const char *event_name); - void end_profile_event(data::ptr node, const char *event_name, uint64_t arg1, bool arg2); + void end_profile_event(data::ptr node, const char *event_name, uint64_t start_time, bool changed); void add_profile_update(data::ptr node, uint64_t duration, bool changed); diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp index ebc6a44..892449a 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp +++ b/Sources/ComputeCxx/Graph/Profile/ProfileTrace.cpp @@ -7,7 +7,7 @@ namespace AG { void Graph::ProfileTrace::begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) { - if (update_stack.graph()->is_profiling()) { + if (update_stack.graph()->is_profiling_enabled()) { uint64_t time = mach_absolute_time(); UpdateData &data = _map.try_emplace(&update_stack).first->second; data.start_time = time; @@ -24,11 +24,11 @@ void Graph::ProfileTrace::end_update(const Graph::UpdateStack &update_stack, dat _map.erase(found); if (auto previous_update_stack = update_stack.previous().get()) { - if (previous_update_stack->graph()->is_profiling()) { + if (previous_update_stack->graph()->is_profiling_enabled()) { auto found_previous = _map.find(previous_update_stack); if (found_previous != _map.end()) { UpdateData &previous_data = found_previous->second; - uint64_t end_time = previous_update_stack->graph()->is_profiling() ? mach_absolute_time() : 0; + uint64_t end_time = previous_update_stack->graph()->is_profiling_enabled() ? mach_absolute_time() : 0; previous_data.child_update_stack_duration += end_time - data.start_time; } } @@ -43,7 +43,7 @@ void Graph::ProfileTrace::begin_update(data::ptr node) { auto found = _map.find(update_stack); if (found != _map.end()) { UpdateData &data = found->second; - data.update_node_start_time = update_stack->graph()->is_profiling() ? mach_absolute_time() : 0; + data.update_node_start_time = update_stack->graph()->is_profiling_enabled() ? mach_absolute_time() : 0; } } @@ -55,7 +55,7 @@ void Graph::ProfileTrace::end_update(data::ptr node, bool changed) { if (found != _map.end()) { UpdateData &data = found->second; if (data.update_node_start_time != 0) { - uint64_t end_time = update_stack->graph()->is_profiling() ? mach_absolute_time() : 0; + uint64_t end_time = update_stack->graph()->is_profiling_enabled() ? mach_absolute_time() : 0; uint64_t duration = 0; if (end_time - data.update_node_start_time >= data.child_update_stack_duration) { duration = (end_time - data.update_node_start_time) - data.child_update_stack_duration; diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 792b704..9be74bc 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -13,7 +13,6 @@ #include "Log/Log.h" #include "Subgraph/Subgraph.h" #include "Time/Time.h" -#include "Trace/AGTrace.h" #include "UniqueID/AGUniqueID.h" #include "UpdateStack.h" @@ -1286,9 +1285,9 @@ void Graph::TraceRecorder::custom_event(const Graph::Context &context, const cha _encoder.end_length_delimited(); } -void Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, +void Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6) { - if (!named_event_enabled(arg2)) { + if (!named_event_enabled(event_id)) { return; } @@ -1297,27 +1296,26 @@ void Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t a _encoder.encode_varint(8); _encoder.encode_varint(0x36); - if (arg2) { + if (event_id) { _encoder.encode_varint(0x50); - _encoder.encode_varint(arg2); + _encoder.encode_varint(event_id); } field_timestamp(_encoder); - if (arg6 != 0) { - if (arg6 & 0x80000000) { - arg6 &= 0x7fffffff; - } - if (arg6) { - _encoder.encode_varint(0x18); - _encoder.encode_varint(arg6); - } + if (arg6 < 0) { + field_backtrace(_encoder, 8); + arg6 &= 0x7fffffff; + } + if (arg6) { + _encoder.encode_varint(0x18); + _encoder.encode_varint(arg6); } - if (num_args > 3) { - num_args = 4; + if (num_event_args >= 4) { + num_event_args = 4; } - for (auto i = 0; i < num_args; ++i) { + for (auto i = 0; i < num_event_args; ++i) { uint64_t event_arg = event_args[i]; if (event_arg) { _encoder.encode_varint(0x20 + 8 * i); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index 04e8daa..7549b9f 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -118,7 +118,7 @@ class Graph::TraceRecorder : public Encoder::Delegate, public Trace { void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type); - void named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, const uint64_t *event_args, + void named_event(const Graph::Context &context, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6); bool named_event_enabled(uint32_t event_id); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 458d1da..6fe7c39 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -23,8 +23,8 @@ Graph::UpdateStack::UpdateStack(Graph *graph, uint8_t options) { graph->_current_update_thread = _thread; - if (graph->_batch_invalidate_subgraphs == false) { - graph->_batch_invalidate_subgraphs = true; + if (graph->_deferring_subgraph_invalidation == false) { + graph->_deferring_subgraph_invalidation = true; _options &= Option::InvalidateSubgraphs; } @@ -44,7 +44,7 @@ Graph::UpdateStack::~UpdateStack() { Graph::set_current_update(_previous); if (_options & Option::InvalidateSubgraphs) { - _graph->_batch_invalidate_subgraphs = false; + _graph->_deferring_subgraph_invalidation = false; } } @@ -321,8 +321,8 @@ Graph::UpdateStatus Graph::UpdateStack::update() { if (reset_node_flags) { // only skips if frame.cancelled - if (node->flags().value4_unknown0x40()) { - node->flags().set_value4_unknown0x40(false); + if (node->flags().self_modified()) { + node->flags().set_self_modified(false); } if (node->state().is_dirty()) { _graph->foreach_trace([&frame](Trace &trace) { trace.set_dirty(frame.attribute, false); }); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.h b/Sources/ComputeCxx/Graph/UpdateStack.h index 0151d94..e62889e 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.h +++ b/Sources/ComputeCxx/Graph/UpdateStack.h @@ -11,6 +11,8 @@ namespace AG { class Graph::UpdateStack { public: enum Option : uint8_t { + Unknown0x02 = 1 << 1, // TODO: set from AGGraphPrefetchValue + Unknown0x04 = 1 << 2, // TODO: set from AGGraphPrefetchValue SetTag = 1 << 3, InvalidateSubgraphs = 1 << 4, }; diff --git a/Sources/ComputeCxx/Layout/AGComparison.cpp b/Sources/ComputeCxx/Layout/AGComparison.cpp index 5201e8b..24cf0bb 100644 --- a/Sources/ComputeCxx/Layout/AGComparison.cpp +++ b/Sources/ComputeCxx/Layout/AGComparison.cpp @@ -19,8 +19,8 @@ bool AGCompareValues(const void *destination, const void *source, AGTypeID type_ if (layout == AG::ValueLayoutEmpty) { layout = nullptr; } - AG::LayoutDescriptor::compare(layout, (const unsigned char *)destination, (const unsigned char *)source, - type->vw_size(), options); + return AG::LayoutDescriptor::compare(layout, (const unsigned char *)destination, (const unsigned char *)source, + type->vw_size(), options); } const unsigned char *AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority) { diff --git a/Sources/ComputeCxx/Layout/AGComparison.h b/Sources/ComputeCxx/Layout/AGComparison.h index 4867067..2682d03 100644 --- a/Sources/ComputeCxx/Layout/AGComparison.h +++ b/Sources/ComputeCxx/Layout/AGComparison.h @@ -18,9 +18,20 @@ struct AGFieldRange { typedef struct AGComparisonStateStorage *_Nonnull AGComparisonState; +CF_EXPORT +CF_REFINED_FOR_SWIFT const void *AGComparisonStateGetDestination(AGComparisonState state); + +CF_EXPORT +CF_REFINED_FOR_SWIFT const void *AGComparisonStateGetSource(AGComparisonState state); + +CF_EXPORT +CF_REFINED_FOR_SWIFT AGFieldRange AGComparisonStateGetFieldRange(AGComparisonState state); + +CF_EXPORT +CF_REFINED_FOR_SWIFT AGTypeID AGComparisonStateGetFieldType(AGComparisonState state); typedef CF_OPTIONS(uint32_t, AGComparisonOptions) { @@ -34,10 +45,16 @@ typedef CF_OPTIONS(uint16_t, AGComparisonMode) { AGComparisonModeDefault = 0, }; +CF_EXPORT +CF_REFINED_FOR_SWIFT bool AGCompareValues(const void *destination, const void *source, AGTypeID type_id, AGComparisonOptions options); +CF_EXPORT +CF_REFINED_FOR_SWIFT const unsigned char *AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority); +CF_EXPORT +CF_REFINED_FOR_SWIFT void AGOverrideComparisonForTypeDescriptor(void *descriptor, AGComparisonMode mode); CF_EXTERN_C_END diff --git a/Sources/ComputeCxx/Layout/Controls.h b/Sources/ComputeCxx/Layout/Controls.h index 81fa7ac..cc2ed1e 100644 --- a/Sources/ComputeCxx/Layout/Controls.h +++ b/Sources/ComputeCxx/Layout/Controls.h @@ -3,6 +3,8 @@ namespace AG { namespace LayoutDescriptor { +// See BytecodeLayouts.h + enum Controls : unsigned char { EqualsItemBegin = '\x01', EqualsItemTypePointerSize = 8, diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h b/Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h new file mode 100644 index 0000000..60d2333 --- /dev/null +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "AGSubgraph.h" +#include "Private/CFRuntime.h" +#include "Subgraph.h" + +CF_ASSUME_NONNULL_BEGIN + +struct AGSubgraphStorage { + CFRuntimeBase base; + AG::Subgraph *subgraph; +}; + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index aeb2006..7ab7e45 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -1,4 +1,40 @@ -#include "AGSubgraph.h" +#include "AGSubgraph-Private.h" + +#include "Subgraph.h" + +namespace { + +CFRuntimeClass &subgraph_type_id() { + static auto finalize = [](CFTypeRef subgraph_ref) { + AGSubgraphStorage *storage = (AGSubgraphStorage *)subgraph_ref; + AG::Subgraph *subgraph = storage->subgraph; + if (subgraph) { + // TODO: should call destructor? + subgraph->clear_object(); + subgraph->invalidate_and_delete_(false); + } + }; + static CFRuntimeClass klass = { + 0, // version + "AGSubgraph", // className + NULL, // init + NULL, // copy, + finalize, + NULL, // equal + NULL, // hash + NULL, // copyFormattingDesc + NULL, // copyDebugDesc, + 0 // ?? + }; + return klass; +} + +} // namespace + +CFTypeID AGSubgraphGetTypeID() { + static CFTypeID type = _CFRuntimeRegisterClass(&subgraph_type_id()); + return type; +} bool AGSubgraphShouldRecordTree() { // TODO: not implemented diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index ea97ff0..d873444 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -8,6 +8,9 @@ CF_EXTERN_C_BEGIN typedef struct CF_BRIDGED_TYPE(id) AGSubgraphStorage *AGSubgraphRef CF_SWIFT_NAME(Subgraph); +CF_EXPORT +CFTypeID AGSubgraphGetTypeID(); + bool AGSubgraphShouldRecordTree(); CF_EXTERN_C_END diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 50e8753..06098e2 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -2,7 +2,7 @@ #include -#include "AGSubgraph.h" +#include "AGSubgraph-Private.h" #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" @@ -61,9 +61,9 @@ Subgraph::Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID } Subgraph::~Subgraph() { - if (observers_vector *vector = observers()) { + if (_observers) { notify_observers(); - delete vector; + delete _observers.get(); // what pointer is deleted? } if (_cache) { _cache->~NodeCache(); @@ -72,9 +72,9 @@ Subgraph::~Subgraph() { #pragma mark - CoreFoundation -Subgraph *Subgraph::from_cf(SubgraphObject *object) { return object->subgraph(); } +Subgraph *Subgraph::from_cf(AGSubgraphStorage *storage) { return storage->subgraph; } -SubgraphObject *Subgraph::to_cf() const { return _object; } +AGSubgraphStorage *Subgraph::to_cf() const { return reinterpret_cast(_object) ; } void Subgraph::clear_object() { auto object = _object; @@ -104,7 +104,7 @@ void Subgraph::invalidate_and_delete_(bool delete_subgraph) { _parents.clear(); // Check Graph::invalidate_subgraphs - if (_graph->batch_invalidate_subgraphs() == false && _graph->main_handler() == nullptr) { + if (_graph->is_deferring_subgraph_invalidation() == false && _graph->main_handler() == nullptr) { invalidate_now(*_graph); _graph->invalidate_subgraphs(); return; @@ -124,7 +124,7 @@ void Subgraph::invalidate_and_delete_(bool delete_subgraph) { void Subgraph::invalidate_now(Graph &graph) { // TODO: double check graph param vs _graph instance var - graph.set_batch_invalidate_subgraphs(true); + graph.set_deferring_subgraph_invalidation(true); auto removed_subgraphs = vector(); auto stack = std::stack>(); @@ -265,7 +265,7 @@ void Subgraph::invalidate_now(Graph &graph) { free(removed_subgraph); // or delete? } - graph.set_batch_invalidate_subgraphs(false); + graph.set_deferring_subgraph_invalidation(false); } void Subgraph::graph_destroyed() { @@ -521,7 +521,7 @@ void Subgraph::update(uint8_t flags) { _last_traversal_seed += 1; // TODO: check atomics auto stack = - std::stack, AG::vector, 32, uint64_t>>(); + std::stack, AG::vector, 32, uint64_t>>(); auto nodes_to_update = vector, 256, uint64_t>(); stack.push(std::move(to_cf())); @@ -530,7 +530,7 @@ void Subgraph::update(uint8_t flags) { bool thread_is_updating = false; while (!stack.empty()) { - util::cf_ptr object = stack.top(); + util::cf_ptr object = stack.top(); stack.pop(); Subgraph *subgraph = Subgraph::from_cf(object); @@ -905,42 +905,39 @@ void Subgraph::propagate_dirty_flags() { // MARK: - Observers -Subgraph::observers_vector *Subgraph::observers() { return *_observers.get(); } - -uint64_t Subgraph::add_observer(ClosureFunctionVV observer) { +uint64_t Subgraph::add_observer(ClosureFunctionVV &&callback) { if (!_observers) { - _observers = (data::ptr)alloc_bytes(sizeof(observers_vector *), 7); - *_observers = new observers_vector(); + _observers = + (data::ptr *>)alloc_bytes(sizeof(vector *), 7); + *_observers = new vector(); } - + auto observer_id = AGMakeUniqueID(); - observers()->push_back({ - observer, - observer_id, - }); + + auto observers = *_observers; + auto observer = Observer(callback, observer_id); + observers->push_back(observer); return observer_id; } void Subgraph::remove_observer(uint64_t observer_id) { - if (auto vector = observers()) { - auto iter = std::remove_if(vector->begin(), vector->end(), [&observer_id](auto pair) -> bool { - if (pair.second == observer_id) { - pair.first.release_context(); // TODO: where is retain? + if (auto observers = *_observers) { + auto iter = std::remove_if(observers->begin(), observers->end(), [&observer_id](auto observer) -> bool { + if (observer.observer_id == observer_id) { return true; } return false; }); - vector->erase(iter, vector->end()); + observers->erase(iter); } } void Subgraph::notify_observers() { - if (auto vector = observers()) { - while (!vector->empty()) { - auto observer = vector->back(); - observer.first(); - observer.first.release_context(); // TODO: where is retain? - vector->pop_back(); + if (auto observers = *_observers) { + while (!observers->empty()) { + auto &observer = observers->back(); + observer.callback(); + observers->pop_back(); } } } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index f03e6c4..f894d1e 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -14,6 +14,8 @@ CF_ASSUME_NONNULL_BEGIN +struct AGSubgraphStorage; + namespace AG { namespace swift { @@ -77,8 +79,11 @@ class Subgraph : public data::zone { indirect_pointer_vector _parents; vector _children; - using observers_vector = vector, uint64_t>, 0, uint64_t>; - data::ptr _observers; + struct Observer { + ClosureFunctionVV callback; + uint64_t observer_id; + }; + data::ptr *> _observers; uint32_t _traversal_seed; uint32_t _index; // TODO: get and set @@ -102,8 +107,8 @@ class Subgraph : public data::zone { // MARK: CoreFoundation - static Subgraph *from_cf(SubgraphObject *object); - SubgraphObject *to_cf() const; + static Subgraph *from_cf(AGSubgraphStorage *storage); + AGSubgraphStorage *to_cf() const; void clear_object(); // MARK: Graph @@ -182,8 +187,7 @@ class Subgraph : public data::zone { // MARK: Managing observers - observers_vector *_Nullable observers(); - uint64_t add_observer(ClosureFunctionVV observer); + uint64_t add_observer(ClosureFunctionVV &&callback); void remove_observer(uint64_t observer_id); void notify_observers(); diff --git a/Sources/ComputeCxx/Swift/AGTuple.cpp b/Sources/ComputeCxx/Swift/AGTuple.cpp new file mode 100644 index 0000000..3156264 --- /dev/null +++ b/Sources/ComputeCxx/Swift/AGTuple.cpp @@ -0,0 +1,208 @@ +#include "AGTuple.h" + +#include + +#include "Errors/Errors.h" + +AGTupleType AGNewTupleType(uint32_t count, const AGTypeID *elements) { + if (count == 1) { + return elements[0]; + } + auto metadata_elements = reinterpret_cast(elements); + auto response = ::swift::swift_getTupleTypeMetadata(::swift::MetadataRequest(), + ::swift::TupleTypeFlags().withNumElements(count), + metadata_elements, nullptr, nullptr); + if (response.State != ::swift::MetadataState::Complete) { + AG::precondition_failure("invalid tuple type."); + } + return reinterpret_cast(response.Value); +} + +size_t AGTupleCount(AGTupleType tuple_type) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + return 1; + } + auto tuple_metadata = static_cast(metadata); + return static_cast(tuple_metadata->NumElements); +} + +size_t AGTupleSize(AGTupleType tuple_type) { + auto metadata = reinterpret_cast(tuple_type); + return metadata->vw_size(); +} + +AGTypeID AGTupleElementType(AGTupleType tuple_type, uint32_t index) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + if (index != 0) { + AG::precondition_failure("index out of range: %d", index); + } + return reinterpret_cast(metadata); + } + auto tuple_metadata = static_cast(metadata); + if (index >= tuple_metadata->NumElements) { + AG::precondition_failure("index out of range: %d", index); + } + auto element = tuple_metadata->getElement(index); + return reinterpret_cast(element.Type); +} + +size_t AGTupleElementSize(AGTupleType tuple_type, uint32_t index) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + if (index != 0) { + AG::precondition_failure("index out of range: %d", index); + } + return metadata->vw_size(); + } + auto tuple_metadata = static_cast(metadata); + if (index >= tuple_metadata->NumElements) { + AG::precondition_failure("index out of range: %d", index); + } + auto element = tuple_metadata->getElement(index); + return element.Type->vw_size(); +} + +size_t AGTupleElementOffset(AGTupleType tuple_type, uint32_t index) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + if (index != 0) { + AG::precondition_failure("index out of range: %d", index); + } + return 0; + } + auto tuple_metadata = static_cast(metadata); + if (index >= tuple_metadata->NumElements) { + AG::precondition_failure("index out of range: %d", index); + } + auto element = tuple_metadata->getElement(index); + return element.Offset; +} + +size_t AGTupleElementOffsetChecked(AGTupleType tuple_type, uint32_t index, AGTypeID element_type) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + if (index != 0) { + AG::precondition_failure("index out of range: %d", index); + } + if (reinterpret_cast(metadata) != element_type) { + AG::precondition_failure("element type mismatch"); + } + return 0; + } + auto tuple_metadata = static_cast(metadata); + if (index >= tuple_metadata->NumElements) { + AG::precondition_failure("index out of range: %d", index); + } + auto element = tuple_metadata->getElement(index); + if (reinterpret_cast(element.Type) != element_type) { + AG::precondition_failure("element type mismatch"); + } + return element.Offset; +} + +void *update(void *dest_ptr, const void *src_ptr, const ::swift::Metadata *metadata, AGTupleCopyOptions options) { + auto dest = reinterpret_cast<::swift::OpaqueValue *>(dest_ptr); + auto src = reinterpret_cast<::swift::OpaqueValue *>(const_cast(src_ptr)); + switch (options) { + case AGTupleCopyOptionsAssignCopy: + return metadata->vw_assignWithCopy(dest, src); + case AGTupleCopyOptionsInitCopy: + return metadata->vw_initializeWithCopy(dest, src); + case AGTupleCopyOptionsAssignTake: + return metadata->vw_assignWithTake(dest, src); + case AGTupleCopyOptionsInitTake: + return metadata->vw_initializeWithTake(dest, src); + default: + AG::precondition_failure("unknown copy options: %d", options); + } +}; + +void *AGTupleGetElement(AGTupleType tuple_type, void *tuple_value, uint32_t index, void *element_value, + AGTypeID element_type, AGTupleCopyOptions options) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + if (index != 0) { + AG::precondition_failure("index out of range: %d", index); + } + if (reinterpret_cast(metadata) != element_type) { + AG::precondition_failure("element type mismatch"); + } + return update(element_value, tuple_value, metadata, options); + } + auto tuple_metadata = static_cast(metadata); + if (index >= tuple_metadata->NumElements) { + AG::precondition_failure("index out of range: %d", index); + } + auto element = tuple_metadata->getElement(index); + if (reinterpret_cast(element.Type) != element_type) { + AG::precondition_failure("element type mismatch"); + } + return update(element_value, element.findIn(reinterpret_cast<::swift::OpaqueValue *>(tuple_value)), element.Type, + options); +} + +void *AGTupleSetElement(AGTupleType tuple_type, void *tuple_value, uint32_t index, const void *element_value, + AGTypeID element_type, AGTupleCopyOptions options) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + if (index != 0) { + AG::precondition_failure("index out of range: %d", index); + } + if (reinterpret_cast(metadata) != element_type) { + AG::precondition_failure("element type mismatch"); + } + return update(tuple_value, element_value, metadata, options); + } + auto tuple_metadata = static_cast(metadata); + if (index >= tuple_metadata->NumElements) { + AG::precondition_failure("index out of range: %d", index); + } + auto element = tuple_metadata->getElement(index); + if (reinterpret_cast(element.Type) != element_type) { + AG::precondition_failure("element type mismatch"); + } + return update(element.findIn(reinterpret_cast<::swift::OpaqueValue *>(tuple_value)), element_value, element.Type, + options); +} + +void AGTupleDestroy(AGTupleType tuple_type, void *tuple_value) { + auto metadata = reinterpret_cast(tuple_type); + metadata->vw_destroy(reinterpret_cast<::swift::OpaqueValue *>(tuple_value)); +} + +void AGTupleDestroyElement(AGTupleType tuple_type, void *tuple_value, uint32_t index) { + auto metadata = reinterpret_cast(tuple_type); + if (metadata->getKind() != ::swift::MetadataKind::Tuple) { + if (index != 0) { + AG::precondition_failure("index out of range: %d", index); + } + metadata->vw_destroy(reinterpret_cast<::swift::OpaqueValue *>(tuple_value)); + } + auto tuple_metadata = static_cast(metadata); + if (index >= tuple_metadata->NumElements) { + AG::precondition_failure("index out of range: %d", index); + } + auto element = tuple_metadata->getElement(index); + element.Type->vw_destroy(element.findIn(reinterpret_cast<::swift::OpaqueValue *>(tuple_value))); +} + +void AGTupleWithBuffer(AGTupleType tuple_type, size_t count, + const void (*function)(const AGUnsafeMutableTuple mutable_tuple, const void *context), + const void *context) { + auto metadata = reinterpret_cast(tuple_type); + auto buffer_size = metadata->vw_stride() * count; + if (buffer_size <= 0x1000) { + char buffer[buffer_size]; + memset(&buffer, 0, buffer_size); + // function(tuple_type, buffer); + } else { + void *buffer = malloc_type_malloc(buffer_size, 0x100004077774924); + if (buffer == nullptr) { + AG::precondition_failure("memory allocation failure"); + } + // function(tuple_type, buffer); + free(buffer); + } +} diff --git a/Sources/ComputeCxx/Swift/AGTuple.h b/Sources/ComputeCxx/Swift/AGTuple.h new file mode 100644 index 0000000..f0c5ac2 --- /dev/null +++ b/Sources/ComputeCxx/Swift/AGTuple.h @@ -0,0 +1,91 @@ +#pragma once + +#include + +#include "AGSwiftSupport.h" +#include "AGType.h" + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +typedef CF_ENUM(uint32_t, AGTupleCopyOptions) { + AGTupleCopyOptionsInitialize = 1 << 0, + AGTupleCopyOptionsWithTake = 1 << 1, + + AGTupleCopyOptionsAssignCopy = 0, + AGTupleCopyOptionsInitCopy = 1, + AGTupleCopyOptionsAssignTake = 2, + AGTupleCopyOptionsInitTake = 3, +} CF_SWIFT_NAME(TupleType.CopyOptions); + +typedef const AGSwiftMetadata *AGTupleType AG_SWIFT_STRUCT CF_SWIFT_NAME(TupleType); + +typedef struct AGUnsafeTuple { + AGTupleType type; + const void *value; +} CF_SWIFT_NAME(UnsafeTuple) AGUnsafeTuple; + +typedef struct AGUnsafeMutableTuple { + AGTupleType type; + void *value; +} CF_SWIFT_NAME(UnsafeMutableTuple) AGUnsafeMutableTuple; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTupleType AGNewTupleType(uint32_t count, const AGTypeID _Nonnull *_Nonnull elements) + CF_SWIFT_NAME(TupleType.init(count:elements:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +size_t AGTupleCount(AGTupleType tuple_type) CF_SWIFT_NAME(getter:AGTupleType.count(self:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +size_t AGTupleSize(AGTupleType tuple_type) CF_SWIFT_NAME(getter:AGTupleType.size(self:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTypeID AGTupleElementType(AGTupleType tuple_type, uint32_t index) CF_SWIFT_NAME(TupleType.elementType(self:at:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +size_t AGTupleElementSize(AGTupleType tuple_type, uint32_t index) CF_SWIFT_NAME(TupleType.elementSize(self:at:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +size_t AGTupleElementOffset(AGTupleType tuple_type, uint32_t index) CF_SWIFT_NAME(TupleType.elementOffset(self:at:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +size_t AGTupleElementOffsetChecked(AGTupleType tuple_type, uint32_t index, AGTypeID element_type) + CF_SWIFT_NAME(TupleType.elementOffset(self:at:type:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void *AGTupleGetElement(AGTupleType tuple_type, void *tuple_value, uint32_t index, const void *element_value, + AGTypeID element_type, AGTupleCopyOptions options); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void *AGTupleSetElement(AGTupleType tuple_type, void *tuple_value, uint32_t index, const void *element_value, + AGTypeID element_type, AGTupleCopyOptions options); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGTupleDestroy(AGTupleType tuple_type, void *tuple_value) CF_SWIFT_NAME(TupleType.destroy(self:_:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGTupleDestroyElement(AGTupleType tuple_type, void *tuple_value, uint32_t index) + CF_SWIFT_NAME(TupleType.destroy(self:_:at:)); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGTupleWithBuffer(AGTupleType tuple_type, size_t count, + const void (*function)(const AGUnsafeMutableTuple mutable_tuple, const void *context), + const void *context); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Trace/AGTrace.cpp b/Sources/ComputeCxx/Trace/AGTrace.cpp deleted file mode 100644 index 4e5c414..0000000 --- a/Sources/ComputeCxx/Trace/AGTrace.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "AGTrace.h" - -const char *AGGraphGetTraceEventName(uint32_t event_id) { - // TODO: not implemented - return nullptr; -} - -const char *AGGraphGetTraceEventSubsystem(uint32_t event_id) { - // TODO: not implemented - return nullptr; -} diff --git a/Sources/ComputeCxx/Trace/AGTrace.h b/Sources/ComputeCxx/Trace/AGTrace.h deleted file mode 100644 index b6a6ef6..0000000 --- a/Sources/ComputeCxx/Trace/AGTrace.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include - -CF_ASSUME_NONNULL_BEGIN - -CF_EXTERN_C_BEGIN - -CF_EXPORT -CF_REFINED_FOR_SWIFT -const char *AGGraphGetTraceEventName(uint32_t event_id); - -CF_EXPORT -CF_REFINED_FOR_SWIFT -const char *AGGraphGetTraceEventSubsystem(uint32_t event_id); - -CF_EXTERN_C_END - -CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index 4c2d914..46d2ba2 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -3,6 +3,7 @@ #include #include "Graph/Graph.h" +#include "UniqueID/AGUniqueID.h" CF_ASSUME_NONNULL_BEGIN @@ -12,12 +13,13 @@ class Node; class Subgraph; class Trace { - private: + protected: uint64_t _trace_id; public: uint64_t trace_id() { return _trace_id; } + Trace() : _trace_id(AGMakeUniqueID()){}; virtual ~Trace(){}; // Trace @@ -84,7 +86,7 @@ class Trace { virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type){}; - virtual void named_event(const Graph::Context &context, uint32_t arg2, uint32_t num_args, + virtual void named_event(const Graph::Context &context, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, CFDataRef data, uint32_t arg6){}; virtual bool named_event_enabled(uint32_t event_id) { return false; }; From 758b0e6d9e50b20fd6b68350d66584da1b5b0e67 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 20 Mar 2025 20:50:40 +1100 Subject: [PATCH 48/74] Add AGSubgraph... functions --- Sources/ComputeCxx/Closure/AGClosure.h | 2 + .../Containers/IndirectPointerVector.h | 19 +- Sources/ComputeCxx/Graph/AGGraph-Private.h | 4 + Sources/ComputeCxx/Graph/AGGraph.cpp | 47 ++- Sources/ComputeCxx/Graph/AGGraph.h | 21 +- Sources/ComputeCxx/Graph/Context.cpp | 1 + Sources/ComputeCxx/Graph/Graph.cpp | 26 +- .../ComputeCxx/Subgraph/AGSubgraph-Private.h | 2 + Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 312 +++++++++++++++++- Sources/ComputeCxx/Subgraph/AGSubgraph.h | 142 ++++++++ Sources/ComputeCxx/Subgraph/Subgraph.cpp | 21 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 15 +- 12 files changed, 551 insertions(+), 61 deletions(-) diff --git a/Sources/ComputeCxx/Closure/AGClosure.h b/Sources/ComputeCxx/Closure/AGClosure.h index e7b9d49..471a057 100644 --- a/Sources/ComputeCxx/Closure/AGClosure.h +++ b/Sources/ComputeCxx/Closure/AGClosure.h @@ -8,6 +8,8 @@ CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN +// TODO: add Swift annotation for retain relase... + typedef struct CF_BRIDGED_TYPE(id) AGClosureStorage *AGClosureRef AG_SWIFT_NAME(Closure); CF_EXPORT diff --git a/Sources/ComputeCxx/Containers/IndirectPointerVector.h b/Sources/ComputeCxx/Containers/IndirectPointerVector.h index 4d5c287..d83b339 100644 --- a/Sources/ComputeCxx/Containers/IndirectPointerVector.h +++ b/Sources/ComputeCxx/Containers/IndirectPointerVector.h @@ -49,12 +49,29 @@ class indirect_pointer_vector { // Element access + reference operator[](size_type pos) { + assert(pos < size()); + if (has_vector()) { + return get_vector()[pos]; + } else { + return reinterpret_cast(_data); + } + }; + const_reference operator[](size_type pos) const { + assert(pos < size()); + if (has_vector()) { + return get_vector()[pos]; + } else { + return *reinterpret_cast(_data); + } + }; + reference front() { return has_vector() ? get_vector().front() : reinterpret_cast(_data); }; const_reference front() const { if (has_vector()) { return get_vector().front(); } else { - return reinterpret_cast(_data); + return *reinterpret_cast(_data); } }; diff --git a/Sources/ComputeCxx/Graph/AGGraph-Private.h b/Sources/ComputeCxx/Graph/AGGraph-Private.h index dad8e42..90e615e 100644 --- a/Sources/ComputeCxx/Graph/AGGraph-Private.h +++ b/Sources/ComputeCxx/Graph/AGGraph-Private.h @@ -8,6 +8,10 @@ CF_ASSUME_NONNULL_BEGIN +struct AGUnownedGraphContext { + AG::Graph::Context value; +}; + struct AGGraphStorage { CFRuntimeBase base; AG::Graph::Context context; diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index fb3d31c..58b342c 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -73,12 +73,11 @@ AGGraphRef AGGraphCreateShared(AGGraphRef original) { return instance; }; -AGGraphContextRef AGGraphGetGraphContext(AGGraphRef graph) { - auto context = AG::Graph::Context::from_cf(graph); // TODO: inline - return reinterpret_cast(context); +AGUnownedGraphContextRef AGGraphGetGraphContext(AGGraphRef graph) { + return reinterpret_cast(AG::Graph::Context::from_cf(graph)); } -AGGraphRef AGGraphContextGetGraph(AGGraphContextRef context) { +AGGraphRef AGGraphContextGetGraph(AGUnownedGraphContextRef context) { return reinterpret_cast(reinterpret_cast(context) - sizeof(CFRuntimeBase)); } @@ -201,26 +200,26 @@ AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute) { AG::precondition_failure("no graph: %u", attribute); } -AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute) { - auto subgraph = AGGraphGetAttributeSubgraph2(attribute); - if (subgraph == nullptr) { - AG::precondition_failure("no subgraph"); - } - - return subgraph; -} - -AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); - - auto subgraph = attribute_id.subgraph(); - if (subgraph == nullptr) { - AG::precondition_failure("internal error"); - } - - return subgraph->to_cf(); -} +// AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute) { +// auto subgraph = AGGraphGetAttributeSubgraph2(attribute); +// if (subgraph == nullptr) { +// AG::precondition_failure("no subgraph"); +// } +// +// return subgraph; +// } +// +// AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute) { +// auto attribute_id = AG::AttributeID(attribute); +// attribute_id.to_node_ptr().assert_valid(); +// +// auto subgraph = attribute_id.subgraph(); +// if (subgraph == nullptr) { +// AG::precondition_failure("internal error"); +// } +// +// return subgraph->to_cf(); +// } AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute) { auto attribute_id = AG::AttributeID(attribute); diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 54cafd5..5a7b859 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -6,7 +6,6 @@ #include "AGSwiftSupport.h" #include "Attribute/AGAttribute.h" #include "Attribute/AGWeakAttribute.h" -#include "Subgraph/AGSubgraph.h" #include "Swift/AGType.h" CF_ASSUME_NONNULL_BEGIN @@ -16,7 +15,7 @@ CF_EXTERN_C_BEGIN // MARK: CFType typedef struct CF_BRIDGED_TYPE(id) AGGraphStorage *AGGraphRef AG_SWIFT_NAME(Graph); -typedef struct CF_BRIDGED_TYPE(id) AGGraphContextStorage *AGGraphContextRef AG_SWIFT_NAME(GraphContext); +typedef struct AGUnownedGraphContext *AGUnownedGraphContextRef; CF_EXPORT CF_REFINED_FOR_SWIFT @@ -34,11 +33,11 @@ AGGraphRef AGGraphCreateShared(AGGraphRef _Nullable graph); CF_EXPORT CF_REFINED_FOR_SWIFT -AGGraphContextRef AGGraphGetGraphContext(AGGraphRef graph); +AGUnownedGraphContextRef AGGraphGetGraphContext(AGGraphRef graph); CF_EXPORT CF_REFINED_FOR_SWIFT -AGGraphRef AGGraphContextGetGraph(AGGraphContextRef context); +AGGraphRef AGGraphContextGetGraph(AGUnownedGraphContextRef context); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -104,13 +103,15 @@ CF_EXPORT CF_REFINED_FOR_SWIFT AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute); -CF_EXPORT -CF_REFINED_FOR_SWIFT -AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute); +// TODO: fix circular types -CF_EXPORT -CF_REFINED_FOR_SWIFT -AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute); +// CF_EXPORT +// CF_REFINED_FOR_SWIFT +// AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute); + +// CF_EXPORT +// CF_REFINED_FOR_SWIFT +// AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute); // TODO: need this? // typedef struct AGAttributeType { diff --git a/Sources/ComputeCxx/Graph/Context.cpp b/Sources/ComputeCxx/Graph/Context.cpp index 37ca61e..e0a53a2 100644 --- a/Sources/ComputeCxx/Graph/Context.cpp +++ b/Sources/ComputeCxx/Graph/Context.cpp @@ -53,6 +53,7 @@ Graph::Context::~Context() { } } +// TODO: AGUnknownedGraphContextRef ? Graph::Context *Graph::Context::from_cf(AGGraphStorage *storage) { if (storage->context._invalidated) { precondition_failure("invalidated graph"); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 9e9d192..21aede6 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -344,7 +344,7 @@ void Graph::with_update(data::ptr node, ClosureFunctionVV body) void Graph::without_update(ClosureFunctionVV body) { // TODO: use RAII pattern here too? // TODO: is tag here update not active or is paused? - + auto previous_update = current_update(); AG::Graph::set_current_update(previous_update != nullptr ? previous_update.with_tag(true) : nullptr); body(); @@ -429,7 +429,7 @@ const AttributeType *Graph::attribute_ref(data::ptr node, const void *_Nul if (self_out) { *self_out = node->get_self(type); } - return type; + return &type; } void Graph::attribute_modify(data::ptr node, const swift::metadata &metadata, @@ -742,7 +742,7 @@ bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, #pragma mark - Indirect attributes data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, AttributeID attribute, uint32_t offset, - std::optional size, bool is_mutable) { + std::optional size, bool is_mutable) { if (subgraph.graph() != attribute.subgraph()->graph()) { precondition_failure("attribute references can't cross graph namespaces"); } @@ -776,7 +776,7 @@ data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, Attrib add_input_dependencies(AttributeID(indirect_node).with_kind(AttributeID::Kind::Indirect), attribute); subgraph.add_indirect((data::ptr)indirect_node, true); - return indirect_node; + return (data::ptr)indirect_node; } else { auto indirect_node = (data::ptr)subgraph.alloc_bytes_recycle(sizeof(Node), 3); @@ -1315,26 +1315,26 @@ void *Graph::input_value_ref(data::ptr node, AttributeID input_attribu if (index < 0) { // TODO: AVGalueOptions is same as InputEdge::Flags ? - return input_value_ref_slow(frame.attribute, attribute_id, zone_id, options, metadata, state_out, index); + return input_value_ref_slow(node, input_attribute, zone_id, input_flags, type, state_out, index); } - AG::OffsetAttributeID resolved = - attribute_id.resolve(AG::TraversalOptions::UpdateDependencies | AG::TraversalOptions::AssertNotNil); - if (resolved.attribute().to_node().state().is_value_initialized() && - !resolved.attribute().to_node().state().is_dirty()) { + AG::OffsetAttributeID resolved_input = + input_attribute.resolve(AG::TraversalOptions::UpdateDependencies | AG::TraversalOptions::AssertNotNil); + if (resolved_input.attribute().to_node().state().is_value_initialized() && + !resolved_input.attribute().to_node().state().is_dirty()) { auto input_edge = node->inputs()[index]; bool changed = input_edge.is_changed(); input_edge.set_unknown4(true); // TODO: does this set by reference? - void *value = resolved.attribute().to_node().get_value(); - value = (uint8_t *)value + resolved.offset(); + void *value = resolved_input.attribute().to_node().get_value(); + value = (uint8_t *)value + resolved_input.offset(); - state_out |= changed; + *state_out |= changed ? 1 : 0; return value; } // TODO: combine with block above, make index signed first though // TODO: AVGalueOptions is same as InputEdge::Flags ? - return graph->input_value_ref_slow(frame.attribute, attribute_id, zone_id, options, metadata, state_out, index); + return input_value_ref_slow(node, input_attribute, zone_id, input_flags, type, state_out, index); } void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_attribute, uint32_t zone_id, diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h b/Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h index 60d2333..4b0963a 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph-Private.h @@ -13,4 +13,6 @@ struct AGSubgraphStorage { AG::Subgraph *subgraph; }; + + CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index 7ab7e45..1517705 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -1,5 +1,9 @@ #include "AGSubgraph-Private.h" +#include + +#include "Graph/AGGraph.h" +#include "Graph/Context.h" #include "Subgraph.h" namespace { @@ -36,7 +40,311 @@ CFTypeID AGSubgraphGetTypeID() { return type; } +AGSubgraphRef AGSubgraphCreate(AGGraphRef graph) { return AGSubgraphCreate2(graph, AGAttributeNil); }; + +AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute) { + uint32_t extra = sizeof(struct AGSubgraphStorage) - sizeof(CFRuntimeBase); + struct AGSubgraphStorage *instance = + (struct AGSubgraphStorage *)_CFRuntimeCreateInstance(kCFAllocatorDefault, AGSubgraphGetTypeID(), extra, NULL); + if (!instance) { + AG::precondition_failure("memory allocation failure."); + } + + AG::Graph::Context *context = AG::Graph::Context::from_cf(graph); + + AG::Subgraph *subgraph = + new (&instance->subgraph) AG::Subgraph((AG::SubgraphObject *)instance, *context, AG::AttributeID(attribute)); + return instance; +}; + +#pragma mark - Current subgraph + +AGSubgraphRef AGSubgraphGetCurrent() { + auto current = AG::Subgraph::current_subgraph(); + if (current == nullptr) { + return nullptr; + } + return current->to_cf(); +} + +void AGSubgraphSetCurrent(AGSubgraphRef subgraph) { + // TODO: use util::cf_ptr here? + AG::Subgraph *old_subgraph = AG::Subgraph::current_subgraph(); + if (subgraph != nullptr) { + AG::Subgraph::set_current_subgraph(subgraph->subgraph); + if (subgraph->subgraph != nullptr) { + CFRetain(subgraph); + } + } else { + AG::Subgraph::set_current_subgraph(nullptr); + } + if (old_subgraph && old_subgraph->to_cf()) { + CFRelease(old_subgraph->to_cf()); + } +} + +#pragma mark - Graph + +AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + + auto context_id = subgraph->subgraph->context_id(); + if (context_id != 0) { + if (auto context = subgraph->subgraph->graph()->context_with_id(context_id)) { + return AGGraphContextGetGraph( + reinterpret_cast(context)); // TODO: proper conversion + } + } + + AG::precondition_failure("accessing invalidated contex"); +} + +AGUnownedGraphContextRef AGSubgraphGetCurrentGraphContext() { + AG::Subgraph *current = AG::Subgraph::current_subgraph(); + if (current == nullptr) { + return nullptr; + } + + // FIXME: this is not the context + return (AGUnownedGraphContextRef)current->graph(); +} + +#pragma mark - Children + +void AGSubgraphAddChild(AGSubgraphRef subgraph, AGSubgraphRef child) { AGSubgraphAddChild2(subgraph, child, 0); } + +void AGSubgraphAddChild2(AGSubgraphRef subgraph, AGSubgraphRef child, uint32_t flags) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + if (child->subgraph != nullptr) { + // TODO: strong type flags + subgraph->subgraph->add_child(*child->subgraph, AG::Subgraph::SubgraphChild::Flags(flags)); + } +} + +void AGSubgraphRemoveChild(AGSubgraphRef subgraph, AGSubgraphRef child) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + + if (child->subgraph) { + subgraph->subgraph->remove_child(*child->subgraph, false); + } +} + +uint32_t AGSubgraphGetChildCount(AGSubgraphRef subgraph) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + return subgraph->subgraph->children().size(); +} + +AGSubgraphRef AGSubgraphGetChild(AGSubgraphRef subgraph, uint32_t index, uint32_t *flags_out) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + if (index >= subgraph->subgraph->children().size()) { + AG::precondition_failure("invalid child index: %u", index); + } + + auto child = subgraph->subgraph->children()[index]; + if (flags_out) { + *flags_out = child.flags(); + } + return child.subgraph()->to_cf(); +} + +uint64_t AGSubgraphGetParentCount(AGSubgraphRef subgraph) { + if (subgraph->subgraph == nullptr) { + return 0; + } + + return subgraph->subgraph->parents().size(); +} + +AGSubgraphRef AGSubgraphGetParent(AGSubgraphRef subgraph, int64_t index) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + + if (index >= subgraph->subgraph->parents().size()) { + AG::precondition_failure("invalid parent index: %u", index); + } + + return subgraph->subgraph->parents()[index]->to_cf(); +} + +bool AGSubgraphIsAncestor(AGSubgraphRef subgraph, AGSubgraphRef possible_descendant) { + if (subgraph->subgraph == nullptr) { + return false; + } + + if (possible_descendant->subgraph == nullptr) { + return false; + } + + return subgraph->subgraph->ancestor_of(*possible_descendant->subgraph); +} + +#pragma mark - Attributes + +bool AGSubgraphIsValid(AGSubgraphRef subgraph) { + if (subgraph->subgraph == nullptr) { + return false; + } + + return subgraph->subgraph->is_valid(); +} + +bool AGSubgraphIsDirty(AGSubgraphRef subgraph, uint8_t mask) { + if (subgraph->subgraph == nullptr) { + return false; + } + + return subgraph->subgraph->is_dirty(mask); +} + +bool AGSubgraphIntersects(AGSubgraphRef subgraph, uint8_t mask) { + if (subgraph->subgraph == nullptr) { + return; + } + + return subgraph->subgraph->intersects(mask); +} + +void AGSubgraphInvalidate(AGSubgraphRef subgraph) { + if (subgraph->subgraph == nullptr) { + return; + } + + subgraph->subgraph->invalidate_and_delete_(false); +} + +void AGSubgraphUpdate(AGSubgraphRef subgraph, uint8_t flags) { + if (subgraph->subgraph == nullptr) { + return; + } + + subgraph->subgraph->update(flags); +} + +void AGSubgraphApply(AGSubgraphRef subgraph, uint32_t flags, + void (*function)(AGAttribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *function_context) { + if (subgraph->subgraph == nullptr) { + return; + } + + subgraph->subgraph->apply(AG::Subgraph::Flags(flags), + AG::ClosureFunctionAV(function, function_context)); +} + +#pragma mark - Tree + +uint32_t AGSubgraphGetTreeRoot(AGSubgraphRef subgraph) { + if (subgraph->subgraph == nullptr) { + return 0; // TODO: nullptr + } + + return subgraph->subgraph->tree_root(); +} + +void AGSubgraphBeginTreeElement(AGSubgraphRef subgraph, AGAttribute owner, AGTypeID type, uint32_t flags) { + AG::Subgraph *current = AG::Subgraph::current_subgraph(); + if (current == nullptr) { + return; + } + + auto metadata = reinterpret_cast(type); + current->begin_tree(AG::AttributeID(owner), metadata, flags); +} + +void AGSubgraphEndTreeElement(AGSubgraphRef subgraph) { + AG::Subgraph *current = AG::Subgraph::current_subgraph(); + if (current == nullptr) { + return; + } + + current->end_tree(); +} + +void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + subgraph->subgraph->set_tree_owner(AG::AttributeID(owner)); +} + +void AGSubgraphAddTreeValue(AGSubgraphRef subgraph, AGAttribute attribute, AGTypeID type, const char *key, + uint32_t flags) { + AG::Subgraph *current = AG::Subgraph::current_subgraph(); + if (current == nullptr) { + return; + } + + auto metadata = reinterpret_cast(type); + current->add_tree_value(AG::AttributeID(attribute), metadata, key, flags); +} + +static dispatch_once_t should_record_tree_once = 0; +static bool should_record_tree = true; + +void init_should_record_tree(void *context) { + char *result = getenv("AG_TREE"); + if (result) { + should_record_tree = atoi(result) != 0; + } else { + should_record_tree = false; + } +} + bool AGSubgraphShouldRecordTree() { - // TODO: not implemented - return false; + dispatch_once_f(&should_record_tree_once, nullptr, init_should_record_tree); + return should_record_tree; +} + +void AGSubgraphSetShouldRecordTree() { + dispatch_once_f(&should_record_tree_once, nullptr, init_should_record_tree); + should_record_tree = true; +} + +#pragma mark - Observers + +uint64_t AGSubgraphAddObserver(AGSubgraphRef subgraph, + void (*observer)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *observer_context) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + + return subgraph->subgraph->add_observer(AG::ClosureFunctionVV(observer, observer_context)); +} + +void AGSubgraphRemoveObserver(AGSubgraphRef subgraph, uint64_t observer_id) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + + subgraph->subgraph->remove_observer(observer_id); +} + +#pragma mark - Index + +uint32_t AGSubgraphGetIndex(AGSubgraphRef subgraph) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + + return subgraph->subgraph->index(); +} + +void AGSubgraphSetIndex(AGSubgraphRef subgraph, uint32_t index) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + + subgraph->subgraph->set_index(index); } diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index d873444..798f546 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -2,6 +2,9 @@ #include +#include "Attribute/AGAttribute.h" +#include "Graph/AGGraph.h" + CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN @@ -9,10 +12,149 @@ CF_EXTERN_C_BEGIN typedef struct CF_BRIDGED_TYPE(id) AGSubgraphStorage *AGSubgraphRef CF_SWIFT_NAME(Subgraph); CF_EXPORT +CF_REFINED_FOR_SWIFT CFTypeID AGSubgraphGetTypeID(); +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGSubgraphCreate(AGGraphRef graph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute); + +// MARK: Current subgraph + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGSubgraphGetCurrent(); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphSetCurrent(AGSubgraphRef _Nullable subgraph); + +// MARK: Graph + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGUnownedGraphContextRef AGSubgraphGetCurrentGraphContext(AGSubgraphRef subgraph); + +// MARK: Children + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphAddChild(AGSubgraphRef subgraph, AGSubgraphRef child); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphAddChild2(AGSubgraphRef subgraph, AGSubgraphRef child, uint32_t flags); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphRemoveChild(AGSubgraphRef subgraph, AGSubgraphRef child); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint32_t AGSubgraphGetChildCount(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGSubgraphGetChild(AGSubgraphRef subgraph, uint32_t index, uint32_t *flags_out); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint64_t AGSubgraphGetParentCount(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGSubgraphGetParent(AGSubgraphRef subgraph, int64_t index); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGSubgraphIsAncestor(AGSubgraphRef subgraph, AGSubgraphRef possible_descendant); + +// MARK: Attributes + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGSubgraphIsValid(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGSubgraphIsDirty(AGSubgraphRef subgraph, uint8_t mask); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +bool AGSubgraphIntersects(AGSubgraphRef subgraph, uint8_t mask); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphInvalidate(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphUpdate(AGSubgraphRef subgraph, uint8_t flags); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphApply(AGSubgraphRef subgraph, uint32_t flags, + void (*function)(AGAttribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *function_context); + +// MARK: Tree + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint32_t AGSubgraphGetTreeRoot(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphBeginTreeElement(AGSubgraphRef subgraph, AGAttribute owner, AGTypeID type, uint32_t flags); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphEndTreeElement(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphAddTreeValue(AGSubgraphRef subgraph, AGAttribute attribute, AGTypeID type, const char *key, + uint32_t flags); + +CF_EXPORT +CF_REFINED_FOR_SWIFT bool AGSubgraphShouldRecordTree(); +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphSetShouldRecordTree(); + +// MARK: Observers + +CF_EXPORT CF_REFINED_FOR_SWIFT uint64_t AGSubgraphAddObserver(AGSubgraphRef subgraph, + void (*observer)(const void *context AG_SWIFT_CONTEXT) + AG_SWIFT_CC(swift), + const void *observer_context); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphRemoveObserver(AGSubgraphRef subgraph, uint64_t observer_id); + +// MARK: Index + +CF_EXPORT CF_REFINED_FOR_SWIFT uint32_t AGSubgraphGetIndex(AGSubgraphRef subgraph); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +void AGSubgraphSetIndex(AGSubgraphRef subgraph, uint32_t index); + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 06098e2..dd72132 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -74,7 +74,7 @@ Subgraph::~Subgraph() { Subgraph *Subgraph::from_cf(AGSubgraphStorage *storage) { return storage->subgraph; } -AGSubgraphStorage *Subgraph::to_cf() const { return reinterpret_cast(_object) ; } +AGSubgraphStorage *Subgraph::to_cf() const { return reinterpret_cast(_object); } void Subgraph::clear_object() { auto object = _object; @@ -520,8 +520,8 @@ void Subgraph::update(uint8_t flags) { _last_traversal_seed += 1; // TODO: check atomics - auto stack = - std::stack, AG::vector, 32, uint64_t>>(); + auto stack = std::stack, + AG::vector, 32, uint64_t>>(); auto nodes_to_update = vector, 256, uint64_t>(); stack.push(std::move(to_cf())); @@ -727,7 +727,7 @@ void Subgraph::begin_tree(AttributeID owner, const swift::metadata *type, uint32 } } -void Subgraph::end_tree(AttributeID attribute) { +void Subgraph::end_tree() { if (_tree_root && _tree_root->parent) { _tree_root = _tree_root->parent; } @@ -743,12 +743,12 @@ void Subgraph::set_tree_owner(AttributeID owner) { _tree_root->owner = owner; } -void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type, const char *value, uint32_t flags) { +void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type, const char *key, uint32_t flags) { if (!_tree_root) { return; } - auto key_id = graph()->intern_key(value); + auto key_id = graph()->intern_key(key); data::ptr tree_value = (data::ptr)alloc_bytes(sizeof(Graph::TreeValue), 7); tree_value->type = type; @@ -903,6 +903,11 @@ void Subgraph::propagate_dirty_flags() { }); } +// TODO: inline +bool Subgraph::is_dirty(uint8_t mask) const { return ((_flags.value3 | _flags.value4) & mask) != 0; } + +bool Subgraph::intersects(uint8_t mask) const { return ((_flags.value1 | _flags.value2) & mask) != 0; } + // MARK: - Observers uint64_t Subgraph::add_observer(ClosureFunctionVV &&callback) { @@ -911,9 +916,9 @@ uint64_t Subgraph::add_observer(ClosureFunctionVV &&callback) { (data::ptr *>)alloc_bytes(sizeof(vector *), 7); *_observers = new vector(); } - + auto observer_id = AGMakeUniqueID(); - + auto observers = *_observers; auto observer = Observer(callback, observer_id); observers->push_back(observer); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index f894d1e..c6a526c 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -46,6 +46,7 @@ class Subgraph : public data::zone { enum Flags : uint32_t {}; SubgraphChild(Subgraph *subgraph, Flags flags) { _data = (uintptr_t)subgraph | (flags & 0x3); }; Subgraph *subgraph() const { return reinterpret_cast(_data & ~0x3); }; + Flags flags() const { return (Flags)(_data & 0x3); }; bool operator==(const Subgraph *other) const { return subgraph() == other; }; }; @@ -86,7 +87,7 @@ class Subgraph : public data::zone { data::ptr *> _observers; uint32_t _traversal_seed; - uint32_t _index; // TODO: get and set + uint32_t _index; data::ptr _cache; data::ptr _tree_root; @@ -163,10 +164,10 @@ class Subgraph : public data::zone { void begin_tree(AttributeID attribute, const swift::metadata *_Nullable type, uint32_t flags); // TODO: check can be null from Subgraph() - void end_tree(AttributeID attribute); + void end_tree(); void set_tree_owner(AttributeID owner); - void add_tree_value(AttributeID attribute, const swift::metadata *type, const char *value, uint32_t flags); + void add_tree_value(AttributeID attribute, const swift::metadata *type, const char *key, uint32_t flags); AttributeID tree_node_at_index(data::ptr tree_element, uint64_t index); uint32_t tree_subgraph_child(data::ptr tree_element); @@ -185,12 +186,20 @@ class Subgraph : public data::zone { void propagate_flags(); void propagate_dirty_flags(); + bool is_dirty(uint8_t mask) const; + bool intersects(uint8_t mask) const; + // MARK: Managing observers uint64_t add_observer(ClosureFunctionVV &&callback); void remove_observer(uint64_t observer_id); void notify_observers(); + // MARK: Index + + uint32_t index() const { return _index; }; + void set_index(uint32_t index) { _index = index; }; + // MARK: Cache data::ptr cache_fetch(uint64_t identifier, const swift::metadata &type, void *body, From 92c89092364fa4353f089e7b937db9ace1d3cdb9 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 20 Mar 2025 22:36:35 +1100 Subject: [PATCH 49/74] Add AGTreeElement... functions --- Sources/ComputeCxx/Graph/Graph+Description.mm | 25 +++--- Sources/ComputeCxx/Graph/Graph.cpp | 10 +-- .../ComputeCxx/Graph/Tree/AGTreeElement.cpp | 87 +++++++++++++++++++ Sources/ComputeCxx/Graph/Tree/AGTreeElement.h | 80 +++++++++++++++++ Sources/ComputeCxx/Graph/Tree/TreeElement.h | 13 +-- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 40 ++++----- Sources/ComputeCxx/Subgraph/Subgraph.h | 4 +- 7 files changed, 214 insertions(+), 45 deletions(-) create mode 100644 Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp create mode 100644 Sources/ComputeCxx/Graph/Tree/AGTreeElement.h diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 8065787..8b46bc1 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -600,11 +600,11 @@ int trap_cycles() { tree_element_indices.try_emplace(tree, index); trees.push_back(tree); - if (tree->next) { - tree_stack.push(tree->next); + if (tree->first_child) { + tree_stack.push(tree->first_child); } - if (tree->old_parent) { - tree_stack.push(tree->old_parent); + if (tree->next_sibling) { + tree_stack.push(tree->next_sibling); } } } @@ -620,8 +620,8 @@ int trap_cycles() { // TODO: what is creator key? - if (tree->owner.without_kind() != 0 && tree->type != nullptr) { - OffsetAttributeID resolved = tree->owner.resolve(TraversalOptions::ReportIndirectionInOffset); + if (tree->node.without_kind() != 0 && tree->type != nullptr) { + OffsetAttributeID resolved = tree->node.resolve(TraversalOptions::ReportIndirectionInOffset); if (resolved.attribute().is_direct()) { tree_dict[@"node"] = [NSNumber numberWithUnsignedLong:node_indices_by_id.find(resolved.attribute().to_node_ptr()) @@ -637,9 +637,9 @@ int trap_cycles() { if (tree->parent == nullptr) { tree_dict[@"root"] = @YES; } - } else if (tree->owner.without_kind() != 0 && tree->type == nullptr) { + } else if (tree->node.without_kind() != 0 && tree->type == nullptr) { tree_dict[@"node"] = [NSNumber - numberWithUnsignedLong:node_indices_by_id.find(tree->owner.to_node_ptr())->second]; // 2 + numberWithUnsignedLong:node_indices_by_id.find(tree->node.to_node_ptr())->second]; // 2 } else { if (tree->parent == nullptr) { tree_dict[@"root"] = @YES; // 1 @@ -650,10 +650,10 @@ int trap_cycles() { tree_dict[@"flags"] = [NSNumber numberWithUnsignedInt:tree->flags]; } - if (tree->next) { + if (tree->first_child) { NSMutableArray *children = [NSMutableArray array]; - for (data::ptr child = tree->next; child != nullptr; - child = child->old_parent) { + for (data::ptr child = tree->first_child; child != nullptr; + child = child->next_sibling) { [children addObject:[NSNumber numberWithUnsignedLong:tree_element_indices.find(child)->second]]; } @@ -685,8 +685,7 @@ int trap_cycles() { } NSMutableDictionary *values = [NSMutableDictionary dictionary]; - for (data::ptr value = tree->last_value; value != nullptr; - value = value->previous_sibling) { + for (data::ptr value = tree->first_value; value != nullptr; value = value->next) { OffsetAttributeID resolved_value = value->value.resolve(TraversalOptions::None); if (resolved_value.attribute().is_direct()) { diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 21aede6..86d3555 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -2108,21 +2108,21 @@ void Graph::encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_ } void Graph::encode_tree(Encoder &encoder, data::ptr tree) { - if (tree->owner.without_kind()) { + if (tree->node.without_kind()) { encoder.encode_varint(0x10); - encoder.encode_varint(tree->owner); + encoder.encode_varint(tree->node); } if (tree->flags) { encoder.encode_varint(0x18); encoder.encode_varint(tree->flags); } - if (tree->next) { + for (auto child = tree->first_child; child != nullptr; child = child->next_sibling) { encoder.encode_varint(0x22); encoder.begin_length_delimited(); - encode_tree(encoder, tree->next->old_parent); + encode_tree(encoder, child); encoder.end_length_delimited(); } - for (auto value = tree->last_value; value != nullptr; value = value->previous_sibling) { + for (auto value = tree->first_value; value != nullptr; value = value->next) { encoder.encode_varint(0x2a); encoder.begin_length_delimited(); if (value->value) { diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp new file mode 100644 index 0000000..fc100be --- /dev/null +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp @@ -0,0 +1,87 @@ +#include "AGTreeElement.h" + +#include "Subgraph/Subgraph.h" +#include "TreeElement.h" + +AGTypeID AGTreeElementGetType(AGTreeElement tree_element) { + auto tree_element_id = AG::TreeElementID(tree_element); + auto type = tree_element_id.to_element_ptr()->type; + return AGTypeID(type); +} + +AGAttribute AGTreeElementGetValue(AGTreeElement tree_element) { + auto tree_element_id = AG::TreeElementID(tree_element); + auto node = tree_element_id.to_element_ptr()->node; + return AGAttribute(node); +} + +uint32_t AGTreeElementGetFlags(AGTreeElement tree_element) { + auto tree_element_id = AG::TreeElementID(tree_element); + return tree_element_id.to_element_ptr()->flags; +} + +AGTreeElement AGTreeElementGetParent(AGTreeElement tree_element) { + auto tree_element_id = AG::TreeElementID(tree_element); + return tree_element_id.to_element_ptr()->parent; +} + +#pragma mark - Iterating values + +AGTreeElementValueIterator AGTreeElementMakeValueIterator(AGTreeElement tree_element) { + auto tree_element_id = AG::TreeElementID(tree_element); + auto tree_value = tree_element_id.to_element_ptr()->first_value; + return AGTreeElementValueIterator(tree_element, tree_value); +} + +AGTreeValue AGTreeElementGetNextValue(AGTreeElementValueIterator iter) { + AGTreeValue tree_value = iter.next_value; + if (tree_value) { + auto tree_value_id = AG::TreeValueID(tree_value); + iter.next_value = tree_value_id.to_tree_value().next; + } + return tree_value; +} + +#pragma mark - Iterating values + +AGTreeElementNodeIterator AGTreeElementMakeNodeIterator(AGTreeElement tree_element) { return {tree_element, 0}; } + +AGAttribute AGTreeElementGetNextNode(AGTreeElementNodeIterator *iter) { + auto tree_element_id = AG::TreeElementID(iter->tree_element); + AG::AttributeID node = + tree_element_id.subgraph()->tree_node_at_index(tree_element_id.to_element_ptr(), iter->index); + if (node.without_kind() == 0) { + return AGAttributeNil; + } + iter->index += 1; + return node; +} + +#pragma mark - Iterating children + +AGTreeElementChildIterator AGTreeElementMakeChildIterator(AGTreeElement tree_element) { + auto tree_element_id = AG::TreeElementID(tree_element); + auto child = tree_element_id.to_element_ptr()->first_child; + return AGTreeElementChildIterator(tree_element, child, 0); +} + +AGTreeElement AGTreeElementGetNextChild(AGTreeElementChildIterator *iter) { + AGTreeElement next_child = iter->next_child; + if (next_child) { + iter->next_child = AG::TreeElementID(next_child).to_element_ptr()->next_sibling; + return next_child; + } + + if (!iter->iterated_subgraph) { + iter->iterated_subgraph = true; + auto tree_element_id = AG::TreeElementID(iter->tree_element); + auto subgraph = tree_element_id.subgraph(); + auto next_child = subgraph->tree_subgraph_child(tree_element_id.to_element_ptr()); + if (next_child) { + iter->next_child = AG::TreeElementID(next_child).to_element_ptr()->next_sibling; + return next_child; + } + } + + return 0; +} diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.h b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.h new file mode 100644 index 0000000..daf8fe5 --- /dev/null +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +#include "AGTreeValue.h" +#include "Attribute/AGAttribute.h" +#include "Swift/AGType.h" + +CF_ASSUME_NONNULL_BEGIN + +CF_EXTERN_C_BEGIN + +typedef uint32_t AGTreeElement; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTypeID AGTreeElementGetType(AGTreeElement tree_element); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGTreeElementGetValue(AGTreeElement tree_element); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +uint32_t AGTreeElementGetFlags(AGTreeElement tree_element); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTreeElement AGTreeElementGetParent(AGTreeElement tree_element); + +// MARK: Iterating values + +typedef struct AGTreeElementValueIterator { + AGTreeElement tree_element; + AGTreeValue next_value; +} AGTreeElementValueIterator; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTreeElementValueIterator AGTreeElementMakeValueIterator(AGTreeElement tree_element); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTreeValue AGTreeElementGetNextValue(AGTreeElementValueIterator iter); + +// MARK: Iterating nodes + +typedef struct AGTreeElementNodeIterator { + AGTreeElement tree_element; + uint32_t index; +} AGTreeElementNodeIterator; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTreeElementNodeIterator AGTreeElementMakeNodeIterator(AGTreeElement tree_element); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGAttribute AGTreeElementGetNextNode(AGTreeElementNodeIterator *iter); + +// MARK: Iterating children + +typedef struct AGTreeElementChildIterator { + AGTreeElement tree_element; + AGTreeElement next_child; + bool iterated_subgraph; +} AGTreeElementChildIterator; + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTreeElementChildIterator AGTreeElementMakeChildIterator(AGTreeElement tree_element); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGTreeElement AGTreeElementGetNextChild(AGTreeElementChildIterator *iter); + +CF_EXTERN_C_END + +CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Tree/TreeElement.h b/Sources/ComputeCxx/Graph/Tree/TreeElement.h index aab9276..ba4f07b 100644 --- a/Sources/ComputeCxx/Graph/Tree/TreeElement.h +++ b/Sources/ComputeCxx/Graph/Tree/TreeElement.h @@ -14,13 +14,14 @@ namespace AG { struct Graph::TreeElement { const swift::metadata *type; - AttributeID owner; // TODO: rename node + AttributeID node; uint32_t flags; data::ptr parent; - data::ptr next; // children_front - data::ptr old_parent; // next_child - data::ptr last_value; + data::ptr first_child; + data::ptr next_sibling; + + data::ptr first_value; }; static_assert(sizeof(Graph::TreeElement) == 0x20); @@ -31,6 +32,8 @@ class TreeElementID { public: TreeElementID(uint32_t value) : _value(value){}; + data::ptr to_element_ptr() const { return data::ptr(_value); }; + Subgraph *_Nullable subgraph() const { return reinterpret_cast(page_ptr()->zone); } data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; }; @@ -40,7 +43,7 @@ struct Graph::TreeValue { AttributeID value; uint32_t key_id; uint32_t flags; - data::ptr previous_sibling; + data::ptr next; }; static_assert(sizeof(Graph::TreeValue) == 0x18); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index dd72132..8d5d341 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -709,21 +709,21 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { // MARK: - Tree -void Subgraph::begin_tree(AttributeID owner, const swift::metadata *type, uint32_t flags) { +void Subgraph::begin_tree(AttributeID attribute, const swift::metadata *type, uint32_t flags) { data::ptr tree = (data::ptr)alloc_bytes(sizeof(Graph::TreeElement), 7); tree->type = type; - tree->owner = owner; + tree->node = attribute; tree->flags = flags; tree->parent = _tree_root; - tree->old_parent = nullptr; + tree->next_sibling = nullptr; auto old_root = _tree_root; _tree_root = tree; if (old_root) { - _tree_root->old_parent = old_root->next; - old_root->next = _tree_root; + _tree_root->next_sibling = old_root->first_child; + old_root->first_child = _tree_root; } } @@ -733,14 +733,14 @@ void Subgraph::end_tree() { } } -void Subgraph::set_tree_owner(AttributeID owner) { +void Subgraph::set_tree_owner(AttributeID attribute) { if (_tree_root) { // TODO: bug? return; } if (_tree_root->parent) { precondition_failure("setting owner of non-root tree"); } - _tree_root->owner = owner; + _tree_root->node = attribute; } void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type, const char *key, uint32_t flags) { @@ -755,9 +755,9 @@ void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type tree_value->value = attribute; tree_value->key_id = key_id; tree_value->flags = flags; - tree_value->previous_sibling = _tree_root->last_value; + tree_value->next = _tree_root->first_value; - _tree_root->last_value = tree_value; + _tree_root->first_value = tree_value; } /// Returns the node after the given tree element. @@ -786,17 +786,17 @@ AttributeID Subgraph::tree_node_at_index(data::ptr tree_elem return AttributeID::make_nil(); } -uint32_t Subgraph::tree_subgraph_child(data::ptr tree_element) { +data::ptr Subgraph::tree_subgraph_child(data::ptr tree_element) { auto map = _graph->tree_data_elements(); if (!map) { - return 0; + return nullptr; } auto tree_data_element = map->find(this); tree_data_element->second.sort_nodes(); auto nodes = tree_data_element->second.nodes(); - if (!nodes.empty()) { - return; + if (nodes.empty()) { + return nullptr; } // TODO: verify this is lower_bound @@ -815,15 +815,15 @@ uint32_t Subgraph::tree_subgraph_child(data::ptr tree_elemen if (subgraph->_tree_root == nullptr) { continue; } - AttributeID owner = subgraph->_tree_root->owner; - if (owner.without_kind() == 0) { + AttributeID attribute = subgraph->_tree_root->node; + if (attribute.without_kind() == 0) { continue; } - OffsetAttributeID resolved = owner.resolve(TraversalOptions::None); - owner = resolved.attribute(); - if (owner.is_direct()) { + OffsetAttributeID resolved = attribute.resolve(TraversalOptions::None); + attribute = resolved.attribute(); + if (attribute.is_direct()) { for (auto node_iter = iter; node_iter != nodes.end(); ++node_iter) { - if (node_iter->first->owner == owner) { + if (node_iter->first->node == attribute) { subgraph_vector.push_back(subgraph); } } @@ -837,7 +837,7 @@ uint32_t Subgraph::tree_subgraph_child(data::ptr tree_elemen uint32_t last_old_parent = 0; for (auto subgraph : subgraph_vector) { result = subgraph->_tree_root; - subgraph->_tree_root->old_parent = last_old_parent; + subgraph->_tree_root->next_sibling = last_old_parent; last_old_parent = result; } return result; diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index c6a526c..48f304c 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -166,11 +166,11 @@ class Subgraph : public data::zone { uint32_t flags); // TODO: check can be null from Subgraph() void end_tree(); - void set_tree_owner(AttributeID owner); + void set_tree_owner(AttributeID attribute); void add_tree_value(AttributeID attribute, const swift::metadata *type, const char *key, uint32_t flags); AttributeID tree_node_at_index(data::ptr tree_element, uint64_t index); - uint32_t tree_subgraph_child(data::ptr tree_element); + data::ptr tree_subgraph_child(data::ptr tree_element); // MARK: Managing flags From ec006a9e65613fc373aac88f9eb3ef9d1177e8e1 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 20 Mar 2025 23:14:27 +1100 Subject: [PATCH 50/74] Finish named events --- Sources/ComputeCxx/Graph/AGGraph.cpp | 51 ++++++++++++++++++++++++---- Sources/ComputeCxx/Graph/AGGraph.h | 5 ++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 58b342c..89b0acf 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -1,5 +1,7 @@ #include "AGGraph-Private.h" +#include + #include "Attribute/AttributeID.h" #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" @@ -943,18 +945,55 @@ void AGGraphAddNamedTraceEvent(AGGraphRef graph, uint32_t event_id, uint32_t num }); } +namespace NamedEvents { + +static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; +static AG::vector, 0, uint32_t> *names; + +} // namespace NamedEvents + const char *AGGraphGetTraceEventName(uint32_t event_id) { - // TODO: not implemented - return nullptr; + const char *event_name = nullptr; + + os_unfair_lock_lock(&NamedEvents::lock); + if (NamedEvents::names != nullptr && event_id < NamedEvents::names->size()) { + event_name = (*NamedEvents::names)[event_id].second; + } + os_unfair_lock_unlock(&NamedEvents::lock); + + return event_name; } const char *AGGraphGetTraceEventSubsystem(uint32_t event_id) { - // TODO: not implemented - return nullptr; + const char *event_subsystem = nullptr; + + os_unfair_lock_lock(&NamedEvents::lock); + if (NamedEvents::names != nullptr && event_id < NamedEvents::names->size()) { + event_subsystem = (*NamedEvents::names)[event_id].first; + } + os_unfair_lock_unlock(&NamedEvents::lock); + + return event_subsystem; } -void AGGraphRegisterNamedTraceEvent() { - // TODO: not implemented +uint32_t AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) { + os_unfair_lock_lock(&NamedEvents::lock); + + if (!NamedEvents::names) { + NamedEvents::names = new AG::vector, 0, uint32_t>(); + NamedEvents::names->push_back({0, 0}); // Disallow 0 as event ID + } + + uint32_t event_id = NamedEvents::names->size(); + if (event_subsystem != nullptr) { + event_subsystem = strdup(event_subsystem); + } + event_name = strdup(event_name); + NamedEvents::names->push_back({event_subsystem, event_name}); + + os_unfair_lock_unlock(&NamedEvents::lock); + + return event_id; } #pragma mark - Profiler diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 5a7b859..68488b6 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -437,13 +437,16 @@ void AGGraphAddNamedTraceEvent(AGGraphRef graph, uint32_t event_id, uint32_t num CFDataRef data, uint32_t arg6); CF_EXPORT +CF_REFINED_FOR_SWIFT const char *AGGraphGetTraceEventName(uint32_t event_id); CF_EXPORT +CF_REFINED_FOR_SWIFT const char *AGGraphGetTraceEventSubsystem(uint32_t event_id); CF_EXPORT -void AGGraphRegisterNamedTraceEvent(); +CF_REFINED_FOR_SWIFT +uint32_t AGGraphRegisterNamedTraceEvent(const char *key, const char *name); // MARK: Profiler From ce66d3b25e9ebe32c5018580b5237db45c0c05b7 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 21 Mar 2025 00:01:30 +1100 Subject: [PATCH 51/74] Finish cached attributes --- Sources/ComputeCxx/Graph/AGGraph-Private.h | 4 ++ Sources/ComputeCxx/Graph/AGGraph.cpp | 79 +++++++++++++++++++--- Sources/ComputeCxx/Graph/AGGraph.h | 12 +++- Sources/ComputeCxx/Graph/Graph.cpp | 4 +- Sources/ComputeCxx/Graph/Graph.h | 2 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 10 +-- Sources/ComputeCxx/Subgraph/Subgraph.h | 4 +- 7 files changed, 94 insertions(+), 21 deletions(-) diff --git a/Sources/ComputeCxx/Graph/AGGraph-Private.h b/Sources/ComputeCxx/Graph/AGGraph-Private.h index 90e615e..04630ba 100644 --- a/Sources/ComputeCxx/Graph/AGGraph-Private.h +++ b/Sources/ComputeCxx/Graph/AGGraph-Private.h @@ -8,6 +8,10 @@ CF_ASSUME_NONNULL_BEGIN +struct AGUnownedGraph { + AG::Graph value; +}; + struct AGUnownedGraphContext { AG::Graph::Context value; }; diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 89b0acf..1cd9d41 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -294,18 +294,77 @@ bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, namespace { -void read_cached_attribute(unsigned long, AGSwiftMetadata const *, void const *, AGSwiftMetadata const *, unsigned int, - unsigned int, unsigned char &, - AG::ClosureFunctionCI) {} +void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &metadata, void *body, + const AG::swift::metadata &value_type, bool flag, AG::AttributeID attribute, + uint8_t *state_out, AG::ClosureFunctionCI closure) { + + auto current_update = AG::Graph::current_update(); + AG::Graph::UpdateStack *stack = current_update.tag() == 0 ? current_update.get() : nullptr; + + AG::Subgraph *subgraph = nullptr; + if (attribute.without_kind() == 0) { + if (stack != nullptr) { + subgraph = AG::AttributeID(stack->frames().back().attribute).subgraph(); + + } else { + subgraph = AG::Subgraph::current_subgraph(); + } + } else { + attribute.to_node_ptr().assert_valid(); + subgraph = attribute.subgraph(); + } + if (subgraph == nullptr) { + AG::precondition_failure("no subgraph"); + } + + AG::data::ptr cached = subgraph->cache_fetch(identifier, metadata, body, closure); + if (cached == nullptr) { + return nullptr; + } + + if (stack == nullptr) { + void *value = subgraph->graph()->value_ref(AG::AttributeID(cached), 0, value_type, state_out); + subgraph->cache_insert(cached); + return value; + } + + uint8_t input_flags = flag ? 0 : 1; + return subgraph->graph()->input_value_ref(stack->frames().back().attribute, AG::AttributeID(cached), 0, input_flags, + value_type, state_out); +} } // namespace -void AGGraphReadCachedAttribute() { - // TODO: not implemented +void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, + AGAttribute attribute, bool *changed_out, + unsigned long (*closure)(AGUnownedGraphRef graph, const void *context AG_SWIFT_CONTEXT) + AG_SWIFT_CC(swift), + const void *closure_context) { + auto metadata = reinterpret_cast(type); + auto value_metadata = reinterpret_cast(value_type); + + uint8_t state = 0; + void *value = + read_cached_attribute(identifier, *metadata, body, *value_metadata, flag, AG::AttributeID(attribute), &state, + AG::ClosureFunctionCI(closure, closure_context)); + if (changed_out) { + *changed_out = state & 1 ? true : false; + } + return value; } -void AGGraphReadCachedAttributeIfExists() { - // TODO: not implemented +void *AGGraphReadCachedAttributeIfExists(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, + AGAttribute attribute, bool *changed_out) { + auto metadata = reinterpret_cast(type); + auto value_metadata = reinterpret_cast(value_type); + + uint8_t state = 0; + void *value = read_cached_attribute(identifier, *metadata, body, *value_metadata, flag, AG::AttributeID(attribute), + &state, nullptr); + if (changed_out) { + *changed_out = state & 1 ? true : false; + } + return value; } #pragma mark - Current attribute @@ -665,10 +724,10 @@ AGValue get_value(AG::AttributeID attribute_id, uint32_t zone_id, AGValueOptions AG::precondition_failure("no graph: %u", attribute_id); } - bool changed = false; - void *value = subgraph->graph()->value_ref(attribute_id, zone_id, metadata, &changed); + uint8_t state = 0; + void *value = subgraph->graph()->value_ref(attribute_id, zone_id, metadata, &state); - return {value, changed}; + return {value, state & 1 ? true : false}; } } // namespace diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 68488b6..ffa0b9a 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -15,6 +15,7 @@ CF_EXTERN_C_BEGIN // MARK: CFType typedef struct CF_BRIDGED_TYPE(id) AGGraphStorage *AGGraphRef AG_SWIFT_NAME(Graph); +typedef struct AGUnownedGraph *AGUnownedGraphRef; typedef struct AGUnownedGraphContext *AGUnownedGraphContextRef; CF_EXPORT @@ -160,10 +161,17 @@ bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, // MARK: Cached attributes CF_EXPORT -void AGGraphReadCachedAttribute(); +CF_REFINED_FOR_SWIFT +void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, + AGAttribute attribute, bool *changed_out, + unsigned long (*closure)(AGUnownedGraphContextRef graph_context, + const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + const void *closure_context); CF_EXPORT -void AGGraphReadCachedAttributeIfExists(); +CF_REFINED_FOR_SWIFT +void *AGGraphReadCachedAttributeIfExists(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, + AGAttribute attribute, bool *changed_out); // MARK: Current attribute diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 86d3555..0cf8895 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -975,7 +975,7 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, #pragma mark - Values -void *Graph::value_ref(AttributeID attribute, uint32_t zone_id, const swift::metadata &value_type, bool *changed_out) { +void *Graph::value_ref(AttributeID attribute, uint32_t zone_id, const swift::metadata &value_type, uint8_t *state_out) { _version += 1; @@ -1001,7 +1001,7 @@ void *Graph::value_ref(AttributeID attribute, uint32_t zone_id, const swift::met UpdateStatus status = update_attribute(resolved.attribute(), 0); if (status != UpdateStatus::NoChange) { - *changed_out = true; + *state_out = 1; // TODO: check not bool } // check new page seed is same as old and zone is not deleted diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 35eedd1..89e1bb8 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -255,7 +255,7 @@ class Graph { // MARK: Values void *value_ref(AttributeID attribute, uint32_t zone_id, const swift::metadata &value_type, - bool *_Nonnull changed_out); + uint8_t *_Nonnull state_out); bool value_set(data::ptr node, const swift::metadata &value_type, const void *value); bool value_set_internal(data::ptr node_ptr, Node &node, const void *value, const swift::metadata &type); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 8d5d341..dd68625 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -950,7 +950,7 @@ void Subgraph::notify_observers() { #pragma mark - Cache data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata &metadata, void *body, - ClosureFunctionCI closure) { + ClosureFunctionCI closure) { if (_cache == nullptr) { _cache = (data::ptr)alloc_bytes(sizeof(NodeCache), 7); new (&_cache) NodeCache(); @@ -968,16 +968,16 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata type->equatable = equatable; type->last_item = nullptr; type->first_item = nullptr; - type->type_id = (uint32_t)closure(_graph); + type->type_id = (uint32_t)closure(reinterpret_cast(_graph)); _cache->types().insert(&metadata, type); } NodeCache::ItemKey item_lookup_key = {identifier << 8, type, nullptr, body}; NodeCache::Item *item = _cache->table2().lookup(&item_lookup_key, nullptr); if (item == nullptr) { - // if (closure == nullptr) { - // return 0; - // } + if (!closure) { + return nullptr; + } item = type->first_item; if (item == 0 || item->field_0x00 < 2) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 48f304c..e90f9f4 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -9,6 +9,7 @@ #include "Containers/IndirectPointerVector.h" #include "Data/Pointer.h" #include "Data/Zone.h" +#include "Graph/AGGraph.h" #include "Graph/Graph.h" #include "Private/CFRuntime.h" @@ -202,8 +203,9 @@ class Subgraph : public data::zone { // MARK: Cache + // FIXME: not AGUnownedGraphContextRef data::ptr cache_fetch(uint64_t identifier, const swift::metadata &type, void *body, - ClosureFunctionCI closure); + ClosureFunctionCI closure); void cache_insert(data::ptr node); void cache_collect(); From 7fae9626e55292a38562e352154caaab8e37618d Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 21 Mar 2025 00:42:25 +1100 Subject: [PATCH 52/74] Fix circular types --- Sources/ComputeCxx/Graph/AGGraph.cpp | 21 --------------------- Sources/ComputeCxx/Graph/AGGraph.h | 7 ------- Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 21 +++++++++++++++++++++ Sources/ComputeCxx/Subgraph/AGSubgraph.h | 8 ++++++++ 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 1cd9d41..7d35631 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -202,27 +202,6 @@ AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute) { AG::precondition_failure("no graph: %u", attribute); } -// AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute) { -// auto subgraph = AGGraphGetAttributeSubgraph2(attribute); -// if (subgraph == nullptr) { -// AG::precondition_failure("no subgraph"); -// } -// -// return subgraph; -// } -// -// AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute) { -// auto attribute_id = AG::AttributeID(attribute); -// attribute_id.to_node_ptr().assert_valid(); -// -// auto subgraph = attribute_id.subgraph(); -// if (subgraph == nullptr) { -// AG::precondition_failure("internal error"); -// } -// -// return subgraph->to_cf(); -// } - AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute) { auto attribute_id = AG::AttributeID(attribute); if (!attribute_id.is_direct()) { diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index ffa0b9a..85ed12d 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -104,15 +104,8 @@ CF_EXPORT CF_REFINED_FOR_SWIFT AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute); -// TODO: fix circular types -// CF_EXPORT -// CF_REFINED_FOR_SWIFT -// AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute); -// CF_EXPORT -// CF_REFINED_FOR_SWIFT -// AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute); // TODO: need this? // typedef struct AGAttributeType { diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index 1517705..2f3d024 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -111,6 +111,27 @@ AGUnownedGraphContextRef AGSubgraphGetCurrentGraphContext() { return (AGUnownedGraphContextRef)current->graph(); } +AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute) { + auto subgraph = AGGraphGetAttributeSubgraph2(attribute); + if (subgraph == nullptr) { + AG::precondition_failure("no subgraph"); + } + + return subgraph; +} + +AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (subgraph == nullptr) { + AG::precondition_failure("internal error"); + } + + return subgraph->to_cf(); +} + #pragma mark - Children void AGSubgraphAddChild(AGSubgraphRef subgraph, AGSubgraphRef child) { AGSubgraphAddChild2(subgraph, child, 0); } diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index 798f546..4ef917d 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -43,6 +43,14 @@ CF_EXPORT CF_REFINED_FOR_SWIFT AGUnownedGraphContextRef AGSubgraphGetCurrentGraphContext(AGSubgraphRef subgraph); +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute); + +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute); + // MARK: Children CF_EXPORT From a8224ed4f5d2363e06f3a639a784316c64b7d71d Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 8 Apr 2025 23:11:00 +0200 Subject: [PATCH 53/74] Implement Swift code --- Sources/Compute/Attribute/AnyAttribute.swift | 126 +++++++---- Sources/Compute/Attribute/Attribute.swift | 207 ++++++++++++------ Sources/Compute/Attribute/AttributeType.swift | 109 ++++++++- .../Attribute/Body/AttributeBody.swift | 29 +-- .../Attribute/Body/AttributeBodyVisitor.swift | 8 + Sources/Compute/Attribute/External.swift | 12 +- Sources/Compute/Attribute/Focus.swift | 6 +- .../Indirect/IndirectAttribute.swift | 53 +++-- Sources/Compute/Attribute/Map.swift | 6 +- .../Observed/ObservedAttribute.swift | 4 +- .../Optional/AnyOptionalAttribute.swift | 40 ++-- .../Optional/OptionalAttribute.swift | 67 +++--- Sources/Compute/Attribute/PointerOffset.swift | 12 +- .../Attribute/Rule/AnyRuleContext.swift | 62 ++++-- Sources/Compute/Attribute/Rule/Rule.swift | 103 +++++++-- .../Compute/Attribute/Rule/RuleContext.swift | 68 ++++-- .../Compute/Attribute/Rule/StatefulRule.swift | 40 ++-- .../Attribute/Weak/AnyWeakAttribute.swift | 37 ++-- .../Attribute/Weak/WeakAttribute.swift | 43 ++-- Sources/Compute/Graph/Graph.swift | 137 +++++++++--- Sources/Compute/Graph/Subgraph.swift | 65 +++++- Sources/Compute/Graph/TreeElement.swift | 22 +- Sources/Compute/Runtime/CompareValues.swift | 25 ++- Sources/Compute/Runtime/Enum.swift | 44 +++- Sources/Compute/Runtime/Tuple.swift | 126 +++++++---- Sources/ComputeCxx/Attribute/AGAttribute.h | 43 +++- Sources/ComputeCxx/Attribute/AttributeType.h | 9 +- Sources/ComputeCxx/Attribute/Node/Node.h | 16 +- Sources/ComputeCxx/Closure/AGClosure.cpp | 14 +- Sources/ComputeCxx/Closure/AGClosure.h | 21 +- .../Containers/IndirectPointerVector.h | 2 +- Sources/ComputeCxx/Debug/DebugServer.mm | 2 +- .../{Graph+Description.h => AGDescription.h} | 0 Sources/ComputeCxx/Graph/AGGraph.cpp | 76 ++++--- Sources/ComputeCxx/Graph/AGGraph.h | 93 ++++---- Sources/ComputeCxx/Graph/AGValue.h | 13 -- Sources/ComputeCxx/Graph/Graph.cpp | 27 ++- Sources/ComputeCxx/Graph/Graph.h | 14 +- .../ComputeCxx/Graph/Tree/AGTreeElement.cpp | 1 + Sources/ComputeCxx/Graph/Tree/AGTreeElement.h | 8 +- Sources/ComputeCxx/Graph/Tree/AGTreeValue.h | 2 +- Sources/ComputeCxx/Graph/Tree/TreeElement.h | 2 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 2 +- Sources/ComputeCxx/Layout/AGComparison.h | 2 + Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 33 ++- Sources/ComputeCxx/Subgraph/AGSubgraph.h | 17 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 14 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 4 +- Sources/ComputeCxx/Swift/AGType.cpp | 16 +- Sources/ComputeCxx/Swift/AGType.h | 12 +- Sources/ComputeCxx/include/Compute.h | 4 + 51 files changed, 1279 insertions(+), 619 deletions(-) rename Sources/ComputeCxx/Graph/{Graph+Description.h => AGDescription.h} (100%) delete mode 100644 Sources/ComputeCxx/Graph/AGValue.h diff --git a/Sources/Compute/Attribute/AnyAttribute.swift b/Sources/Compute/Attribute/AnyAttribute.swift index 3796125..7a65a7d 100644 --- a/Sources/Compute/Attribute/AnyAttribute.swift +++ b/Sources/Compute/Attribute/AnyAttribute.swift @@ -1,96 +1,136 @@ import ComputeCxx -public struct InputOptions {} - -public struct SearchOptions {} - -public struct AttributeFlags {} - extension AnyAttribute { public static var current: AnyAttribute? { - fatalError("not implemented") + let attribute = __AGGraphGetCurrentAttribute() + return attribute == .nil ? nil : attribute } public init(_ attribute: Attribute) { - fatalError("not implemented") + self = attribute.identifier } public func unsafeOffset(at offset: Int) -> AnyAttribute { - fatalError("not implemented") + // TODO: swift Int instead of UInt32 + return __AGGraphCreateOffsetAttribute(self, UInt32(offset)) } public func unsafeCast(to type: Value.Type) -> Attribute { - fatalError("not implemented") + return Attribute(identifier: self) } - public var _bodyType: Any.Type { - fatalError("not implemented") + public func visitBody(_ visitor: inout Visitor) { + let info = __AGGraphGetAttributeInfo(self) + let type = info.type.pointee.body_type_id.type as! _AttributeBody.Type + type._visitSelf(info.body, visitor: &visitor) } - public var _bodyPointer: UnsafeRawPointer { - fatalError("not implemented") - } + private struct MutateBodyContext { + let mutator: (UnsafeMutableRawPointer) -> Void - public func visitBody(_ visitor: inout Visitor) { - fatalError("not implemented") } + // XXX: Swift compiler crashes when capturing a generic type + public func mutateBody(as type: Body.Type, invalidating: Bool, _ mutator: (inout Body) -> Void) { - fatalError("not implemented") + withoutActuallyEscaping(mutator) { escapingMutator in + let context = MutateBodyContext(mutator: { bodyPointer in + escapingMutator(&bodyPointer.assumingMemoryBound(to: Body.self).pointee) + }) + withUnsafePointer(to: context) { contextPointer in + __AGGraphMutateAttribute( + self, + Metadata(type), + invalidating, + { context, body in + context.assumingMemoryBound(to: MutateBodyContext.self).pointee.mutator(body) + }, + contextPointer + ) + } + } + } - public func breadthFirstSearch(options: SearchOptions, _ predicate: (AnyAttribute) -> Bool) -> Bool { - fatalError("not implemented") + public func breadthFirstSearch(options: AGSearchOptions, _ predicate: (AnyAttribute) -> Bool) -> Bool { + // TODO: @silgen? + struct Context { + let predicate: (AnyAttribute) -> Bool + } + return withoutActuallyEscaping(predicate) { escapingPredicate in + let context = Context(predicate: escapingPredicate) + return withUnsafePointer(to: context) { contextPointer in + return __AGGraphSearch( + self, + options, + { attribute, context in + context.assumingMemoryBound(to: Context.self).pointee.predicate(attribute) + }, + contextPointer + ) + } + } } - public var valueType: Any.Type { - fatalError("not implemented") + public var flags: AGAttributeFlags { + get { + return __AGGraphGetFlags(self) + } + set { + __AGGraphSetFlags(self, newValue) + } } - public func setFlags(_ newFlags: AttributeFlags, mask: AttributeFlags) { - fatalError("not implemented") + public func setFlags(_ newFlags: AGAttributeFlags, mask: AGAttributeFlags) { + let oldFlags = __AGGraphGetFlags(self) + let newFlags = oldFlags.subtracting(mask).union(newFlags.intersection(mask)) + __AGGraphSetFlags(self, newFlags) } - public func addInput(_ input: AnyAttribute, options: InputOptions, token: Int) { - fatalError("not implemented") + public func addInput(_ input: AnyAttribute, options: AGInputOptions, token: Int) { + __AGGraphAddInput(self, input, options) } - public func addInput(_ input: Attribute, options: InputOptions, token: Int) { - fatalError("not implemented") + public func addInput(_ input: Attribute, options: AGInputOptions, token: Int) { + addInput(input.identifier, options: options, token: token) } public var indirectDependency: AnyAttribute? { get { - fatalError("not implemented") + let indirectDependency = __AGGraphGetIndirectDependency(self) + return indirectDependency == .nil ? nil : indirectDependency } set { - fatalError("not implemented") + __AGGraphSetIndirectDependency(self, newValue ?? .nil) } } -} + public var _bodyType: Any.Type { + let info = __AGGraphGetAttributeInfo(self) + return info.type.pointee.body_type_id.type + } -extension AnyAttribute: @retroactive CustomStringConvertible { + public var _bodyPointer: UnsafeRawPointer { + let info = __AGGraphGetAttributeInfo(self) + return info.body + } - public var description: String { - fatalError("not implemented") + public var valueType: Any.Type { + let info = __AGGraphGetAttributeInfo(self) + return info.type.pointee.value_type_id.type } } -extension AnyAttribute: @retroactive Equatable { +extension AnyAttribute: @retroactive CustomStringConvertible { - public static func == (_ lhs: AnyAttribute, _ rhs: AnyAttribute) -> Bool { - fatalError("not implemented") + public var description: String { + return "#\(rawValue)" } } -extension AnyAttribute: @retroactive Hashable { +extension AnyAttribute: @retroactive Equatable {} - public func hash(into hasher: inout Hasher) { - fatalError("not implemented") - } - -} +extension AnyAttribute: @retroactive Hashable {} diff --git a/Sources/Compute/Attribute/Attribute.swift b/Sources/Compute/Attribute/Attribute.swift index 3305b60..7c95e01 100644 --- a/Sources/Compute/Attribute/Attribute.swift +++ b/Sources/Compute/Attribute/Attribute.swift @@ -1,11 +1,5 @@ import ComputeCxx -public struct ValueState {} - -public struct ValueOptions {} - -public struct ChangedValueFlags {} - @propertyWrapper @dynamicMemberLookup public struct Attribute { @@ -17,150 +11,205 @@ public struct Attribute { } public init(_ attribute: Attribute) { - fatalError("not implemented") + self = attribute } - public init(type: Value.Type) { - fatalError("not implemented") + public init(value: Value) { + self = withUnsafePointer(to: value) { valuePointer in + return withUnsafePointer(to: External()) { bodyPointer in + return Attribute(body: bodyPointer, value: valuePointer, flags: .option16) { + return External._update + } + } + } } - public init(value: Value) { - fatalError("not implemented") + public init(type: Value.Type) { + self = withUnsafePointer(to: External()) { bodyPointer in + // TODO: what are flags + return Attribute(body: bodyPointer, value: nil, flags: .option16) { + return External._update + } + } } public init( - body: UnsafePointer, value: UnsafePointer?, flags: AttributeTypeFlags, + body: UnsafePointer, + value: UnsafePointer?, + flags: AGAttributeTypeFlags, update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) { - fatalError("not implemented") - } - public func unsafeCast(to type: T.Type) -> Attribute { - fatalError("not implemented") - } + guard let subgraph = Subgraph.current else { + // or preconditionFailure... + fatalError("attempting to create attribute with no subgraph: \(Body.self)") + } - public func unsafeOffset(at offset: Int, as type: T) -> Attribute { - fatalError("not implemented") + let graph = subgraph.graph + let typeID = graph.internAttributeType( + selfType: Body.self, + bodyType: Body.self, // TODO: make this function generic, don't need to pass witness tables + valueType: Value.self, + flags: flags, + update: update + ) + identifier = __AGGraphCreateAttribute(typeID, body, value) } public func applying(offset: PointerOffset) -> Attribute { - fatalError("not implemented") + return unsafeOffset(at: offset.byteOffset, as: Member.self) } - public func breadthFirstSearch(options: SearchOptions, _ predicate: (AnyAttribute) -> Bool) -> Bool { - fatalError("not implemented") + public func breadthFirstSearch(options: AGSearchOptions, _ predicate: (AnyAttribute) -> Bool) -> Bool { + return identifier.breadthFirstSearch(options: options, predicate) } public func visitBody(_ visitor: inout Visitor) { - fatalError("not implemented") + identifier.visitBody(&visitor) } public func mutateBody(as bodyType: Body.Type, invalidating: Bool, _ mutator: (inout Body) -> Void) { - fatalError("not implemented") + identifier.mutateBody(as: bodyType, invalidating: invalidating, mutator) } public func validate() { - fatalError("not implemented") + __AGGraphVerifyType(identifier, Metadata(Value.self)) } public var value: Value { unsafeAddress { - fatalError("not implemented") + return __AGGraphGetValue(identifier, [], Metadata(Value.self)) + .value + .assumingMemoryBound(to: Value.self) } - set { - fatalError("not implemented") + nonmutating set { + _ = setValue(newValue) } } public func setValue(_ value: Value) -> Bool { - fatalError("not implemented") + return withUnsafePointer(to: value) { valuePointer in + return __AGGraphSetValue(identifier, valuePointer, Metadata(Value.self)) + } } public var hasValue: Bool { - fatalError("not implemented") + return __AGGraphHasValue(identifier) } - public var valueState: ValueState { - fatalError("not implemented") + public var valueState: AGValueState { + return __AGGraphGetValueState(identifier) } public func prefetchValue() { - fatalError("not implemented") + __AGGraphPrefetchValue(identifier) } public func updateValue() { - fatalError("not implemented") + __AGGraphUpdateValue(identifier, 0) } public func invalidateValue() { - fatalError("not implemented") + __AGGraphInvalidateValue(identifier) } - public func changedValue(options: ValueOptions) -> (value: Value, changed: Bool) { - fatalError("not implemented") + public func changedValue(options: AGValueOptions) -> (value: Value, changed: Bool) { + let value = __AGGraphGetValue(identifier, options, Metadata(Value.self)) + return ( + value.value.assumingMemoryBound(to: Value.self).pointee, + value.changed + ) } - public var flags: AttributeFlags { + public var flags: AGAttributeFlags { get { - fatalError("not implemented") + return identifier.flags } set { - fatalError("not implemented") + identifier.flags = newValue } } - public func setFlags(_ flags: AttributeFlags, mask: AttributeFlags) { - fatalError("not implemented") + public func setFlags(_ newFlags: AGAttributeFlags, mask: AGAttributeFlags) { + identifier.setFlags(newFlags, mask: mask) } - public func valueAndFlags(options: ValueOptions) -> (value: Value, flags: ChangedValueFlags) { - fatalError("not implemented") + public func valueAndFlags(options: AGValueOptions) -> (value: Value, flags: AGChangedValueFlags) { + let value = __AGGraphGetValue(identifier, options, Metadata(Value.self)) + return ( + value.value.assumingMemoryBound(to: Value.self).pointee, + value.changed ? .changed : [] + ) } - public func addInput(_ input: Attribute, options: InputOptions, token: Int) { - fatalError("not implemented") + public func addInput(_ input: Attribute, options: AGInputOptions, token: Int) { + identifier.addInput(input, options: options, token: token) } - public func addInput(_ input: AnyAttribute, options: InputOptions, token: Int) { - fatalError("not implemented") + public func addInput(_ input: AnyAttribute, options: AGInputOptions, token: Int) { + identifier.addInput(input, options: options, token: token) } public var graph: Graph { - fatalError("not implemented") + // TODO: retain or not? + return __AGGraphGetAttributeGraph(identifier).takeUnretainedValue() } public var subgraph: Subgraph { - fatalError("not implemented") + // TODO: retain or not? + return __AGGraphGetAttributeSubgraph(identifier).takeUnretainedValue() } public var subgraphOrNil: Subgraph? { - fatalError("not implemented") + // TODO: retain or not? + return __AGGraphGetAttributeSubgraph(identifier).takeUnretainedValue() } public var wrappedValue: Value { unsafeAddress { - fatalError("not implemented") + return __AGGraphGetValue(identifier, [], Metadata(Value.self)) + .value + .assumingMemoryBound(to: Value.self) } - set { - fatalError("not implemented") + nonmutating set { + _ = setValue(newValue) } } public var projectedValue: Attribute { get { - fatalError("not implemented") + return self } set { - fatalError("not implemented") + self = newValue } } - public subscript(offset: (inout Value) -> PointerOffset) -> Attribute { - fatalError("not implemented") + public subscript(offset body: (inout Value) -> PointerOffset) -> Attribute { + unsafeOffset(at: PointerOffset.offset(body).byteOffset, as: Member.self) + } + + public subscript(keyPath keyPath: KeyPath) -> Attribute { + if let offset = MemoryLayout.offset(of: keyPath) { + return unsafeOffset(at: offset, as: Member.self) + } else { + return Attribute(Focus(root: self, keyPath: keyPath)) + } } public subscript(dynamicMember keyPath: KeyPath) -> Attribute { - fatalError("not implemented") + self[keyPath: keyPath] + } + + public func unsafeCast(to type: T.Type) -> Attribute { + return identifier.unsafeCast(to: type) + } + + public func unsafeOffset(at offset: Int, as _: Member.Type) -> Attribute { + // TODO: fix Int/UInt32 + return Attribute( + identifier: __AGGraphCreateOffsetAttribute2(identifier, UInt32(offset), MemoryLayout.size) + ) } } @@ -168,7 +217,7 @@ public struct Attribute { extension Attribute: CustomStringConvertible { public var description: String { - fatalError("not implemented") + return identifier.description } } @@ -191,12 +240,42 @@ extension Attribute: Hashable { extension Attribute { - public init(_ body: Body, initialValue: Value? = nil) where Body.Value == Value { - fatalError("not implemented") + public init(_ body: Body) where Body.Value == Value { + self = withUnsafePointer(to: body) { bodyPointer in + return Attribute(body: bodyPointer, value: nil, flags: []) { + return Body._update + } + } + } - public init(_ body: Body, initialValue: Value? = nil) where Body.Value == Value { - fatalError("not implemented") + public init(_ body: Body, initialValue: Body.Value) where Body.Value == Value { + self = withUnsafePointer(to: body) { bodyPointer in + return withUnsafePointer(to: initialValue) { initialValuePointer in + return Attribute(body: bodyPointer, value: initialValuePointer, flags: []) { + return Body._update + } + } + } + + } + + public init(_ body: Body) where Body.Value == Value { + self = withUnsafePointer(to: body) { bodyPointer in + return Attribute(body: bodyPointer, value: nil, flags: []) { + return Body._update + } + } + } + + public init(_ body: Body, initialValue: Body.Value) where Body.Value == Value { + self = withUnsafePointer(to: body) { bodyPointer in + return withUnsafePointer(to: initialValue) { initialValuePointer in + return Attribute(body: bodyPointer, value: initialValuePointer, flags: []) { + return Body._update + } + } + } } } diff --git a/Sources/Compute/Attribute/AttributeType.swift b/Sources/Compute/Attribute/AttributeType.swift index c6fe2f5..65dd811 100644 --- a/Sources/Compute/Attribute/AttributeType.swift +++ b/Sources/Compute/Attribute/AttributeType.swift @@ -1,3 +1,110 @@ -public struct AttributeTypeFlags { +import ComputeCxx +extension Graph { + + struct InternAttributeTypeContext { + let selfType: Any.Type + let bodyType: _AttributeBody.Type + let valueType: Any.Type + let flags: AGAttributeTypeFlags + let update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void + } + + func internAttributeType( + selfType: Any.Type, + bodyType: _AttributeBody.Type, + valueType: Any.Type, + flags: AGAttributeTypeFlags, + update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void + ) -> UInt32 { + return withoutActuallyEscaping(update) { escapingUpdate in + let context = InternAttributeTypeContext( + selfType: selfType, + bodyType: bodyType, + valueType: valueType, + flags: flags, + update: escapingUpdate + ) + return withUnsafePointer(to: context) { contextPointer in + return __AGGraphInternAttributeType( + self, + Metadata(selfType), + { contextPointer in + // FUN_1afe82038 + let context = contextPointer.assumingMemoryBound(to: InternAttributeTypeContext.self).pointee + let pointer = AGAttributeType.allocate( + selfType: context.selfType, + bodyType: context.bodyType, + valueType: context.valueType, + flags: context.flags, + update: context.update + ) + return UnsafeRawPointer(pointer) + }, + contextPointer + ) + } + } + } + +} + +extension AGAttributeType { + + // FUN_1afe864c4 + static func allocate( + selfType: Any.Type, + bodyType: _AttributeBody.Type, + valueType: Any.Type, + flags: AGAttributeTypeFlags, + update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void + ) -> UnsafeMutablePointer { + struct Context { + let update: (UnsafeMutableRawPointer, AnyAttribute) -> Void + } + return withUnsafePointer(to: Context(update: update())) { contextPointer in + let attributeType = AGAttributeType( + selfType: selfType, + bodyType: bodyType, + valueType: valueType, + flags: flags, + update: { context, body, attribute in + context.assumingMemoryBound(to: Context.self).pointee.update(body, attribute) + }, + updateContext: contextPointer + ) + let pointer = UnsafeMutablePointer.allocate(capacity: 1) + pointer.initialize(to: attributeType) + return pointer + } + } + + // sub_1AFE86960 + init( + selfType: Any.Type, + bodyType: _AttributeBody.Type, // witness table + valueType: Any.Type, + flags: AGAttributeTypeFlags, + update: @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer, AnyAttribute) -> Void, + updateContext: UnsafeRawPointer? + ) { + self.init() + + var effectiveFlags = flags + effectiveFlags.insert(bodyType.flags) + effectiveFlags.insert(AGAttributeTypeFlags(rawValue: UInt32(bodyType.comparisonMode.rawValue))) + if bodyType._hasDestroySelf && !effectiveFlags.contains(.option4) { + effectiveFlags.insert(.option4) // TODO: is this flag reallyHasDestroySelf?? + } + + self.body_type_id = Metadata(selfType) + self.value_type_id = Metadata(valueType) + self.update_function = update + self.update_function_context = updateContext + self.callbacks = nil + self.flags = effectiveFlags + + self.initial_body_type_id = Metadata(selfType) + self.initial_body_witness_table = Metadata(bodyType) // not sure if Metadata works for witness tables + } } diff --git a/Sources/Compute/Attribute/Body/AttributeBody.swift b/Sources/Compute/Attribute/Body/AttributeBody.swift index 322aee5..68331bb 100644 --- a/Sources/Compute/Attribute/Body/AttributeBody.swift +++ b/Sources/Compute/Attribute/Body/AttributeBody.swift @@ -1,34 +1,37 @@ +import ComputeCxx + public protocol _AttributeBody { static func _destroySelf(_ self: UnsafeMutableRawPointer) + static var _hasDestroySelf: Bool { get } + static func _updateDefault(_ default: UnsafeMutableRawPointer) - static var comparisonMode: ComparisonMode { get } - static var _hasDestroySelf: Bool { get } - static var flags: AttributeTypeFlags { get } + static var comparisonMode: AGComparisonMode { get } + static var flags: AGAttributeTypeFlags { get } } extension _AttributeBody { public static func _destroySelf(_ self: UnsafeMutableRawPointer) { - fatalError("not implemented") + } - public static func _updateDefault(_ default: UnsafeMutableRawPointer) { - fatalError("not implemented") + public static var _hasDestroySelf: Bool { + return false } - public static var comparisonMode: ComparisonMode { - fatalError("not implemented") + public static func _updateDefault(_ default: UnsafeMutableRawPointer) { + } - public static var _hasDestroySelf: Bool { - fatalError("not implemented") + public static var comparisonMode: AGComparisonMode { + return .option2 } - public static var flags: AttributeTypeFlags { - fatalError("not implemented") + public static var flags: AGAttributeTypeFlags { + return .option8 } } @@ -36,7 +39,7 @@ extension _AttributeBody { extension _AttributeBody { public var updateWasCancelled: Bool { - fatalError("not implemented") + return __AGGraphUpdateWasCancelled() } } diff --git a/Sources/Compute/Attribute/Body/AttributeBodyVisitor.swift b/Sources/Compute/Attribute/Body/AttributeBodyVisitor.swift index 37a9437..7f19492 100644 --- a/Sources/Compute/Attribute/Body/AttributeBodyVisitor.swift +++ b/Sources/Compute/Attribute/Body/AttributeBodyVisitor.swift @@ -3,3 +3,11 @@ public protocol AttributeBodyVisitor { func visit(body: UnsafePointer) } + +extension _AttributeBody { + + static func _visitSelf(_ self: UnsafeRawPointer, visitor: inout Visitor) { + visitor.visit(body: self.assumingMemoryBound(to: Self.self)) + } + +} diff --git a/Sources/Compute/Attribute/External.swift b/Sources/Compute/Attribute/External.swift index bcfe0de..89d1efe 100644 --- a/Sources/Compute/Attribute/External.swift +++ b/Sources/Compute/Attribute/External.swift @@ -5,19 +5,19 @@ public struct External { public init() {} public static func _update(_: UnsafeMutableRawPointer, attribute: AnyAttribute) { - fatalError("not implemented") + } } extension External: _AttributeBody { - public static var comparisonMode: ComparisonMode { - fatalError("not implemented") + public static var comparisonMode: AGComparisonMode { + return [.option1, .option2] } - public static var flags: AttributeTypeFlags { - fatalError("not implemented") + public static var flags: AGAttributeTypeFlags { + return [] } } @@ -25,7 +25,7 @@ extension External: _AttributeBody { extension External: CustomStringConvertible { public var description: String { - fatalError("not implemented") + return Metadata(Value.self).description } } diff --git a/Sources/Compute/Attribute/Focus.swift b/Sources/Compute/Attribute/Focus.swift index df1a7f9..3b790f6 100644 --- a/Sources/Compute/Attribute/Focus.swift +++ b/Sources/Compute/Attribute/Focus.swift @@ -16,8 +16,8 @@ extension Focus: Rule { return root.value[keyPath: keyPath] } - public static var flags: AttributeTypeFlags { - fatalError("not implemented") + public static var flags: AGAttributeTypeFlags { + return [] } } @@ -25,7 +25,7 @@ extension Focus: Rule { extension Focus: CustomStringConvertible { public var description: String { - fatalError("not implemented") + return "• \(Metadata(Value.self).description)" } } diff --git a/Sources/Compute/Attribute/Indirect/IndirectAttribute.swift b/Sources/Compute/Attribute/Indirect/IndirectAttribute.swift index f2dbfe0..35eec34 100644 --- a/Sources/Compute/Attribute/Indirect/IndirectAttribute.swift +++ b/Sources/Compute/Attribute/Indirect/IndirectAttribute.swift @@ -7,63 +7,72 @@ public struct IndirectAttribute { public var identifier: AnyAttribute public init(source: Attribute) { - fatalError("not implemented") - } - - public var attribute: Attribute { - fatalError("not implemented") + identifier = __AGGraphCreateIndirectAttribute2(source.identifier, MemoryLayout.size) } public var source: Attribute { get { - fatalError("not implemented") + return Attribute(identifier: __AGGraphGetIndirectAttribute(identifier)) } set { - fatalError("not implemented") + __AGGraphSetIndirectAttribute(identifier, newValue.identifier) } } public func resetSource() { - fatalError("not implemented") + __AGGraphResetIndirectAttribute(identifier, false) + } + + public var attribute: Attribute { + return Attribute(identifier: identifier) } public var dependency: AnyAttribute? { get { - fatalError("not implemented") + let result = __AGGraphGetIndirectDependency(identifier) + return result == .nil ? nil : result } set { - fatalError("not implemented") + __AGGraphSetIndirectDependency(identifier, newValue ?? .nil) } } - + public var value: Value { get { - fatalError("not implemented") + return Attribute(identifier: identifier).value } - set { - fatalError("not implemented") + nonmutating set { + Attribute(identifier: identifier).value = newValue + } + nonmutating _modify { + yield &Attribute(identifier: identifier).value } } - public func changedValue(options: ValueOptions) -> (value: Value, changed: Bool) { - fatalError("not implemented") + public func changedValue(options: AGValueOptions) -> (value: Value, changed: Bool) { + return Attribute(identifier: identifier).changedValue(options: options) } - + public var wrappedValue: Value { get { - fatalError("not implemented") + value } - set { - fatalError("not implemented") + nonmutating set { + value = newValue + } + nonmutating _modify { + yield &value } } + + public var projectedValue: Attribute { - fatalError("not implemented") + return Attribute(identifier: identifier) } public subscript(dynamicMember keyPath: KeyPath) -> Attribute { - fatalError("not implemented") + return Attribute(identifier: identifier)[dynamicMember: keyPath] } } diff --git a/Sources/Compute/Attribute/Map.swift b/Sources/Compute/Attribute/Map.swift index 1b3825c..57ddc62 100644 --- a/Sources/Compute/Attribute/Map.swift +++ b/Sources/Compute/Attribute/Map.swift @@ -16,8 +16,8 @@ extension Map: Rule { return body(arg.value) } - public static var flags: AttributeTypeFlags { - fatalError("not implemented") + public static var flags: AGAttributeTypeFlags { + return [] } } @@ -25,7 +25,7 @@ extension Map: Rule { extension Map: CustomStringConvertible { public var description: String { - fatalError("not implemented") + return "• \(Metadata(Value.self).description)" } } diff --git a/Sources/Compute/Attribute/Observed/ObservedAttribute.swift b/Sources/Compute/Attribute/Observed/ObservedAttribute.swift index 67329a1..10b1fa5 100644 --- a/Sources/Compute/Attribute/Observed/ObservedAttribute.swift +++ b/Sources/Compute/Attribute/Observed/ObservedAttribute.swift @@ -6,11 +6,11 @@ public protocol ObservedAttribute: _AttributeBody { extension ObservedAttribute { public static func _destroySelf(_ self: UnsafeMutableRawPointer) { - fatalError("not implemented") + self.assumingMemoryBound(to: Self.self).pointee.destroy() } public static var _hasDestroySelf: Bool { - fatalError("not implemented") + return true } } diff --git a/Sources/Compute/Attribute/Optional/AnyOptionalAttribute.swift b/Sources/Compute/Attribute/Optional/AnyOptionalAttribute.swift index 2d9fa0c..846f15e 100644 --- a/Sources/Compute/Attribute/Optional/AnyOptionalAttribute.swift +++ b/Sources/Compute/Attribute/Optional/AnyOptionalAttribute.swift @@ -2,47 +2,51 @@ import ComputeCxx public struct AnyOptionalAttribute { - public static var current: AnyOptionalAttribute? { - fatalError("not implemented") + public static var current: AnyOptionalAttribute { + return AnyOptionalAttribute(__AGGraphGetCurrentAttribute()) } public var identifier: AnyAttribute public init() { - fatalError("not implemented") + identifier = .nil } - public init(_ weakAttribute: AnyWeakAttribute) { - fatalError("not implemented") + public init(_ attribute: AnyAttribute) { + identifier = attribute } public init(_ attribute: AnyAttribute?) { - fatalError("not implemented") + identifier = attribute ?? .nil } - - public init(_ attribute: AnyAttribute) { - fatalError("not implemented") + + public init(_ weakAttribute: AnyWeakAttribute) { + identifier = __AGWeakAttributeGetAttribute(weakAttribute) } public init(_ optionalAttribute: OptionalAttribute) { - fatalError("not implemented") - } - - public func unsafeCast(to _: Value.Type) -> OptionalAttribute { - fatalError("not implemented") + self = optionalAttribute.base } public var attribute: AnyAttribute? { get { - fatalError("not implemented") + return identifier == .nil ? nil : identifier } set { - fatalError("not implemented") + identifier = newValue ?? .nil } } public func map(_ transform: (AnyAttribute) -> T) -> T? { - fatalError("not implemented") + if let attribute = attribute { + return transform(attribute) + } else { + return nil + } + } + + public func unsafeCast(to _: Value.Type) -> OptionalAttribute { + return OptionalAttribute(base: self) } } @@ -50,7 +54,7 @@ public struct AnyOptionalAttribute { extension AnyOptionalAttribute: CustomStringConvertible { public var description: String { - fatalError("not implemented") + return attribute?.description ?? "nil" } } diff --git a/Sources/Compute/Attribute/Optional/OptionalAttribute.swift b/Sources/Compute/Attribute/Optional/OptionalAttribute.swift index 893234a..7610e31 100644 --- a/Sources/Compute/Attribute/Optional/OptionalAttribute.swift +++ b/Sources/Compute/Attribute/Optional/OptionalAttribute.swift @@ -1,65 +1,72 @@ @propertyWrapper @dynamicMemberLookup public struct OptionalAttribute { - + public var base: AnyOptionalAttribute - - public init(base: AnyOptionalAttribute) { - self.base = base - } - + public init() { - fatalError("not implemented") + base = AnyOptionalAttribute() } - - public init(_ weakAttribute: WeakAttribute) { - fatalError("not implemented") + + public init(base: AnyOptionalAttribute) { + self.base = base } - + public init(_ attribute: Attribute) { - fatalError("not implemented") + base = AnyOptionalAttribute(attribute.identifier) } - + public init(_ attribute: Attribute?) { - fatalError("not implemented") + base = AnyOptionalAttribute(attribute?.identifier) } - + + public init(_ weakAttribute: WeakAttribute) { + base = AnyOptionalAttribute(weakAttribute.base) + } + public var attribute: Attribute? { get { - fatalError("not implemented") + return base.attribute?.unsafeCast(to: Value.self) } set { - fatalError("not implemented") + base.attribute = newValue?.identifier } } - + public var value: Value? { - fatalError("not implemented") + return attribute?.value } - public func changedValue() -> (value: Value, changed: Bool)? { - fatalError("not implemented") + public func changedValue(options: AGValueOptions) -> (value: Value, changed: Bool)? { + return attribute?.changedValue(options: options) } public func map(_ transform: (Attribute) -> T) -> T? { - fatalError("not implemented") + if let attribute = attribute { + return transform(attribute) + } else { + return nil + } } - + public var wrappedValue: Value? { - fatalError("not implemented") + return value } - + public var projectedValue: Attribute? { get { - fatalError("not implemented") + return attribute } set { - fatalError("not implemented") + attribute = newValue + } + _modify { + yield &attribute } } - + public subscript(dynamicMember keyPath: KeyPath) -> Attribute? { - fatalError("not implemented") + return attribute?[dynamicMember: keyPath] } } @@ -67,7 +74,7 @@ public struct OptionalAttribute { extension OptionalAttribute: CustomStringConvertible { public var description: String { - fatalError("not implemented") + return attribute?.description ?? "nil" } } diff --git a/Sources/Compute/Attribute/PointerOffset.swift b/Sources/Compute/Attribute/PointerOffset.swift index b0265bd..d25aaf7 100644 --- a/Sources/Compute/Attribute/PointerOffset.swift +++ b/Sources/Compute/Attribute/PointerOffset.swift @@ -7,15 +7,21 @@ public struct PointerOffset { } public static func of(_ member: inout Member) -> PointerOffset { - fatalError("not implemented") + return withUnsafePointer(to: &member) { memberPointer in + let offset = UnsafeRawPointer(memberPointer) - UnsafeRawPointer(invalidScenePointer()) + return PointerOffset(byteOffset: offset) + } } public static func offset(_ body: (inout Base) -> PointerOffset) -> PointerOffset { - fatalError("not implemented") + guard MemoryLayout.size != 0 else { + return PointerOffset(byteOffset: 0) + } + return body(&invalidScenePointer().pointee) } public static func invalidScenePointer() -> UnsafeMutablePointer { - fatalError("not implemented") + return UnsafeMutablePointer(bitPattern: MemoryLayout.stride)! } } diff --git a/Sources/Compute/Attribute/Rule/AnyRuleContext.swift b/Sources/Compute/Attribute/Rule/AnyRuleContext.swift index 4b5b73d..c837efe 100644 --- a/Sources/Compute/Attribute/Rule/AnyRuleContext.swift +++ b/Sources/Compute/Attribute/Rule/AnyRuleContext.swift @@ -9,41 +9,69 @@ public struct AnyRuleContext { } public init(_ ruleContext: RuleContext) { - fatalError("not implemented") - } - - public func unsafeCast(to type: Value.Type) -> RuleContext { - fatalError("not implemented") + self.attribute = ruleContext.attribute.identifier } public func update(body: () -> Void) { - fatalError("not implemented") + struct Context { + let body: () -> Void + } + // TODO: use silgen? + withoutActuallyEscaping(body) { escapingBody in + withUnsafePointer(to: Context(body: escapingBody)) { contextPointer in + __AGGraphWithUpdate( + attribute, + { + $0.assumingMemoryBound(to: Context.self).pointee.body() + }, + contextPointer + ) + } + } } - public func changedValue(of attribute: Attribute, options: ValueOptions) -> ( + public func changedValue(of input: Attribute, options: AGValueOptions) -> ( value: Value, changed: Bool ) { - fatalError("not implemented") + let result = __AGGraphGetInputValue(attribute, input.identifier, options, Metadata(Value.self)) + return ( + result.value.assumingMemoryBound(to: Value.self).pointee, + result.changed + ) } - public func valueAndFlags(of attribute: Attribute, options: ValueOptions) -> ( - value: Value, flags: ChangedValueFlags + public func valueAndFlags(of input: Attribute, options: AGValueOptions) -> ( + value: Value, flags: AGChangedValueFlags ) { - fatalError("not implemented") + let result = __AGGraphGetInputValue(attribute, input.identifier, options, Metadata(Value.self)) + return ( + result.value.assumingMemoryBound(to: Value.self).pointee, + result.changed ? .changed : [] + ) } - public subscript(_ attribute: Attribute) -> Value { + public subscript(_ input: Attribute) -> Value { unsafeAddress { - fatalError("not implemented") + return __AGGraphGetInputValue(attribute, input.identifier, [], Metadata(Value.self)) + .value + .assumingMemoryBound(to: Value.self) } } - public subscript(_ weakAttribute: WeakAttribute) -> Value? { - fatalError("not implemented") + public subscript(_ weakInput: WeakAttribute) -> Value? { + return weakInput.attribute.map { input in + return self[input] + } } - public subscript(_ optionalAttribute: OptionalAttribute) -> Value? { - fatalError("not implemented") + public subscript(_ optionalInput: OptionalAttribute) -> Value? { + return optionalInput.attribute.map { input in + return self[input] + } + } + + public func unsafeCast(to type: Value.Type) -> RuleContext { + return RuleContext(attribute: Attribute(identifier: attribute)) } } diff --git a/Sources/Compute/Attribute/Rule/Rule.swift b/Sources/Compute/Attribute/Rule/Rule.swift index 066c475..f2e3970 100644 --- a/Sources/Compute/Attribute/Rule/Rule.swift +++ b/Sources/Compute/Attribute/Rule/Rule.swift @@ -16,12 +16,20 @@ extension Rule { } public static func _updateDefault(_ self: UnsafeMutableRawPointer) { - fatalError("not implemented") - + guard let initialValue = initialValue else { + return + } + withUnsafePointer(to: initialValue) { initialValuePointer in + __AGGraphSetOutputValue(initialValuePointer, Metadata(Value.self)) + } } - public static func _update(_ self: UnsafeMutableRawPointer, attribute: AnyAttribute) { - fatalError("not implemented") + public static func _update(_ pointer: UnsafeMutableRawPointer, attribute: AnyAttribute) { + let rule = pointer.assumingMemoryBound(to: Self.self) + let value = rule.pointee.value + withUnsafePointer(to: value) { valuePointer in + __AGGraphSetOutputValue(valuePointer, Metadata(Value.self)) + } } } @@ -29,36 +37,101 @@ extension Rule { extension Rule { public var bodyChanged: Bool { - fatalError("not implemented") + // guessing this is it + return __AGGraphCurrentAttributeWasModified() } public var attribute: Attribute { - fatalError("not implemented") + guard let attribute = AnyAttribute.current else { + preconditionFailure() + } + return Attribute(identifier: attribute) } public var context: RuleContext { - fatalError("not implemented") + return RuleContext(attribute: attribute) } } -public struct CachedValueOptions {} +private struct RuleCachedValueContext {} + +// AutoClass1::FUN_1afe81fc +func makeSomething( + graph: Graph, + update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void, + selfType: Any.Type, + bodyType: _AttributeBody.Type, + valueType: Any.Type +) -> UInt32 { + return graph.internAttributeType( + selfType: selfType, + bodyType: bodyType, + valueType: valueType, + flags: [], + update: update + ) +} -extension Rule where Value: Hashable { +extension Rule where Self: Hashable { - public func cachedValue(options: CachedValueOptions, owner: AnyAttribute?) -> Value { - fatalError("not implemented") + public func cachedValue(options: AGCachedValueOptions, owner: AnyAttribute?) -> Value { + return withUnsafePointer(to: self) { selfPointer in + let result = Self._cachedValue(options: options, owner: owner, hashValue: hashValue, bodyPtr: selfPointer) { + return Self._update(_:attribute:) + } + return result.pointee + } } - public func cachedValueIfExists(options: CachedValueOptions, owner: AnyAttribute?) -> Value? { - fatalError("not implemented") + public func cachedValueIfExists(options: AGCachedValueOptions, owner: AnyAttribute?) -> Value? { + let result = withUnsafePointer(to: self) { selfPointer in + return __AGGraphReadCachedAttributeIfExists( + UInt64(hashValue), // TODO: bitPattern? + Metadata(Self.self), + UnsafeMutablePointer(mutating: selfPointer), // TODO: need to mutate this? make const at C and propogate... + Metadata(Value.self), + options, + owner ?? .nil, + nil + ) + } + guard let result = result else { + return nil + } + return result.assumingMemoryBound(to: Value.self).pointee } public static func _cachedValue( - options: CachedValueOptions, owner: AnyAttribute?, hashValue: Int, bodyPtr: UnsafeRawPointer, + options: AGCachedValueOptions, + owner: AnyAttribute?, + hashValue: Int, + bodyPtr: UnsafeRawPointer, update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) -> UnsafePointer { - fatalError("not implemented") + let result = withUnsafePointer(to: self) { selfPointer in + var context = RuleCachedValueContext() + return __AGGraphReadCachedAttribute( + UInt64(hashValue), // TODO: bitPattern? + Metadata(Self.self), + UnsafeMutablePointer(mutating: selfPointer), + Metadata(Value.self), + options, + owner ?? .nil, + nil, + { + graph, + context in + + // TODO: fix as! Graph + // return makeSomething(graph: graph as! Graph, update: update, selfType: Self.self, bodyType: Self.self, valueType: Value.self) + return 0 + }, + &context + ) + } + let pointer = result.assumingMemoryBound(to: Value.self) + return UnsafePointer(pointer) } } diff --git a/Sources/Compute/Attribute/Rule/RuleContext.swift b/Sources/Compute/Attribute/Rule/RuleContext.swift index bf815ea..dbe714f 100644 --- a/Sources/Compute/Attribute/Rule/RuleContext.swift +++ b/Sources/Compute/Attribute/Rule/RuleContext.swift @@ -1,3 +1,7 @@ +struct RuleContextUpdateContext { + let body: () -> Void +} + public struct RuleContext { public var attribute: Attribute @@ -7,44 +11,76 @@ public struct RuleContext { } public func update(body: () -> Void) { - fatalError("not implemented") + // TODO: use silgen? + withoutActuallyEscaping(body) { escapingBody in + withUnsafePointer(to: RuleContextUpdateContext(body: escapingBody)) { contextPointer in + __AGGraphWithUpdate( + attribute.identifier, + { + $0.assumingMemoryBound(to: RuleContextUpdateContext.self).pointee.body() + }, + contextPointer + ) + } + } } public var value: Value { unsafeAddress { - fatalError("not implemented") + guard let result = __AGGraphGetOutputValue(Metadata(Value.self)) else { + preconditionFailure() + } + let pointer = result.assumingMemoryBound(to: Value.self) + return UnsafePointer(pointer) } - set { - fatalError("not implemented") + nonmutating set { + withUnsafePointer(to: newValue) { newValuePointer in + __AGGraphSetOutputValue(newValuePointer, Metadata(Value.self)) + } } } public var hasValue: Bool { - fatalError("not implemented") + let valuePointer = __AGGraphGetOutputValue(Metadata(Value.self)) + return valuePointer != nil } - public func changedValue(of attribute: Attribute, options: ValueOptions) -> (value: Value, changed: Bool) { - fatalError("not implemented") + public func changedValue(of input: Attribute, options: AGValueOptions) -> (value: Value, changed: Bool) { + let result = __AGGraphGetInputValue(attribute.identifier, input.identifier, options, Metadata(Value.self)) + return ( + result.value.assumingMemoryBound(to: Value.self).pointee, + result.changed + ) } - public func valueAndFlags(of attribute: Attribute, options: ValueOptions) -> ( - value: Value, flags: ChangedValueFlags + public func valueAndFlags(of input: Attribute, options: AGValueOptions) -> ( + value: Value, flags: AGChangedValueFlags ) { - fatalError("not implemented") + let result = __AGGraphGetInputValue(attribute.identifier, input.identifier, options, Metadata(Value.self)) + return ( + result.value.assumingMemoryBound(to: Value.self).pointee, + result.changed ? .changed : [] + ) } - public subscript(_ attribute: Attribute) -> T { + public subscript(_ input: Attribute) -> InputValue { unsafeAddress { - fatalError("not implemented") + return __AGGraphGetInputValue(attribute.identifier, input.identifier, [], Metadata(InputValue.self)) + .value + .assumingMemoryBound(to: InputValue.self) } } - public subscript(_ weakAttribute: WeakAttribute) -> T? { - fatalError("not implemented") + public subscript(_ weakInput: WeakAttribute) -> InputValue? { + return weakInput.attribute.map { input in + return self[input] + } } - public subscript(_ optionalAttribute: OptionalAttribute) -> T? { - fatalError("not implemented") + public subscript(_ optionalInput: OptionalAttribute) -> InputValue? { + return optionalInput.attribute.map { input in + return self[input] + } } } diff --git a/Sources/Compute/Attribute/Rule/StatefulRule.swift b/Sources/Compute/Attribute/Rule/StatefulRule.swift index cd0cbcd..b029676 100644 --- a/Sources/Compute/Attribute/Rule/StatefulRule.swift +++ b/Sources/Compute/Attribute/Rule/StatefulRule.swift @@ -16,39 +16,53 @@ extension StatefulRule { } public static func _updateDefault(_ default: UnsafeMutableRawPointer) { - fatalError("not implemented") + guard let initialValue = initialValue else { + return + } + withUnsafePointer(to: initialValue) { initialValuePointer in + __AGGraphSetOutputValue(initialValuePointer, Metadata(Value.self)) + } + } + + public static func _update(_ pointer: UnsafeMutableRawPointer, attribute: AnyAttribute) { + let rule = pointer.assumingMemoryBound(to: Self.self) + rule.pointee.updateValue() } } extension StatefulRule { - public static func _update(_ body: UnsafeMutableRawPointer, attribute: AnyAttribute) { - fatalError("not implemented") - } - public var bodyChanged: Bool { - fatalError("not implemented") + // guessing this is it + return __AGGraphCurrentAttributeWasModified() } public var value: Value { unsafeAddress { - fatalError("not implemented") + guard let result = __AGGraphGetOutputValue(Metadata(Value.self)) else { + preconditionFailure() + } + let pointer = result.assumingMemoryBound(to: Value.self) + return UnsafePointer(pointer) } - set { - fatalError("not implemented") + nonmutating set { + context.value = newValue } } public var hasValue: Bool { - fatalError("not implemented") + return context.hasValue } public var attribute: Attribute { - fatalError("not implemented") + guard let attribute = AnyAttribute.current else { + preconditionFailure() + } + return Attribute(identifier: attribute) } - + public var context: RuleContext { - fatalError("not implemented") + return RuleContext(attribute: attribute) } } diff --git a/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift b/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift index 8d170cb..bc89938 100644 --- a/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift +++ b/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift @@ -1,50 +1,39 @@ import ComputeCxx -public struct AnyWeakAttribute { +extension AnyWeakAttribute { public init(_ attribute: WeakAttribute) { - fatalError("not implemented") + self = attribute.base } public init(_ attribute: AnyAttribute?) { - fatalError("not implemented") - } - - public func unsafeCast(to type: Value.Type) -> WeakAttribute { - fatalError("not implemented") + self = __AGCreateWeakAttribute(attribute ?? .nil) } public var attribute: AnyAttribute? { get { - fatalError("not implemented") + let attribute = __AGWeakAttributeGetAttribute(self) + return attribute == .nil ? nil : attribute } set { - fatalError("not implemented") + self = AnyWeakAttribute(newValue) } } -} - -extension AnyWeakAttribute: CustomStringConvertible { - - public var description: String { - fatalError("not implemented") + public func unsafeCast(to type: Value.Type) -> WeakAttribute { + return WeakAttribute(base: self) } } -extension AnyWeakAttribute: Equatable { +extension AnyWeakAttribute: @retroactive CustomStringConvertible { - public static func == (lhs: AnyWeakAttribute, rhs: AnyWeakAttribute) -> Bool { - fatalError("not implemented") + public var description: String { + return attribute?.description ?? "nil" } } -extension AnyWeakAttribute: Hashable { +extension AnyWeakAttribute: @retroactive Equatable {} - public func hash(into hasher: inout Hasher) { - fatalError("not implemented") - } - -} +extension AnyWeakAttribute: @retroactive Hashable {} diff --git a/Sources/Compute/Attribute/Weak/WeakAttribute.swift b/Sources/Compute/Attribute/Weak/WeakAttribute.swift index 3605cf4..566fa3c 100644 --- a/Sources/Compute/Attribute/Weak/WeakAttribute.swift +++ b/Sources/Compute/Attribute/Weak/WeakAttribute.swift @@ -7,51 +7,56 @@ public struct WeakAttribute { public init(base: AnyWeakAttribute) { self.base = base } - + public init() { - fatalError("not implemented") + base = AnyWeakAttribute(rawValue: 0) // TODO: memberwise init } - + public init(_ attribute: Attribute) { - fatalError("not implemented") + base = AnyWeakAttribute(attribute.identifier) } - + public init(_ attribute: Attribute?) { - fatalError("not implemented") + base = AnyWeakAttribute(attribute?.identifier) } - public func changedValue(options: ValueOptions) -> (value: Value, changed: Bool)? { - fatalError("not implemented") + public func changedValue(options: AGValueOptions) -> (value: Value, changed: Bool)? { + let result = __AGGraphGetWeakValue(base, options, Metadata(Value.self)) + return (result.value.assumingMemoryBound(to: Value.self).pointee, result.changed) } public var value: Value? { - fatalError("not implemented") + let result = __AGGraphGetWeakValue(base, [], Metadata(Value.self)) + return result.value.assumingMemoryBound(to: Value.self).pointee } public var attribute: Attribute? { get { - fatalError("not implemented") + return base.attribute?.unsafeCast(to: Value.self) } set { - fatalError("not implemented") + base.attribute = newValue?.identifier } } - + public var wrappedValue: Value? { - fatalError("not implemented") + return value } - + public var projectedValue: Attribute? { get { - fatalError("not implemented") + return attribute } set { - fatalError("not implemented") + attribute = newValue + } + _modify { + yield &attribute } } - + public subscript(dynamicMember keyPath: KeyPath) -> Attribute? { - fatalError("not implemented") + attribute?[keyPath: keyPath] } } @@ -59,7 +64,7 @@ public struct WeakAttribute { extension WeakAttribute: CustomStringConvertible { public var description: String { - fatalError("not implemented") + return base.description } } diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index 1f44862..62783ac 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -1,29 +1,96 @@ import ComputeCxx -extension Graph { +struct ContextVV { + let body: () -> Void +} - public func onUpdate(_ handler: () -> Void) { - fatalError("not implemented") - } +struct ContextAV { + let body: (AnyAttribute) -> Void +} + +struct ContextPV { + let body: (() -> Void) -> Void +} + +extension Graph { - public func onInvalidation(_ handler: (AnyAttribute) -> Void) { - fatalError("not implemented") + public func onUpdate(_ handler: @escaping () -> Void) { + withUnsafePointer(to: ContextVV(body: handler)) { contextPointer in + __AGGraphSetUpdateCallback( + self, + { + $0.assumingMemoryBound(to: ContextVV.self).pointee.body() + }, + contextPointer + ) + } + } + + public func onInvalidation(_ handler: @escaping (AnyAttribute) -> Void) { + withUnsafePointer(to: ContextAV(body: handler)) { contextPointer in + __AGGraphSetInvalidationCallback( + self, + { + // TODO: swap params around + $1.assumingMemoryBound(to: ContextAV.self).pointee.body($0) + }, + contextPointer + ) + } } public func withDeadline(_ deadline: UInt64, _ body: () -> T) -> T { - fatalError("not implemented") + let oldDeadline = __AGGraphGetDeadline(self) + __AGGraphSetDeadline(self, deadline) + let result = body() + __AGGraphSetDeadline(self, oldDeadline) + return result } public func withoutUpdate(_ body: () -> T) -> T { - fatalError("not implemented") + let previousUpdate = __AGGraphClearUpdate() + let result = body() + __AGGraphSetUpdate(previousUpdate) + return result } public func withoutSubgraphInvalidation(_ body: () -> T) -> T { - fatalError("not implemented") - } - - public func withMainThreadHandler(_ body: (() -> Void) -> Void, do: () -> Void) { - fatalError("not implemented") + let wasDeferringSubgraphInvalidation = __AGGraphBeginDeferringSubgraphInvalidation(self) + let result = body() + __AGGraphEndDeferringSubgraphInvalidation(self, wasDeferringSubgraphInvalidation) + return result + } + + // todo: delete @escaping + public func withMainThreadHandler( + _ mainThreadHandler: @escaping (() -> Void) -> Void, + do body: @escaping () -> Void + ) { + // let bodyContext = ContextVV(body: body) + // let mainThreadHandlerContext = ContextPV(body: mainThreadHandler) + // withUnsafePointer(to: bodyContext) { bodyContextPointer in + // withUnsafePointer( + // to: mainThreadHandlerContext + // ) { mainThreadHandlerContextPointer in + // __AGGraphWithMainThreadHandler( + // self, + // { + // $0.assumingMemoryBound(to: ContextVV.self).pointee.body() + // }, + // bodyContextPointer, + // { thunk, context in + // + // + // // TODO: swap params + // context.assumingMemoryBound(to: ContextPV.self).pointee.body({ + // thunk() + // }) + // }, + // mainThreadHandlerContextPointer + // ) + // } + // + // } } } @@ -31,32 +98,34 @@ extension Graph { extension Graph { public static func startProfiling() { - fatalError("not implemented") + __AGGraphStartProfiling(nil) } public static func stopProfiling() { - fatalError("not implemented") + __AGGraphStopProfiling(nil) } public static func markProfile(name: UnsafePointer) { - fatalError("not implemented") + __AGGraphMarkProfile(nil, name) } public static func resetProfile() { - fatalError("not implemented") + __AGGraphResetProfile(nil) } } extension Graph { - public func addTraceEvent(_ event: UnsafePointer, value: T) { - fatalError("not implemented") + public func addTraceEvent(_ event: UnsafePointer, context: UnsafePointer) { + __AGGraphAddTraceEvent(self, event, context, Metadata(T.self)) } - public func addTraceEvent(_ event: UnsafePointer, context: UnsafePointer) { - fatalError("not implemented") + public func addTraceEvent(_ event: UnsafePointer, value: T) { + withUnsafePointer(to: value) { valuePointer in + __AGGraphAddTraceEvent(self, event, valuePointer, Metadata(T.self)) + } } } @@ -64,23 +133,37 @@ extension Graph { extension Graph { public func print(includeValues: Bool) { - fatalError("not implemented") + Swift.print(graphvizDescription(includeValues: includeValues)) } public func archiveJSON(name: String?) { - fatalError("not implemented") + __AGGraphArchiveJSON(name?.cString(using: .utf8)) } public func graphvizDescription(includeValues: Bool) -> String { - fatalError("not implemented") + let result = __AGGraphDescription( + self, + [AGDescriptionFormat: "graph/dot", AGDescriptionIncludeValues: includeValues] as CFDictionary + ).takeUnretainedValue() + guard let description = result as? String else { + preconditionFailure() + } + return description } public static func printStack(maxFrames: Int) { - fatalError("not implemented") + Swift.print(stackDescription(maxFrames: maxFrames)) } public static func stackDescription(maxFrames: Int) -> String { - fatalError("not implemented") + let result = __AGGraphDescription( + nil, + [AGDescriptionFormat: "stack/text", AGDescriptionMaxFrames: maxFrames] as CFDictionary + ).takeUnretainedValue() + guard let description = result as? String else { + preconditionFailure() + } + return description } } @@ -88,7 +171,7 @@ extension Graph { extension Graph: @retroactive Equatable { public static func == (_ lhs: Graph, _ rhs: Graph) -> Bool { - fatalError("not implemented") + return __AGGraphGetCounter(lhs, .graphID) == __AGGraphGetCounter(rhs, .graphID) } } diff --git a/Sources/Compute/Graph/Subgraph.swift b/Sources/Compute/Graph/Subgraph.swift index 00f381c..3dd1426 100644 --- a/Sources/Compute/Graph/Subgraph.swift +++ b/Sources/Compute/Graph/Subgraph.swift @@ -1,33 +1,76 @@ import ComputeCxx +struct SubgraphContextVV { + let body: () -> Void +} + +struct SubgraphContextAV { + let body: (AnyAttribute) -> Void +} + extension Subgraph { - public func addObserver(_ observer: () -> Void) -> Int { - fatalError("not implemented") + public func addObserver(_ observer: @escaping () -> Void) -> Int { + let result = withUnsafePointer(to: SubgraphContextVV(body: observer)) { contextPointer in + return __AGSubgraphAddObserver( + self, + { + $0.assumingMemoryBound(to: SubgraphContextVV.self).pointee.body() + }, + contextPointer + ) + } + return Int(result) // TODO: where is this converted? } public func apply(_ body: () -> T) -> T { - fatalError("not implemented") + let previousSubgraph = Subgraph.current + let previousUpdate = __AGGraphClearUpdate() + defer { + Subgraph.current = previousSubgraph + __AGGraphSetUpdate(previousUpdate) + } + Subgraph.current = self + return body() } - public func forEach(_ flags: AttributeFlags, _ body: (AnyAttribute) -> Void) { - fatalError("not implemented") + public func forEach(_ flags: AGAttributeFlags, _ body: (AnyAttribute) -> Void) { + withoutActuallyEscaping(body) { escapingBody in + withUnsafePointer(to: SubgraphContextAV(body: escapingBody)) { contextPointer in + __AGSubgraphApply( + self, + flags, + { + // TODO: swap params + $1.assumingMemoryBound(to: SubgraphContextAV.self).pointee.body($0) + }, + contextPointer + ) + } + } + } } extension Subgraph { - public func addTreeValue(_ attribute: Attribute, forKey key: UnsafePointer, flags: UInt32) { - fatalError("not implemented") + public static func addTreeValue(_ value: Attribute, forKey key: UnsafePointer, flags: UInt32) { + if __AGSubgraphShouldRecordTree() { + __AGSubgraphAddTreeValue(value.identifier, Metadata(Value.self), key, flags) + } } - public func beginTreeElement(value: Attribute, flags: UInt32) { - fatalError("not implemented") + public static func beginTreeElement(value: Attribute, flags: UInt32) { + if __AGSubgraphShouldRecordTree() { + __AGSubgraphBeginTreeElement(value.identifier, Metadata(Value.self), flags) + } } - public func endTreeElement(value: Attribute) { - fatalError("not implemented") + public static func endTreeElement(value: Attribute) { + if __AGSubgraphShouldRecordTree() { + __AGSubgraphEndTreeElement(value.identifier) + } } } diff --git a/Sources/Compute/Graph/TreeElement.swift b/Sources/Compute/Graph/TreeElement.swift index 2394072..70cc517 100644 --- a/Sources/Compute/Graph/TreeElement.swift +++ b/Sources/Compute/Graph/TreeElement.swift @@ -1,35 +1,35 @@ import ComputeCxx -public struct TreeElement {} - extension TreeElement { public var value: AnyAttribute? { - fatalError("not implemented") + let result = __AGTreeElementGetValue(self) + return result == .nil ? nil : result } } -struct Nodes: Sequence, IteratorProtocol { +extension Nodes: @retroactive Sequence, @retroactive IteratorProtocol { - public func next() -> AnyAttribute? { - fatalError("not implemented") + public mutating func next() -> AnyAttribute? { + let result = __AGTreeElementGetNextNode(&self) + return result == .nil ? nil : result } } -struct Children: Sequence, IteratorProtocol { +extension Children: @retroactive Sequence, @retroactive IteratorProtocol { public mutating func next() -> TreeElement? { - fatalError("not implemented") + return __AGTreeElementGetNextChild(&self) } } -struct Values: Sequence, IteratorProtocol { +extension Values: @retroactive Sequence, @retroactive IteratorProtocol { - public func next() -> TreeElement? { - fatalError("not implemented") + public func next() -> TreeValue? { + return __AGTreeElementGetNextValue(self) } } diff --git a/Sources/Compute/Runtime/CompareValues.swift b/Sources/Compute/Runtime/CompareValues.swift index 0189207..795c7d0 100644 --- a/Sources/Compute/Runtime/CompareValues.swift +++ b/Sources/Compute/Runtime/CompareValues.swift @@ -1,21 +1,22 @@ -public enum ComparisonMode { +import ComputeCxx -} +extension AGComparisonOptions { -public struct ComparisonOptions { - - init(mode: ComparisonMode) { - fatalError("not implemented") + init(mode: AGComparisonMode) { + self.init(rawValue: UInt32(mode.rawValue)) } - -} -func compareValues(_ lhs: Value, _ rhs: Value, mode: ComparisonMode) -> Bool { - fatalError("not implemented") } -func compareValues(_ lhs: Value, _ rhs: Value, mode: ComparisonOptions) -> Bool { - fatalError("not implemented") +func compareValues(_ lhs: Value, _ rhs: Value, mode: AGComparisonMode) -> Bool { + return compareValues(lhs, rhs, options: AGComparisonOptions(mode: mode)) } +func compareValues(_ lhs: Value, _ rhs: Value, options: AGComparisonOptions) -> Bool { + return withUnsafePointer(to: lhs) { lhsPointer in + return withUnsafePointer(to: rhs) { rhsPointer in + return __AGCompareValues(lhsPointer, rhsPointer, Metadata(Value.self), options.union(.copyOnWrite)) + } + } +} diff --git a/Sources/Compute/Runtime/Enum.swift b/Sources/Compute/Runtime/Enum.swift index c4b59a4..d8fb84d 100644 --- a/Sources/Compute/Runtime/Enum.swift +++ b/Sources/Compute/Runtime/Enum.swift @@ -1,11 +1,47 @@ +import ComputeCxx + +struct AGTypeApplyEnumDataContext { + let body: (Int, Any.Type, UnsafeRawPointer) -> Void +} + public func withUnsafePointerToEnumCase( - of enumValue: UnsafeMutablePointer, do body: (Int, Any.Type, UnsafeRawPointer) -> Void + of enumValue: UnsafeMutablePointer, + do body: (Int, Any.Type, UnsafeRawPointer) -> Void ) -> Bool { - fatalError("not implemented") + return withoutActuallyEscaping(body) { escapingBody in + let context = AGTypeApplyEnumDataContext(body: escapingBody) + return withUnsafePointer(to: context) { contextPointer in + return __AGTypeApplyEnumData( + Metadata(Value.self), + enumValue, + { + $0.assumingMemoryBound(to: AGTypeApplyEnumDataContext.self).pointee.body(Int($1), $2.type, $3) + }, + contextPointer + ) + } + } +} + +struct AGTypeApplyMutableEnumDataContext { + let body: (Int, Any.Type, UnsafeMutableRawPointer) -> Void } public func withUnsafeMutablePointerToEnumCase( - of enumValue: UnsafeMutablePointer, do body: (Int, Any.Type, UnsafeMutableRawPointer) -> Void + of enumValue: UnsafeMutablePointer, + do body: (Int, Any.Type, UnsafeMutableRawPointer) -> Void ) -> Bool { - fatalError("not implemented") + return withoutActuallyEscaping(body) { escapingBody in + let context = AGTypeApplyMutableEnumDataContext(body: escapingBody) + return withUnsafePointer(to: context) { contextPointer in + return __AGTypeApplyMutableEnumData( + Metadata(Value.self), + enumValue, + { + $0.assumingMemoryBound(to: AGTypeApplyMutableEnumDataContext.self).pointee.body(Int($1), $2.type, $3) + }, + contextPointer + ) + } + } } diff --git a/Sources/Compute/Runtime/Tuple.swift b/Sources/Compute/Runtime/Tuple.swift index e36736a..e16218e 100644 --- a/Sources/Compute/Runtime/Tuple.swift +++ b/Sources/Compute/Runtime/Tuple.swift @@ -1,148 +1,182 @@ public func withUnsafeTuple(of type: TupleType, count: Int, _ body: (UnsafeMutableTuple) -> Void) { - fatalError("not implemented") + struct Context { + let body: (UnsafeMutableTuple) -> Void + } + withoutActuallyEscaping(body) { escapingBody in + withUnsafePointer(to: Context(body: escapingBody)) { contextPointer in + // TODO: why didn't I call function from implementation? + __AGTupleWithBuffer( + type, + count, + { $1.assumingMemoryBound(to: Context.self).pointee.body($0) }, + contextPointer + ) + } + } } -public struct TupleType { - - public struct CopyOptions {} +extension TupleType { public init(_ types: [Any.Type]) { - fatalError("not implemented") + self.init(count: UInt32(types.count), elements: types.map(Metadata.init)) } public init(_ type: Any.Type) { - fatalError("not implemented") + self.init(rawValue: OpaquePointer.init(unsafeBitCast(type, to: UnsafePointer.self))) } public var type: Any.Type { - fatalError("not implemented") + return unsafeBitCast(rawValue, to: Any.Type.self) } public var isEmpty: Bool { - fatalError("not implemented") + return count == 0 } public var indices: Range { - fatalError("not implemented") + return 0.. Any.Type { - fatalError("not implemented") + return elementType(at: UInt32(index)).type } public func offset(at index: Int, as type: T.Type) -> Int { - fatalError("not implemented") + return elementOffset(at: UInt32(index), type: Metadata(type)) } public func getElement( - in tupleValue: UnsafeMutableRawPointer, at index: Int, to destinationValue: UnsafeMutablePointer, - options: TupleType.CopyOptions + in tupleValue: UnsafeMutableRawPointer, + at index: Int, + to destinationValue: UnsafeMutablePointer, + options: CopyOptions ) { - fatalError("not implemented") + __AGTupleGetElement(self, tupleValue, UInt32(index), destinationValue, Metadata(T.self), options) } public func setElement( - in tupleValue: UnsafeMutableRawPointer, at index: Int, from sourceValue: UnsafePointer, - options: TupleType.CopyOptions + in tupleValue: UnsafeMutableRawPointer, + at index: Int, + from sourceValue: UnsafePointer, + options: CopyOptions ) { - fatalError("not implemented") + __AGTupleSetElement(self, tupleValue, UInt32(index), sourceValue, Metadata(T.self), options) } } -public struct UnsafeTuple { +extension UnsafeTuple { public var count: Int { - fatalError("not implemented") + return type.count } public var isEmpty: Bool { - fatalError("not implemented") + return type.isEmpty } public var indices: Range { - fatalError("not implemented") + return type.indices } - public func address(as type: T.Type) -> UnsafePointer { - fatalError("not implemented") + public func address(as expectedType: T.Type) -> UnsafePointer { + guard type.type == expectedType else { + preconditionFailure() + } + return value.assumingMemoryBound(to: expectedType) } public func address(of index: Int, as elementType: T.Type) -> UnsafePointer { - fatalError("not implemented") + return value.advanced(by: type.elementOffset(at: UInt32(index), type: Metadata(elementType))) + .assumingMemoryBound(to: elementType) } public subscript() -> T { unsafeAddress { - fatalError("not implemented") + return address(as: T.self) } } public subscript(_ index: Int) -> T { unsafeAddress { - fatalError("not implemented") + return address(of: index, as: T.self) } } } -public struct UnsafeMutableTuple { +@_silgen_name("swift_slowAlloc") +private func slowAlloc(_ size: Int, _ alignMask: Int) -> UnsafeMutableRawPointer + +@_silgen_name("swift_slowDealloc") +private func slowDealloc(_ ptr: UnsafeMutableRawPointer, _ size: Int, _ alignMask: Int) + +extension UnsafeMutableTuple { public init(with tupleType: TupleType) { - fatalError("not implemented") + self.init(type: tupleType, value: slowAlloc(tupleType.size, -1)) } public func deallocate(initialized: Bool) { - fatalError("not implemented") + if initialized { + deinitialize() + } + slowDealloc(value, -1, -1) } public func initialize(at index: Int, to element: T) { - fatalError("not implemented") + withUnsafePointer(to: element) { elementPointer in + type.setElement(in: value, at: index, from: elementPointer, options: .initCopy) + } } public func deinitialize() { - fatalError("not implemented") + type.destroy(value) } public func deinitialize(at index: Int) { - fatalError("not implemented") + type.destroy(value, at: UInt32(index)) } public var count: Int { - fatalError("not implemented") + return type.count } - + public var isEmpty: Bool { - fatalError("not implemented") + return type.isEmpty } - + public var indices: Range { - fatalError("not implemented") + return type.indices } - public func address(as type: T.Type) -> UnsafeMutablePointer { - fatalError("not implemented") + public func address(as expectedType: T.Type) -> UnsafeMutablePointer { + guard type.type == expectedType else { + preconditionFailure() + } + return value.assumingMemoryBound(to: expectedType) } public func address(of index: Int, as elementType: T.Type) -> UnsafeMutablePointer { - fatalError("not implemented") + return value.advanced(by: type.elementOffset(at: UInt32(index), type: Metadata(elementType))) + .assumingMemoryBound(to: elementType) } public subscript() -> T { unsafeAddress { - fatalError("not implemented") + return UnsafePointer(address(as: T.self)) } - unsafeMutableAddress { - fatalError("not implemented") + nonmutating unsafeMutableAddress { + return address(as: T.self) } } public subscript(_ index: Int) -> T { unsafeAddress { - fatalError("not implemented") + return UnsafePointer(address(of: index, as: T.self)) } - unsafeMutableAddress { - fatalError("not implemented") + nonmutating unsafeMutableAddress { + return address(of: index, as: T.self) } } diff --git a/Sources/ComputeCxx/Attribute/AGAttribute.h b/Sources/ComputeCxx/Attribute/AGAttribute.h index 42bb46f..80aa8e9 100644 --- a/Sources/ComputeCxx/Attribute/AGAttribute.h +++ b/Sources/ComputeCxx/Attribute/AGAttribute.h @@ -4,6 +4,8 @@ #include #include "AGSwiftSupport.h" +#include "Closure/AGClosure.h" +#include "Swift/AGType.h" CF_ASSUME_NONNULL_BEGIN @@ -14,7 +16,46 @@ typedef uint32_t AGAttribute AG_SWIFT_STRUCT AG_SWIFT_NAME(AnyAttribute); CF_EXPORT const AGAttribute AGAttributeNil; - +typedef CF_OPTIONS(uint8_t, AGAttributeFlags) { + AGAttributeFlagsNone = 0, +}; + +typedef CF_OPTIONS(uint32_t, AGAttributeTypeFlags) { + AGAttributeTypeFlagsNone = 0, + AGAttributeTypeFlagsOption4 = 4, + AGAttributeTypeFlagsOption8 = 8, + AGAttributeTypeFlagsOption16 = 16, +}; + +typedef struct AGAttributeVTable { + void *callback0; + void *callback1; + void *callback2; + void *callback3; + void *callback4; + void *callback5; +} AGAttributeVTable; + +typedef struct AGAttributeType { + AGTypeID body_type_id; + AGTypeID value_type_id; + void (*update_function)(const void *, void *, AGAttribute); + const void *_Nullable update_function_context; + AGAttributeVTable *_Nullable callbacks; + AGAttributeTypeFlags flags; + + // set after construction + uint32_t attribute_offset; + const unsigned char *_Nullable layout; + + AGTypeID initial_body_type_id; + AGTypeID initial_body_witness_table; +} AGAttributeType; + +typedef struct AGAttributeInfo { + const AGAttributeType *type; + const void *body; +} AGAttributeInfo; CF_EXTERN_C_END diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index 6c8d6e5..c949e74 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -36,7 +36,8 @@ class AttributeType { Unknown0x20 = 1 << 5, // 0x20 // used in update_main_refs }; - using UpdateFunction = void (*)(void *context, void *body); + // TODO: closure context here is first param, is this consistent??? + using UpdateFunction = void (*)(const void *context, void *body, AttributeID attribute); private: swift::metadata *_self_metadata; @@ -45,6 +46,8 @@ class AttributeType { void *_update_context; AttributeVTable *_vtable; Flags _flags; + + // set after construction uint32_t _attribute_offset; ValueLayout _layout; @@ -77,7 +80,9 @@ class AttributeType { } }; - void perform_update(void *body) const { _update_function(_update_context, body); }; + void perform_update(void *body, AttributeID attribute) const { + _update_function(_update_context, body, attribute); + }; // V table methods void vt_destroy_self(void *body) { diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 43bc228..2877910 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -18,7 +18,9 @@ class Graph; class NodeFlags { public: - enum SubgraphFlags : uint8_t {}; + enum SubgraphFlags : uint8_t { + None = 0 + }; enum Flags4 : uint8_t { HasIndirectSelf = 1 << 0, // 0x01 HasIndirectValue = 1 << 1, // 0x02 @@ -27,24 +29,24 @@ class NodeFlags { InputsUnsorted = 1 << 3, // 0x08 Cacheable = 1 << 4, // 0x10 - Unknown0x20 = 1 << 5, // 0x20 - initial value + Unknown0x20 = 1 << 5, // 0x20 - initial value SelfModified = 1 << 6, // 0x40 }; private: uint16_t _relative_offset; - uint8_t _subgraph_flags; + SubgraphFlags _subgraph_flags; uint8_t _value4; public: - NodeFlags(uint8_t value4 = 0) : _value4(value4){}; + NodeFlags(uint8_t value4 = 0) : _value4(value4) {}; uint16_t relative_offset() const { return _relative_offset; }; void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; // Flags 3 - uint8_t subgraph_flags() const { return _subgraph_flags; }; - void set_subgraph_flags(uint8_t subgraph_flags) { _subgraph_flags = subgraph_flags; }; + SubgraphFlags subgraph_flags() const { return _subgraph_flags; }; + void set_subgraph_flags(SubgraphFlags subgraph_flags) { _subgraph_flags = subgraph_flags; }; // Flags 4 bool has_indirect_self() const { return _value4 & Flags4::HasIndirectSelf; } @@ -97,7 +99,7 @@ class Node { uint8_t _data; public: - explicit constexpr State(uint8_t data = 0) : _data(data){}; + explicit constexpr State(uint8_t data = 0) : _data(data) {}; uint8_t data() { return _data; }; bool is_dirty() { return _data & Dirty; } diff --git a/Sources/ComputeCxx/Closure/AGClosure.cpp b/Sources/ComputeCxx/Closure/AGClosure.cpp index 0a5b192..c1876bb 100644 --- a/Sources/ComputeCxx/Closure/AGClosure.cpp +++ b/Sources/ComputeCxx/Closure/AGClosure.cpp @@ -2,16 +2,6 @@ #include -struct AGClosureStorage { - const void * _Nullable function; - const void * _Nullable context; -}; +void AGRetainClosure(AGClosureStorage *closure) { ::swift::swift_retain((::swift::HeapObject *)closure->context); } -AGClosureRef AGRetainClosure(AGClosureRef closure) { - ::swift::swift_retain((::swift::HeapObject *)closure->context); - return closure; -} - -void AGReleaseClosure(AGClosureRef closure) { - ::swift::swift_release((::swift::HeapObject *)closure->context); -} +void AGReleaseClosure(AGClosureStorage *closure) { ::swift::swift_release((::swift::HeapObject *)closure->context); } diff --git a/Sources/ComputeCxx/Closure/AGClosure.h b/Sources/ComputeCxx/Closure/AGClosure.h index 471a057..299550d 100644 --- a/Sources/ComputeCxx/Closure/AGClosure.h +++ b/Sources/ComputeCxx/Closure/AGClosure.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "AGSwiftSupport.h" @@ -8,17 +9,21 @@ CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN -// TODO: add Swift annotation for retain relase... +struct AGOpaqueValue; -typedef struct CF_BRIDGED_TYPE(id) AGClosureStorage *AGClosureRef AG_SWIFT_NAME(Closure); +struct AGClosureStorage { + const AGOpaqueValue *_Nullable function; + const AGOpaqueValue *_Nullable context; -CF_EXPORT -CF_REFINED_FOR_SWIFT -AGClosureRef AGRetainClosure(AGClosureRef closure); + AGClosureStorage() : function(nullptr), context(nullptr){}; + AGClosureStorage(const AGOpaqueValue *fun, const AGOpaqueValue *ctx) : function(fun), context(ctx){}; +} SWIFT_SHARED_REFERENCE(AGRetainClosure, AGReleaseClosure); -CF_EXPORT -CF_REFINED_FOR_SWIFT -void AGReleaseClosure(AGClosureRef closure); +void AGRetainClosure(AGClosureStorage *closure); +void AGReleaseClosure(AGClosureStorage *closure); + + +typedef struct AGClosureStorage *AGClosureRef AG_SWIFT_NAME(Closure); CF_EXTERN_C_END diff --git a/Sources/ComputeCxx/Containers/IndirectPointerVector.h b/Sources/ComputeCxx/Containers/IndirectPointerVector.h index d83b339..8f08afe 100644 --- a/Sources/ComputeCxx/Containers/IndirectPointerVector.h +++ b/Sources/ComputeCxx/Containers/IndirectPointerVector.h @@ -181,7 +181,7 @@ void indirect_pointer_vector::push_back(const value_type &value) { _data = value; } else { vector_type *vector = new vector_type(); - vector->push_back(this->get_element()); + vector->push_back(_data); vector->push_back(value); _data = vector | 1; } diff --git a/Sources/ComputeCxx/Debug/DebugServer.mm b/Sources/ComputeCxx/Debug/DebugServer.mm index 2cf8ba8..45323ff 100644 --- a/Sources/ComputeCxx/Debug/DebugServer.mm +++ b/Sources/ComputeCxx/Debug/DebugServer.mm @@ -9,7 +9,7 @@ #include #include -#include "Graph/Graph+Description.h" +#include "Graph/AGDescription.h" #include "Graph/Graph.h" #include "Log/Log.h" #include "Utilities/FreeDeleter.h" diff --git a/Sources/ComputeCxx/Graph/Graph+Description.h b/Sources/ComputeCxx/Graph/AGDescription.h similarity index 100% rename from Sources/ComputeCxx/Graph/Graph+Description.h rename to Sources/ComputeCxx/Graph/AGDescription.h diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 7d35631..d5a4748 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -152,10 +152,10 @@ void AGGraphEndDeferringSubgraphInvalidation(AGGraphRef graph, bool was_deferrin // TODO: is this AGGraphRef or AG::Graph ? uint32_t AGGraphInternAttributeType(AGGraphRef graph, AGTypeID type, - void *(*intern)(const void *context AG_SWIFT_CONTEXT)AG_SWIFT_CC(swift), + const void *(*intern)(const void *context AG_SWIFT_CONTEXT)AG_SWIFT_CC(swift), const void *context) { auto metadata = reinterpret_cast(type); - return graph->context.graph().intern_type(metadata, AG::ClosureFunctionVP(intern, context)); + return graph->context.graph().intern_type(metadata, AG::ClosureFunctionVP(intern, context)); } void AGGraphVerifyType(AGAttribute attribute, AGTypeID type) { @@ -179,7 +179,7 @@ void AGGraphVerifyType(AGAttribute attribute, AGTypeID type) { #pragma mark - Attributes -AGAttribute AGGraphCreateAttribute(uint32_t type_id, void *body, void *value) { +AGAttribute AGGraphCreateAttribute(uint32_t type_id, const void *body, const void *_Nullable value) { auto current_subgraph = AG::Subgraph::current_subgraph(); if (!current_subgraph) { AG::precondition_failure("no subgraph active while adding attribute"); @@ -215,11 +215,11 @@ AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute) { } const void *body = nullptr; - auto type = subgraph->graph()->attribute_ref(attribute_id.to_node_ptr(), &body); - return AGAttributeInfo(type, body); + const AG::AttributeType *type = subgraph->graph()->attribute_ref(attribute_id.to_node_ptr(), &body); + return AGAttributeInfo(reinterpret_cast(type), body); } -uint8_t AGGraphGetFlags(AGAttribute attribute) { +AGAttributeFlags AGGraphGetFlags(AGAttribute attribute) { auto attribute_id = AG::AttributeID(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); @@ -227,7 +227,7 @@ uint8_t AGGraphGetFlags(AGAttribute attribute) { return attribute_id.to_node().flags().subgraph_flags(); } -void AGGraphSetFlags(AGAttribute attribute, uint8_t flags) { +void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags) { auto attribute_id = AG::AttributeID(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); @@ -235,7 +235,7 @@ void AGGraphSetFlags(AGAttribute attribute, uint8_t flags) { attribute_id.subgraph()->set_flags(attribute_id.to_node_ptr(), AG::NodeFlags::SubgraphFlags(flags)); } -void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool flag, +void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidating, void (*modify)(void *body, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *context) { auto attribute_id = AG::AttributeID(attribute); @@ -251,7 +251,7 @@ void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool flag, subgraph->graph()->attribute_modify(attribute_id.to_node_ptr(), *reinterpret_cast(type), - AG::ClosureFunctionPV(modify, context), flag); + AG::ClosureFunctionPV(modify, context), invalidating); } bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, @@ -274,8 +274,9 @@ bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, namespace { void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &metadata, void *body, - const AG::swift::metadata &value_type, bool flag, AG::AttributeID attribute, - uint8_t *state_out, AG::ClosureFunctionCI closure) { + const AG::swift::metadata &value_type, AGCachedValueOptions options, + AG::AttributeID attribute, uint8_t *state_out, + AG::ClosureFunctionCI closure) { auto current_update = AG::Graph::current_update(); AG::Graph::UpdateStack *stack = current_update.tag() == 0 ? current_update.get() : nullptr; @@ -307,16 +308,16 @@ void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &meta return value; } - uint8_t input_flags = flag ? 0 : 1; + uint8_t input_flags = (options & 1) == 0 ? 0 : 1; return subgraph->graph()->input_value_ref(stack->frames().back().attribute, AG::AttributeID(cached), 0, input_flags, value_type, state_out); } } // namespace -void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, - AGAttribute attribute, bool *changed_out, - unsigned long (*closure)(AGUnownedGraphRef graph, const void *context AG_SWIFT_CONTEXT) +void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, + AGCachedValueOptions options, AGAttribute attribute, bool *changed_out, + uint32_t (*closure)(AGUnownedGraphRef graph, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *closure_context) { auto metadata = reinterpret_cast(type); @@ -324,22 +325,22 @@ void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, uint8_t state = 0; void *value = - read_cached_attribute(identifier, *metadata, body, *value_metadata, flag, AG::AttributeID(attribute), &state, - AG::ClosureFunctionCI(closure, closure_context)); + read_cached_attribute(identifier, *metadata, body, *value_metadata, options, AG::AttributeID(attribute), &state, + AG::ClosureFunctionCI(closure, closure_context)); if (changed_out) { *changed_out = state & 1 ? true : false; } return value; } -void *AGGraphReadCachedAttributeIfExists(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, - AGAttribute attribute, bool *changed_out) { +void *AGGraphReadCachedAttributeIfExists(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, + AGCachedValueOptions options, AGAttribute attribute, bool *changed_out) { auto metadata = reinterpret_cast(type); auto value_metadata = reinterpret_cast(value_type); uint8_t state = 0; - void *value = read_cached_attribute(identifier, *metadata, body, *value_metadata, flag, AG::AttributeID(attribute), - &state, nullptr); + void *value = read_cached_attribute(identifier, *metadata, body, *value_metadata, options, + AG::AttributeID(attribute), &state, nullptr); if (changed_out) { *changed_out = state & 1 ? true : false; } @@ -602,11 +603,12 @@ void AGGraphSetUpdateCallback(AGGraphRef graph, context->set_update_callback(AG::ClosureFunctionVV(callback, callback_context)); } -void AGGraphClearUpdate() { +const void *AGGraphClearUpdate() { auto current_update = AG::Graph::current_update(); if (current_update != nullptr && current_update.tag() == 0) { AG::Graph::set_current_update(current_update.with_tag(true)); // TODO: tag is cleared... } + return (const void *)current_update.value(); } uint64_t AGGraphGetDeadline(AGGraphRef graph) { @@ -665,7 +667,7 @@ void AGGraphWithoutUpdate(void (*function)(const void *context AG_SWIFT_CONTEXT) void AGGraphWithMainThreadHandler(AGGraphRef graph, void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *body_context, - void (*main_thread_handler)(void (*thunk)(void *), + void (*main_thread_handler)(void (*thunk)(const void *), const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *main_thread_handler_context) { auto context = AG::Graph::Context::from_cf(graph); @@ -732,6 +734,18 @@ AGValue AGGraphGetValue(AGAttribute attribute, AGValueOptions options, AGTypeID return get_value(attribute_id, 0, options, *metadata); } +AGValueState AGGraphGetValueState(AGAttribute attribute) { + auto attribute_id = AG::AttributeID(attribute); + attribute_id.to_node_ptr().assert_valid(); + + auto subgraph = attribute_id.subgraph(); + if (!subgraph) { + AG::precondition_failure("no graph: %u", attribute); + } + + return subgraph->graph()->value_state(attribute_id); +} + AGValue AGGraphGetWeakValue(AGWeakAttribute attribute, AGValueOptions options, AGTypeID type) { auto weak_attribute_id = AG::WeakAttributeID(attribute); auto attribute_id = weak_attribute_id.evaluate(); @@ -743,7 +757,7 @@ AGValue AGGraphGetWeakValue(AGWeakAttribute attribute, AGValueOptions options, A return get_value(attribute_id, weak_attribute_id.zone_id(), options, *metadata); } -bool AGGraphSetValue(AGAttribute attribute, void *value, AGTypeID type) { +bool AGGraphSetValue(AGAttribute attribute, const void *value, AGTypeID type) { auto attribute_id = AG::AttributeID(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); @@ -808,7 +822,7 @@ AGValue AGGraphGetInputValue(AGAttribute attribute, AGAttribute input_attribute, return {value, (state & 1) == 1}; } -uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, uint8_t input_edge_flags) { +uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, AGInputOptions options) { auto attribute_id = AG::AttributeID(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); @@ -827,7 +841,7 @@ uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, uint8_t input AG::precondition_failure("accessing attribute in a different namespace: %u", input); } - return subgraph->graph()->add_input(attribute_id.to_node_ptr(), input_attribute_id, false, input_edge_flags); + return subgraph->graph()->add_input(attribute_id.to_node_ptr(), input_attribute_id, false, options); } bool AGGraphAnyInputsChanged(const AGAttribute *exclude_attributes, uint64_t exclude_attributes_count) { @@ -861,7 +875,7 @@ void *AGGraphGetOutputValue(AGTypeID type) { return graph->output_value_ref(frame.attribute, *metadata); } -void AGGraphSetOutputValue(void *value, AGTypeID type) { +void AGGraphSetOutputValue(const void *value, AGTypeID type) { // TODO: tag must be whether frames is empty or not auto update_stack_ptr = AG::Graph::current_update(); if (update_stack_ptr.tag() != 0 || update_stack_ptr.get() == nullptr) { @@ -968,7 +982,7 @@ bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id) { return false; } -void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, void *value, AGTypeID type) { +void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void *value, AGTypeID type) { auto context = AG::Graph::Context::from_cf(graph); context->graph().foreach_trace([&context, &event_name, &value, &type](AG::Trace &trace) { trace.custom_event(*context, event_name, value, *reinterpret_cast(type)); @@ -1092,14 +1106,14 @@ void AGGraphStopProfiling(AGGraphRef graph) { context->graph().stop_profiling(); } -void AGGraphMarkProfile(AGGraphRef graph, const char *name, uint64_t time) { +void AGGraphMarkProfile(AGGraphRef graph, const char *name) { if (!graph) { AG::Graph::all_mark_profile(name); return; } auto context = AG::Graph::Context::from_cf(graph); uint32_t event_id = context->graph().intern_key(name); - context->graph().mark_profile(event_id, time); + context->graph().mark_profile(event_id, 0); } void AGGraphResetProfile(AGGraphRef graph) { @@ -1113,7 +1127,7 @@ void AGGraphResetProfile(AGGraphRef graph) { #pragma mark - Description -void AGGraphDescription(AGGraphRef graph, CFDictionaryRef options) { +CFTypeRef AGGraphDescription(AGGraphRef graph, CFDictionaryRef options) { if (graph == nullptr) { return AG::Graph::description(nullptr, options); } diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 85ed12d..329f775 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -2,6 +2,7 @@ #include #include +#include #include "AGSwiftSupport.h" #include "Attribute/AGAttribute.h" @@ -79,14 +80,14 @@ bool AGGraphBeginDeferringSubgraphInvalidation(AGGraphRef graph); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphEndDeferringSubgraphInvalidation(AGGraphRef graph); +void AGGraphEndDeferringSubgraphInvalidation(AGGraphRef graph, bool was_deferring_subgraph_invalidation); // MARK: Attribute types CF_EXPORT CF_REFINED_FOR_SWIFT uint32_t AGGraphInternAttributeType(AGGraphRef graph, AGTypeID type, - void *_Nonnull (*_Nonnull intern)(const void *context AG_SWIFT_CONTEXT) + const void *_Nonnull (*_Nonnull intern)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *context); @@ -98,45 +99,28 @@ void AGGraphVerifyType(AGAttribute attribute, AGTypeID type); CF_EXPORT CF_REFINED_FOR_SWIFT -AGAttribute AGGraphCreateAttribute(uint32_t type_id, void *body, void *value); +AGAttribute AGGraphCreateAttribute(uint32_t type_id, const void *body, const void *_Nullable value); CF_EXPORT CF_REFINED_FOR_SWIFT AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute); - - - -// TODO: need this? -// typedef struct AGAttributeType { -// AGTypeID typeID; -// AGTypeID valueTypeID; -// AGClosureStorage update; -// AGAttributeVTable vTable; -// AGAttributeTypeFlags flags; -//} AGAttributeType; - -typedef struct AGAttributeInfo { - const void *type; - const void *body; -} AGAttributeInfo; - CF_EXPORT CF_REFINED_FOR_SWIFT AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute); CF_EXPORT CF_REFINED_FOR_SWIFT -uint8_t AGGraphGetFlags(AGAttribute attribute); +AGAttributeFlags AGGraphGetFlags(AGAttribute attribute); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphSetFlags(AGAttribute attribute, uint8_t flags); +void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool flag, - const void (*modify)(void *body, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), +void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidating, + const void (*modify)(const void *context AG_SWIFT_CONTEXT, void *body) AG_SWIFT_CC(swift), const void *context); typedef CF_OPTIONS(uint32_t, AGSearchOptions) { @@ -153,18 +137,21 @@ bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, // MARK: Cached attributes +typedef CF_OPTIONS(uint8_t, AGCachedValueOptions) { AGCachedValueOptionsNone = 0 }; + CF_EXPORT CF_REFINED_FOR_SWIFT -void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, - AGAttribute attribute, bool *changed_out, - unsigned long (*closure)(AGUnownedGraphContextRef graph_context, - const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), +void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, + AGCachedValueOptions options, AGAttribute attribute, bool *_Nullable changed_out, + uint32_t (*closure)(AGUnownedGraphContextRef graph_context, + const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *closure_context); CF_EXPORT CF_REFINED_FOR_SWIFT -void *AGGraphReadCachedAttributeIfExists(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, bool flag, - AGAttribute attribute, bool *changed_out); +void *_Nullable AGGraphReadCachedAttributeIfExists(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, + AGCachedValueOptions options, AGAttribute attribute, + bool *_Nullable changed_out); // MARK: Current attribute @@ -259,7 +246,7 @@ bool AGGraphUpdateWasCancelled(); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphSetUpdate(void *update); +void AGGraphSetUpdate(const void *update); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -269,7 +256,7 @@ void AGGraphSetUpdateCallback(AGGraphRef graph, CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphClearUpdate(); +const void *AGGraphClearUpdate(); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -302,17 +289,23 @@ CF_REFINED_FOR_SWIFT void AGGraphWithMainThreadHandler(AGGraphRef graph, void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *body_context, - void (*main_thread_handler)(void (*thunk)(void *), + void (*main_thread_handler)(void (*thunk)(const void *), const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *main_thread_handler_context); // MARK: Values typedef struct AGValue { - void *value; + const void *value; bool changed; } AGValue; +typedef CF_OPTIONS(uint8_t, AGChangedValueFlags) { + AGChangedValueFlagsChanged = 1, +}; + +typedef uint8_t AGValueState; + typedef CF_OPTIONS(uint32_t, AGValueOptions) { // options & 3 == AG::InputEdge::Flags AGValueOptionsInputEdgeFlagsUnprefetched = 1 << 0, @@ -342,13 +335,17 @@ CF_EXPORT CF_REFINED_FOR_SWIFT AGValue AGGraphGetValue(AGAttribute attribute, AGValueOptions options, AGTypeID type); +CF_EXPORT +CF_REFINED_FOR_SWIFT +AGValueState AGGraphGetValueState(AGAttribute attribute); + CF_EXPORT CF_REFINED_FOR_SWIFT AGValue AGGraphGetWeakValue(AGWeakAttribute attribute, AGValueOptions options, AGTypeID type); CF_EXPORT CF_REFINED_FOR_SWIFT -bool AGGraphSetValue(AGAttribute attribute, void *value, AGTypeID type); +bool AGGraphSetValue(AGAttribute attribute, const void *value, AGTypeID type); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -356,13 +353,17 @@ AGGraphUpdateStatus AGGraphPrefetchValue(AGAttribute attribute); // MARK: Inputs +typedef CF_OPTIONS(uint32_t, AGInputOptions) { + AGInputOptionsNone = 0, +}; + CF_EXPORT CF_REFINED_FOR_SWIFT -AGValue AGGraphGetInputValue(AGAttribute attribute, AGValueOptions options, AGTypeID type); +AGValue AGGraphGetInputValue(AGAttribute attribute, AGAttribute input_attribute, AGValueOptions options, AGTypeID type); CF_EXPORT CF_REFINED_FOR_SWIFT -uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, uint8_t input_edge_flags); +uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, AGInputOptions options); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -372,11 +373,11 @@ bool AGGraphAnyInputsChanged(const AGAttribute *exclude_attributes, uint64_t exc CF_EXPORT CF_REFINED_FOR_SWIFT -void *AGGraphGetOutputValue(AGTypeID type); +void *_Nullable AGGraphGetOutputValue(AGTypeID type); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphSetOutputValue(void *value, AGTypeID type); +void AGGraphSetOutputValue(const void *value, AGTypeID type); // MARK: Tracing @@ -430,7 +431,7 @@ bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, void *value, AGTypeID type); +void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void *value, AGTypeID type); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -465,29 +466,29 @@ void AGGraphEndProfileEvent(AGAttribute attribute, const char *event_name, uint6 CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphStartProfiling(AGGraphRef graph); +void AGGraphStartProfiling(AGGraphRef _Nullable graph); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphStopProfiling(AGGraphRef graph); +void AGGraphStopProfiling(AGGraphRef _Nullable graph); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphMarkProfile(AGGraphRef graph, const char *event_name, uint64_t time); +void AGGraphMarkProfile(AGGraphRef _Nullable graph, const char *event_name); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphResetProfile(AGGraphRef graph); +void AGGraphResetProfile(AGGraphRef _Nullable graph); // MARK: Description CF_EXPORT CF_REFINED_FOR_SWIFT -CFStringRef AGGraphDescription(); +CFTypeRef AGGraphDescription(AGGraphRef _Nullable graph, CFDictionaryRef options); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphArchiveJSON(const char *filename); +void AGGraphArchiveJSON(const char *_Nullable filename); CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Graph/AGValue.h b/Sources/ComputeCxx/Graph/AGValue.h deleted file mode 100644 index ef15e5e..0000000 --- a/Sources/ComputeCxx/Graph/AGValue.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -CF_ASSUME_NONNULL_BEGIN - -CF_EXTERN_C_BEGIN - -typedef uint8_t AGValueState; - -CF_EXTERN_C_END - -CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 0cf8895..761c15e 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -375,8 +375,8 @@ void Graph::call_main_handler(void *context, void (*body)(void *)) { void *context; void (*handler)(void *); - static void thunk(void *arg) { - auto trampoline = reinterpret_cast(arg); + static void thunk(const void *arg) { + auto trampoline = reinterpret_cast(arg); trampoline->handler(trampoline->context); }; }; @@ -389,6 +389,7 @@ void Graph::call_main_handler(void *context, void (*body)(void *)) { _main_handler = nullptr; _main_handler_context = nullptr; + // TODO: is context passed in as arg, or is it _main_handler_context MainTrampoline trampoline = {this, current_update_thread, context, body}; main_handler(MainTrampoline::thunk, &trampoline); @@ -433,7 +434,7 @@ const AttributeType *Graph::attribute_ref(data::ptr node, const void *_Nul } void Graph::attribute_modify(data::ptr node, const swift::metadata &metadata, - ClosureFunctionPV modify, bool update_flags) { + ClosureFunctionPV modify, bool invalidating) { if (!node->state().is_self_initialized()) { precondition_failure("no self data: %u", node); } @@ -446,20 +447,20 @@ void Graph::attribute_modify(data::ptr node, const swift::metadata &metada foreach_trace([&node](Trace &trace) { trace.begin_modify(node); }); void *body = node->get_self(type); - modify(body); + modify(body); // TODO: make context first parameter... foreach_trace([&node](Trace &trace) { trace.end_modify(node); }); - if (update_flags) { + if (invalidating) { node->flags().set_self_modified(true); mark_pending(node, node.get()); } } -data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *value) { +data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, const void *body, const void *value) { const AttributeType &type = attribute_type(type_id); - void *value_source = nullptr; + const void *value_source = nullptr; if (type.use_graph_as_initial_value()) { value_source = this; } @@ -1448,7 +1449,7 @@ void Graph::input_value_add(data::ptr node, AttributeID input_attribute, u } } -uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_nil, uint8_t input_edge_flags) { +uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_nil, AGInputOptions options) { auto resolved = input.resolve(TraversalOptions::EvaluateWeakReferences); if (resolved.attribute().without_kind() == 0) { if (allow_nil) { @@ -1460,9 +1461,7 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni precondition_failure("cyclic edge: %u -> %u", resolved.attribute(), node); } - foreach_trace([&node, &resolved, &input_edge_flags](Trace &trace) { - trace.add_edge(node, resolved.attribute(), input_edge_flags); - }); + foreach_trace([&node, &resolved, &options](Trace &trace) { trace.add_edge(node, resolved.attribute(), options); }); auto subgraph = AttributeID(node).subgraph(); auto graph_context_id = subgraph ? subgraph->context_id() : 0; @@ -1476,7 +1475,7 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni InputEdge new_input_edge = { resolved.attribute(), - InputEdge::Flags(input_edge_flags & 5), + InputEdge::Flags(options & 5), }; if (node->state().is_dirty()) { new_input_edge.set_changed(true); @@ -1499,7 +1498,7 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni reset_update(node); } if (node->state().is_dirty()) { - foreach_trace([&node, &index, &input_edge_flags](Trace &trace) { trace.set_edge_pending(node, index, true); }); + foreach_trace([&node, &index](Trace &trace) { trace.set_edge_pending(node, index, true); }); } return index; @@ -1924,7 +1923,7 @@ const char *Graph::key_name(uint32_t key_id) const { AG::precondition_failure("invalid string key id: %u", key_id); } -uint32_t Graph::intern_type(const swift::metadata *metadata, ClosureFunctionVP make_type) { +uint32_t Graph::intern_type(const swift::metadata *metadata, ClosureFunctionVP make_type) { uint32_t type_id = uint32_t(reinterpret_cast(_type_ids_by_metadata.lookup(metadata, nullptr))); if (type_id) { return type_id; diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 89e1bb8..5caaf37 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -7,8 +7,8 @@ #include #include +#include "AGGraph.h" #include "AGSwiftSupport.h" -#include "AGValue.h" #include "Attribute/AttributeID.h" #include "Attribute/Node/Edge.h" #include "Closure/ClosureFunction.h" @@ -67,7 +67,7 @@ class Graph { void push_back(TreeElementNodePair pair) { _nodes.push_back(pair); }; }; - typedef void (*MainHandler)(void (*thunk)(void *), const void *thunk_context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift); + typedef void (*MainHandler)(void (*thunk)(const void *), const void *thunk_context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift); private: static Graph *_all_graphs; @@ -223,9 +223,9 @@ class Graph { const AttributeType *attribute_ref(data::ptr node, const void *_Nullable *_Nullable self_out) const; void attribute_modify(data::ptr node, const swift::metadata &type, ClosureFunctionPV closure, - bool update_flags); + bool invalidating); - data::ptr add_attribute(Subgraph &subgraph, uint32_t type_id, void *body, void *_Nullable value); + data::ptr add_attribute(Subgraph &subgraph, uint32_t type_id, const void *body, const void *_Nullable value); UpdateStatus update_attribute(AttributeID attribute, uint8_t options); void update_main_refs(AttributeID attribute); @@ -279,7 +279,7 @@ class Graph { void input_value_add(data::ptr node, AttributeID input, uint8_t input_edge_flags); - uint32_t add_input(data::ptr node, AttributeID input, bool allow_nil, uint8_t input_edge_flags); + uint32_t add_input(data::ptr node, AttributeID input, bool allow_nil, AGInputOptions options); void remove_input(data::ptr node, uint32_t index); void remove_all_inputs(data::ptr node); @@ -302,7 +302,7 @@ class Graph { // MARK: Outputs - void *output_value_ref(data::ptr node, const swift::metadata &type); + void *_Nullable output_value_ref(data::ptr node, const swift::metadata &type); template void add_output_edge(data::ptr node, AttributeID output); template <> void add_output_edge(data::ptr node, AttributeID output); @@ -340,7 +340,7 @@ class Graph { uint32_t intern_key(const char *key); const char *key_name(uint32_t key_id) const; - uint32_t intern_type(const swift::metadata *metadata, ClosureFunctionVP make_type); + uint32_t intern_type(const swift::metadata *metadata, ClosureFunctionVP make_type); // MARK: Encoding diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp index fc100be..7d3ea51 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp @@ -9,6 +9,7 @@ AGTypeID AGTreeElementGetType(AGTreeElement tree_element) { return AGTypeID(type); } +// TODO: rename to value AGAttribute AGTreeElementGetValue(AGTreeElement tree_element) { auto tree_element_id = AG::TreeElementID(tree_element); auto node = tree_element_id.to_element_ptr()->node; diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.h b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.h index daf8fe5..5e0e648 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.h +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.h @@ -11,7 +11,7 @@ CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN -typedef uint32_t AGTreeElement; +typedef uint32_t AGTreeElement AG_SWIFT_STRUCT AG_SWIFT_NAME(TreeElement); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -34,7 +34,7 @@ AGTreeElement AGTreeElementGetParent(AGTreeElement tree_element); typedef struct AGTreeElementValueIterator { AGTreeElement tree_element; AGTreeValue next_value; -} AGTreeElementValueIterator; +} AG_SWIFT_NAME(Values) AGTreeElementValueIterator; CF_EXPORT CF_REFINED_FOR_SWIFT @@ -49,7 +49,7 @@ AGTreeValue AGTreeElementGetNextValue(AGTreeElementValueIterator iter); typedef struct AGTreeElementNodeIterator { AGTreeElement tree_element; uint32_t index; -} AGTreeElementNodeIterator; +} AG_SWIFT_NAME(Nodes) AGTreeElementNodeIterator; CF_EXPORT CF_REFINED_FOR_SWIFT @@ -65,7 +65,7 @@ typedef struct AGTreeElementChildIterator { AGTreeElement tree_element; AGTreeElement next_child; bool iterated_subgraph; -} AGTreeElementChildIterator; +} AG_SWIFT_NAME(Children) AGTreeElementChildIterator; CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.h b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.h index 035880c..38aea3f 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.h +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.h @@ -10,7 +10,7 @@ CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN -typedef uint32_t AGTreeValue; +typedef uint32_t AGTreeValue AG_SWIFT_STRUCT AG_SWIFT_NAME(TreeValue); CF_EXPORT AGTypeID AGTreeValueGetType(AGTreeValue tree_value); diff --git a/Sources/ComputeCxx/Graph/Tree/TreeElement.h b/Sources/ComputeCxx/Graph/Tree/TreeElement.h index ba4f07b..94f66bc 100644 --- a/Sources/ComputeCxx/Graph/Tree/TreeElement.h +++ b/Sources/ComputeCxx/Graph/Tree/TreeElement.h @@ -14,7 +14,7 @@ namespace AG { struct Graph::TreeElement { const swift::metadata *type; - AttributeID node; + AttributeID node; // TODO: check named value from AGSubgraphBeginTreeElement uint32_t flags; data::ptr parent; diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 6fe7c39..ccce589 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -264,7 +264,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { const AttributeType &type = _graph->attribute_type(node->type_id()); void *self = node->get_self(type); - type.perform_update(self); + type.perform_update(self, frame.attribute); if (!node->state().is_value_initialized()) { if (type.value_metadata().vw_size() > 0) { diff --git a/Sources/ComputeCxx/Layout/AGComparison.h b/Sources/ComputeCxx/Layout/AGComparison.h index 2682d03..5db7274 100644 --- a/Sources/ComputeCxx/Layout/AGComparison.h +++ b/Sources/ComputeCxx/Layout/AGComparison.h @@ -43,6 +43,8 @@ typedef CF_OPTIONS(uint32_t, AGComparisonOptions) { typedef CF_OPTIONS(uint16_t, AGComparisonMode) { AGComparisonModeDefault = 0, + AGComparisonModeOption1 = 1, + AGComparisonModeOption2 = 2 }; CF_EXPORT diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index 2f3d024..7a3d08a 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -252,14 +252,14 @@ void AGSubgraphUpdate(AGSubgraphRef subgraph, uint8_t flags) { subgraph->subgraph->update(flags); } -void AGSubgraphApply(AGSubgraphRef subgraph, uint32_t flags, +void AGSubgraphApply(AGSubgraphRef subgraph, AGAttributeFlags flags, void (*function)(AGAttribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *function_context) { if (subgraph->subgraph == nullptr) { return; } - subgraph->subgraph->apply(AG::Subgraph::Flags(flags), + subgraph->subgraph->apply(AG::Subgraph::Flags(flags), // TODO: consolidate AGAttributeFlags and AG::Subgraph::Flags AG::ClosureFunctionAV(function, function_context)); } @@ -273,41 +273,40 @@ uint32_t AGSubgraphGetTreeRoot(AGSubgraphRef subgraph) { return subgraph->subgraph->tree_root(); } -void AGSubgraphBeginTreeElement(AGSubgraphRef subgraph, AGAttribute owner, AGTypeID type, uint32_t flags) { +void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner) { + if (subgraph->subgraph == nullptr) { + AG::precondition_failure("accessing invalidated subgraph"); + } + subgraph->subgraph->set_tree_owner(AG::AttributeID(owner)); +} + +void AGSubgraphAddTreeValue(AGAttribute value, AGTypeID type, const char *key, uint32_t flags) { AG::Subgraph *current = AG::Subgraph::current_subgraph(); if (current == nullptr) { return; } auto metadata = reinterpret_cast(type); - current->begin_tree(AG::AttributeID(owner), metadata, flags); + current->add_tree_value(AG::AttributeID(value), metadata, key, flags); } -void AGSubgraphEndTreeElement(AGSubgraphRef subgraph) { +void AGSubgraphBeginTreeElement(AGAttribute value, AGTypeID type, uint32_t flags) { AG::Subgraph *current = AG::Subgraph::current_subgraph(); if (current == nullptr) { return; } - current->end_tree(); -} - -void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner) { - if (subgraph->subgraph == nullptr) { - AG::precondition_failure("accessing invalidated subgraph"); - } - subgraph->subgraph->set_tree_owner(AG::AttributeID(owner)); + auto metadata = reinterpret_cast(type); + current->begin_tree(AG::AttributeID(value), metadata, flags); } -void AGSubgraphAddTreeValue(AGSubgraphRef subgraph, AGAttribute attribute, AGTypeID type, const char *key, - uint32_t flags) { +void AGSubgraphEndTreeElement(AGAttribute value) { AG::Subgraph *current = AG::Subgraph::current_subgraph(); if (current == nullptr) { return; } - auto metadata = reinterpret_cast(type); - current->add_tree_value(AG::AttributeID(attribute), metadata, key, flags); + current->end_tree(); } static dispatch_once_t should_record_tree_once = 0; diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index 4ef917d..959fa0e 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -27,17 +27,17 @@ AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute); CF_EXPORT CF_REFINED_FOR_SWIFT -AGSubgraphRef AGSubgraphGetCurrent(); +AGSubgraphRef _Nullable AGSubgraphGetCurrent() CF_SWIFT_NAME(getter:AGSubgraphRef.current()); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGSubgraphSetCurrent(AGSubgraphRef _Nullable subgraph); +void AGSubgraphSetCurrent(AGSubgraphRef _Nullable subgraph) CF_SWIFT_NAME(setter:AGSubgraphRef.current(_:)); // MARK: Graph CF_EXPORT CF_REFINED_FOR_SWIFT -AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph); +AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph) CF_SWIFT_NAME(getter:AGSubgraphRef.graph(self:)); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -109,7 +109,7 @@ void AGSubgraphUpdate(AGSubgraphRef subgraph, uint8_t flags); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGSubgraphApply(AGSubgraphRef subgraph, uint32_t flags, +void AGSubgraphApply(AGSubgraphRef subgraph, AGAttributeFlags flags, void (*function)(AGAttribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *function_context); @@ -121,20 +121,19 @@ uint32_t AGSubgraphGetTreeRoot(AGSubgraphRef subgraph); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGSubgraphBeginTreeElement(AGSubgraphRef subgraph, AGAttribute owner, AGTypeID type, uint32_t flags); +void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGSubgraphEndTreeElement(AGSubgraphRef subgraph); +void AGSubgraphAddTreeValue(AGAttribute value, AGTypeID type, const char *key, uint32_t flags); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner); +void AGSubgraphBeginTreeElement(AGAttribute value, AGTypeID type, uint32_t flags); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGSubgraphAddTreeValue(AGSubgraphRef subgraph, AGAttribute attribute, AGTypeID type, const char *key, - uint32_t flags); +void AGSubgraphEndTreeElement(AGAttribute value); CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index dd68625..004c16f 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -385,7 +385,7 @@ void Subgraph::foreach_ancestor(Callable body) { #pragma mark - Attributes void Subgraph::add_node(data::ptr node) { - node->flags().set_subgraph_flags(0); + node->flags().set_subgraph_flags(NodeFlags::SubgraphFlags::None); insert_attribute(AttributeID(node), true); if (_tree_root) { @@ -709,11 +709,11 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { // MARK: - Tree -void Subgraph::begin_tree(AttributeID attribute, const swift::metadata *type, uint32_t flags) { +void Subgraph::begin_tree(AttributeID value, const swift::metadata *type, uint32_t flags) { data::ptr tree = (data::ptr)alloc_bytes(sizeof(Graph::TreeElement), 7); tree->type = type; - tree->node = attribute; + tree->node = value; tree->flags = flags; tree->parent = _tree_root; tree->next_sibling = nullptr; @@ -743,7 +743,7 @@ void Subgraph::set_tree_owner(AttributeID attribute) { _tree_root->node = attribute; } -void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type, const char *key, uint32_t flags) { +void Subgraph::add_tree_value(AttributeID value, const swift::metadata *type, const char *key, uint32_t flags) { if (!_tree_root) { return; } @@ -752,7 +752,7 @@ void Subgraph::add_tree_value(AttributeID attribute, const swift::metadata *type data::ptr tree_value = (data::ptr)alloc_bytes(sizeof(Graph::TreeValue), 7); tree_value->type = type; - tree_value->value = attribute; + tree_value->value = value; tree_value->key_id = key_id; tree_value->flags = flags; tree_value->next = _tree_root->first_value; @@ -950,7 +950,7 @@ void Subgraph::notify_observers() { #pragma mark - Cache data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata &metadata, void *body, - ClosureFunctionCI closure) { + ClosureFunctionCI closure) { if (_cache == nullptr) { _cache = (data::ptr)alloc_bytes(sizeof(NodeCache), 7); new (&_cache) NodeCache(); @@ -968,7 +968,7 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata type->equatable = equatable; type->last_item = nullptr; type->first_item = nullptr; - type->type_id = (uint32_t)closure(reinterpret_cast(_graph)); + type->type_id = closure(reinterpret_cast(_graph)); _cache->types().insert(&metadata, type); } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index e90f9f4..cdaf0fe 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -163,7 +163,7 @@ class Subgraph : public data::zone { data::ptr tree_root() { return _tree_root; }; - void begin_tree(AttributeID attribute, const swift::metadata *_Nullable type, + void begin_tree(AttributeID value, const swift::metadata *_Nullable type, uint32_t flags); // TODO: check can be null from Subgraph() void end_tree(); @@ -205,7 +205,7 @@ class Subgraph : public data::zone { // FIXME: not AGUnownedGraphContextRef data::ptr cache_fetch(uint64_t identifier, const swift::metadata &type, void *body, - ClosureFunctionCI closure); + ClosureFunctionCI closure); void cache_insert(data::ptr node); void cache_collect(); diff --git a/Sources/ComputeCxx/Swift/AGType.cpp b/Sources/ComputeCxx/Swift/AGType.cpp index 90b0a82..d151c73 100644 --- a/Sources/ComputeCxx/Swift/AGType.cpp +++ b/Sources/ComputeCxx/Swift/AGType.cpp @@ -175,8 +175,9 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, } } -void AGTypeApplyEnumData(AGTypeID typeID, void *value, - void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)) { +bool AGTypeApplyEnumData(AGTypeID typeID, void *value, + void (*body)(const void *context, uint32_t tag, AGTypeID field_type, void *field_value), + const void *context) { auto type = reinterpret_cast(typeID); auto value_witness = type->getValueWitnesses(); if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { @@ -204,7 +205,7 @@ void AGTypeApplyEnumData(AGTypeID typeID, void *value, size_t offset = (sizeof(::swift::HeapObject) + alignment_mask) & ~alignment_mask; field_value = *(char **)value + offset; } - body(tag, AGTypeID(field_type), field_value); + body(context, tag, AGTypeID(field_type), field_value); enum_value_witness->destructiveInjectEnumTag(reinterpret_cast(value), tag, type); @@ -217,8 +218,9 @@ void AGTypeApplyEnumData(AGTypeID typeID, void *value, return false; } -void AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, - void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)) { +bool AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, + void (*body)(const void *context, uint32_t tag, AGTypeID field_type, void *field_value), + const void *context) { auto type = reinterpret_cast(typeID); auto value_witness = type->getValueWitnesses(); if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { @@ -247,7 +249,7 @@ void AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, size_t offset = (sizeof(::swift::HeapObject) + alignment_mask) & ~alignment_mask; field_value = *(char **)value + offset; } - body(tag, AGTypeID(field_type), field_value); + body(context, tag, AGTypeID(field_type), field_value); enum_value_witness->destructiveInjectEnumTag(reinterpret_cast(value), tag, type); @@ -288,7 +290,7 @@ void AGTypeInjectEnumTag(AGTypeID typeID, void *value, uint32_t tag) { if (!value_witness || !value_witness->flags.hasEnumWitnesses()) { AG::precondition_failure("not an enum type: %s", type->name(false)); } - + auto enum_value_witness = value_witness->_asEVWT(); enum_value_witness->destructiveInjectEnumTag(reinterpret_cast(value), tag, type); } diff --git a/Sources/ComputeCxx/Swift/AGType.h b/Sources/ComputeCxx/Swift/AGType.h index 11aa6d0..e100ddd 100644 --- a/Sources/ComputeCxx/Swift/AGType.h +++ b/Sources/ComputeCxx/Swift/AGType.h @@ -76,13 +76,17 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, CF_EXPORT CF_REFINED_FOR_SWIFT -void AGTypeApplyEnumData(AGTypeID typeID, void *value, - void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)); +bool AGTypeApplyEnumData(AGTypeID typeID, void *value, + void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, + void *field_value) AG_SWIFT_CC(swift), + const void *context); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, - void (*body)(uint32_t tag, AGTypeID field_type, void *field_value)); +bool AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, + void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, + void *field_value) AG_SWIFT_CC(swift), + const void *context); CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/include/Compute.h b/Sources/ComputeCxx/include/Compute.h index 75134f4..0008ae8 100644 --- a/Sources/ComputeCxx/include/Compute.h +++ b/Sources/ComputeCxx/include/Compute.h @@ -1,4 +1,8 @@ #include "Attribute/AGAttribute.h" +#include "Graph/AGDescription.h" #include "Graph/AGGraph.h" +#include "Graph/Tree/AGTreeElement.h" +#include "Layout/AGComparison.h" #include "Subgraph/AGSubgraph.h" +#include "Swift/AGTuple.h" #include "Swift/AGType.h" From 878479011cc647af08f7789e6099d35a73c60a1a Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 10 Apr 2025 14:15:12 +0200 Subject: [PATCH 54/74] Flip page list direction --- Sources/ComputeCxx/Data/Page.h | 12 ++--- Sources/ComputeCxx/Data/Table.cpp | 2 +- Sources/ComputeCxx/Data/Zone.cpp | 44 +++++++++---------- Sources/ComputeCxx/Data/Zone.h | 4 +- Sources/ComputeCxx/Graph/Graph+Description.mm | 8 ++-- Sources/ComputeCxx/Graph/Graph.cpp | 6 +-- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 16 +++---- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Sources/ComputeCxx/Data/Page.h b/Sources/ComputeCxx/Data/Page.h index 0aa34c9..9535b23 100644 --- a/Sources/ComputeCxx/Data/Page.h +++ b/Sources/ComputeCxx/Data/Page.h @@ -13,12 +13,12 @@ namespace data { class zone; struct page { - zone *zone; // 0 - ptr previous; // 8 - uint32_t total; // 12 - uint32_t in_use; // 16 - uint16_t relative_offset_1; // 0x14/20 - uint16_t relative_offset_2; // 0x16/22 + zone *zone; + ptr next; + uint32_t total; + uint32_t in_use; + uint16_t relative_offset_1; + uint16_t relative_offset_2; }; static_assert(sizeof(page) == 0x18); diff --git a/Sources/ComputeCxx/Data/Table.cpp b/Sources/ComputeCxx/Data/Table.cpp index 81873dd..fa397ac 100644 --- a/Sources/ComputeCxx/Data/Table.cpp +++ b/Sources/ComputeCxx/Data/Table.cpp @@ -191,7 +191,7 @@ ptr table::alloc_page(zone *zone, uint32_t needed_size) { // ptr offsets are "one"-based, so that we can treat 0 as null. ptr new_page = ptr((new_page_index + 1) * page_size); new_page->zone = zone; - new_page->previous = nullptr; + new_page->next = nullptr; new_page->total = (needed_size + page_alignment_mask) & ~page_alignment_mask; new_page->in_use = sizeof(page); diff --git a/Sources/ComputeCxx/Data/Zone.cpp b/Sources/ComputeCxx/Data/Zone.cpp index f511b6d..b001e2f 100644 --- a/Sources/ComputeCxx/Data/Zone.cpp +++ b/Sources/ComputeCxx/Data/Zone.cpp @@ -16,9 +16,9 @@ zone::~zone() { clear(); } void zone::clear() { table::shared().lock(); - while (_last_page) { - auto page = _last_page; - _last_page = page->previous; + while (_first_page) { + auto page = _first_page; + _first_page = page->next; table::shared().dealloc_page_locked(page); } table::shared().unlock(); @@ -86,12 +86,12 @@ ptr zone::alloc_bytes_recycle(uint32_t size, uint32_t alignment_mask) { } ptr zone::alloc_bytes(uint32_t size, uint32_t alignment_mask) { - if (_last_page) { - uint32_t aligned_in_use = (_last_page->in_use + alignment_mask) & ~alignment_mask; + if (_first_page) { + uint32_t aligned_in_use = (_first_page->in_use + alignment_mask) & ~alignment_mask; uint32_t new_used_size = aligned_in_use + size; - if (new_used_size <= _last_page->total) { - _last_page->in_use = new_used_size; - return _last_page + aligned_in_use; + if (new_used_size <= _first_page->total) { + _first_page->in_use = new_used_size; + return _first_page + aligned_in_use; } } @@ -99,14 +99,14 @@ ptr zone::alloc_bytes(uint32_t size, uint32_t alignment_mask) { } ptr zone::alloc_slow(uint32_t size, uint32_t alignment_mask) { - if (_last_page) { + if (_first_page) { // check if we can use remaining bytes in this page - ptr next_bytes = _last_page + _last_page->in_use; - if (next_bytes.page_ptr() == _last_page) { + ptr next_bytes = _first_page + _first_page->in_use; + if (next_bytes.page_ptr() == _first_page) { ptr aligned_next_bytes = next_bytes.aligned(); - int32_t remaining_size = _last_page->total - _last_page->in_use + (next_bytes - aligned_next_bytes); + int32_t remaining_size = _first_page->total - _first_page->in_use + (next_bytes - aligned_next_bytes); if (remaining_size >= sizeof(bytes_info)) { bytes_info *remaining_bytes = aligned_next_bytes.get(); remaining_bytes->next = _free_bytes; @@ -115,25 +115,25 @@ ptr zone::alloc_slow(uint32_t size, uint32_t alignment_mask) { } // consume this entire page - _last_page->in_use = _last_page->total; + _first_page->in_use = _first_page->total; } } ptr new_page; if (size <= page_size / 2) { new_page = table::shared().alloc_page(this, page_size); - new_page->previous = _last_page; - _last_page = new_page; + new_page->next = _first_page; + _first_page = new_page; } else { uint32_t aligned_size = ((sizeof(page) + size) + alignment_mask) & ~alignment_mask; new_page = table::shared().alloc_page(this, aligned_size); - if (_last_page) { + if (_first_page) { // It's less likely we will be able to alloc unused bytes from this page, - // so insert it before the last page. - new_page->previous = _last_page->previous; - _last_page->previous = new_page; + // so insert it after the first page. + new_page->next = _first_page->next; + _first_page->next = new_page; } else { - _last_page = new_page; + _first_page = new_page; } } @@ -173,10 +173,10 @@ void zone::print() { unsigned long num_pages = 0; double pages_total_kb = 0.0; double pages_in_use_kb = 0.0; - if (_last_page) { + if (_first_page) { int64_t pages_total = 0; int64_t pages_in_use = 0; - for (auto page = _last_page; page; page = page->previous) { + for (auto page = _first_page; page; page = page->next) { num_pages++; pages_total += page->total; pages_in_use += page->in_use; diff --git a/Sources/ComputeCxx/Data/Zone.h b/Sources/ComputeCxx/Data/Zone.h index b425d08..bb1e84e 100644 --- a/Sources/ComputeCxx/Data/Zone.h +++ b/Sources/ComputeCxx/Data/Zone.h @@ -40,7 +40,7 @@ class zone { } bytes_info; vector, 0, uint32_t> _malloc_buffers; - ptr _last_page; + ptr _first_page; ptr _free_bytes; info _info; @@ -51,7 +51,7 @@ class zone { info info() const { return _info; }; void mark_deleted() { _info = _info.with_deleted(); }; - ptr last_page() const { return _last_page; }; + ptr first_page() const { return _first_page; }; void clear(); void realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask); diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 8b46bc1..78b2c9a 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -251,7 +251,7 @@ int trap_cycles() { auto node_indices_by_id = std::unordered_map, uint64_t>(); for (auto subgraph : graph->subgraphs()) { - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); @@ -343,7 +343,7 @@ int trap_cycles() { } for (auto subgraph : graph->subgraphs()) { - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); @@ -536,7 +536,7 @@ int trap_cycles() { // Nodes NSMutableArray *nodes = [NSMutableArray array]; - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); @@ -776,7 +776,7 @@ int trap_cycles() { if (!subgraphs().empty()) { for (auto subgraph : subgraphs()) { - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 761c15e..e471e7e 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1148,7 +1148,7 @@ void Graph::value_mark_all() { } for (auto subgraph : _subgraphs) { - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); @@ -2180,7 +2180,7 @@ void Graph::prepare_trace(Trace &trace) { } for (auto subgraph : _subgraphs) { for (uint32_t iteration = 0; iteration < 2; ++iteration) { - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { bool should_break = false; uint16_t relative_offset = iteration == 0 ? page->relative_offset_2 : page->relative_offset_1; while (relative_offset) { @@ -2224,7 +2224,7 @@ void Graph::prepare_trace(Trace &trace) { } for (auto subgraph : _subgraphs) { for (uint32_t iteration = 0; iteration < 2; ++iteration) { - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { bool should_break = false; uint16_t relative_offset = iteration == 0 ? page->relative_offset_2 : page->relative_offset_1; while (relative_offset) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 004c16f..c3a7491 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -203,7 +203,7 @@ void Subgraph::invalidate_now(Graph &graph) { } for (auto removed_subgraph : removed_subgraphs) { - for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = removed_subgraph->first_page(); page != nullptr; page = page->next) { bool found_nil_attribute = false; uint16_t relative_offset = page->relative_offset_1; @@ -229,7 +229,7 @@ void Subgraph::invalidate_now(Graph &graph) { } for (auto removed_subgraph : removed_subgraphs) { - for (data::ptr page = removed_subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = removed_subgraph->first_page(); page != nullptr; page = page->next) { bool found_nil_attribute = false; uint16_t relative_offset = page->relative_offset_1; @@ -277,7 +277,7 @@ void Subgraph::graph_destroyed() { } notify_observers(); - for (data::ptr page = last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); @@ -558,8 +558,8 @@ void Subgraph::update(uint8_t flags) { uint8_t old_flags_1 = subgraph->_flags.value1; subgraph->_flags.value3 &= ~flags; if (flags == 0 || (old_flags_1 & flags)) { - for (data::ptr page = subgraph->last_page(); page != nullptr; - page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; + page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); @@ -660,7 +660,7 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { if (flags.value4 & 1 || subgraph->context_id() == _context_id) { if (flags.is_null() || (flags.value1 & subgraph->_flags.value1)) { - for (data::ptr page = subgraph->last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); @@ -1133,7 +1133,7 @@ void Subgraph::encode(Encoder &encoder) const { encoder.encode_varint(1); } - for (data::ptr page = last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = first_page(); page != nullptr; page = page->next) { for (uint32_t iteration = 0; iteration < 2; ++iteration) { uint16_t relative_offset = iteration == 0 ? page->relative_offset_1 : page->relative_offset_2; while (relative_offset) { @@ -1199,7 +1199,7 @@ void Subgraph::print(uint32_t indent_level) { fprintf(stdout, "%s+ %p: %u in %lu [", indent_string, this, info().zone_id(), (unsigned long)_context_id); bool first = true; - for (data::ptr page = last_page(); page != nullptr; page = page->previous) { + for (data::ptr page = first_page(); page != nullptr; page = page->next) { uint16_t relative_offset = page->relative_offset_1; while (relative_offset) { AttributeID attribute = AttributeID(page + relative_offset); From 10ae34bdee838de511b2cf7dab8da811ab780eff Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 10 Apr 2025 19:02:50 +0200 Subject: [PATCH 55/74] Add iterators for pages and attributes --- .../ComputeCxx/Attribute/AttributeIDList.h | 69 +++++ Sources/ComputeCxx/Data/Page.h | 36 ++- Sources/ComputeCxx/Data/Pointer.h | 4 +- Sources/ComputeCxx/Data/Zone.h | 4 +- Sources/ComputeCxx/Graph/Graph+Description.mm | 240 +++++++----------- Sources/ComputeCxx/Graph/Graph.cpp | 52 +--- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 230 +++++------------ Sources/ComputeCxx/Subgraph/Subgraph.h | 2 +- 8 files changed, 280 insertions(+), 357 deletions(-) create mode 100644 Sources/ComputeCxx/Attribute/AttributeIDList.h diff --git a/Sources/ComputeCxx/Attribute/AttributeIDList.h b/Sources/ComputeCxx/Attribute/AttributeIDList.h new file mode 100644 index 0000000..d633f2c --- /dev/null +++ b/Sources/ComputeCxx/Attribute/AttributeIDList.h @@ -0,0 +1,69 @@ +#pragma once + +#include "Data/Page.h" +#include "Node/IndirectNode.h" +#include "Node/Node.h" + +namespace AG { + +class AttributeIDIterator { + private: + data::ptr _page; + uint16_t _offset; + + public: + AttributeIDIterator(data::ptr page, uint16_t offset) : _page(page), _offset(offset) {} + + bool operator==(const AttributeIDIterator &other) const { return _page == other._page && _offset == other._offset; } + bool operator!=(const AttributeIDIterator &other) const { return _page != other._page || _offset != other._offset; } + + AttributeID operator*() { + assert(_page); + return AttributeID(_page + _offset); + } + + AttributeIDIterator &operator++() { + assert(_page); + + AttributeID attribute_id = AttributeID(_page + _offset); + if (attribute_id.is_direct()) { + _offset = attribute_id.to_node().flags().relative_offset(); + } else if (attribute_id.is_indirect()) { + _offset = attribute_id.to_indirect_node().relative_offset(); + } else { + _page = nullptr; + _offset = 0; + } + return *this; + } +}; + +class AttributeIDList { + public: + virtual AttributeIDIterator begin(); + virtual AttributeIDIterator end(); +}; + +class AttributeIDList1: public AttributeIDList { + private: + data::ptr _page; + + public: + AttributeIDList1(data::ptr page) : _page(page) {} + + AttributeIDIterator begin() { return AttributeIDIterator(_page, _page->first_child_1); } + AttributeIDIterator end() { return AttributeIDIterator(nullptr, 0); } +}; + +class AttributeIDList2: public AttributeIDList { + private: + data::ptr _page; + + public: + AttributeIDList2(data::ptr page) : _page(page) {} + + AttributeIDIterator begin() { return AttributeIDIterator(_page, _page->first_child_2); } + AttributeIDIterator end() { return AttributeIDIterator(nullptr, 0); } +}; + +} // namespace AG diff --git a/Sources/ComputeCxx/Data/Page.h b/Sources/ComputeCxx/Data/Page.h index 9535b23..3154825 100644 --- a/Sources/ComputeCxx/Data/Page.h +++ b/Sources/ComputeCxx/Data/Page.h @@ -17,11 +17,43 @@ struct page { ptr next; uint32_t total; uint32_t in_use; - uint16_t relative_offset_1; - uint16_t relative_offset_2; + + uint16_t first_child_1; + uint16_t first_child_2; }; static_assert(sizeof(page) == 0x18); +class page_ptr_list { + public: + class iterator { + private: + ptr _current; + + public: + iterator(ptr current) : _current(current) {} + + bool operator==(const iterator &other) const { return _current == other._current; } + bool operator!=(const iterator &other) const { return _current != other._current; } + + ptr operator*() { return _current; } + + iterator &operator++() { + assert(_current); + _current = _current->next; + return *this; + } + }; + + private: + ptr _front; + + public: + page_ptr_list(ptr front) : _front(front) {} + + iterator begin() { return iterator(_front); } + iterator end() { return iterator(nullptr); } +}; + } // namespace data } // namespace AG diff --git a/Sources/ComputeCxx/Data/Pointer.h b/Sources/ComputeCxx/Data/Pointer.h index e79733b..b3d8c48 100644 --- a/Sources/ComputeCxx/Data/Pointer.h +++ b/Sources/ComputeCxx/Data/Pointer.h @@ -26,8 +26,8 @@ template class ptr { template friend class ptr; public: - ptr(difference_type offset = 0) : _offset(offset){}; - ptr(nullptr_t){}; + ptr(difference_type offset = 0) : _offset(offset) {}; + ptr(nullptr_t) {}; void assert_valid() const { if (_offset >= table::shared().ptr_max_offset()) { diff --git a/Sources/ComputeCxx/Data/Zone.h b/Sources/ComputeCxx/Data/Zone.h index bb1e84e..deae108 100644 --- a/Sources/ComputeCxx/Data/Zone.h +++ b/Sources/ComputeCxx/Data/Zone.h @@ -21,7 +21,7 @@ class zone { deleted = 0x80000000, }; uint32_t _value; - info(uint32_t value) : _value(value){}; + info(uint32_t value) : _value(value) {}; public: uint32_t zone_id() const { return _value & id_mask; }; // TODO: id() @@ -51,7 +51,7 @@ class zone { info info() const { return _info; }; void mark_deleted() { _info = _info.with_deleted(); }; - ptr first_page() const { return _first_page; }; + page_ptr_list pages() const { return page_ptr_list(_first_page); }; void clear(); void realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask); diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 78b2c9a..312dbb8 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -4,6 +4,7 @@ #include #include "AGGraph.h" +#include "Attribute/AttributeIDList.h" #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" @@ -251,138 +252,114 @@ int trap_cycles() { auto node_indices_by_id = std::unordered_map, uint64_t>(); for (auto subgraph : graph->subgraphs()) { - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - if (attribute.is_nil()) { - break; - } - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else { - relative_offset = 0; + for (auto page : subgraph->pages()) { + for (auto attribute : AttributeIDList1(page)) { + if (!attribute.is_direct()) { + continue; } - if (attribute.is_direct()) { - uint64_t index = node_indices_by_id.size() + 1; - node_indices_by_id.emplace(attribute, index); + uint64_t index = node_indices_by_id.size() + 1; + node_indices_by_id.emplace(attribute, index); - auto node = attribute.to_node(); + auto node = attribute.to_node(); - uint32_t type_id = node.type_id(); - uint64_t type_index; - auto found = type_indices_by_id.find(type_id); - if (found != type_indices_by_id.end()) { - type_index = found->second; - } else { - type_index = type_ids.size(); - type_ids.push_back(type_id); - type_indices_by_id.emplace(type_id, index); - } + uint32_t type_id = node.type_id(); + uint64_t type_index; + auto found = type_indices_by_id.find(type_id); + if (found != type_indices_by_id.end()) { + type_index = found->second; + } else { + type_index = type_ids.size(); + type_ids.push_back(type_id); + type_indices_by_id.emplace(type_id, index); + } - NSMutableDictionary *node_dict = [NSMutableDictionary dictionary]; - node_dict[@"type"] = [NSNumber numberWithUnsignedLong:type_index]; - node_dict[@"id"] = [NSNumber numberWithUnsignedLong:attribute]; + NSMutableDictionary *node_dict = [NSMutableDictionary dictionary]; + node_dict[@"type"] = [NSNumber numberWithUnsignedLong:type_index]; + node_dict[@"id"] = [NSNumber numberWithUnsignedLong:attribute]; - const AttributeType &attribute_type = graph->attribute_type(node.type_id()); - if (node.state().is_self_initialized()) { - void *self = node.get_self(attribute_type); - if (auto desc = attribute_type.vt_self_description(self)) { - node_dict[@"desc"] = escaped_string((__bridge NSString *)desc, truncation_limit); - } + const AttributeType &attribute_type = graph->attribute_type(node.type_id()); + if (node.state().is_self_initialized()) { + void *self = node.get_self(attribute_type); + if (auto desc = attribute_type.vt_self_description(self)) { + node_dict[@"desc"] = escaped_string((__bridge NSString *)desc, truncation_limit); } - if (include_values && node.state().is_value_initialized()) { - void *value = node.get_value(); - if (auto value_desc = attribute_type.vt_value_description(value)) { - node_dict[@"value"] = escaped_string((__bridge NSString *)value_desc, truncation_limit); - } + } + if (include_values && node.state().is_value_initialized()) { + void *value = node.get_value(); + if (auto value_desc = attribute_type.vt_value_description(value)) { + node_dict[@"value"] = escaped_string((__bridge NSString *)value_desc, truncation_limit); } + } - auto flags = attribute.to_node().value_state(); - if (flags) { - node_dict[@"flags"] = [NSNumber numberWithUnsignedInt:flags]; - } + auto flags = attribute.to_node().value_state(); + if (flags) { + node_dict[@"flags"] = [NSNumber numberWithUnsignedInt:flags]; + } - auto profile_data = graph->_profile_data.get(); - if (profile_data) { - auto found = profile_data->all_events().items_by_attribute().find(attribute.to_node_ptr()); - if (found != profile_data->all_events().items_by_attribute().end()) { - CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); - if (item_json) { - node_dict[@"profile"] = (__bridge NSDictionary *)item_json; - } + auto profile_data = graph->_profile_data.get(); + if (profile_data) { + auto found = profile_data->all_events().items_by_attribute().find(attribute.to_node_ptr()); + if (found != profile_data->all_events().items_by_attribute().end()) { + CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); + if (item_json) { + node_dict[@"profile"] = (__bridge NSDictionary *)item_json; } - if (profile_data->categories().size()) { - NSMutableDictionary *event_dicts = [NSMutableDictionary dictionary]; - for (auto &entry : profile_data->categories()) { - uint32_t event_id = entry.first; - auto found = entry.second.items_by_attribute().find(attribute.to_node_ptr()); - if (found != entry.second.items_by_attribute().end()) { - CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); - if (item_json) { - NSString *event_name = - [NSString stringWithUTF8String:graph->key_name(event_id)]; - event_dicts[event_name] = (__bridge NSDictionary *)item_json; - } + } + if (profile_data->categories().size()) { + NSMutableDictionary *event_dicts = [NSMutableDictionary dictionary]; + for (auto &entry : profile_data->categories()) { + uint32_t event_id = entry.first; + auto found = entry.second.items_by_attribute().find(attribute.to_node_ptr()); + if (found != entry.second.items_by_attribute().end()) { + CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); + if (item_json) { + NSString *event_name = + [NSString stringWithUTF8String:graph->key_name(event_id)]; + event_dicts[event_name] = (__bridge NSDictionary *)item_json; } } - if ([event_dicts count]) { - node_dict[@"events"] = event_dicts; - } + } + if ([event_dicts count]) { + node_dict[@"events"] = event_dicts; } } - - [node_dicts addObject:node_dict]; } + + [node_dicts addObject:node_dict]; } } } for (auto subgraph : graph->subgraphs()) { - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - if (attribute.is_nil()) { - break; + for (auto page : subgraph->pages()) { + for (auto attribute : AttributeIDList1(page)) { + if (!attribute.is_direct()) { + continue; } - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else { - relative_offset = 0; - } + auto node = attribute.to_node(); + for (auto input_edge : node.inputs()) { + OffsetAttributeID resolved = input_edge.value.resolve(TraversalOptions::None); + if (resolved.attribute().is_direct()) { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - if (attribute.is_direct()) { - auto node = attribute.to_node(); - for (auto input_edge : node.inputs()) { - OffsetAttributeID resolved = input_edge.value.resolve(TraversalOptions::None); - if (resolved.attribute().is_direct()) { - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - - auto src = node_indices_by_id.find(resolved.attribute().to_node_ptr())->second; - dict[@"src"] = [NSNumber numberWithUnsignedLong:src]; - auto dest = node_indices_by_id.find(attribute.to_node_ptr())->second; - dict[@"dest"] = [NSNumber numberWithUnsignedLong:dest]; - bool indirect = attribute.is_indirect(); - if (indirect) { - if (resolved.offset()) { - dict[@"offset"] = [NSNumber numberWithUnsignedLong:resolved.offset()]; - } - } - if (indirect || input_edge.is_always_enabled() || input_edge.is_changed() || - input_edge.is_unknown4() || input_edge.is_unprefetched()) { - dict[@"flags"] = @YES; // + auto src = node_indices_by_id.find(resolved.attribute().to_node_ptr())->second; + dict[@"src"] = [NSNumber numberWithUnsignedLong:src]; + auto dest = node_indices_by_id.find(attribute.to_node_ptr())->second; + dict[@"dest"] = [NSNumber numberWithUnsignedLong:dest]; + bool indirect = attribute.is_indirect(); + if (indirect) { + if (resolved.offset()) { + dict[@"offset"] = [NSNumber numberWithUnsignedLong:resolved.offset()]; } - - [edge_dicts addObject:dict]; } + if (indirect || input_edge.is_always_enabled() || input_edge.is_changed() || + input_edge.is_unknown4() || input_edge.is_unprefetched()) { + dict[@"flags"] = @YES; // + } + + [edge_dicts addObject:dict]; } } } @@ -536,32 +513,20 @@ int trap_cycles() { // Nodes NSMutableArray *nodes = [NSMutableArray array]; - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - if (attribute.is_nil()) { + for (auto page : subgraph->pages()) { + for (auto attribute : AttributeIDList1(page)) { + if (!attribute.is_direct()) { break; } - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else { - relative_offset = 0; - } - - if (attribute.is_direct()) { - uint8_t subgraph_flags = attribute.to_node().flags().subgraph_flags(); - auto found_node_index = node_indices_by_id.find(attribute.to_node_ptr()); - if (found_node_index != node_indices_by_id.end()) { - [nodes addObject:[NSNumber numberWithUnsignedLong:found_node_index->second]]; - if (subgraph_flags) { - NSMutableDictionary *node_dict = nodes[found_node_index->second]; - node_dict[@"subgraph_flags"] = [NSNumber numberWithUnsignedChar:subgraph_flags]; - nodes[found_node_index->second] = node_dict; - } + uint8_t subgraph_flags = attribute.to_node().flags().subgraph_flags(); + auto found_node_index = node_indices_by_id.find(attribute.to_node_ptr()); + if (found_node_index != node_indices_by_id.end()) { + [nodes addObject:[NSNumber numberWithUnsignedLong:found_node_index->second]]; + if (subgraph_flags) { + NSMutableDictionary *node_dict = nodes[found_node_index->second]; + node_dict[@"subgraph_flags"] = [NSNumber numberWithUnsignedChar:subgraph_flags]; + nodes[found_node_index->second] = node_dict; } } } @@ -776,21 +741,8 @@ int trap_cycles() { if (!subgraphs().empty()) { for (auto subgraph : subgraphs()) { - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else if (attribute.is_nil()) { - break; - } else { - break; - } - + for (auto page : subgraph->pages()) { + for (auto attribute : AttributeIDList1(page)) { if (!attribute.is_direct()) { continue; } diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index e471e7e..bf5151b 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -8,6 +8,7 @@ #include #include +#include "Attribute/AttributeIDList.h" #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" @@ -1148,22 +1149,11 @@ void Graph::value_mark_all() { } for (auto subgraph : _subgraphs) { - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); + for (auto page : subgraph->pages()) { + for (auto attribute : AttributeIDList1(page)) { if (attribute.is_nil()) { break; // TODO: check if this should break out of entire loop } - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else { - relative_offset = 0; - } - if (attribute.is_direct()) { auto node = attribute.to_node(); AttributeType &type = *_types[node.type_id()]; @@ -2180,22 +2170,14 @@ void Graph::prepare_trace(Trace &trace) { } for (auto subgraph : _subgraphs) { for (uint32_t iteration = 0; iteration < 2; ++iteration) { - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { + for (auto page : subgraph->pages()) { bool should_break = false; - uint16_t relative_offset = iteration == 0 ? page->relative_offset_2 : page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else if (attribute.is_nil()) { - relative_offset = 0; + AttributeIDList list = + iteration == 0 ? (AttributeIDList)AttributeIDList2(page) : (AttributeIDList)AttributeIDList1(page); + for (auto attribute : list) { + if (attribute.is_nil()) { should_break = true; break; - } else { - relative_offset = 0; } if (attribute.is_direct()) { @@ -2224,22 +2206,14 @@ void Graph::prepare_trace(Trace &trace) { } for (auto subgraph : _subgraphs) { for (uint32_t iteration = 0; iteration < 2; ++iteration) { - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { + for (auto page : subgraph->pages()) { bool should_break = false; - uint16_t relative_offset = iteration == 0 ? page->relative_offset_2 : page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else if (attribute.is_nil()) { - relative_offset = 0; + AttributeIDList list = + iteration == 0 ? (AttributeIDList)AttributeIDList2(page) : (AttributeIDList)AttributeIDList1(page); + for (auto attribute : list) { + if (attribute.is_nil()) { should_break = true; break; - } else { - relative_offset = 0; } if (attribute.is_direct()) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index c3a7491..8e68928 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -3,6 +3,7 @@ #include #include "AGSubgraph-Private.h" +#include "Attribute/AttributeIDList.h" #include "Attribute/AttributeType.h" #include "Attribute/Node/IndirectNode.h" #include "Attribute/Node/Node.h" @@ -203,23 +204,15 @@ void Subgraph::invalidate_now(Graph &graph) { } for (auto removed_subgraph : removed_subgraphs) { - for (data::ptr page = removed_subgraph->first_page(); page != nullptr; page = page->next) { + for (auto page : removed_subgraph->pages()) { bool found_nil_attribute = false; - - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); + for (auto attribute : AttributeIDList1(page)) { if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - graph.remove_node(attribute.to_node_ptr()); + graph.remove_node(attribute.to_node_ptr()); // TODO: does this muck up iteration } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - graph.remove_indirect_node(attribute.to_indirect_node_ptr()); + graph.remove_indirect_node(attribute.to_indirect_node_ptr()); // TODO: does this muck up iteration } else if (attribute.is_nil()) { - relative_offset = 0; found_nil_attribute = true; - } else { - relative_offset = 0; } } if (found_nil_attribute) { @@ -229,28 +222,15 @@ void Subgraph::invalidate_now(Graph &graph) { } for (auto removed_subgraph : removed_subgraphs) { - for (data::ptr page = removed_subgraph->first_page(); page != nullptr; page = page->next) { + for (auto page : removed_subgraph->pages()) { bool found_nil_attribute = false; - - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - + for (auto attribute : AttributeIDList1(page)) { if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else if (attribute.is_nil()) { - relative_offset = 0; - found_nil_attribute = true; - } else { - relative_offset = 0; - } - - if (attribute.is_direct()) { - attribute.to_node().destroy(*_graph); + attribute.to_node().destroy(*_graph); // TODO: does this muck up iteration _graph->did_destroy_node(); // decrement counter + } else if (attribute.is_nil()) { + found_nil_attribute = true; } } if (found_nil_attribute) { @@ -277,24 +257,12 @@ void Subgraph::graph_destroyed() { } notify_observers(); - for (data::ptr page = first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - if (attribute.is_nil()) { - break; // TODO: check if this should break out of entire loop - } - + for (auto page : pages()) { + for (auto attribute : AttributeIDList1(page)) { if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else { - relative_offset = 0; - } - - if (attribute.is_direct()) { - attribute.to_node().destroy(*_graph); + attribute.to_node().destroy(*_graph); // TODO: does this muck up iteration? + } else if (attribute.is_nil()) { + break; // TODO: check if this should break out of entire loop } } } @@ -405,85 +373,57 @@ void Subgraph::add_indirect(data::ptr node, bool flag) { graph()->foreach_trace([&node](Trace &trace) { trace.added(node); }); } -void Subgraph::insert_attribute(AttributeID attribute, bool flag) { - AttributeID source = AttributeID::make_nil(); +void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) { + AttributeID before_attribute = AttributeID::make_nil(); - if (flag) { + if (after_flagged_nodes) { if (!attribute.is_direct() || attribute.to_node().flags().subgraph_flags() == 0) { - uint16_t relative_offset = attribute.page_ptr()->relative_offset_1; - while (relative_offset) { - AttributeID next_attribute = AttributeID(attribute.page_ptr().offset() + relative_offset); - if (next_attribute.is_direct()) { - auto node = next_attribute.to_node(); - if (node.flags().subgraph_flags() == 0) { - break; - } - source = next_attribute; - relative_offset = node.flags().relative_offset(); - if (relative_offset == 0) { - break; - } - } else { - relative_offset = 0; // TODO: check this line is here + for (auto candidate_attribute : AttributeIDList1(attribute.page_ptr())) { + if (!candidate_attribute.is_direct() || candidate_attribute.to_node().flags().subgraph_flags() == 0) { + break; } + before_attribute = candidate_attribute; } } } - // TODO: add RelativeAttributeID and simplify this - - uint16_t next_value = (attribute.without_kind() - attribute.page_ptr()) | attribute.kind(); - - uint16_t previous_value = 0; - if (source.is_direct()) { - previous_value = source.to_node().flags().relative_offset(); - source.to_node().flags().set_relative_offset(next_value); - } else if (source.is_indirect()) { - previous_value = source.to_indirect_node().relative_offset(); - source.to_indirect_node().set_relative_offset(next_value); + uint16_t inserted_offset = (attribute.without_kind() - attribute.page_ptr()) | attribute.kind(); + uint16_t next_offset; + if (before_attribute.is_direct()) { + next_offset = before_attribute.to_node().flags().relative_offset(); + before_attribute.to_node().flags().set_relative_offset(inserted_offset); + } else if (before_attribute.is_indirect()) { + next_offset = before_attribute.to_indirect_node().relative_offset(); + before_attribute.to_indirect_node().set_relative_offset(inserted_offset); } else { - if (flag) { - previous_value = source.page_ptr()->relative_offset_1; - source.page_ptr()->relative_offset_1 = next_value; + if (after_flagged_nodes) { + next_offset = attribute.page_ptr()->first_child_1; + attribute.page_ptr()->first_child_1 = inserted_offset; } else { - previous_value = source.page_ptr()->relative_offset_2; - source.page_ptr()->relative_offset_2 = next_value; + next_offset = attribute.page_ptr()->first_child_2; + attribute.page_ptr()->first_child_2 = inserted_offset; } } if (attribute.is_direct()) { - attribute.to_node().flags().set_relative_offset(previous_value); + attribute.to_node().flags().set_relative_offset(next_offset); } else if (attribute.is_indirect()) { - attribute.to_indirect_node().set_relative_offset(previous_value); + attribute.to_indirect_node().set_relative_offset(next_offset); } } void Subgraph::unlink_attribute(AttributeID attribute) { - - uint16_t relative_offset = attribute.page_ptr()->relative_offset_1; - // Find the attribute before the given attribute // TODO: what happens if attribute is not found - AttributeID before_attribute = AttributeID::make_nil(); - while (relative_offset) { - AttributeID next_attribute = AttributeID(attribute.page_ptr().offset() + relative_offset); - if (next_attribute.is_nil()) { + AttributeID previous_attribute = AttributeID::make_nil(); + for (auto candidate_attribute : AttributeIDList1(attribute.page_ptr())) { + if (candidate_attribute.is_nil()) { break; } - - if (next_attribute == attribute) { + if (candidate_attribute == attribute) { break; } - - if (next_attribute.is_direct()) { - relative_offset = next_attribute.to_node().flags().relative_offset(); - before_attribute = next_attribute; - } else if (next_attribute.is_indirect()) { - relative_offset = next_attribute.to_indirect_node().relative_offset(); - before_attribute = next_attribute; - } else { - relative_offset = 0; - } + previous_attribute = candidate_attribute; } uint16_t old_value = 0; @@ -495,12 +435,12 @@ void Subgraph::unlink_attribute(AttributeID attribute) { attribute.to_indirect_node().set_relative_offset(0); } - if (before_attribute.is_direct()) { - before_attribute.to_node().flags().set_relative_offset(old_value); - } else if (before_attribute.is_indirect()) { - before_attribute.to_indirect_node().set_relative_offset(old_value); + if (previous_attribute.is_direct()) { + previous_attribute.to_node().flags().set_relative_offset(old_value); + } else if (previous_attribute.is_indirect()) { + previous_attribute.to_indirect_node().set_relative_offset(old_value); } else { - attribute.page_ptr()->relative_offset_1 = old_value; + attribute.page_ptr()->first_child_1 = old_value; } } @@ -558,17 +498,9 @@ void Subgraph::update(uint8_t flags) { uint8_t old_flags_1 = subgraph->_flags.value1; subgraph->_flags.value3 &= ~flags; if (flags == 0 || (old_flags_1 & flags)) { - for (data::ptr page = subgraph->first_page(); page != nullptr; - page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - if (attribute.is_nil()) { - break; - } - + for (auto page : subgraph->pages()) { + for (auto attribute : AttributeIDList1(page)) { if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); auto node = attribute.to_node(); if (flags) { if (node.flags().subgraph_flags() == 0) { @@ -582,12 +514,9 @@ void Subgraph::update(uint8_t flags) { nodes_to_update.push_back(attribute.to_node_ptr()); } } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); if (flags) { break; } - } else { - relative_offset = 0; } } } @@ -660,17 +589,12 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { if (flags.value4 & 1 || subgraph->context_id() == _context_id) { if (flags.is_null() || (flags.value1 & subgraph->_flags.value1)) { - for (data::ptr page = subgraph->first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); + for (auto page : subgraph->pages()) { + for (auto attribute : AttributeIDList1(page)) { if (attribute.is_nil()) { break; // TODO: check if this should break out of entire loop } - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - if (!flags.is_null()) { if (attribute.to_node().flags().subgraph_flags() == 0) { break; @@ -682,12 +606,9 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { body(attribute); } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); if (!flags.is_null()) { break; } - } else { - relative_offset = 0; } } } @@ -1133,23 +1054,11 @@ void Subgraph::encode(Encoder &encoder) const { encoder.encode_varint(1); } - for (data::ptr page = first_page(); page != nullptr; page = page->next) { + for (auto page : pages()) { for (uint32_t iteration = 0; iteration < 2; ++iteration) { - uint16_t relative_offset = iteration == 0 ? page->relative_offset_1 : page->relative_offset_2; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else if (attribute.is_nil() || relative_offset == 0) { - break; - } else { - relative_offset = 0; - continue; - } - + AttributeIDList list = + iteration == 0 ? (AttributeIDList)AttributeIDList1(page) : (AttributeIDList)AttributeIDList2(page); + for (auto attribute : list) { encoder.encode_varint(0x32); encoder.begin_length_delimited(); @@ -1199,29 +1108,16 @@ void Subgraph::print(uint32_t indent_level) { fprintf(stdout, "%s+ %p: %u in %lu [", indent_string, this, info().zone_id(), (unsigned long)_context_id); bool first = true; - for (data::ptr page = first_page(); page != nullptr; page = page->next) { - uint16_t relative_offset = page->relative_offset_1; - while (relative_offset) { - AttributeID attribute = AttributeID(page + relative_offset); - if (attribute.is_nil()) { - break; + for (auto page : pages()) { + for (auto attribute : AttributeIDList1(page)) { + if (!attribute.is_direct()) { + continue; } - - if (attribute.is_direct()) { - relative_offset = attribute.to_node().flags().relative_offset(); - } else if (attribute.is_indirect()) { - relative_offset = attribute.to_indirect_node().relative_offset(); - } else { - relative_offset = 0; - } - - if (attribute.is_direct()) { - fprintf(stdout, "%s%u", first ? "" : " ", attribute.value()); - if (attribute.to_node().flags().subgraph_flags()) { - fprintf(stdout, "(%u)", attribute.to_node().flags().subgraph_flags()); - } - first = false; + fprintf(stdout, "%s%u", first ? "" : " ", attribute.value()); + if (attribute.to_node().flags().subgraph_flags()) { + fprintf(stdout, "(%u)", attribute.to_node().flags().subgraph_flags()); } + first = false; } } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index cdaf0fe..2a2ca84 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -147,7 +147,7 @@ class Subgraph : public data::zone { void add_node(data::ptr node); void add_indirect(data::ptr node, bool flag); - void insert_attribute(AttributeID attribute, bool dirty); + void insert_attribute(AttributeID attribute, bool after_flagged_nodes); void unlink_attribute(AttributeID attribute); From 94872d9f58eec2ec2a270323f591f529f627f5d9 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 10 Apr 2025 19:05:24 +0200 Subject: [PATCH 56/74] Move Node::relative_offset out of flags struct --- Sources/ComputeCxx/Attribute/AttributeIDList.h | 2 +- Sources/ComputeCxx/Attribute/Node/Node.h | 12 +++++------- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 12 ++++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/AttributeIDList.h b/Sources/ComputeCxx/Attribute/AttributeIDList.h index d633f2c..73a3a03 100644 --- a/Sources/ComputeCxx/Attribute/AttributeIDList.h +++ b/Sources/ComputeCxx/Attribute/AttributeIDList.h @@ -27,7 +27,7 @@ class AttributeIDIterator { AttributeID attribute_id = AttributeID(_page + _offset); if (attribute_id.is_direct()) { - _offset = attribute_id.to_node().flags().relative_offset(); + _offset = attribute_id.to_node().relative_offset(); } else if (attribute_id.is_indirect()) { _offset = attribute_id.to_indirect_node().relative_offset(); } else { diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 2877910..697e4e1 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -18,9 +18,7 @@ class Graph; class NodeFlags { public: - enum SubgraphFlags : uint8_t { - None = 0 - }; + enum SubgraphFlags : uint8_t { None = 0 }; enum Flags4 : uint8_t { HasIndirectSelf = 1 << 0, // 0x01 HasIndirectValue = 1 << 1, // 0x02 @@ -34,16 +32,12 @@ class NodeFlags { }; private: - uint16_t _relative_offset; SubgraphFlags _subgraph_flags; uint8_t _value4; public: NodeFlags(uint8_t value4 = 0) : _value4(value4) {}; - uint16_t relative_offset() const { return _relative_offset; }; - void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; - // Flags 3 SubgraphFlags subgraph_flags() const { return _subgraph_flags; }; void set_subgraph_flags(SubgraphFlags subgraph_flags) { _subgraph_flags = subgraph_flags; }; @@ -141,6 +135,7 @@ class Node { static_assert(sizeof(Info) == 4); Info _info; + uint16_t _relative_offset; NodeFlags _flags; data::ptr _value; @@ -164,6 +159,9 @@ class Node { (state().is_main_thread() ? 1 : 0) << 4 | (flags().value4_unknown0x20() ? 1 : 0) << 5 | (state().is_main_thread_only() ? 1 : 0) << 6 | (flags().self_modified() ? 1 : 0) << 7; }; + + uint16_t relative_offset() const { return _relative_offset; }; + void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; NodeFlags &flags() { return _flags; }; const NodeFlags &flags() const { return _flags; }; diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 8e68928..c93c494 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -390,8 +390,8 @@ void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) uint16_t inserted_offset = (attribute.without_kind() - attribute.page_ptr()) | attribute.kind(); uint16_t next_offset; if (before_attribute.is_direct()) { - next_offset = before_attribute.to_node().flags().relative_offset(); - before_attribute.to_node().flags().set_relative_offset(inserted_offset); + next_offset = before_attribute.to_node().relative_offset(); + before_attribute.to_node().set_relative_offset(inserted_offset); } else if (before_attribute.is_indirect()) { next_offset = before_attribute.to_indirect_node().relative_offset(); before_attribute.to_indirect_node().set_relative_offset(inserted_offset); @@ -406,7 +406,7 @@ void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) } if (attribute.is_direct()) { - attribute.to_node().flags().set_relative_offset(next_offset); + attribute.to_node().set_relative_offset(next_offset); } else if (attribute.is_indirect()) { attribute.to_indirect_node().set_relative_offset(next_offset); } @@ -428,15 +428,15 @@ void Subgraph::unlink_attribute(AttributeID attribute) { uint16_t old_value = 0; if (attribute.is_direct()) { - old_value = attribute.to_node().flags().relative_offset(); - attribute.to_node().flags().set_relative_offset(0); + old_value = attribute.to_node().relative_offset(); + attribute.to_node().set_relative_offset(0); } else { old_value = attribute.to_indirect_node().relative_offset(); attribute.to_indirect_node().set_relative_offset(0); } if (previous_attribute.is_direct()) { - previous_attribute.to_node().flags().set_relative_offset(old_value); + previous_attribute.to_node().set_relative_offset(old_value); } else if (previous_attribute.is_indirect()) { previous_attribute.to_indirect_node().set_relative_offset(old_value); } else { From bb78ddb0ad37a2621f34b414f0c9e16439f1200e Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 10 Apr 2025 19:59:04 +0200 Subject: [PATCH 57/74] Flatten fields on Node --- Sources/ComputeCxx/Attribute/Node/Node.cpp | 10 +- Sources/ComputeCxx/Attribute/Node/Node.h | 160 +++++++++--------- Sources/ComputeCxx/Graph/AGGraph.cpp | 5 +- Sources/ComputeCxx/Graph/Graph+Description.mm | 2 +- Sources/ComputeCxx/Graph/Graph.cpp | 33 ++-- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 33 ++-- Sources/ComputeCxx/Subgraph/Subgraph.h | 6 +- 7 files changed, 125 insertions(+), 124 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/Node/Node.cpp b/Sources/ComputeCxx/Attribute/Node/Node.cpp index a3ae575..0fe4a29 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.cpp +++ b/Sources/ComputeCxx/Attribute/Node/Node.cpp @@ -17,7 +17,7 @@ void *Node::get_self(const AttributeType &type) const { } void Node::update_self(const Graph &graph, void *new_self) { - auto type = graph.attribute_type(_info.type_id); + auto type = graph.attribute_type(_type_id); void *self = get_self(type); if (!state().is_self_initialized()) { @@ -36,7 +36,7 @@ void Node::destroy_self(const Graph &graph) { } set_state(state().with_self_initialized(false)); - auto type = graph.attribute_type(_info.type_id); + auto type = graph.attribute_type(_type_id); void *self = get_self(type); type.vt_destroy_self(self); @@ -56,7 +56,7 @@ void Node::allocate_value(Graph &graph, data::zone &zone) { return; } - auto type = graph.attribute_type(_info.type_id); + auto type = graph.attribute_type(_type_id); size_t size = type.value_metadata().vw_size(); size_t alignment = type.value_metadata().getValueWitnesses()->getAlignmentMask(); @@ -81,14 +81,14 @@ void Node::destroy_value(Graph &graph) { } set_state(state().with_value_initialized(false)); - auto type = graph.attribute_type(_info.type_id); + auto type = graph.attribute_type(_type_id); void *value = get_value(); type.value_metadata().vw_destroy(static_cast(value)); } void Node::destroy(Graph &graph) { - auto type = graph.attribute_type(_info.type_id); + auto type = graph.attribute_type(_type_id); if (state().is_value_initialized()) { void *value = get_value(); diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index 697e4e1..e974504 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -16,66 +16,23 @@ class zone; class AttributeType; class Graph; -class NodeFlags { - public: - enum SubgraphFlags : uint8_t { None = 0 }; - enum Flags4 : uint8_t { - HasIndirectSelf = 1 << 0, // 0x01 - HasIndirectValue = 1 << 1, // 0x02 - - InputsTraverseContexts = 1 << 2, // 0x04 - InputsUnsorted = 1 << 3, // 0x08 - Cacheable = 1 << 4, // 0x10 - - Unknown0x20 = 1 << 5, // 0x20 - initial value - SelfModified = 1 << 6, // 0x40 - }; - +// AGAttributeFlags +class AttributeFlags { private: - SubgraphFlags _subgraph_flags; - uint8_t _value4; + uint8_t _data; public: - NodeFlags(uint8_t value4 = 0) : _value4(value4) {}; + AttributeFlags() : _data(0) {} + AttributeFlags(uint8_t data) : _data(data) {} + uint8_t data() const { return _data; } - // Flags 3 - SubgraphFlags subgraph_flags() const { return _subgraph_flags; }; - void set_subgraph_flags(SubgraphFlags subgraph_flags) { _subgraph_flags = subgraph_flags; }; - - // Flags 4 - bool has_indirect_self() const { return _value4 & Flags4::HasIndirectSelf; } - void set_has_indirect_self(bool value) { - _value4 = (_value4 & ~Flags4::HasIndirectSelf) | (value ? Flags4::HasIndirectSelf : 0); - }; - bool has_indirect_value() const { return _value4 & Flags4::HasIndirectValue; } - void set_has_indirect_value(bool value) { - _value4 = (_value4 & ~Flags4::HasIndirectValue) | (value ? Flags4::HasIndirectValue : 0); - }; - - bool inputs_traverse_contexts() { return _value4 & Flags4::InputsTraverseContexts; }; - void set_inputs_traverse_contexts(bool value) { - _value4 = (_value4 & ~Flags4::InputsTraverseContexts) | (value ? Flags4::InputsTraverseContexts : 0); - }; - bool inputs_unsorted() { return _value4 & Flags4::InputsUnsorted; }; - void set_inputs_unsorted(bool value) { - _value4 = (_value4 & ~Flags4::InputsUnsorted) | (value ? Flags4::InputsUnsorted : 0); - }; - bool cacheable() const { return _value4 & Flags4::Cacheable; }; - void set_cacheable(bool value) { _value4 = (_value4 & ~Flags4::Cacheable) | (value ? Flags4::Cacheable : 0); }; - bool value4_unknown0x20() const { return _value4 & Flags4::Unknown0x20; }; - void set_value4_unknown0x20(bool value) { - _value4 = (_value4 & ~Flags4::Unknown0x20) | (value ? Flags4::Unknown0x20 : 0); - }; - bool self_modified() const { return _value4 & Flags4::SelfModified; }; - void set_self_modified(bool value) { - _value4 = (_value4 & ~Flags4::SelfModified) | (value ? Flags4::SelfModified : 0); - }; + operator bool() const { return _data == 0; }; }; class Node { public: class State { - public: + private: enum : uint8_t { Dirty = 1 << 0, // 0x01 // Unknown0 = 1 << 0, Pending = 1 << 1, // 0x02 // Unknown1 = 1 << 1, @@ -88,83 +45,124 @@ class Node { UpdatingCyclic = 1 << 7, // 0x80 }; - - private: uint8_t _data; public: explicit constexpr State(uint8_t data = 0) : _data(data) {}; - uint8_t data() { return _data; }; + uint8_t data() const { return _data; }; - bool is_dirty() { return _data & Dirty; } + bool is_dirty() const { return _data & Dirty; } State with_dirty(bool value) const { return State((_data & ~Dirty) | (value ? Dirty : 0)); }; - bool is_pending() { return _data & Pending; } + bool is_pending() const { return _data & Pending; } State with_pending(bool value) const { return State((_data & ~Pending) | (value ? Pending : 0)); }; - bool is_main_thread() { return _data & MainThread; } + bool is_main_thread() const { return _data & MainThread; } State with_main_thread(bool value) const { return State((_data & ~MainThread) | (value ? MainThread : 0)); }; - bool is_main_thread_only() { return _data & MainThreadOnly; } + bool is_main_thread_only() const { return _data & MainThreadOnly; } State with_main_thread_only(bool value) const { return State((_data & ~MainThreadOnly) | (value ? MainThreadOnly : 0)); }; - bool is_value_initialized() { return _data & ValueInitialized; }; + bool is_value_initialized() const { return _data & ValueInitialized; }; State with_value_initialized(bool value) const { return State((_data & ~ValueInitialized) | (value ? ValueInitialized : 0)); }; - bool is_self_initialized() { return _data & SelfInitialized; }; + bool is_self_initialized() const { return _data & SelfInitialized; }; State with_self_initialized(bool value) const { return State((_data & ~SelfInitialized) | (value ? SelfInitialized : 0)); }; State with_updating(bool value) const { return State((_data & ~Updating) | (value ? Updating : 0)); }; - bool is_updating() { return _data & (Updating | UpdatingCyclic); } - bool is_updating_cyclic() { return (_data & (Updating | UpdatingCyclic)) == (Updating | UpdatingCyclic); } + bool is_updating() const { return _data & (Updating | UpdatingCyclic); } + bool is_updating_cyclic() const { return (_data & (Updating | UpdatingCyclic)) == (Updating | UpdatingCyclic); } uint8_t update_count() const { return _data >> 6; } }; - private: - struct Info { - unsigned int state : 8; - unsigned int type_id : 24; + class Flags { + private: + enum : uint8_t { + HasIndirectSelf = 1 << 0, // 0x01 + HasIndirectValue = 1 << 1, // 0x02 + + InputsTraverseContexts = 1 << 2, // 0x04 + InputsUnsorted = 1 << 3, // 0x08 + Cacheable = 1 << 4, // 0x10 + + Unknown0x20 = 1 << 5, // 0x20 - initial value // see Graph::update_main_refs + SelfModified = 1 << 6, // 0x40 + }; + uint8_t _data; + + public: + explicit constexpr Flags(uint8_t data = 0) : _data(data) {}; + uint8_t data() const { return _data; }; + + bool has_indirect_self() const { return _data & HasIndirectSelf; } + void set_has_indirect_self(bool value) { _data = (_data & ~HasIndirectSelf) | (value ? HasIndirectSelf : 0); }; + + bool has_indirect_value() const { return _data & HasIndirectValue; } + void set_has_indirect_value(bool value) { + _data = (_data & ~HasIndirectValue) | (value ? HasIndirectValue : 0); + }; + + bool inputs_traverse_contexts() const { return _data & InputsTraverseContexts; }; + void set_inputs_traverse_contexts(bool value) { + _data = (_data & ~InputsTraverseContexts) | (value ? InputsTraverseContexts : 0); + }; + + bool inputs_unsorted() const { return _data & InputsUnsorted; }; + void set_inputs_unsorted(bool value) { _data = (_data & ~InputsUnsorted) | (value ? InputsUnsorted : 0); }; + + bool cacheable() const { return _data & Cacheable; }; + void set_cacheable(bool value) { _data = (_data & ~Cacheable) | (value ? Cacheable : 0); }; + + bool unknown0x20() const { return _data & Unknown0x20; }; + void set_unknown0x20(bool value) { _data = (_data & ~Unknown0x20) | (value ? Unknown0x20 : 0); }; + + bool self_modified() const { return _data & SelfModified; }; + void set_self_modified(bool value) { _data = (_data & ~SelfModified) | (value ? SelfModified : 0); }; }; - static_assert(sizeof(Info) == 4); - Info _info; + private: + State _state; + unsigned int _type_id : 24; uint16_t _relative_offset; - NodeFlags _flags; + AttributeFlags _subgraph_flags; + Flags _flags; data::ptr _value; data::vector _inputs; data::vector _outputs; public: - Node(State state, uint32_t type_id, uint8_t flags4) { - _info.state = state.data(); - _info.type_id = type_id; - _flags = NodeFlags(flags4); - }; + Node(State state, uint32_t type_id, Flags flags) : _state(state), _type_id(type_id), _flags(flags) {}; + + const State &state() const { return _state; }; + void set_state(State state) { _state = state; }; - State state() const { return State(_info.state); }; - void set_state(State state) { _info.state = state.data(); }; - uint32_t type_id() const { return uint32_t(_info.type_id); }; + uint32_t type_id() const { return _type_id; }; uint8_t value_state() const { return (state().is_dirty() ? 1 : 0) << 0 | (state().is_pending() ? 1 : 0) << 1 | (state().is_updating() ? 1 : 0) << 2 | (state().is_value_initialized() ? 1 : 0) << 3 | - (state().is_main_thread() ? 1 : 0) << 4 | (flags().value4_unknown0x20() ? 1 : 0) << 5 | + (state().is_main_thread() ? 1 : 0) << 4 | (flags().unknown0x20() ? 1 : 0) << 5 | (state().is_main_thread_only() ? 1 : 0) << 6 | (flags().self_modified() ? 1 : 0) << 7; }; - + uint16_t relative_offset() const { return _relative_offset; }; void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; - NodeFlags &flags() { return _flags; }; - const NodeFlags &flags() const { return _flags; }; + AttributeFlags &subgraph_flags() { return _subgraph_flags; }; + const AttributeFlags &subgraph_flags() const { return _subgraph_flags; }; + void set_subgraph_flags(AttributeFlags subgraph_flags) { _subgraph_flags = subgraph_flags; }; + + Flags &flags() { return _flags; }; + const Flags &flags() const { return _flags; }; + void set_flags(Flags flags) { _flags = flags; }; void sort_inputs_if_needed() { if (_flags.inputs_unsorted()) { diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index d5a4748..09e1ff6 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -224,7 +224,8 @@ AGAttributeFlags AGGraphGetFlags(AGAttribute attribute) { if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - return attribute_id.to_node().flags().subgraph_flags(); + auto flags = attribute_id.to_node().subgraph_flags(); + return AGAttributeFlags(flags.data()); } void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags) { @@ -232,7 +233,7 @@ void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags) { if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.subgraph()->set_flags(attribute_id.to_node_ptr(), AG::NodeFlags::SubgraphFlags(flags)); + attribute_id.subgraph()->set_flags(attribute_id.to_node_ptr(), flags); } void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidating, diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 312dbb8..e29a70c 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -519,7 +519,7 @@ int trap_cycles() { break; } - uint8_t subgraph_flags = attribute.to_node().flags().subgraph_flags(); + AttributeFlags subgraph_flags = attribute.to_node().subgraph_flags(); auto found_node_index = node_indices_by_id.find(attribute.to_node_ptr()); if (found_node_index != node_indices_by_id.end()) { [nodes addObject:[NSNumber numberWithUnsignedLong:found_node_index->second]]; diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index bf5151b..5d6500c 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -487,7 +487,8 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, const node = (data::ptr)subgraph.alloc_bytes(uint32_t(total_size), uint32_t(alignment_mask | 3)); } bool main_thread = type.main_thread(); - *node = Node(Node::State().with_main_thread(main_thread).with_main_thread_only(main_thread), type_id, 0x20); + *node = Node(Node::State().with_main_thread(main_thread).with_main_thread_only(main_thread), type_id, + Node::Flags(0x20)); if (type_id >= 0x100000) { precondition_failure("too many node types allocated"); @@ -497,9 +498,9 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, const node->set_state(node->state().with_self_initialized(true)); if (node->state().is_main_thread_only() && !type.value_metadata().getValueWitnesses()->isPOD()) { - node->flags().set_value4_unknown0x20(!type.unknown_0x20()); // toggle + node->flags().set_unknown0x20(!type.unknown_0x20()); // toggle } else { - node->flags().set_value4_unknown0x20(false); + node->flags().set_unknown0x20(false); } if (buffer != nullptr) { node->flags().set_has_indirect_self(true); @@ -525,7 +526,7 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, const value_set_internal(node, *node.get(), value_source, type.value_metadata()); } else { node->set_state(node->state().with_dirty(true).with_pending(true)); - subgraph.add_dirty_flags(node->flags().subgraph_flags()); + subgraph.add_dirty_flags(node->subgraph_flags()); } subgraph.add_node(node); @@ -606,14 +607,14 @@ void Graph::update_main_refs(AttributeID attribute) { std::any_of(node.inputs().begin(), node.inputs().end(), [](auto input_edge) -> bool { auto resolved = input_edge.value.resolve(TraversalOptions::EvaluateWeakReferences); if (resolved.attribute().is_direct() && - resolved.attribute().to_node().flags().value4_unknown0x20()) { + resolved.attribute().to_node().flags().unknown0x20()) { return true; } }); } } - if (node.flags().value4_unknown0x20() != new_unknown0x20) { - node.flags().set_value4_unknown0x20(new_unknown0x20); + if (node.flags().unknown0x20() != new_unknown0x20) { + node.flags().set_unknown0x20(new_unknown0x20); output_edge_arrays.push_back({ &node.outputs().front(), node.outputs().size(), @@ -1133,9 +1134,9 @@ void Graph::value_mark(data::ptr node) { foreach_trace([&node](Trace &trace) { trace.set_pending(node, true); }); node->set_state(node->state().with_pending(true)); } - if (node->flags().subgraph_flags()) { + if (node->subgraph_flags()) { Subgraph *subgraph = AttributeID(node).subgraph(); - subgraph->add_dirty_flags(node->flags().subgraph_flags()); + subgraph->add_dirty_flags(node->subgraph_flags()); } } @@ -1159,7 +1160,7 @@ void Graph::value_mark_all() { AttributeType &type = *_types[node.type_id()]; if (!type.use_graph_as_initial_value()) { node.set_state(node.state().with_dirty(true).with_pending(true)); - subgraph->add_dirty_flags(node.flags().subgraph_flags()); + subgraph->add_dirty_flags(node.subgraph_flags()); } for (auto input_edge : node.inputs()) { input_edge.set_changed(true); @@ -1226,7 +1227,7 @@ void Graph::propagate_dirty(AttributeID attribute) { output_node.set_state(output_node.state().with_dirty(true)); if (auto subgraph = output.subgraph()) { - subgraph->add_dirty_flags(output_node.flags().subgraph_flags()); + subgraph->add_dirty_flags(output_node.subgraph_flags()); } dirty_outputs = output_node.outputs(); @@ -1882,7 +1883,7 @@ void Graph::mark_pending(data::ptr node_ptr, Node *node) { foreach_trace([&node_ptr](Trace &trace) { trace.set_dirty(node_ptr, true); }); node->set_state(node->state().with_dirty(true)); - uint8_t subgraph_flags = node->flags().subgraph_flags(); + AttributeFlags subgraph_flags = node->subgraph_flags(); Subgraph *subgraph = AttributeID(node_ptr).subgraph(); if (subgraph_flags && subgraph != nullptr) { subgraph->add_dirty_flags(subgraph_flags); @@ -2027,9 +2028,9 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { encoder.encode_varint(0x38); encoder.encode_varint(true); } - if (node.flags().subgraph_flags()) { + if (node.subgraph_flags()) { encoder.encode_varint(0x40); - encoder.encode_varint(node.flags().subgraph_flags()); + encoder.encode_varint(node.subgraph_flags()); } if (node.state().is_main_thread()) { encoder.encode_varint(0x48); @@ -2039,9 +2040,9 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool flag) { encoder.encode_varint(0x50); encoder.encode_varint(true); } - if (node.flags().value4_unknown0x20()) { + if (node.flags().unknown0x20()) { encoder.encode_varint(0x58); - encoder.encode_varint(node.flags().value4_unknown0x20()); + encoder.encode_varint(node.flags().unknown0x20()); } if (node.state().is_value_initialized()) { encoder.encode_varint(0x60); diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index c93c494..9a9c752 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -353,7 +353,7 @@ void Subgraph::foreach_ancestor(Callable body) { #pragma mark - Attributes void Subgraph::add_node(data::ptr node) { - node->flags().set_subgraph_flags(NodeFlags::SubgraphFlags::None); + node->set_subgraph_flags(0); // TODO: check value insert_attribute(AttributeID(node), true); if (_tree_root) { @@ -377,9 +377,9 @@ void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) AttributeID before_attribute = AttributeID::make_nil(); if (after_flagged_nodes) { - if (!attribute.is_direct() || attribute.to_node().flags().subgraph_flags() == 0) { + if (!attribute.is_direct() || attribute.to_node().subgraph_flags() == 0) { for (auto candidate_attribute : AttributeIDList1(attribute.page_ptr())) { - if (!candidate_attribute.is_direct() || candidate_attribute.to_node().flags().subgraph_flags() == 0) { + if (!candidate_attribute.is_direct() || candidate_attribute.to_node().subgraph_flags() == 0) { break; } before_attribute = candidate_attribute; @@ -503,10 +503,10 @@ void Subgraph::update(uint8_t flags) { if (attribute.is_direct()) { auto node = attribute.to_node(); if (flags) { - if (node.flags().subgraph_flags() == 0) { + if (node.subgraph_flags() == 0) { break; } - if ((node.flags().subgraph_flags() & flags) == 0) { + if ((node.subgraph_flags() & flags) == 0) { continue; } } @@ -596,10 +596,10 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { } if (attribute.is_direct()) { if (!flags.is_null()) { - if (attribute.to_node().flags().subgraph_flags() == 0) { + if (attribute.to_node().subgraph_flags() == 0) { break; } - if (flags.value3 & (attribute.to_node().flags().subgraph_flags() == 0)) { + if (flags.value3 & (attribute.to_node().subgraph_flags() == 0)) { continue; } } @@ -766,16 +766,17 @@ data::ptr Subgraph::tree_subgraph_child(data::ptr node, NodeFlags::SubgraphFlags flags) { - if (node->flags().subgraph_flags() == flags) { +void Subgraph::set_flags(data::ptr node, AttributeFlags flags) { + if (node->subgraph_flags() == flags) { return; } - if (node->flags().subgraph_flags() == 0 || flags == 0) { + if (node->subgraph_flags() == 0 || flags == 0) { + // potentially reorder unlink_attribute(node); - node->flags().set_subgraph_flags(flags); + node->set_subgraph_flags(flags); insert_attribute(node, true); } else { - node->flags().set_subgraph_flags(flags); + node->set_subgraph_flags(flags); } add_flags(flags); @@ -784,7 +785,7 @@ void Subgraph::set_flags(data::ptr node, NodeFlags::SubgraphFlags flags) { } } -void Subgraph::add_flags(uint8_t flags) { +void Subgraph::add_flags(AttributeFlags flags) { // Status: doesn't exist in decompile if (flags & ~_flags.value1) { _flags.value1 |= flags; @@ -792,7 +793,7 @@ void Subgraph::add_flags(uint8_t flags) { } } -void Subgraph::add_dirty_flags(uint8_t dirty_flags) { +void Subgraph::add_dirty_flags(AttributeFlags dirty_flags) { // Status: Verified if (dirty_flags & ~_flags.value3) { _flags.value3 |= dirty_flags; @@ -1114,8 +1115,8 @@ void Subgraph::print(uint32_t indent_level) { continue; } fprintf(stdout, "%s%u", first ? "" : " ", attribute.value()); - if (attribute.to_node().flags().subgraph_flags()) { - fprintf(stdout, "(%u)", attribute.to_node().flags().subgraph_flags()); + if (attribute.to_node().subgraph_flags()) { + fprintf(stdout, "(%u)", attribute.to_node().subgraph_flags().data()); } first = false; } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 2a2ca84..45e1789 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -179,10 +179,10 @@ class Subgraph : public data::zone { // flags 1 and 3 are the values themselvs // flags 3 and 4 are the dirty subset of 1 and 2 - void set_flags(data::ptr node, NodeFlags::SubgraphFlags flags3); + void set_flags(data::ptr node, AttributeFlags flags); - void add_flags(uint8_t flags); - void add_dirty_flags(uint8_t dirty_flags); + void add_flags(AttributeFlags flags); + void add_dirty_flags(AttributeFlags dirty_flags); void propagate_flags(); void propagate_dirty_flags(); From 9c18e938a1871fac7a794167b7098470496c707c Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 11 Apr 2025 12:27:33 +0200 Subject: [PATCH 58/74] Add RelativeAttributeID --- .../Attribute/Weak/AnyWeakAttribute.swift | 17 +- .../Attribute/Weak/WeakAttribute.swift | 2 +- Sources/ComputeCxx/Attribute/AGAttribute.cpp | 2 +- .../ComputeCxx/Attribute/AGWeakAttribute.cpp | 14 +- .../ComputeCxx/Attribute/AGWeakAttribute.h | 5 +- Sources/ComputeCxx/Attribute/AttributeID.cpp | 9 +- Sources/ComputeCxx/Attribute/AttributeID.h | 96 +++++++--- .../ComputeCxx/Attribute/AttributeIDList.h | 34 ++-- .../ComputeCxx/Attribute/Node/IndirectNode.h | 11 +- Sources/ComputeCxx/Attribute/Node/Node.h | 6 +- .../ComputeCxx/Attribute/WeakAttributeID.cpp | 4 +- .../ComputeCxx/Attribute/WeakAttributeID.h | 14 +- Sources/ComputeCxx/Data/Pointer.h | 1 - Sources/ComputeCxx/Data/Zone.cpp | 4 +- Sources/ComputeCxx/Graph/AGGraph.cpp | 170 +++++++++--------- Sources/ComputeCxx/Graph/Graph+Description.mm | 42 ++--- Sources/ComputeCxx/Graph/Graph.cpp | 133 +++++++------- Sources/ComputeCxx/Graph/Graph.h | 6 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 6 +- .../ComputeCxx/Graph/Tree/AGTreeElement.cpp | 2 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 6 +- Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 12 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 43 ++--- 23 files changed, 350 insertions(+), 289 deletions(-) diff --git a/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift b/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift index bc89938..144aefa 100644 --- a/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift +++ b/Sources/Compute/Attribute/Weak/AnyWeakAttribute.swift @@ -34,6 +34,19 @@ extension AnyWeakAttribute: @retroactive CustomStringConvertible { } -extension AnyWeakAttribute: @retroactive Equatable {} +extension AnyWeakAttribute: @retroactive Equatable { + + public static func ==(lhs: AnyWeakAttribute, rhs: AnyWeakAttribute) -> Bool { + return lhs.attribute == rhs.attribute && lhs.subgraph_id == rhs.subgraph_id + } + +} -extension AnyWeakAttribute: @retroactive Hashable {} +extension AnyWeakAttribute: @retroactive Hashable { + + public func hash(into hasher: inout Hasher) { + hasher.combine(attribute) + hasher.combine(subgraph_id) + } + +} diff --git a/Sources/Compute/Attribute/Weak/WeakAttribute.swift b/Sources/Compute/Attribute/Weak/WeakAttribute.swift index 566fa3c..8a111fc 100644 --- a/Sources/Compute/Attribute/Weak/WeakAttribute.swift +++ b/Sources/Compute/Attribute/Weak/WeakAttribute.swift @@ -9,7 +9,7 @@ public struct WeakAttribute { } public init() { - base = AnyWeakAttribute(rawValue: 0) // TODO: memberwise init + base = AnyWeakAttribute(attribute: AnyAttribute(rawValue: 0), subgraph_id: 0) } public init(_ attribute: Attribute) { diff --git a/Sources/ComputeCxx/Attribute/AGAttribute.cpp b/Sources/ComputeCxx/Attribute/AGAttribute.cpp index a477087..983409d 100644 --- a/Sources/ComputeCxx/Attribute/AGAttribute.cpp +++ b/Sources/ComputeCxx/Attribute/AGAttribute.cpp @@ -2,4 +2,4 @@ #include "AttributeID.h" -const AGAttribute AGAttributeNil = AGAttribute(AG::AttributeID::Kind::NilAttribute); +const AGAttribute AGAttributeNil = AGAttribute(AG::AttributeIDNil.to_storage()); diff --git a/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp b/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp index 605b1fd..61b5cd2 100644 --- a/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp +++ b/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp @@ -5,16 +5,16 @@ #include "Data/Zone.h" AGWeakAttribute AGCreateWeakAttribute(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); - if (attribute_id.without_kind() == 0) { - return AG::WeakAttributeID(attribute_id, 0).to_opaque_value(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + if (!attribute_id.has_value()) { + return AG::WeakAttributeID(attribute_id, 0).to_cf(); } - auto zone_id = attribute_id.to_node_ptr().page_ptr()->zone->info().zone_id(); - return AG::WeakAttributeID(attribute_id, zone_id).to_opaque_value(); + auto zone_id = attribute_id.page().zone->info().zone_id(); + return AG::WeakAttributeID(attribute_id, zone_id).to_cf(); } AGAttribute AGWeakAttributeGetAttribute(AGWeakAttribute attribute) { - auto attribute_id = AG::WeakAttributeID(attribute); - return attribute_id.evaluate(); + auto attribute_id = AG::WeakAttributeID::from_cf(attribute); + return attribute_id.evaluate().to_storage(); } diff --git a/Sources/ComputeCxx/Attribute/AGWeakAttribute.h b/Sources/ComputeCxx/Attribute/AGWeakAttribute.h index 388b5ee..fddde8f 100644 --- a/Sources/ComputeCxx/Attribute/AGWeakAttribute.h +++ b/Sources/ComputeCxx/Attribute/AGWeakAttribute.h @@ -10,7 +10,10 @@ CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN -typedef uint64_t AGWeakAttribute AG_SWIFT_STRUCT AG_SWIFT_NAME(AnyWeakAttribute); +typedef struct AGWeakAttribute { + AGAttribute attribute; + uint32_t subgraph_id; +} AG_SWIFT_NAME(AnyWeakAttribute) AGWeakAttribute; CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Attribute/AttributeID.cpp b/Sources/ComputeCxx/Attribute/AttributeID.cpp index 2e98fe9..e6ed1bd 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.cpp +++ b/Sources/ComputeCxx/Attribute/AttributeID.cpp @@ -11,7 +11,11 @@ namespace AG { -AttributeID AttributeIDNil = AttributeID::make_nil(); +AttributeID AttributeIDNil = AttributeID::from_storage(2); + +RelativeAttributeID AttributeID::to_relative() const { + return RelativeAttributeID(((_value & ~KindMask) - page_ptr()) | kind()); +} std::optional AttributeID::size() const { if (is_direct()) { @@ -58,7 +62,6 @@ OffsetAttributeID AttributeID::resolve_slow(TraversalOptions options) const { } auto indirect_node = to_indirect_node(); - if (indirect_node.is_mutable()) { if (options & TraversalOptions::SkipMutableReference) { return OffsetAttributeID(result, offset); @@ -80,7 +83,7 @@ OffsetAttributeID AttributeID::resolve_slow(TraversalOptions options) const { if (options & TraversalOptions::AssertNotNil) { precondition_failure("invalid indirect ref: %u", _value); } - return OffsetAttributeID(AttributeID::make_nil()); + return OffsetAttributeID(AttributeIDNil); } } diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index 059f978..c78f143 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -47,11 +47,15 @@ inline TraversalOptions operator|(TraversalOptions lhs, TraversalOptions rhs) { } class AttributeID { + friend RelativeAttributeID; + private: static constexpr uint32_t KindMask = 0x3; uint32_t _value; + explicit constexpr AttributeID(uint32_t value) : _value(value) {}; + public: enum Kind : uint32_t { Direct = 0, @@ -59,55 +63,79 @@ class AttributeID { NilAttribute = 1 << 1, }; - explicit AttributeID(uint32_t value) : _value(value){}; - AttributeID(data::ptr node) : _value(node | Kind::Direct){}; - AttributeID(data::ptr indirect_node) : _value(indirect_node | Kind::Indirect){}; - static AttributeID make_nil() { return AttributeID(Kind::NilAttribute); }; + explicit constexpr AttributeID() : _value(0) {}; + explicit AttributeID(data::ptr node) : _value(node | Kind::Direct) {}; + explicit AttributeID(data::ptr indirect_node) : _value(indirect_node | Kind::Indirect) {}; + explicit AttributeID(data::ptr indirect_node) : _value(indirect_node | Kind::Indirect) {}; + + constexpr uint32_t to_storage() const { return _value; } + static constexpr AttributeID from_storage(uint32_t value) { return AttributeID(value); } + + // + + // MARK: Operators + + bool operator==(const AttributeID &other) const { return _value == other._value; } + bool operator!=(const AttributeID &other) const { return _value != other._value; } + + bool operator<(const AttributeID &other) const { return _value < other._value; } + + operator bool() const { return _value != 0; } + + // MARK: Accessing zone data - operator bool() const { return _value == 0; }; + // data::ptr as_ptr() const { return data::ptr(_value); }; - uint32_t value() const { return _value; }; + // uint32_t value() const { return _value; } + // operator bool() const { return _value == 0; }; + + data::page &page() const { return *data::ptr(_value).page_ptr(); }; + data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; + + void validate_data_offset() const { data::ptr(_value).assert_valid(); }; + + // MARK: Relative + + RelativeAttributeID to_relative() const; + + // MARK: Accessing graph data Kind kind() const { return Kind(_value & KindMask); }; AttributeID with_kind(Kind kind) const { return AttributeID((_value & ~KindMask) | kind); }; - AttributeID without_kind() const { return AttributeID((_value & ~KindMask)); }; bool is_direct() const { return kind() == Kind::Direct; }; bool is_indirect() const { return kind() == Kind::Indirect; }; bool is_nil() const { return kind() == Kind::NilAttribute; }; // TODO: return true if whole thing is zero? - // TODO: make these data::ptr<> - Node &to_node() const { - assert(is_direct()); - return *data::ptr(_value & ~KindMask); - }; - data::ptr to_node_ptr() const { + bool has_value() const { return (_value & ~KindMask) != 0; } + + template data::ptr to_ptr() const { return data::ptr(_value & ~KindMask); } + template <> data::ptr to_ptr() const { assert(is_direct()); return data::ptr(_value & ~KindMask); - }; - - IndirectNode &to_indirect_node() const { - assert(is_indirect()); - return *data::ptr(_value & ~KindMask); - }; - data::ptr to_indirect_node_ptr() const { + } + template <> data::ptr to_ptr() const { assert(is_indirect()); return data::ptr(_value & ~KindMask); - }; - data::ptr to_mutable_indirect_node_ptr() const { - assert(is_indirect()); // XXX: no assertion that it is mutable + } + template <> data::ptr to_ptr() const { + assert(is_indirect()); return data::ptr(_value & ~KindMask); - }; + } - Subgraph *_Nullable subgraph() const { return reinterpret_cast(page_ptr()->zone); } + Node &to_node() const { return *to_ptr(); }; + IndirectNode &to_indirect_node() const { return *to_ptr(); }; - data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; + Subgraph *_Nullable subgraph() const { return reinterpret_cast(page().zone); } + + // MARK: Value metdata - // Value metadata std::optional size() const; - // Graph traversal + // MARK: Graph traversal + bool traverses(AttributeID other, TraversalOptions options) const; + OffsetAttributeID resolve(TraversalOptions options) const; OffsetAttributeID resolve_slow(TraversalOptions options) const; }; @@ -115,6 +143,18 @@ class AttributeID { class RelativeAttributeID { private: uint16_t _value; + + public: + constexpr RelativeAttributeID() : _value(0) {}; + constexpr RelativeAttributeID(nullptr_t) : _value(0) {}; + constexpr RelativeAttributeID(uint16_t value) : _value(value) {}; + + uint16_t value() const { return _value; } + + bool operator==(const RelativeAttributeID &other) const { return _value == other._value; } + bool operator!=(const RelativeAttributeID &other) const { return _value != other._value; } + + AttributeID resolve(data::ptr page_ptr) { return AttributeID(page_ptr + _value); } }; extern AttributeID AttributeIDNil; diff --git a/Sources/ComputeCxx/Attribute/AttributeIDList.h b/Sources/ComputeCxx/Attribute/AttributeIDList.h index 73a3a03..96e7dca 100644 --- a/Sources/ComputeCxx/Attribute/AttributeIDList.h +++ b/Sources/ComputeCxx/Attribute/AttributeIDList.h @@ -9,30 +9,34 @@ namespace AG { class AttributeIDIterator { private: data::ptr _page; - uint16_t _offset; + RelativeAttributeID _current; public: - AttributeIDIterator(data::ptr page, uint16_t offset) : _page(page), _offset(offset) {} + AttributeIDIterator(data::ptr page, RelativeAttributeID current) : _page(page), _current(current) {} - bool operator==(const AttributeIDIterator &other) const { return _page == other._page && _offset == other._offset; } - bool operator!=(const AttributeIDIterator &other) const { return _page != other._page || _offset != other._offset; } + bool operator==(const AttributeIDIterator &other) const { + return _page == other._page && _current == other._current; + } + bool operator!=(const AttributeIDIterator &other) const { + return _page != other._page || _current != other._current; + } AttributeID operator*() { assert(_page); - return AttributeID(_page + _offset); + return _current.resolve(_page); } AttributeIDIterator &operator++() { assert(_page); - AttributeID attribute_id = AttributeID(_page + _offset); + AttributeID attribute_id = _current.resolve(_page); if (attribute_id.is_direct()) { - _offset = attribute_id.to_node().relative_offset(); + _current = attribute_id.to_node().relative_offset(); } else if (attribute_id.is_indirect()) { - _offset = attribute_id.to_indirect_node().relative_offset(); + _current = attribute_id.to_indirect_node().relative_offset(); } else { _page = nullptr; - _offset = 0; + _current = nullptr; } return *this; } @@ -44,26 +48,26 @@ class AttributeIDList { virtual AttributeIDIterator end(); }; -class AttributeIDList1: public AttributeIDList { +class AttributeIDList1 : public AttributeIDList { private: data::ptr _page; public: AttributeIDList1(data::ptr page) : _page(page) {} - AttributeIDIterator begin() { return AttributeIDIterator(_page, _page->first_child_1); } - AttributeIDIterator end() { return AttributeIDIterator(nullptr, 0); } + AttributeIDIterator begin() { return AttributeIDIterator(_page, RelativeAttributeID(_page->first_child_1)); } + AttributeIDIterator end() { return AttributeIDIterator(nullptr, nullptr); } }; -class AttributeIDList2: public AttributeIDList { +class AttributeIDList2 : public AttributeIDList { private: data::ptr _page; public: AttributeIDList2(data::ptr page) : _page(page) {} - AttributeIDIterator begin() { return AttributeIDIterator(_page, _page->first_child_2); } - AttributeIDIterator end() { return AttributeIDIterator(nullptr, 0); } + AttributeIDIterator begin() { return AttributeIDIterator(_page, RelativeAttributeID(_page->first_child_2)); } + AttributeIDIterator end() { return AttributeIDIterator(nullptr, nullptr); } }; } // namespace AG diff --git a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h index 71a1411..99cd391 100644 --- a/Sources/ComputeCxx/Attribute/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/Node/IndirectNode.h @@ -30,7 +30,7 @@ class IndirectNode { WeakAttributeID _source; Info _info; uint16_t _size; - uint16_t _relative_offset; // could be relative offset, see Subgraph::insert_attribute + RelativeAttributeID _relative_offset; public: IndirectNode(WeakAttributeID source, bool traverses_contexts, uint32_t offset, uint16_t size) : _source(source) { @@ -38,7 +38,6 @@ class IndirectNode { _info.traverses_contexts = traverses_contexts; _info.offset = offset; _size = size; - _relative_offset = 0; } const WeakAttributeID &source() const { return _source; }; @@ -56,8 +55,8 @@ class IndirectNode { return _size != InvalidSize ? std::optional(size_t(_size)) : std::optional(); }; - uint16_t relative_offset() const { return _relative_offset; }; - void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; + RelativeAttributeID relative_offset() const { return _relative_offset; }; + void set_relative_offset(RelativeAttributeID relative_offset) { _relative_offset = relative_offset; }; void modify(WeakAttributeID source, uint32_t offset); }; @@ -74,8 +73,8 @@ class MutableIndirectNode : public IndirectNode { public: MutableIndirectNode(WeakAttributeID source, bool traverses_contexts, uint32_t offset, uint16_t size, WeakAttributeID initial_source, uint32_t initial_offset) - : IndirectNode(source, traverses_contexts, offset, size), _dependency(0), _initial_source(initial_source), - _initial_offset(initial_offset){ + : IndirectNode(source, traverses_contexts, offset, size), _dependency(), _initial_source(initial_source), + _initial_offset(initial_offset) { }; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index e974504..dedc95b 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -130,7 +130,7 @@ class Node { private: State _state; unsigned int _type_id : 24; - uint16_t _relative_offset; + RelativeAttributeID _relative_offset; AttributeFlags _subgraph_flags; Flags _flags; data::ptr _value; @@ -153,8 +153,8 @@ class Node { (state().is_main_thread_only() ? 1 : 0) << 6 | (flags().self_modified() ? 1 : 0) << 7; }; - uint16_t relative_offset() const { return _relative_offset; }; - void set_relative_offset(uint16_t relative_offset) { _relative_offset = relative_offset; }; + RelativeAttributeID relative_offset() const { return _relative_offset; }; + void set_relative_offset(RelativeAttributeID relative_offset) { _relative_offset = relative_offset; }; AttributeFlags &subgraph_flags() { return _subgraph_flags; }; const AttributeFlags &subgraph_flags() const { return _subgraph_flags; }; diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp b/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp index cc200a6..cf3ee4a 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.cpp @@ -10,7 +10,7 @@ bool WeakAttributeID::expired() const { uint64_t raw_page_seed = data::table::shared().raw_page_seed(_attribute.page_ptr()); if (raw_page_seed & 0xff00000000) { auto zone_info = data::zone::info::from_raw_value(uint32_t(raw_page_seed)); - if (zone_info.zone_id() == _zone_id) { + if (zone_info.zone_id() == _subgraph_id) { return false; } } @@ -18,7 +18,7 @@ bool WeakAttributeID::expired() const { } const AttributeID &WeakAttributeID::evaluate() const { - return _attribute.without_kind() != 0 && !expired() ? _attribute : AttributeIDNil; + return _attribute.has_value() && !expired() ? _attribute : AttributeIDNil; }; } // namespace AG diff --git a/Sources/ComputeCxx/Attribute/WeakAttributeID.h b/Sources/ComputeCxx/Attribute/WeakAttributeID.h index 8940750..a2e4cdd 100644 --- a/Sources/ComputeCxx/Attribute/WeakAttributeID.h +++ b/Sources/ComputeCxx/Attribute/WeakAttributeID.h @@ -13,18 +13,18 @@ namespace AG { class WeakAttributeID { private: AttributeID _attribute; - uint32_t _zone_id; + uint32_t _subgraph_id; public: - WeakAttributeID(AGWeakAttribute opaque_value) - : _attribute(opaque_value & 0xffffffff), _zone_id(opaque_value >> 0x20){}; - WeakAttributeID(AttributeID attribute, uint32_t zone_id) : _attribute(attribute), _zone_id(zone_id){}; + WeakAttributeID(AttributeID attribute, uint32_t subgraph_id) : _attribute(attribute), _subgraph_id(subgraph_id) {}; - // TODO: rename - uint64_t to_opaque_value() const { return _attribute.value() | ((uint64_t)_zone_id << 0x20); }; + AGWeakAttribute to_cf() const { return AGWeakAttribute(_attribute, _subgraph_id); }; + static WeakAttributeID from_cf(AGWeakAttribute data) { + return WeakAttributeID(AttributeID::from_storage(data.attribute), data.subgraph_id); + }; const AttributeID &attribute() const { return _attribute; }; - uint32_t zone_id() const { return _zone_id; }; + uint32_t subgraph_id() const { return _subgraph_id; }; bool expired() const; diff --git a/Sources/ComputeCxx/Data/Pointer.h b/Sources/ComputeCxx/Data/Pointer.h index b3d8c48..4ca4e69 100644 --- a/Sources/ComputeCxx/Data/Pointer.h +++ b/Sources/ComputeCxx/Data/Pointer.h @@ -45,7 +45,6 @@ template class ptr { ptr page_ptr() const noexcept { return ptr(_offset & ~page_alignment_mask); } difference_type offset() const noexcept { return _offset; } - difference_type offset_from_page() const noexcept { return _offset & page_alignment_mask; } template ptr aligned(difference_type alignment_mask = sizeof(difference_type) - 1) const { return ptr((_offset + alignment_mask) & ~alignment_mask); diff --git a/Sources/ComputeCxx/Data/Zone.cpp b/Sources/ComputeCxx/Data/Zone.cpp index b001e2f..d6762fe 100644 --- a/Sources/ComputeCxx/Data/Zone.cpp +++ b/Sources/ComputeCxx/Data/Zone.cpp @@ -27,8 +27,8 @@ void zone::clear() { void zone::realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask) { if (new_size > size && *buffer) { auto page = buffer->page_ptr(); - if ((page->in_use == buffer->offset_from_page() + size && - page->total >= buffer->offset_from_page() + new_size)) { + uint32_t buffer_offset_from_page = buffer->offset() - page; + if ((page->in_use == buffer_offset_from_page + size && page->total >= buffer_offset_from_page + new_size)) { page->in_use += new_size - size; } else { ptr new_buffer = alloc_bytes_recycle(new_size, alignment_mask); diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 09e1ff6..4d01b9f 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -159,8 +159,8 @@ uint32_t AGGraphInternAttributeType(AGGraphRef graph, AGTypeID type, } void AGGraphVerifyType(AGAttribute attribute, AGTypeID type) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -188,8 +188,8 @@ AGAttribute AGGraphCreateAttribute(uint32_t type_id, const void *body, const voi } AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); if (auto subgraph = attribute_id.subgraph()) { auto context_id = subgraph->context_id(); @@ -203,11 +203,11 @@ AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute) { } AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -215,12 +215,12 @@ AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute) { } const void *body = nullptr; - const AG::AttributeType *type = subgraph->graph()->attribute_ref(attribute_id.to_node_ptr(), &body); + const AG::AttributeType *type = subgraph->graph()->attribute_ref(attribute_id.to_ptr(), &body); return AGAttributeInfo(reinterpret_cast(type), body); } AGAttributeFlags AGGraphGetFlags(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } @@ -229,28 +229,28 @@ AGAttributeFlags AGGraphGetFlags(AGAttribute attribute) { } void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.subgraph()->set_flags(attribute_id.to_node_ptr(), flags); + attribute_id.subgraph()->set_flags(attribute_id.to_ptr(), flags); } void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidating, void (*modify)(void *body, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *context) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { AG::precondition_failure("no graph: %u", attribute); } - subgraph->graph()->attribute_modify(attribute_id.to_node_ptr(), + subgraph->graph()->attribute_modify(attribute_id.to_ptr(), *reinterpret_cast(type), AG::ClosureFunctionPV(modify, context), invalidating); } @@ -258,8 +258,8 @@ void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidat bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, bool (*predicate)(uint32_t attribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *context) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -283,16 +283,15 @@ void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &meta AG::Graph::UpdateStack *stack = current_update.tag() == 0 ? current_update.get() : nullptr; AG::Subgraph *subgraph = nullptr; - if (attribute.without_kind() == 0) { + if (attribute.has_value()) { + attribute.validate_data_offset(); + subgraph = attribute.subgraph(); + } else { if (stack != nullptr) { subgraph = AG::AttributeID(stack->frames().back().attribute).subgraph(); - } else { subgraph = AG::Subgraph::current_subgraph(); } - } else { - attribute.to_node_ptr().assert_valid(); - subgraph = attribute.subgraph(); } if (subgraph == nullptr) { AG::precondition_failure("no subgraph"); @@ -325,9 +324,9 @@ void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, auto value_metadata = reinterpret_cast(value_type); uint8_t state = 0; - void *value = - read_cached_attribute(identifier, *metadata, body, *value_metadata, options, AG::AttributeID(attribute), &state, - AG::ClosureFunctionCI(closure, closure_context)); + void *value = read_cached_attribute(identifier, *metadata, body, *value_metadata, options, + AG::AttributeID::from_storage(attribute), &state, + AG::ClosureFunctionCI(closure, closure_context)); if (changed_out) { *changed_out = state & 1 ? true : false; } @@ -341,7 +340,7 @@ void *AGGraphReadCachedAttributeIfExists(uint64_t identifier, AGTypeID type, voi uint8_t state = 0; void *value = read_cached_attribute(identifier, *metadata, body, *value_metadata, options, - AG::AttributeID(attribute), &state, nullptr); + AG::AttributeID::from_storage(attribute), &state, nullptr); if (changed_out) { *changed_out = state & 1 ? true : false; } @@ -394,17 +393,17 @@ AG::AttributeID create_indirect_attribute(AG::AttributeID attribute_id, std::opt } // namespace AGAttribute AGGraphCreateIndirectAttribute(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); return create_indirect_attribute(attribute_id, std::optional()); } AGAttribute AGGraphCreateIndirectAttribute2(AGAttribute attribute, size_t size) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); return create_indirect_attribute(attribute_id, std::optional(size)); } AGAttribute AGGraphGetIndirectAttribute(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_indirect()) { return attribute_id; } @@ -412,45 +411,45 @@ AGAttribute AGGraphGetIndirectAttribute(AGAttribute attribute) { } void AGGraphSetIndirectAttribute(AGAttribute attribute, AGAttribute source) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { AG::precondition_failure("invalid indirect attribute: %u", attribute); } - auto source_id = AG::AttributeID(source); + auto source_id = AG::AttributeID::from_storage(source); if (source_id.is_nil()) { - attribute_id.subgraph()->graph()->indirect_attribute_reset(attribute_id.to_indirect_node_ptr(), false); + attribute_id.subgraph()->graph()->indirect_attribute_reset(attribute_id.to_ptr(), false); } else { - attribute_id.subgraph()->graph()->indirect_attribute_set(attribute_id.to_indirect_node_ptr(), source_id); + attribute_id.subgraph()->graph()->indirect_attribute_set(attribute_id.to_ptr(), source_id); } } void AGGraphResetIndirectAttribute(AGAttribute attribute, bool non_nil) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { AG::precondition_failure("invalid indirect attribute: %u", attribute); } - attribute_id.subgraph()->graph()->indirect_attribute_reset(attribute_id.to_indirect_node_ptr(), non_nil); + attribute_id.subgraph()->graph()->indirect_attribute_reset(attribute_id.to_ptr(), non_nil); } AGAttribute AGGraphGetIndirectDependency(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { AG::precondition_failure("invalid indirect attribute: %u", attribute); } - return attribute_id.subgraph()->graph()->indirect_attribute_dependency(attribute_id.to_indirect_node_ptr()); + return attribute_id.subgraph()->graph()->indirect_attribute_dependency(attribute_id.to_ptr()); } void AGGraphSetIndirectDependency(AGAttribute attribute, AGAttribute dependency) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_indirect() || attribute_id.subgraph() == nullptr) { AG::precondition_failure("invalid indirect attribute: %u", attribute); } - return attribute_id.subgraph()->graph()->indirect_attribute_set_dependency(attribute_id.to_indirect_node_ptr(), - AG::AttributeID(dependency)); + return attribute_id.subgraph()->graph()->indirect_attribute_set_dependency( + attribute_id.to_ptr(), AG::AttributeID::from_storage(dependency)); } void AGGraphRegisterDependency(AGAttribute dependency, uint8_t input_edge_flags) { @@ -463,7 +462,7 @@ void AGGraphRegisterDependency(AGAttribute dependency, uint8_t input_edge_flags) auto graph = update_stack->graph(); auto frame = update_stack->frames().back(); - graph->input_value_add(frame.attribute, AG::AttributeID(dependency), input_edge_flags); + graph->input_value_add(frame.attribute, AG::AttributeID::from_storage(dependency), input_edge_flags); } #pragma mark - Offset attributes @@ -495,12 +494,12 @@ AG::AttributeID create_offset_attribute(AG::AttributeID attribute_id, uint32_t o } // namespace AGAttribute AGGraphCreateOffsetAttribute(AGAttribute attribute, uint32_t offset) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); return create_offset_attribute(attribute_id, offset, std::optional()); } AGAttribute AGGraphCreateOffsetAttribute2(AGAttribute attribute, uint32_t offset, size_t size) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); return create_offset_attribute(attribute_id, offset, std::optional(size)); } @@ -520,22 +519,22 @@ void AGGraphInvalidateAllValues(AGGraphRef graph) { } void AGGraphInvalidateValue(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { AG::precondition_failure("no graph: %u", attribute); } - subgraph->graph()->value_mark(attribute_id.to_node_ptr()); + subgraph->graph()->value_mark(attribute_id.to_ptr()); } void AGGraphSetInvalidationCallback(AGGraphRef graph, - void (*callback)(AGAttribute, const void *context AG_SWIFT_CONTEXT) + void (*callback)(const void *context AG_SWIFT_CONTEXT, AGAttribute) AG_SWIFT_CC(swift), const void *callback_context) { auto context = AG::Graph::Context::from_cf(graph); @@ -543,11 +542,11 @@ void AGGraphSetInvalidationCallback(AGGraphRef graph, } void AGGraphUpdateValue(AGAttribute attribute, uint8_t options) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -637,7 +636,7 @@ void AGGraphSetNeedsUpdate(AGGraphRef graph) { void AGGraphWithUpdate(AGAttribute attribute, void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *context) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (attribute_id.is_nil()) { // TODO: AGGraphWithoutUpdate auto update = AG::Graph::current_update(); @@ -650,14 +649,14 @@ void AGGraphWithUpdate(AGAttribute attribute, void (*function)(const void *conte if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { AG::precondition_failure("no graph: %u", attribute); } - subgraph->graph()->with_update(attribute_id.to_node_ptr(), AG::ClosureFunctionVV(function, context)); + subgraph->graph()->with_update(attribute_id.to_ptr(), AG::ClosureFunctionVV(function, context)); } void AGGraphWithoutUpdate(void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), @@ -680,9 +679,8 @@ void AGGraphWithMainThreadHandler(AGGraphRef graph, namespace { -// TODO: inline -AGValue get_value(AG::AttributeID attribute_id, uint32_t zone_id, AGValueOptions options, - const AG::swift::metadata &metadata) { +inline AGValue get_value(AG::AttributeID attribute_id, uint32_t subgraph_id, AGValueOptions options, + const AG::swift::metadata &metadata) { if (!(options & AGValueOptions0x04)) { auto update_stack_ptr = AG::Graph::current_update(); if (update_stack_ptr.tag() == 0 && update_stack_ptr.get() != nullptr) { @@ -692,14 +690,15 @@ AGValue get_value(AG::AttributeID attribute_id, uint32_t zone_id, AGValueOptions auto frame = update_stack->frames().back(); uint8_t state = 0; - void *value = graph->input_value_ref(frame.attribute, attribute_id, zone_id, options & 3, metadata, &state); + void *value = + graph->input_value_ref(frame.attribute, attribute_id, subgraph_id, options & 3, metadata, &state); // TODO: check if this is state or changed return {value, (state & 1) == 1}; } } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -707,7 +706,7 @@ AGValue get_value(AG::AttributeID attribute_id, uint32_t zone_id, AGValueOptions } uint8_t state = 0; - void *value = subgraph->graph()->value_ref(attribute_id, zone_id, metadata, &state); + void *value = subgraph->graph()->value_ref(attribute_id, subgraph_id, metadata, &state); return {value, state & 1 ? true : false}; } @@ -715,29 +714,29 @@ AGValue get_value(AG::AttributeID attribute_id, uint32_t zone_id, AGValueOptions } // namespace bool AGGraphHasValue(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { AG::precondition_failure("no graph: %u", attribute); } - return subgraph->graph()->value_exists(attribute_id.to_node_ptr()); + return subgraph->graph()->value_exists(attribute_id.to_ptr()); } AGValue AGGraphGetValue(AGAttribute attribute, AGValueOptions options, AGTypeID type) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); auto metadata = reinterpret_cast(type); return get_value(attribute_id, 0, options, *metadata); } AGValueState AGGraphGetValueState(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -748,22 +747,22 @@ AGValueState AGGraphGetValueState(AGAttribute attribute) { } AGValue AGGraphGetWeakValue(AGWeakAttribute attribute, AGValueOptions options, AGTypeID type) { - auto weak_attribute_id = AG::WeakAttributeID(attribute); + auto weak_attribute_id = AG::WeakAttributeID::from_cf(attribute); auto attribute_id = weak_attribute_id.evaluate(); if (attribute_id.is_nil()) { return {nullptr, false}; } auto metadata = reinterpret_cast(type); - return get_value(attribute_id, weak_attribute_id.zone_id(), options, *metadata); + return get_value(attribute_id, weak_attribute_id.subgraph_id(), options, *metadata); } bool AGGraphSetValue(AGAttribute attribute, const void *value, AGTypeID type) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -771,13 +770,12 @@ bool AGGraphSetValue(AGAttribute attribute, const void *value, AGTypeID type) { } auto metadata = reinterpret_cast(type); - return subgraph->graph()->value_set(attribute_id.to_node_ptr(), *metadata, value); + return subgraph->graph()->value_set(attribute_id.to_ptr(), *metadata, value); } AGGraphUpdateStatus AGGraphPrefetchValue(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr() - .assert_valid(); // TODO: make assert_value on AttributeID, don't care about kind at this point + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -798,7 +796,7 @@ AGGraphUpdateStatus AGGraphPrefetchValue(AGAttribute attribute) { AGValue AGGraphGetInputValue(AGAttribute attribute, AGAttribute input_attribute, AGValueOptions options, AGTypeID type) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (options & AGValueOptions0x04 || attribute_id.is_nil()) { return AGGraphGetValue(input_attribute, options, type); } @@ -806,14 +804,14 @@ AGValue AGGraphGetInputValue(AGAttribute attribute, AGAttribute input_attribute, if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { AG::precondition_failure("no graph: %u", attribute); } - auto input_attribute_id = AG::AttributeID(input_attribute); + auto input_attribute_id = AG::AttributeID::from_storage(input_attribute); auto metadata = reinterpret_cast(type); uint8_t state = 0; @@ -824,25 +822,25 @@ AGValue AGGraphGetInputValue(AGAttribute attribute, AGAttribute input_attribute, } uint32_t AGGraphAddInput(AGAttribute attribute, AGAttribute input, AGInputOptions options) { - auto attribute_id = AG::AttributeID(attribute); + auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - attribute_id.to_node_ptr().assert_valid(); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { AG::precondition_failure("no graph: %u", attribute); } - auto input_attribute_id = AG::AttributeID(input); - input_attribute_id.to_node_ptr().assert_valid(); + auto input_attribute_id = AG::AttributeID::from_storage(input); + input_attribute_id.validate_data_offset(); if (input_attribute_id.subgraph() != nullptr && input_attribute_id.subgraph()->graph() != subgraph->graph()) { AG::precondition_failure("accessing attribute in a different namespace: %u", input); } - return subgraph->graph()->add_input(attribute_id.to_node_ptr(), input_attribute_id, false, options); + return subgraph->graph()->add_input(attribute_id.to_ptr(), input_attribute_id, false, options); } bool AGGraphAnyInputsChanged(const AGAttribute *exclude_attributes, uint64_t exclude_attributes_count) { @@ -1052,8 +1050,8 @@ uint32_t AGGraphRegisterNamedTraceEvent(const char *event_name, const char *even #pragma mark - Profiler bool AGGraphIsProfilingEnabled(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -1064,8 +1062,8 @@ bool AGGraphIsProfilingEnabled(AGAttribute attribute) { } uint64_t AGGraphBeginProfileEvent(AGAttribute attribute, const char *event_name) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -1073,12 +1071,12 @@ uint64_t AGGraphBeginProfileEvent(AGAttribute attribute, const char *event_name) } auto resolved = attribute_id.resolve(AG::TraversalOptions::AssertNotNil); - return subgraph->graph()->begin_profile_event(resolved.attribute().to_node_ptr(), event_name); + return subgraph->graph()->begin_profile_event(resolved.attribute().to_ptr(), event_name); } void AGGraphEndProfileEvent(AGAttribute attribute, const char *event_name, uint64_t start_time, bool changed) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (!subgraph) { @@ -1086,7 +1084,7 @@ void AGGraphEndProfileEvent(AGAttribute attribute, const char *event_name, uint6 } auto resolved = attribute_id.resolve(AG::TraversalOptions::AssertNotNil); - subgraph->graph()->end_profile_event(resolved.attribute().to_node_ptr(), event_name, start_time, changed); + subgraph->graph()->end_profile_event(resolved.attribute().to_ptr(), event_name, start_time, changed); } void AGGraphStartProfiling(AGGraphRef graph) { diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index e29a70c..b2bce5c 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -299,7 +299,7 @@ int trap_cycles() { auto profile_data = graph->_profile_data.get(); if (profile_data) { - auto found = profile_data->all_events().items_by_attribute().find(attribute.to_node_ptr()); + auto found = profile_data->all_events().items_by_attribute().find(attribute.to_ptr()); if (found != profile_data->all_events().items_by_attribute().end()) { CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); if (item_json) { @@ -310,7 +310,7 @@ int trap_cycles() { NSMutableDictionary *event_dicts = [NSMutableDictionary dictionary]; for (auto &entry : profile_data->categories()) { uint32_t event_id = entry.first; - auto found = entry.second.items_by_attribute().find(attribute.to_node_ptr()); + auto found = entry.second.items_by_attribute().find(attribute.to_ptr()); if (found != entry.second.items_by_attribute().end()) { CFDictionaryRef item_json = profile_data->json_data(found->second, *graph); if (item_json) { @@ -344,9 +344,9 @@ int trap_cycles() { if (resolved.attribute().is_direct()) { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - auto src = node_indices_by_id.find(resolved.attribute().to_node_ptr())->second; + auto src = node_indices_by_id.find(resolved.attribute().to_ptr())->second; dict[@"src"] = [NSNumber numberWithUnsignedLong:src]; - auto dest = node_indices_by_id.find(attribute.to_node_ptr())->second; + auto dest = node_indices_by_id.find(attribute.to_ptr())->second; dict[@"dest"] = [NSNumber numberWithUnsignedLong:dest]; bool indirect = attribute.is_indirect(); if (indirect) { @@ -520,7 +520,7 @@ int trap_cycles() { } AttributeFlags subgraph_flags = attribute.to_node().subgraph_flags(); - auto found_node_index = node_indices_by_id.find(attribute.to_node_ptr()); + auto found_node_index = node_indices_by_id.find(attribute.to_ptr()); if (found_node_index != node_indices_by_id.end()) { [nodes addObject:[NSNumber numberWithUnsignedLong:found_node_index->second]]; if (subgraph_flags) { @@ -585,11 +585,11 @@ int trap_cycles() { // TODO: what is creator key? - if (tree->node.without_kind() != 0 && tree->type != nullptr) { + if (tree->node.has_value() && tree->type != nullptr) { OffsetAttributeID resolved = tree->node.resolve(TraversalOptions::ReportIndirectionInOffset); if (resolved.attribute().is_direct()) { tree_dict[@"node"] = [NSNumber - numberWithUnsignedLong:node_indices_by_id.find(resolved.attribute().to_node_ptr()) + numberWithUnsignedLong:node_indices_by_id.find(resolved.attribute().to_ptr()) ->second]; if (resolved.offset() != 0) { tree_dict[@"offset"] = [NSNumber numberWithUnsignedLong:resolved.offset() - 1]; // 3 @@ -602,9 +602,9 @@ int trap_cycles() { if (tree->parent == nullptr) { tree_dict[@"root"] = @YES; } - } else if (tree->node.without_kind() != 0 && tree->type == nullptr) { + } else if (tree->node.has_value() && tree->type == nullptr) { tree_dict[@"node"] = [NSNumber - numberWithUnsignedLong:node_indices_by_id.find(tree->node.to_node_ptr())->second]; // 2 + numberWithUnsignedLong:node_indices_by_id.find(tree->node.to_ptr())->second]; // 2 } else { if (tree->parent == nullptr) { tree_dict[@"root"] = @YES; // 1 @@ -657,7 +657,7 @@ int trap_cycles() { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[@"node"] = [NSNumber numberWithUnsignedLong:node_indices_by_id - .find(resolved_value.attribute().to_node_ptr()) + .find(resolved_value.attribute().to_ptr()) ->second]; if (resolved_value.offset() != 0) { dict[@"offset"] = [NSNumber numberWithUnsignedLong:resolved_value.offset()]; @@ -749,7 +749,7 @@ int trap_cycles() { if (!attribute_ids || [attribute_ids containsIndex:attribute]) { - [result appendFormat:@" _%d[label=\"%d", attribute.value(), attribute.value()]; + [result appendFormat:@" _%d[label=\"%d", attribute.to_storage(), attribute.to_storage()]; Node &node = attribute.to_node(); const AttributeType &node_type = attribute_type(node.type_id()); @@ -776,7 +776,7 @@ int trap_cycles() { double duration_fraction = 0.0; if (auto profile_data = _profile_data.get()) { auto items_by_attribute = profile_data->all_events().items_by_attribute(); - auto found_item = items_by_attribute.find(attribute.to_node_ptr()); + auto found_item = items_by_attribute.find(attribute.to_ptr()); if (found_item != items_by_attribute.end()) { auto item = found_item->second; if (item.data().update_count) { @@ -832,16 +832,17 @@ int trap_cycles() { AttributeID resolved_input_attribute = input_edge.value.resolve(TraversalOptions::None).attribute(); if (resolved_input_attribute.is_direct() && - (!attribute_ids || [attribute_ids containsIndex:resolved_input_attribute.value()])) { + (!attribute_ids || + [attribute_ids containsIndex:resolved_input_attribute.to_storage()])) { - [result appendFormat:@" _%d -> _%d[", input_edge.value.without_kind().value(), - attribute.value()]; + [result appendFormat:@" _%d -> _%d[", input_edge.value.to_ptr().offset(), + attribute.to_storage()]; // collect source inputs AttributeID intermediate = input_edge.value; while (intermediate.is_indirect()) { - indirect_nodes.try_emplace(intermediate.without_kind().to_indirect_node_ptr(), - intermediate.without_kind().to_indirect_node_ptr()); + indirect_nodes.try_emplace(intermediate.to_ptr(), + intermediate.to_ptr()); AttributeID source = intermediate.to_indirect_node().source().attribute(); if (source.is_direct()) { @@ -884,12 +885,13 @@ int trap_cycles() { OffsetAttributeID resolved_source = indirect_node->source().attribute().resolve(TraversalOptions::SkipMutableReference); - [result appendFormat:@" _%d -> _%d[label=\"@%d\"];\n", resolved_source.attribute().without_kind().value(), + [result appendFormat:@" _%d -> _%d[label=\"@%d\"];\n", resolved_source.attribute().to_ptr().offset(), indirect_node.offset(), resolved_source.offset()]; if (indirect_node->is_mutable()) { if (auto dependency = indirect_node->to_mutable().dependency()) { - [result appendFormat:@" _%d -> _%d[color=blue];\n", dependency.value(), indirect_node.offset()]; + [result + appendFormat:@" _%d -> _%d[color=blue];\n", dependency.to_storage(), indirect_node.offset()]; } } } @@ -920,7 +922,7 @@ int trap_cycles() { [description appendString:@" -- inputs:\n"]; for (auto input_edge : frame->attribute->inputs()) { OffsetAttributeID resolved = input_edge.value.resolve(TraversalOptions::ReportIndirectionInOffset); - [description appendFormat:@" %u", resolved.attribute().value()]; + [description appendFormat:@" %u", resolved.attribute().to_storage()]; if (resolved.offset() != 0) { [description appendFormat:@"[@%d]", resolved.offset() - 1]; } diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 5d6500c..4320a26 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -553,11 +553,11 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio UpdateStack update_stack = UpdateStack(this, options); foreach_trace([&update_stack, &attribute, &options](Trace &trace) { - trace.begin_update(update_stack, attribute.to_node_ptr(), options); + trace.begin_update(update_stack, attribute.to_ptr(), options); }); UpdateStatus status = UpdateStatus::Changed; - if (update_stack.push(attribute.to_node_ptr(), node, false, (options & 1) == 0)) { + if (update_stack.push(attribute.to_ptr(), node, false, (options & 1) == 0)) { status = update_stack.update(); if (status == UpdateStatus::NeedsCallMainHandler) { @@ -575,7 +575,7 @@ Graph::UpdateStatus Graph::update_attribute(AttributeID attribute, uint8_t optio } foreach_trace([&update_stack, &attribute, &status](Trace &trace) { - trace.end_update(update_stack, attribute.to_node_ptr(), status); + trace.end_update(update_stack, attribute.to_ptr(), status); }); // ~UpdateStatus called @@ -648,10 +648,11 @@ void Graph::remove_node(data::ptr node) { } for (auto input_edge : node->inputs()) { - this->remove_removed_input(node, input_edge.value); + // TODO: check conversion is necessary here... + this->remove_removed_input(AttributeID(node), input_edge.value); } for (auto output_edge : node->outputs()) { - this->remove_removed_output(node, output_edge.value, false); + this->remove_removed_output(AttributeID(node), output_edge.value, false); } if (_profile_data != nullptr) { @@ -660,9 +661,9 @@ void Graph::remove_node(data::ptr node) { } bool Graph::breadth_first_search(AttributeID attribute, SearchOptions options, - ClosureFunctionAB predicate) const { + ClosureFunctionAB predicate) const { auto resolved = attribute.resolve(TraversalOptions::SkipMutableReference); - if (resolved.attribute().without_kind() == 0) { + if (!resolved.attribute().has_value()) { return false; } @@ -771,7 +772,7 @@ data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, Attrib // TODO: check accessing zone_id directly or through raw_page_seed // check references of raw_page_seed in ghidra - uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; + uint32_t zone_id = attribute.has_value() ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); bool traverses_contexts = subgraph.context_id() != attribute.subgraph()->context_id(); uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; @@ -783,7 +784,7 @@ data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, Attrib } else { auto indirect_node = (data::ptr)subgraph.alloc_bytes_recycle(sizeof(Node), 3); - uint32_t zone_id = attribute.without_kind() != 0 ? attribute.subgraph()->info().zone_id() : 0; + uint32_t zone_id = attribute.has_value() ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); bool traverses_contexts = subgraph.context_id() != attribute.subgraph()->context_id(); uint16_t node_size = size.has_value() && size.value() <= 0xfffe ? uint16_t(size.value()) : 0xffff; @@ -821,32 +822,31 @@ void Graph::remove_indirect_node(data::ptr indirect_node_ptr) { } if (source.is_direct()) { - // auto removed_outputs = vector(); - auto removed_outputs = vector(); + auto removed_outputs = vector(); for (auto output_edge : source.to_node().outputs()) { if (remove_removed_output(AttributeID(indirect_node_ptr), output_edge.value, false)) { removed_outputs.push_back(output_edge.value); } } for (auto output : removed_outputs) { - remove_removed_input(AttributeID(output), AttributeID(indirect_node_ptr)); + remove_removed_input(output, AttributeID(indirect_node_ptr)); // remove_removed_input(output, attribute); } break; } else if (source.is_indirect()) { if (source.to_indirect_node().is_mutable()) { - auto removed_outputs = vector(); + auto removed_outputs = vector(); for (auto output_edge : source.to_indirect_node().to_mutable().outputs()) { if (remove_removed_output(AttributeID(indirect_node_ptr), output_edge.value, false)) { removed_outputs.push_back(output_edge.value); } } for (auto output : removed_outputs) { - remove_removed_input(AttributeID(output), AttributeID(indirect_node_ptr)); + remove_removed_input(output, AttributeID(indirect_node_ptr)); } break; } else { - indirect_node_ptr = source.to_indirect_node_ptr(); + indirect_node_ptr = source.to_ptr(); } } else { break; @@ -898,11 +898,11 @@ bool Graph::indirect_attribute_reset(data::ptr attribute, bool non } auto initial_source = attribute->to_mutable().initial_source(); - if (initial_source.attribute().without_kind() == 0 && non_nil) { + if (!initial_source.attribute().has_value() && non_nil) { return false; } - WeakAttributeID new_source = {AttributeID::make_nil(), 0}; + WeakAttributeID new_source = {AttributeIDNil, 0}; uint32_t new_offset = 0; if (!initial_source.expired()) { new_source = initial_source; @@ -944,7 +944,7 @@ const AttributeID &Graph::indirect_attribute_dependency(data::ptr void Graph::indirect_attribute_set_dependency(data::ptr attribute, AttributeID dependency) { // Status: Verified - if (dependency.without_kind()) { + if (dependency.has_value()) { if (!dependency.is_direct()) { precondition_failure("indirect dependencies must be attributes"); } @@ -952,7 +952,7 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, precondition_failure("indirect dependencies must share a subgraph with their attribute"); } } else { - dependency = AttributeID(0); + dependency = AttributeID(); // TODO: check } if (!attribute->is_mutable()) { precondition_failure("not an indirect attribute: %u", attribute); @@ -964,11 +964,11 @@ void Graph::indirect_attribute_set_dependency(data::ptr attribute, if (old_dependency != dependency) { AttributeID indirect_attribute = AttributeID(attribute).with_kind(AttributeID::Kind::Indirect); if (old_dependency) { - remove_output_edge(old_dependency.to_node_ptr(), indirect_attribute); + remove_output_edge(old_dependency.to_ptr(), indirect_attribute); } attribute->to_mutable().set_dependency(dependency); if (dependency) { - add_output_edge(dependency.to_node_ptr(), indirect_attribute); + add_output_edge(dependency.to_ptr(), indirect_attribute); if (dependency.to_node().state().is_dirty()) { propagate_dirty(indirect_attribute); } @@ -986,7 +986,7 @@ void *Graph::value_ref(AttributeID attribute, uint32_t zone_id, const swift::met attribute.resolve(TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | (zone_id != 0 ? TraversalOptions::EvaluateWeakReferences : TraversalOptions::AssertNotNil)); - if (zone_id != 0 && (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct())) { + if (zone_id != 0 && (!resolved.attribute().has_value() || !resolved.attribute().is_direct())) { return nullptr; } @@ -1046,7 +1046,7 @@ bool Graph::value_set(data::ptr node, const swift::metadata &value_type, c bool changed = value_set_internal(node, *node.get(), value, value_type); if (changed) { - propagate_dirty(node); + propagate_dirty(AttributeID(node)); } return changed; } @@ -1140,7 +1140,7 @@ void Graph::value_mark(data::ptr node) { } } - propagate_dirty(node); + propagate_dirty(AttributeID(node)); } void Graph::value_mark_all() { @@ -1223,7 +1223,7 @@ void Graph::propagate_dirty(AttributeID attribute) { next_state = Node::State(output_node.state().data() | state.data()); if (!output_node.state().is_dirty()) { - foreach_trace([&output](Trace &trace) { trace.set_dirty(output.to_node_ptr(), true); }); + foreach_trace([&output](Trace &trace) { trace.set_dirty(output.to_ptr(), true); }); output_node.set_state(output_node.state().with_dirty(true)); if (auto subgraph = output.subgraph()) { @@ -1294,7 +1294,7 @@ void Graph::propagate_dirty(AttributeID attribute) { #pragma mark - Inputs // TODO: inline -void *Graph::input_value_ref(data::ptr node, AttributeID input_attribute, uint32_t zone_id, +void *Graph::input_value_ref(data::ptr node, AttributeID input_attribute, uint32_t subgraph_id, uint8_t input_flags, const swift::metadata &type, uint8_t *state_out) { // TODO: double check this is input_attribute and not node @@ -1307,7 +1307,7 @@ void *Graph::input_value_ref(data::ptr node, AttributeID input_attribu if (index < 0) { // TODO: AVGalueOptions is same as InputEdge::Flags ? - return input_value_ref_slow(node, input_attribute, zone_id, input_flags, type, state_out, index); + return input_value_ref_slow(node, input_attribute, subgraph_id, input_flags, type, state_out, index); } AG::OffsetAttributeID resolved_input = @@ -1326,10 +1326,10 @@ void *Graph::input_value_ref(data::ptr node, AttributeID input_attribu // TODO: combine with block above, make index signed first though // TODO: AVGalueOptions is same as InputEdge::Flags ? - return input_value_ref_slow(node, input_attribute, zone_id, input_flags, type, state_out, index); + return input_value_ref_slow(node, input_attribute, subgraph_id, input_flags, type, state_out, index); } -void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_attribute, uint32_t zone_id, +void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_attribute, uint32_t subgraph_id, uint8_t input_flags, const swift::metadata &type, uint8_t *state_out, uint32_t index) { @@ -1346,17 +1346,17 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at } if (!node->state().is_dirty()) { auto resolved = input_attribute.resolve( - zone_id != 0 ? TraversalOptions::UpdateDependencies | TraversalOptions::EvaluateWeakReferences - : TraversalOptions::UpdateDependencies | TraversalOptions::AssertNotNil); + subgraph_id != 0 ? TraversalOptions::UpdateDependencies | TraversalOptions::EvaluateWeakReferences + : TraversalOptions::UpdateDependencies | TraversalOptions::AssertNotNil); - if (zone_id != 0) { - if (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct()) { + if (subgraph_id != 0) { + if (!resolved.attribute().has_value() || !resolved.attribute().is_direct()) { return 0; } } update_attribute(resolved.attribute(), 0); } - index = add_input(node, input_attribute, zone_id != 0, input_flags & 1); + index = add_input(node, input_attribute, subgraph_id != 0, input_flags & 1); if (index < 0) { return nullptr; } @@ -1368,20 +1368,20 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at input_edge.set_unknown4(true); OffsetAttributeID resolved = input_edge.value.resolve( - zone_id != 0 ? TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | - TraversalOptions::EvaluateWeakReferences - : TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | - TraversalOptions::AssertNotNil); + subgraph_id != 0 ? TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + TraversalOptions::EvaluateWeakReferences + : TraversalOptions::UpdateDependencies | TraversalOptions::ReportIndirectionInOffset | + TraversalOptions::AssertNotNil); - if (zone_id != 0) { - if (resolved.attribute().without_kind() == 0 || !resolved.attribute().is_direct()) { + if (subgraph_id != 0) { + if (!resolved.attribute().has_value() || !resolved.attribute().is_direct()) { return nullptr; } } if (!resolved.attribute().to_node().state().is_value_initialized() || resolved.attribute().to_node().state().is_dirty()) { - if (zone_id != 0) { + if (subgraph_id != 0) { auto zone_id_before_update = data::table::shared().raw_page_seed(resolved.attribute().page_ptr()); update_attribute(resolved.attribute(), 0); @@ -1428,7 +1428,7 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at } void Graph::input_value_add(data::ptr node, AttributeID input_attribute, uint8_t input_flags) { - input_attribute.to_node_ptr().assert_valid(); + input_attribute.validate_data_offset(); // TODO: check flag and mask are right way around auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, @@ -1442,7 +1442,7 @@ void Graph::input_value_add(data::ptr node, AttributeID input_attribute, u uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_nil, AGInputOptions options) { auto resolved = input.resolve(TraversalOptions::EvaluateWeakReferences); - if (resolved.attribute().without_kind() == 0) { + if (!resolved.attribute().has_value()) { if (allow_nil) { return -1; } @@ -1483,7 +1483,7 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni index = (uint32_t)(pos - node->inputs().begin()); } - add_input_dependencies(node, resolved.attribute()); + add_input_dependencies(AttributeID(node), resolved.attribute()); if (node->state().is_updating()) { reset_update(node); @@ -1510,10 +1510,10 @@ void Graph::remove_all_inputs(data::ptr node) { void Graph::add_input_dependencies(AttributeID attribute, AttributeID input) { auto resolved = input.resolve(TraversalOptions::SkipMutableReference); if (resolved.attribute().is_direct()) { - add_output_edge(resolved.attribute().to_node_ptr(), attribute); + add_output_edge(resolved.attribute().to_ptr(), attribute); } else if (resolved.attribute().is_indirect()) { assert(resolved.attribute().to_indirect_node().is_mutable()); - add_output_edge(resolved.attribute().to_mutable_indirect_node_ptr(), attribute); + add_output_edge(resolved.attribute().to_ptr(), attribute); } update_main_refs(attribute); } @@ -1521,10 +1521,10 @@ void Graph::add_input_dependencies(AttributeID attribute, AttributeID input) { void Graph::remove_input_dependencies(AttributeID attribute, AttributeID input) { auto resolved = input.resolve(TraversalOptions::SkipMutableReference); if (resolved.attribute().is_direct()) { - remove_output_edge(resolved.attribute().to_node_ptr(), attribute); + remove_output_edge(resolved.attribute().to_ptr(), attribute); } else if (resolved.attribute().is_indirect()) { assert(resolved.attribute().to_indirect_node().is_mutable()); - remove_output_edge(resolved.attribute().to_mutable_indirect_node_ptr(), attribute); + remove_output_edge(resolved.attribute().to_ptr(), attribute); } update_main_refs(attribute); } @@ -1543,10 +1543,10 @@ void Graph::remove_removed_input(AttributeID attribute, AttributeID input) { auto resolved = input.resolve(TraversalOptions::SkipMutableReference | TraversalOptions::EvaluateWeakReferences); if (resolved.attribute().subgraph()->validation_state() != Subgraph::ValidationState::Invalidated) { if (resolved.attribute().is_direct()) { - remove_output_edge(resolved.attribute().to_node_ptr(), attribute); + remove_output_edge(resolved.attribute().to_ptr(), attribute); } else if (resolved.attribute().is_indirect()) { assert(resolved.attribute().to_indirect_node().is_mutable()); - remove_output_edge(resolved.attribute().to_mutable_indirect_node_ptr(), attribute); + remove_output_edge(resolved.attribute().to_ptr(), attribute); } } } @@ -1715,7 +1715,7 @@ bool Graph::remove_removed_output(AttributeID attribute, AttributeID output, boo } if (output.is_direct()) { - auto output_node_ptr = output.to_node_ptr(); + auto output_node_ptr = output.to_ptr(); uint32_t index = 0; for (auto input : output_node_ptr->inputs()) { @@ -1732,16 +1732,15 @@ bool Graph::remove_removed_output(AttributeID attribute, AttributeID output, boo return false; } - auto indirect_node = output.to_indirect_node_ptr(); + auto indirect_node = output.to_ptr(); if (indirect_node->source().attribute() != attribute) { // clear dependency auto dependency = indirect_node->to_mutable().dependency(); if (dependency && dependency == attribute) { - foreach_trace( - [&indirect_node](Trace &trace) { trace.set_dependency(indirect_node, AttributeID::make_nil()); }); - indirect_node->to_mutable().set_dependency(AttributeID(0)); // TODO: 0 or nullptr + foreach_trace([&indirect_node](Trace &trace) { trace.set_dependency(indirect_node, AttributeIDNil); }); + indirect_node->to_mutable().set_dependency(AttributeID()); // TODO: 0 or nullptr return true; } @@ -1750,9 +1749,9 @@ bool Graph::remove_removed_output(AttributeID attribute, AttributeID output, boo // reset source auto initial_source = indirect_node->to_mutable().initial_source(); - WeakAttributeID new_source = {AttributeID::make_nil(), 0}; + WeakAttributeID new_source = {AttributeIDNil, 0}; uint32_t new_offset = 0; - if (initial_source.attribute().without_kind() != 0 && !initial_source.expired()) { + if (initial_source.attribute().has_value() && !initial_source.expired()) { new_source = initial_source; new_offset = indirect_node->to_mutable().initial_offset(); } @@ -1761,7 +1760,7 @@ bool Graph::remove_removed_output(AttributeID attribute, AttributeID output, boo [&indirect_node, &new_source](Trace &trace) { trace.set_source(indirect_node, new_source.attribute()); }); indirect_node->modify(new_source, new_offset); - if (new_source.attribute().without_kind() != 0 && !new_source.expired()) { + if (new_source.attribute().has_value() && !new_source.expired()) { add_input_dependencies(output, new_source.attribute()); } @@ -1854,7 +1853,7 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c } } foreach_trace([&output_edge, &input_index](Trace &trace) { - trace.set_edge_pending(output_edge->value.to_node_ptr(), input_index, true); + trace.set_edge_pending(output_edge->value.to_ptr(), input_index, true); }); input_edge.set_changed(true); } @@ -1889,7 +1888,7 @@ void Graph::mark_pending(data::ptr node_ptr, Node *node) { subgraph->add_dirty_flags(subgraph_flags); } - propagate_dirty(node_ptr); + propagate_dirty(AttributeID(node_ptr)); } } @@ -2067,9 +2066,9 @@ void Graph::encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_ encoder.encode_varint(8); encoder.encode_varint(indirect_node.source().attribute()); } - if (indirect_node.source().zone_id()) { + if (indirect_node.source().subgraph_id()) { encoder.encode_varint(0x10); - encoder.encode_varint(indirect_node.source().zone_id()); + encoder.encode_varint(indirect_node.source().subgraph_id()); } if (indirect_node.offset()) { encoder.encode_varint(0x18); @@ -2098,7 +2097,7 @@ void Graph::encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_ } void Graph::encode_tree(Encoder &encoder, data::ptr tree) { - if (tree->node.without_kind()) { + if (tree->node.has_value()) { encoder.encode_varint(0x10); encoder.encode_varint(tree->node); } @@ -2182,7 +2181,7 @@ void Graph::prepare_trace(Trace &trace) { } if (attribute.is_direct()) { - auto node = attribute.to_node_ptr(); + auto node = attribute.to_ptr(); trace.added(node); if (node->state().is_dirty()) { trace.set_dirty(node, true); @@ -2195,7 +2194,7 @@ void Graph::prepare_trace(Trace &trace) { trace.set_value(node, value); } } else if (attribute.is_indirect()) { - auto indirect_node = attribute.to_indirect_node_ptr(); + auto indirect_node = attribute.to_ptr(); trace.added(indirect_node); } } @@ -2218,7 +2217,7 @@ void Graph::prepare_trace(Trace &trace) { } if (attribute.is_direct()) { - auto node = attribute.to_node_ptr(); + auto node = attribute.to_ptr(); uint32_t edge_index = 0; for (auto input_edge : node->inputs()) { trace.add_edge(node, input_edge.value, @@ -2229,7 +2228,7 @@ void Graph::prepare_trace(Trace &trace) { edge_index += 1; } } else if (attribute.is_indirect()) { - auto indirect_node = attribute.to_indirect_node_ptr(); + auto indirect_node = attribute.to_ptr(); trace.set_source(indirect_node, indirect_node->source().attribute()); if (indirect_node->is_mutable() && indirect_node->to_mutable().dependency() != 0) { trace.set_dependency(indirect_node, indirect_node->to_mutable().dependency()); diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 5caaf37..f8318f9 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -270,10 +270,10 @@ class Graph { // MARK: Inputs - void *_Nullable input_value_ref(data::ptr node, AttributeID input, uint32_t zone_id, uint8_t input_flags, - const swift::metadata &type, uint8_t *state_out); + void *_Nullable input_value_ref(data::ptr node, AttributeID input, uint32_t subgraph_id, + uint8_t input_flags, const swift::metadata &type, uint8_t *state_out); - void *_Nullable input_value_ref_slow(data::ptr node, AttributeID input, uint32_t zone_id, + void *_Nullable input_value_ref_slow(data::ptr node, AttributeID input, uint32_t subgraph_id, uint8_t input_flags, const swift::metadata &type, uint8_t *state_out, uint32_t index); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 9be74bc..4c5dfcd 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -1172,10 +1172,10 @@ void Graph::TraceRecorder::set_source(data::ptr indirect_node, Att _encoder.encode_varint(0x20); _encoder.encode_varint(source_attribute); } - auto zone_id = indirect_node->source().zone_id(); - if (zone_id) { + auto subgraph_id = indirect_node->source().subgraph_id(); + if (subgraph_id) { _encoder.encode_varint(0x28); - _encoder.encode_varint(zone_id); + _encoder.encode_varint(subgraph_id); } field_backtrace(_encoder, 8); diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp index 7d3ea51..39d7a4a 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp @@ -51,7 +51,7 @@ AGAttribute AGTreeElementGetNextNode(AGTreeElementNodeIterator *iter) { auto tree_element_id = AG::TreeElementID(iter->tree_element); AG::AttributeID node = tree_element_id.subgraph()->tree_node_at_index(tree_element_id.to_element_ptr(), iter->index); - if (node.without_kind() == 0) { + if (!node.has_value()) { return AGAttributeNil; } iter->index += 1; diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index ccce589..f9b5ae0 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -217,7 +217,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { frame.num_pushed_inputs = input_index; - push(dependency.to_node_ptr(), dependency.to_node(), false, false); + push(dependency.to_ptr(), dependency.to_node(), false, false); // go to top regardless return update(); } @@ -239,7 +239,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { frame.num_pushed_inputs = input_index + 1; - if (push(input_attribute.to_node_ptr(), input_node, true, true)) { + if (push(input_attribute.to_ptr(), input_node, true, true)) { // go to top return update(); } @@ -264,7 +264,7 @@ Graph::UpdateStatus Graph::UpdateStack::update() { const AttributeType &type = _graph->attribute_type(node->type_id()); void *self = node->get_self(type); - type.perform_update(self, frame.attribute); + type.perform_update(self, AttributeID(frame.attribute)); // could be ptr? if (!node->state().is_value_initialized()) { if (type.value_metadata().vw_size() > 0) { diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index 7a3d08a..cb5103f 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -53,7 +53,7 @@ AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute) { AG::Graph::Context *context = AG::Graph::Context::from_cf(graph); AG::Subgraph *subgraph = - new (&instance->subgraph) AG::Subgraph((AG::SubgraphObject *)instance, *context, AG::AttributeID(attribute)); + new (&instance->subgraph) AG::Subgraph((AG::SubgraphObject *)instance, *context, AG::AttributeID::from_storage(attribute)); return instance; }; @@ -121,8 +121,8 @@ AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute) { } AGSubgraphRef AGGraphGetAttributeSubgraph2(AGAttribute attribute) { - auto attribute_id = AG::AttributeID(attribute); - attribute_id.to_node_ptr().assert_valid(); + auto attribute_id = AG::AttributeID::from_storage(attribute); + attribute_id.validate_data_offset(); auto subgraph = attribute_id.subgraph(); if (subgraph == nullptr) { @@ -277,7 +277,7 @@ void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner) { if (subgraph->subgraph == nullptr) { AG::precondition_failure("accessing invalidated subgraph"); } - subgraph->subgraph->set_tree_owner(AG::AttributeID(owner)); + subgraph->subgraph->set_tree_owner(AG::AttributeID::from_storage(owner)); } void AGSubgraphAddTreeValue(AGAttribute value, AGTypeID type, const char *key, uint32_t flags) { @@ -287,7 +287,7 @@ void AGSubgraphAddTreeValue(AGAttribute value, AGTypeID type, const char *key, u } auto metadata = reinterpret_cast(type); - current->add_tree_value(AG::AttributeID(value), metadata, key, flags); + current->add_tree_value(AG::AttributeID::from_storage(value), metadata, key, flags); } void AGSubgraphBeginTreeElement(AGAttribute value, AGTypeID type, uint32_t flags) { @@ -297,7 +297,7 @@ void AGSubgraphBeginTreeElement(AGAttribute value, AGTypeID type, uint32_t flags } auto metadata = reinterpret_cast(type); - current->begin_tree(AG::AttributeID(value), metadata, flags); + current->begin_tree(AG::AttributeID::from_storage(value), metadata, flags); } void AGSubgraphEndTreeElement(AGAttribute value) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 9a9c752..487f542 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -10,6 +10,7 @@ #include "Attribute/OffsetAttributeID.h" #include "Encoder/Encoder.h" #include "Errors/Errors.h" +#include "Graph/AGGraph-Private.h" #include "Graph/Context.h" #include "Graph/Graph.h" #include "Graph/Tree/TreeElement.h" @@ -47,11 +48,11 @@ Subgraph::Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID graph->add_subgraph(*this); if (AGSubgraphShouldRecordTree()) { - if (owner.without_kind() == 0) { + if (!owner.has_value()) { auto update = Graph::current_update(); if (update.tag() == 0 && update.get() != nullptr) { if (auto top = update.get()->global_top()) { - owner = top->attribute; + owner = AttributeID(top->attribute); } } } @@ -208,9 +209,9 @@ void Subgraph::invalidate_now(Graph &graph) { bool found_nil_attribute = false; for (auto attribute : AttributeIDList1(page)) { if (attribute.is_direct()) { - graph.remove_node(attribute.to_node_ptr()); // TODO: does this muck up iteration + graph.remove_node(attribute.to_ptr()); // TODO: does this muck up iteration } else if (attribute.is_indirect()) { - graph.remove_indirect_node(attribute.to_indirect_node_ptr()); // TODO: does this muck up iteration + graph.remove_indirect_node(attribute.to_ptr()); // TODO: does this muck up iteration } else if (attribute.is_nil()) { found_nil_attribute = true; } @@ -374,7 +375,7 @@ void Subgraph::add_indirect(data::ptr node, bool flag) { } void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) { - AttributeID before_attribute = AttributeID::make_nil(); + AttributeID before_attribute = AttributeIDNil; if (after_flagged_nodes) { if (!attribute.is_direct() || attribute.to_node().subgraph_flags() == 0) { @@ -387,8 +388,8 @@ void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) } } - uint16_t inserted_offset = (attribute.without_kind() - attribute.page_ptr()) | attribute.kind(); - uint16_t next_offset; + RelativeAttributeID inserted_offset = attribute.to_relative(); + RelativeAttributeID next_offset; if (before_attribute.is_direct()) { next_offset = before_attribute.to_node().relative_offset(); before_attribute.to_node().set_relative_offset(inserted_offset); @@ -398,10 +399,10 @@ void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) } else { if (after_flagged_nodes) { next_offset = attribute.page_ptr()->first_child_1; - attribute.page_ptr()->first_child_1 = inserted_offset; + attribute.page_ptr()->first_child_1 = inserted_offset.value(); } else { next_offset = attribute.page_ptr()->first_child_2; - attribute.page_ptr()->first_child_2 = inserted_offset; + attribute.page_ptr()->first_child_2 = inserted_offset.value(); } } @@ -415,7 +416,7 @@ void Subgraph::insert_attribute(AttributeID attribute, bool after_flagged_nodes) void Subgraph::unlink_attribute(AttributeID attribute) { // Find the attribute before the given attribute // TODO: what happens if attribute is not found - AttributeID previous_attribute = AttributeID::make_nil(); + AttributeID previous_attribute = AttributeIDNil; for (auto candidate_attribute : AttributeIDList1(attribute.page_ptr())) { if (candidate_attribute.is_nil()) { break; @@ -426,13 +427,13 @@ void Subgraph::unlink_attribute(AttributeID attribute) { previous_attribute = candidate_attribute; } - uint16_t old_value = 0; + RelativeAttributeID old_value = RelativeAttributeID(); if (attribute.is_direct()) { old_value = attribute.to_node().relative_offset(); - attribute.to_node().set_relative_offset(0); + attribute.to_node().set_relative_offset(nullptr); } else { old_value = attribute.to_indirect_node().relative_offset(); - attribute.to_indirect_node().set_relative_offset(0); + attribute.to_indirect_node().set_relative_offset(nullptr); } if (previous_attribute.is_direct()) { @@ -440,7 +441,7 @@ void Subgraph::unlink_attribute(AttributeID attribute) { } else if (previous_attribute.is_indirect()) { previous_attribute.to_indirect_node().set_relative_offset(old_value); } else { - attribute.page_ptr()->first_child_1 = old_value; + attribute.page_ptr()->first_child_1 = old_value.value(); } } @@ -511,7 +512,7 @@ void Subgraph::update(uint8_t flags) { } } if (node.state().is_dirty()) { - nodes_to_update.push_back(attribute.to_node_ptr()); + nodes_to_update.push_back(attribute.to_ptr()); } } else if (attribute.is_indirect()) { if (flags) { @@ -532,7 +533,7 @@ void Subgraph::update(uint8_t flags) { for (auto node : nodes_to_update) { if (!thread_is_updating) { _graph->increment_transaction_count_if_needed(); - _graph->update_attribute(node, true); + _graph->update_attribute(AttributeID(node), true); if (!subgraph->is_valid()) { break; @@ -704,7 +705,7 @@ AttributeID Subgraph::tree_node_at_index(data::ptr tree_elem } } } - return AttributeID::make_nil(); + return AttributeIDNil; } data::ptr Subgraph::tree_subgraph_child(data::ptr tree_element) { @@ -737,7 +738,7 @@ data::ptr Subgraph::tree_subgraph_child(data::ptr_tree_root->node; - if (attribute.without_kind() == 0) { + if (!attribute.has_value()) { continue; } OffsetAttributeID resolved = attribute.resolve(TraversalOptions::None); @@ -772,9 +773,9 @@ void Subgraph::set_flags(data::ptr node, AttributeFlags flags) { } if (node->subgraph_flags() == 0 || flags == 0) { // potentially reorder - unlink_attribute(node); + unlink_attribute(AttributeID(node)); node->set_subgraph_flags(flags); - insert_attribute(node, true); + insert_attribute(AttributeID(node), true); } else { node->set_subgraph_flags(flags); } @@ -1114,7 +1115,7 @@ void Subgraph::print(uint32_t indent_level) { if (!attribute.is_direct()) { continue; } - fprintf(stdout, "%s%u", first ? "" : " ", attribute.value()); + fprintf(stdout, "%s%u", first ? "" : " ", attribute.to_storage()); if (attribute.to_node().subgraph_flags()) { fprintf(stdout, "(%u)", attribute.to_node().subgraph_flags().data()); } From 55e1b8144aa2dac63177096ef3d6131dafd2032b Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 11 Apr 2025 12:27:48 +0200 Subject: [PATCH 59/74] Make closure function context first parameter --- Sources/Compute/Attribute/AnyAttribute.swift | 36 +++++---- Sources/Compute/Attribute/AttributeType.swift | 68 ++++++++--------- .../Attribute/Rule/AnyRuleContext.swift | 13 ++-- Sources/Compute/Attribute/Rule/Rule.swift | 66 ++++++++-------- .../Compute/Attribute/Rule/RuleContext.swift | 17 +---- Sources/Compute/Graph/Graph.swift | 76 +++++++++---------- Sources/Compute/Graph/Subgraph.swift | 17 ++--- Sources/Compute/Runtime/Enum.swift | 18 ++--- Sources/Compute/Runtime/Metadata.swift | 42 +++++----- Sources/Compute/Runtime/Tuple.swift | 16 ++-- Sources/ComputeCxx/Attribute/AttributeType.h | 1 - Sources/ComputeCxx/Attribute/Node/Node.h | 2 +- Sources/ComputeCxx/Closure/ClosureFunction.h | 16 ++-- Sources/ComputeCxx/Graph/AGGraph-Private.h | 6 +- Sources/ComputeCxx/Graph/AGGraph.cpp | 23 +++--- Sources/ComputeCxx/Graph/AGGraph.h | 16 ++-- Sources/ComputeCxx/Graph/Graph.cpp | 3 +- Sources/ComputeCxx/Graph/Graph.h | 5 +- Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 6 +- Sources/ComputeCxx/Subgraph/AGSubgraph.h | 2 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 5 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 2 +- Sources/ComputeCxx/Swift/AGTuple.cpp | 4 +- Sources/ComputeCxx/Swift/AGTuple.h | 5 +- Sources/ComputeCxx/Swift/AGType.cpp | 40 +++++----- Sources/ComputeCxx/Swift/AGType.h | 12 +-- 26 files changed, 247 insertions(+), 270 deletions(-) diff --git a/Sources/Compute/Attribute/AnyAttribute.swift b/Sources/Compute/Attribute/AnyAttribute.swift index 7a65a7d..b698b5e 100644 --- a/Sources/Compute/Attribute/AnyAttribute.swift +++ b/Sources/Compute/Attribute/AnyAttribute.swift @@ -1,5 +1,13 @@ import ComputeCxx +struct AGGraphMutateAttributeThunk { + let body: (UnsafeMutableRawPointer) -> Void +} + +struct AGGraphSearchThunk { + let body: (AnyAttribute) -> Bool +} + extension AnyAttribute { public static var current: AnyAttribute? { @@ -26,27 +34,21 @@ extension AnyAttribute { type._visitSelf(info.body, visitor: &visitor) } - private struct MutateBodyContext { - let mutator: (UnsafeMutableRawPointer) -> Void - - } - // XXX: Swift compiler crashes when capturing a generic type - public func mutateBody(as type: Body.Type, invalidating: Bool, _ mutator: (inout Body) -> Void) { withoutActuallyEscaping(mutator) { escapingMutator in - let context = MutateBodyContext(mutator: { bodyPointer in + let thunk = AGGraphMutateAttributeThunk(body: { bodyPointer in escapingMutator(&bodyPointer.assumingMemoryBound(to: Body.self).pointee) }) - withUnsafePointer(to: context) { contextPointer in + withUnsafePointer(to: thunk) { thunkPointer in __AGGraphMutateAttribute( self, Metadata(type), invalidating, - { context, body in - context.assumingMemoryBound(to: MutateBodyContext.self).pointee.mutator(body) + { + $0.assumingMemoryBound(to: AGGraphMutateAttributeThunk.self).pointee.body($1) }, - contextPointer + thunkPointer ) } } @@ -55,19 +57,15 @@ extension AnyAttribute { public func breadthFirstSearch(options: AGSearchOptions, _ predicate: (AnyAttribute) -> Bool) -> Bool { // TODO: @silgen? - struct Context { - let predicate: (AnyAttribute) -> Bool - } return withoutActuallyEscaping(predicate) { escapingPredicate in - let context = Context(predicate: escapingPredicate) - return withUnsafePointer(to: context) { contextPointer in + return withUnsafePointer(to: AGGraphSearchThunk(body: escapingPredicate)) { thunkPointer in return __AGGraphSearch( self, options, - { attribute, context in - context.assumingMemoryBound(to: Context.self).pointee.predicate(attribute) + { + $0.assumingMemoryBound(to: AGGraphSearchThunk.self).pointee.body($1) }, - contextPointer + thunkPointer ) } } diff --git a/Sources/Compute/Attribute/AttributeType.swift b/Sources/Compute/Attribute/AttributeType.swift index 65dd811..050075a 100644 --- a/Sources/Compute/Attribute/AttributeType.swift +++ b/Sources/Compute/Attribute/AttributeType.swift @@ -1,14 +1,14 @@ import ComputeCxx -extension Graph { +struct AGGraphInternAttributeTypeThunk { + let body: () -> UnsafeRawPointer +} - struct InternAttributeTypeContext { - let selfType: Any.Type - let bodyType: _AttributeBody.Type - let valueType: Any.Type - let flags: AGAttributeTypeFlags - let update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void - } +struct AGAttributeTypeUpdateThunk { + let body: (UnsafeMutableRawPointer, AnyAttribute) -> Void +} + +extension Graph { func internAttributeType( selfType: Any.Type, @@ -18,30 +18,31 @@ extension Graph { update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) -> UInt32 { return withoutActuallyEscaping(update) { escapingUpdate in - let context = InternAttributeTypeContext( - selfType: selfType, - bodyType: bodyType, - valueType: valueType, - flags: flags, - update: escapingUpdate - ) - return withUnsafePointer(to: context) { contextPointer in + let thunk = AGGraphInternAttributeTypeThunk(body: { + return withUnsafePointer(to: AGAttributeTypeUpdateThunk(body: escapingUpdate())) { updateThunkPointer in + let attributeType = AGAttributeType( + selfType: selfType, + bodyType: bodyType, + valueType: valueType, + flags: flags, + update: { + $0.assumingMemoryBound(to: AGAttributeTypeUpdateThunk.self).pointee.body($1, $2) + }, + updateContext: updateThunkPointer + ) + let pointer = UnsafeMutablePointer.allocate(capacity: 1) + pointer.initialize(to: attributeType) + return UnsafeRawPointer(pointer) + } + }) + return withUnsafePointer(to: thunk) { thunkPointer in return __AGGraphInternAttributeType( self, Metadata(selfType), - { contextPointer in - // FUN_1afe82038 - let context = contextPointer.assumingMemoryBound(to: InternAttributeTypeContext.self).pointee - let pointer = AGAttributeType.allocate( - selfType: context.selfType, - bodyType: context.bodyType, - valueType: context.valueType, - flags: context.flags, - update: context.update - ) - return UnsafeRawPointer(pointer) + { + return $0.assumingMemoryBound(to: AGGraphInternAttributeTypeThunk.self).pointee.body() }, - contextPointer + thunkPointer ) } } @@ -59,19 +60,16 @@ extension AGAttributeType { flags: AGAttributeTypeFlags, update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) -> UnsafeMutablePointer { - struct Context { - let update: (UnsafeMutableRawPointer, AnyAttribute) -> Void - } - return withUnsafePointer(to: Context(update: update())) { contextPointer in + return withUnsafePointer(to: AGAttributeTypeUpdateThunk(body: update())) { updateThunkPointer in let attributeType = AGAttributeType( selfType: selfType, bodyType: bodyType, valueType: valueType, flags: flags, - update: { context, body, attribute in - context.assumingMemoryBound(to: Context.self).pointee.update(body, attribute) + update: { + $0.assumingMemoryBound(to: AGAttributeTypeUpdateThunk.self).pointee.body($1, $2) }, - updateContext: contextPointer + updateContext: updateThunkPointer ) let pointer = UnsafeMutablePointer.allocate(capacity: 1) pointer.initialize(to: attributeType) diff --git a/Sources/Compute/Attribute/Rule/AnyRuleContext.swift b/Sources/Compute/Attribute/Rule/AnyRuleContext.swift index c837efe..1a5c1a8 100644 --- a/Sources/Compute/Attribute/Rule/AnyRuleContext.swift +++ b/Sources/Compute/Attribute/Rule/AnyRuleContext.swift @@ -1,5 +1,9 @@ import ComputeCxx +struct AGGraphWithUpdateThunk { + let body: () -> Void +} + public struct AnyRuleContext { public var attribute: AnyAttribute @@ -13,18 +17,15 @@ public struct AnyRuleContext { } public func update(body: () -> Void) { - struct Context { - let body: () -> Void - } // TODO: use silgen? withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: Context(body: escapingBody)) { contextPointer in + withUnsafePointer(to: AGGraphWithUpdateThunk(body: escapingBody)) { thunkPointer in __AGGraphWithUpdate( attribute, { - $0.assumingMemoryBound(to: Context.self).pointee.body() + $0.assumingMemoryBound(to: AGGraphWithUpdateThunk.self).pointee.body() }, - contextPointer + thunkPointer ) } } diff --git a/Sources/Compute/Attribute/Rule/Rule.swift b/Sources/Compute/Attribute/Rule/Rule.swift index f2e3970..b0ea682 100644 --- a/Sources/Compute/Attribute/Rule/Rule.swift +++ b/Sources/Compute/Attribute/Rule/Rule.swift @@ -54,23 +54,8 @@ extension Rule { } -private struct RuleCachedValueContext {} - -// AutoClass1::FUN_1afe81fc -func makeSomething( - graph: Graph, - update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void, - selfType: Any.Type, - bodyType: _AttributeBody.Type, - valueType: Any.Type -) -> UInt32 { - return graph.internAttributeType( - selfType: selfType, - bodyType: bodyType, - valueType: valueType, - flags: [], - update: update - ) +struct AGGraphReadCachedAttributeThunk { + let body: (AGUnownedGraphContextRef) -> UInt32 } extension Rule where Self: Hashable { @@ -109,26 +94,35 @@ extension Rule where Self: Hashable { bodyPtr: UnsafeRawPointer, update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) -> UnsafePointer { + let result = withUnsafePointer(to: self) { selfPointer in - var context = RuleCachedValueContext() - return __AGGraphReadCachedAttribute( - UInt64(hashValue), // TODO: bitPattern? - Metadata(Self.self), - UnsafeMutablePointer(mutating: selfPointer), - Metadata(Value.self), - options, - owner ?? .nil, - nil, - { - graph, - context in - - // TODO: fix as! Graph - // return makeSomething(graph: graph as! Graph, update: update, selfType: Self.self, bodyType: Self.self, valueType: Value.self) - return 0 - }, - &context - ) + return withoutActuallyEscaping(update) { escapingUpdate in + let thunk = AGGraphReadCachedAttributeThunk { graphContextRef in + let graph = __AGGraphContextGetGraph(graphContextRef) + return graph.takeUnretainedValue().internAttributeType( + selfType: Self.self, + bodyType: Self.self, + valueType: Value.self, + flags: [], + update: escapingUpdate + ) + } + return withUnsafePointer(to: thunk) { thunkPointer in + return __AGGraphReadCachedAttribute( + UInt64(hashValue), // TODO: bitPattern? + Metadata(Self.self), + UnsafeMutablePointer(mutating: selfPointer), + Metadata(Value.self), + options, + owner ?? .nil, + nil, + { + return $0.assumingMemoryBound(to: AGGraphReadCachedAttributeThunk.self).pointee.body($1) + }, + thunkPointer + ) + } + } } let pointer = result.assumingMemoryBound(to: Value.self) return UnsafePointer(pointer) diff --git a/Sources/Compute/Attribute/Rule/RuleContext.swift b/Sources/Compute/Attribute/Rule/RuleContext.swift index dbe714f..f4e12fe 100644 --- a/Sources/Compute/Attribute/Rule/RuleContext.swift +++ b/Sources/Compute/Attribute/Rule/RuleContext.swift @@ -1,7 +1,3 @@ -struct RuleContextUpdateContext { - let body: () -> Void -} - public struct RuleContext { public var attribute: Attribute @@ -11,18 +7,7 @@ public struct RuleContext { } public func update(body: () -> Void) { - // TODO: use silgen? - withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: RuleContextUpdateContext(body: escapingBody)) { contextPointer in - __AGGraphWithUpdate( - attribute.identifier, - { - $0.assumingMemoryBound(to: RuleContextUpdateContext.self).pointee.body() - }, - contextPointer - ) - } - } + AnyRuleContext(attribute: attribute.identifier).update(body: body) } public var value: Value { diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index 62783ac..2aa454a 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -1,40 +1,43 @@ import ComputeCxx -struct ContextVV { +struct AGGraphSetUpdateCallbackThunk { let body: () -> Void } -struct ContextAV { +struct AGGraphSetInvalidationCallbackThunk { let body: (AnyAttribute) -> Void } -struct ContextPV { - let body: (() -> Void) -> Void +struct AGGraphWithMainThreadHandlerThunk { + let body: () -> Void +} + +struct MainThreadHandlerThunk { + let body: ((UnsafeRawPointer) -> Void, UnsafeRawPointer) -> Void } extension Graph { public func onUpdate(_ handler: @escaping () -> Void) { - withUnsafePointer(to: ContextVV(body: handler)) { contextPointer in + withUnsafePointer(to: AGGraphSetUpdateCallbackThunk(body: handler)) { thunkPointer in __AGGraphSetUpdateCallback( self, { - $0.assumingMemoryBound(to: ContextVV.self).pointee.body() + $0.assumingMemoryBound(to: AGGraphSetUpdateCallbackThunk.self).pointee.body() }, - contextPointer + thunkPointer ) } } public func onInvalidation(_ handler: @escaping (AnyAttribute) -> Void) { - withUnsafePointer(to: ContextAV(body: handler)) { contextPointer in + withUnsafePointer(to: AGGraphSetInvalidationCallbackThunk(body: handler)) { thunkPointer in __AGGraphSetInvalidationCallback( self, { - // TODO: swap params around - $1.assumingMemoryBound(to: ContextAV.self).pointee.body($0) + $0.assumingMemoryBound(to: AGGraphSetInvalidationCallbackThunk.self).pointee.body($1) }, - contextPointer + thunkPointer ) } } @@ -61,36 +64,33 @@ extension Graph { return result } - // todo: delete @escaping public func withMainThreadHandler( + // TODO: does this need to be escaping? _ mainThreadHandler: @escaping (() -> Void) -> Void, - do body: @escaping () -> Void + do body: () -> Void ) { - // let bodyContext = ContextVV(body: body) - // let mainThreadHandlerContext = ContextPV(body: mainThreadHandler) - // withUnsafePointer(to: bodyContext) { bodyContextPointer in - // withUnsafePointer( - // to: mainThreadHandlerContext - // ) { mainThreadHandlerContextPointer in - // __AGGraphWithMainThreadHandler( - // self, - // { - // $0.assumingMemoryBound(to: ContextVV.self).pointee.body() - // }, - // bodyContextPointer, - // { thunk, context in - // - // - // // TODO: swap params - // context.assumingMemoryBound(to: ContextPV.self).pointee.body({ - // thunk() - // }) - // }, - // mainThreadHandlerContextPointer - // ) - // } - // - // } + withoutActuallyEscaping(body) { escapingBody in + withUnsafePointer(to: AGGraphWithMainThreadHandlerThunk(body: escapingBody)) { thunkPointer in + let mainThreadHandlerThunk = MainThreadHandlerThunk(body: { handler, context in + mainThreadHandler({ + handler(context) + }) + }) + withUnsafePointer(to: mainThreadHandlerThunk) { mainThreadHandlerThunkPointer in + __AGGraphWithMainThreadHandler( + self, + { + $0.assumingMemoryBound(to: AGGraphWithMainThreadHandlerThunk.self).pointee.body() + }, + thunkPointer, + { + $0.assumingMemoryBound(to: MainThreadHandlerThunk.self).pointee.body($1, $2) + }, + mainThreadHandlerThunkPointer + ) + } + } + } } } diff --git a/Sources/Compute/Graph/Subgraph.swift b/Sources/Compute/Graph/Subgraph.swift index 3dd1426..067ba6b 100644 --- a/Sources/Compute/Graph/Subgraph.swift +++ b/Sources/Compute/Graph/Subgraph.swift @@ -1,23 +1,23 @@ import ComputeCxx -struct SubgraphContextVV { +struct AGSubgraphAddObserverThunk { let body: () -> Void } -struct SubgraphContextAV { +struct AGSubgraphApplyThunk { let body: (AnyAttribute) -> Void } extension Subgraph { public func addObserver(_ observer: @escaping () -> Void) -> Int { - let result = withUnsafePointer(to: SubgraphContextVV(body: observer)) { contextPointer in + let result = withUnsafePointer(to: AGSubgraphAddObserverThunk(body: observer)) { thunkPointer in return __AGSubgraphAddObserver( self, { - $0.assumingMemoryBound(to: SubgraphContextVV.self).pointee.body() + $0.assumingMemoryBound(to: AGSubgraphAddObserverThunk.self).pointee.body() }, - contextPointer + thunkPointer ) } return Int(result) // TODO: where is this converted? @@ -36,15 +36,14 @@ extension Subgraph { public func forEach(_ flags: AGAttributeFlags, _ body: (AnyAttribute) -> Void) { withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: SubgraphContextAV(body: escapingBody)) { contextPointer in + withUnsafePointer(to: AGSubgraphApplyThunk(body: escapingBody)) { thunkPointer in __AGSubgraphApply( self, flags, { - // TODO: swap params - $1.assumingMemoryBound(to: SubgraphContextAV.self).pointee.body($0) + $0.assumingMemoryBound(to: AGSubgraphApplyThunk.self).pointee.body($1) }, - contextPointer + thunkPointer ) } } diff --git a/Sources/Compute/Runtime/Enum.swift b/Sources/Compute/Runtime/Enum.swift index d8fb84d..465d23c 100644 --- a/Sources/Compute/Runtime/Enum.swift +++ b/Sources/Compute/Runtime/Enum.swift @@ -1,6 +1,6 @@ import ComputeCxx -struct AGTypeApplyEnumDataContext { +struct AGTypeApplyEnumDataThunk { let body: (Int, Any.Type, UnsafeRawPointer) -> Void } @@ -9,21 +9,20 @@ public func withUnsafePointerToEnumCase( do body: (Int, Any.Type, UnsafeRawPointer) -> Void ) -> Bool { return withoutActuallyEscaping(body) { escapingBody in - let context = AGTypeApplyEnumDataContext(body: escapingBody) - return withUnsafePointer(to: context) { contextPointer in + return withUnsafePointer(to: AGTypeApplyEnumDataThunk(body: escapingBody)) { thunkPointer in return __AGTypeApplyEnumData( Metadata(Value.self), enumValue, { - $0.assumingMemoryBound(to: AGTypeApplyEnumDataContext.self).pointee.body(Int($1), $2.type, $3) + $0.assumingMemoryBound(to: AGTypeApplyEnumDataThunk.self).pointee.body(Int($1), $2.type, $3) }, - contextPointer + thunkPointer ) } } } -struct AGTypeApplyMutableEnumDataContext { +struct AGTypeApplyMutableEnumDatThunk { let body: (Int, Any.Type, UnsafeMutableRawPointer) -> Void } @@ -32,15 +31,14 @@ public func withUnsafeMutablePointerToEnumCase( do body: (Int, Any.Type, UnsafeMutableRawPointer) -> Void ) -> Bool { return withoutActuallyEscaping(body) { escapingBody in - let context = AGTypeApplyMutableEnumDataContext(body: escapingBody) - return withUnsafePointer(to: context) { contextPointer in + return withUnsafePointer(to: AGTypeApplyMutableEnumDatThunk(body: escapingBody)) { thunkPointer in return __AGTypeApplyMutableEnumData( Metadata(Value.self), enumValue, { - $0.assumingMemoryBound(to: AGTypeApplyMutableEnumDataContext.self).pointee.body(Int($1), $2.type, $3) + $0.assumingMemoryBound(to: AGTypeApplyMutableEnumDatThunk.self).pointee.body(Int($1), $2.type, $3) }, - contextPointer + thunkPointer ) } } diff --git a/Sources/Compute/Runtime/Metadata.swift b/Sources/Compute/Runtime/Metadata.swift index 138de7a..ce5e478 100644 --- a/Sources/Compute/Runtime/Metadata.swift +++ b/Sources/Compute/Runtime/Metadata.swift @@ -1,22 +1,25 @@ import ComputeCxx import Foundation +struct AGTypeApplyFieldsThunk { + var body: (UnsafePointer, Int, Any.Type) -> Void +} + +struct AGTypeApplyFields2Thunk { + var body: (UnsafePointer, Int, Any.Type) -> Bool +} + public func forEachField(of type: Any.Type, do body: (UnsafePointer, Int, Any.Type) -> Void) { - struct Context { - var body: (UnsafePointer, Int, Any.Type) -> Void - } withoutActuallyEscaping(body) { escapingClosure in - var context = Context(body: escapingClosure) - withUnsafeMutablePointer(to: &context) { contextPointer in + withUnsafePointer(to: AGTypeApplyFieldsThunk(body: escapingClosure)) { thunkPointer in __AGTypeApplyFields( Metadata(type), - { name, offset, metadata, context in - guard let context = context?.assumingMemoryBound(to: Context.self).pointee else { - return - } - context.body(name, offset, metadata.type) - }, contextPointer) + { + $0.assumingMemoryBound(to: AGTypeApplyFieldsThunk.self).pointee.body($1, $2, $3.type) + }, + thunkPointer + ) } } } @@ -39,16 +42,15 @@ extension Metadata { } return withoutActuallyEscaping(body) { escapingClosure in - var context = Context(body: escapingClosure) - return withUnsafeMutablePointer(to: &context) { contextPointer in + return withUnsafePointer(to: AGTypeApplyFields2Thunk(body: escapingClosure)) { thunkPointer in return __AGTypeApplyFields2( - self, options, - { name, offsetOrIndex, metadata, context in - guard let context = context?.assumingMemoryBound(to: Context.self).pointee else { - return false - } - return context.body(name, offsetOrIndex, metadata.type) - }, contextPointer) + self, + options, + { + return $0.assumingMemoryBound(to: AGTypeApplyFields2Thunk.self).pointee.body($1, $2, $3.type) + }, + thunkPointer + ) } } } diff --git a/Sources/Compute/Runtime/Tuple.swift b/Sources/Compute/Runtime/Tuple.swift index e16218e..1e68e83 100644 --- a/Sources/Compute/Runtime/Tuple.swift +++ b/Sources/Compute/Runtime/Tuple.swift @@ -1,15 +1,17 @@ +struct AGTupleWithBufferThunk { + let body: (UnsafeMutableTuple) -> Void +} + public func withUnsafeTuple(of type: TupleType, count: Int, _ body: (UnsafeMutableTuple) -> Void) { - struct Context { - let body: (UnsafeMutableTuple) -> Void - } withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: Context(body: escapingBody)) { contextPointer in - // TODO: why didn't I call function from implementation? + withUnsafePointer(to: AGTupleWithBufferThunk(body: escapingBody)) { thunkPointer in __AGTupleWithBuffer( type, count, - { $1.assumingMemoryBound(to: Context.self).pointee.body($0) }, - contextPointer + { + $0.assumingMemoryBound(to: AGTupleWithBufferThunk.self).pointee.body($1) + }, + thunkPointer ) } } diff --git a/Sources/ComputeCxx/Attribute/AttributeType.h b/Sources/ComputeCxx/Attribute/AttributeType.h index c949e74..6e2759c 100644 --- a/Sources/ComputeCxx/Attribute/AttributeType.h +++ b/Sources/ComputeCxx/Attribute/AttributeType.h @@ -36,7 +36,6 @@ class AttributeType { Unknown0x20 = 1 << 5, // 0x20 // used in update_main_refs }; - // TODO: closure context here is first param, is this consistent??? using UpdateFunction = void (*)(const void *context, void *body, AttributeID attribute); private: diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index dedc95b..ca1cb22 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -171,7 +171,7 @@ class Node { } } - void *get_self(const AttributeType &type) const; // TODO: inline + void *get_self(const AttributeType &type) const; void update_self(const Graph &graph, void *new_self); void destroy_self(const Graph &graph); diff --git a/Sources/ComputeCxx/Closure/ClosureFunction.h b/Sources/ComputeCxx/Closure/ClosureFunction.h index 3bc4093..76b5135 100644 --- a/Sources/ComputeCxx/Closure/ClosureFunction.h +++ b/Sources/ComputeCxx/Closure/ClosureFunction.h @@ -10,53 +10,47 @@ namespace AG { template class ClosureFunction { public: using Context = const void *_Nullable; - using Callable = AG_SWIFT_CC(swift) ReturnType (*_Nullable)(Args..., Context AG_SWIFT_CONTEXT); + using Callable = AG_SWIFT_CC(swift) ReturnType (*_Nullable)(Context AG_SWIFT_CONTEXT, Args...); private: Callable _function; Context _context; public: - inline ClosureFunction(Callable function, Context context) : _function(function), _context(context) { + inline ClosureFunction(Callable function, Context context) noexcept : _function(function), _context(context) { ::swift::swift_retain((::swift::HeapObject *)_context); } - inline ClosureFunction(std::nullptr_t = nullptr) : _function(nullptr), _context(nullptr) {} + inline ClosureFunction() : _function(), _context() {} + inline ClosureFunction(std::nullptr_t) : _function(nullptr), _context(nullptr) {} inline ~ClosureFunction() { ::swift::swift_release((::swift::HeapObject *)_context); } operator bool() { return _function != nullptr; } const ReturnType operator()(Args... args) const noexcept { - // TODO: check _context is first or last parameter - return _function(std::forward(args)..., _context); + return _function(_context, std::forward(args)...); } }; -// void * template requires std::is_pointer_v using ClosureFunctionVP = ClosureFunction; -// void template requires std::same_as using ClosureFunctionVV = ClosureFunction; -// unsigned long, AGUnownedGraphContext * template requires std::unsigned_integral using ClosureFunctionCI = ClosureFunction; -// void, void * template requires std::same_as using ClosureFunctionPV = ClosureFunction; -// void, unsigned int template requires std::same_as && std::unsigned_integral using ClosureFunctionAV = ClosureFunction; -// bool, unsigned int template requires std::same_as && std::unsigned_integral using ClosureFunctionAB = ClosureFunction; diff --git a/Sources/ComputeCxx/Graph/AGGraph-Private.h b/Sources/ComputeCxx/Graph/AGGraph-Private.h index 04630ba..7939330 100644 --- a/Sources/ComputeCxx/Graph/AGGraph-Private.h +++ b/Sources/ComputeCxx/Graph/AGGraph-Private.h @@ -8,12 +8,8 @@ CF_ASSUME_NONNULL_BEGIN -struct AGUnownedGraph { - AG::Graph value; -}; - struct AGUnownedGraphContext { - AG::Graph::Context value; + AG::Graph *graph; }; struct AGGraphStorage { diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 4d01b9f..31ea72f 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -237,7 +237,7 @@ void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags) { } void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidating, - void (*modify)(void *body, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + void (*modify)(const void *context AG_SWIFT_CONTEXT, void *body) AG_SWIFT_CC(swift), const void *context) { auto attribute_id = AG::AttributeID::from_storage(attribute); if (!attribute_id.is_direct()) { @@ -256,7 +256,7 @@ void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidat } bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, - bool (*predicate)(uint32_t attribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + bool (*predicate)(const void *context AG_SWIFT_CONTEXT, AGAttribute attribute) AG_SWIFT_CC(swift), const void *context) { auto attribute_id = AG::AttributeID::from_storage(attribute); attribute_id.validate_data_offset(); @@ -267,7 +267,7 @@ bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, } return subgraph->graph()->breadth_first_search(attribute_id, AG::Graph::SearchOptions(options), - AG::ClosureFunctionAB(predicate, context)); + AG::ClosureFunctionAB(predicate, context)); } #pragma mark - Cached attributes @@ -277,7 +277,7 @@ namespace { void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &metadata, void *body, const AG::swift::metadata &value_type, AGCachedValueOptions options, AG::AttributeID attribute, uint8_t *state_out, - AG::ClosureFunctionCI closure) { + AG::ClosureFunctionCI closure) { auto current_update = AG::Graph::current_update(); AG::Graph::UpdateStack *stack = current_update.tag() == 0 ? current_update.get() : nullptr; @@ -317,16 +317,16 @@ void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &meta void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, AGCachedValueOptions options, AGAttribute attribute, bool *changed_out, - uint32_t (*closure)(AGUnownedGraphRef graph, const void *context AG_SWIFT_CONTEXT) - AG_SWIFT_CC(swift), + uint32_t (*closure)(const void *context AG_SWIFT_CONTEXT, + AGUnownedGraphContextRef graph) AG_SWIFT_CC(swift), const void *closure_context) { auto metadata = reinterpret_cast(type); auto value_metadata = reinterpret_cast(value_type); uint8_t state = 0; - void *value = read_cached_attribute(identifier, *metadata, body, *value_metadata, options, - AG::AttributeID::from_storage(attribute), &state, - AG::ClosureFunctionCI(closure, closure_context)); + void *value = read_cached_attribute( + identifier, *metadata, body, *value_metadata, options, AG::AttributeID::from_storage(attribute), &state, + AG::ClosureFunctionCI(closure, closure_context)); if (changed_out) { *changed_out = state & 1 ? true : false; } @@ -667,8 +667,9 @@ void AGGraphWithoutUpdate(void (*function)(const void *context AG_SWIFT_CONTEXT) void AGGraphWithMainThreadHandler(AGGraphRef graph, void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *body_context, - void (*main_thread_handler)(void (*thunk)(const void *), - const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + void (*main_thread_handler)(const void *context AG_SWIFT_CONTEXT, + void (*trampoline_thunk)(const void *), + const void *trampoline) AG_SWIFT_CC(swift), const void *main_thread_handler_context) { auto context = AG::Graph::Context::from_cf(graph); context->graph().with_main_handler(AG::ClosureFunctionVV(function, body_context), main_thread_handler, diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 329f775..0ba79b1 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -16,7 +16,6 @@ CF_EXTERN_C_BEGIN // MARK: CFType typedef struct CF_BRIDGED_TYPE(id) AGGraphStorage *AGGraphRef AG_SWIFT_NAME(Graph); -typedef struct AGUnownedGraph *AGUnownedGraphRef; typedef struct AGUnownedGraphContext *AGUnownedGraphContextRef; CF_EXPORT @@ -120,7 +119,7 @@ void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags); CF_EXPORT CF_REFINED_FOR_SWIFT void AGGraphMutateAttribute(AGAttribute attribute, AGTypeID type, bool invalidating, - const void (*modify)(const void *context AG_SWIFT_CONTEXT, void *body) AG_SWIFT_CC(swift), + void (*modify)(const void *context AG_SWIFT_CONTEXT, void *body) AG_SWIFT_CC(swift), const void *context); typedef CF_OPTIONS(uint32_t, AGSearchOptions) { @@ -132,7 +131,7 @@ typedef CF_OPTIONS(uint32_t, AGSearchOptions) { CF_EXPORT CF_REFINED_FOR_SWIFT bool AGGraphSearch(AGAttribute attribute, AGSearchOptions options, - bool (*predicate)(AGAttribute attribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + bool (*predicate)(const void *context AG_SWIFT_CONTEXT, AGAttribute attribute) AG_SWIFT_CC(swift), const void *context); // MARK: Cached attributes @@ -143,8 +142,8 @@ CF_EXPORT CF_REFINED_FOR_SWIFT void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, AGCachedValueOptions options, AGAttribute attribute, bool *_Nullable changed_out, - uint32_t (*closure)(AGUnownedGraphContextRef graph_context, - const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + uint32_t (*closure)(const void *context AG_SWIFT_CONTEXT, + AGUnownedGraphContextRef graph_context) AG_SWIFT_CC(swift), const void *closure_context); CF_EXPORT @@ -224,7 +223,7 @@ void AGGraphInvalidateValue(AGAttribute attribute); CF_EXPORT CF_REFINED_FOR_SWIFT void AGGraphSetInvalidationCallback(AGGraphRef graph, - void (*callback)(AGAttribute, const void *context AG_SWIFT_CONTEXT) + void (*callback)(const void *context AG_SWIFT_CONTEXT, AGAttribute) AG_SWIFT_CC(swift), const void *callback_context); @@ -289,8 +288,9 @@ CF_REFINED_FOR_SWIFT void AGGraphWithMainThreadHandler(AGGraphRef graph, void (*function)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *body_context, - void (*main_thread_handler)(void (*thunk)(const void *), - const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + void (*main_thread_handler)(const void *context AG_SWIFT_CONTEXT, + void (*trampoline_thunk)(const void *), + const void *trampoline) AG_SWIFT_CC(swift), const void *main_thread_handler_context); // MARK: Values diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 4320a26..5336cf0 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -390,9 +390,8 @@ void Graph::call_main_handler(void *context, void (*body)(void *)) { _main_handler = nullptr; _main_handler_context = nullptr; - // TODO: is context passed in as arg, or is it _main_handler_context MainTrampoline trampoline = {this, current_update_thread, context, body}; - main_handler(MainTrampoline::thunk, &trampoline); + main_handler(main_handler_context, MainTrampoline::thunk, &trampoline); _main_handler = main_handler; _main_handler_context = main_handler_context; diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index f8318f9..bb5c452 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -67,7 +67,8 @@ class Graph { void push_back(TreeElementNodePair pair) { _nodes.push_back(pair); }; }; - typedef void (*MainHandler)(void (*thunk)(const void *), const void *thunk_context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift); + typedef void (*MainHandler)(const void *_Nullable context AG_SWIFT_CONTEXT, void (*trampoline_thunk)(const void *), + const void *trampoline) AG_SWIFT_CC(swift); private: static Graph *_all_graphs; @@ -233,7 +234,7 @@ class Graph { void remove_node(data::ptr node); bool breadth_first_search(AttributeID attribute, SearchOptions options, - ClosureFunctionAB predicate) const; + ClosureFunctionAB predicate) const; void did_allocate_node_value(size_t size) { _num_node_value_bytes += size; }; void did_destroy_node_value(size_t size) { _num_node_value_bytes -= size; }; diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index cb5103f..887f763 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -52,8 +52,8 @@ AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute) { AG::Graph::Context *context = AG::Graph::Context::from_cf(graph); - AG::Subgraph *subgraph = - new (&instance->subgraph) AG::Subgraph((AG::SubgraphObject *)instance, *context, AG::AttributeID::from_storage(attribute)); + AG::Subgraph *subgraph = new (&instance->subgraph) + AG::Subgraph((AG::SubgraphObject *)instance, *context, AG::AttributeID::from_storage(attribute)); return instance; }; @@ -253,7 +253,7 @@ void AGSubgraphUpdate(AGSubgraphRef subgraph, uint8_t flags) { } void AGSubgraphApply(AGSubgraphRef subgraph, AGAttributeFlags flags, - void (*function)(AGAttribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + void (*function)(const void *context AG_SWIFT_CONTEXT, AGAttribute) AG_SWIFT_CC(swift), const void *function_context) { if (subgraph->subgraph == nullptr) { return; diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index 959fa0e..67dc938 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -110,7 +110,7 @@ void AGSubgraphUpdate(AGSubgraphRef subgraph, uint8_t flags); CF_EXPORT CF_REFINED_FOR_SWIFT void AGSubgraphApply(AGSubgraphRef subgraph, AGAttributeFlags flags, - void (*function)(AGAttribute, const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), + void (*function)(const void *context AG_SWIFT_CONTEXT, AGAttribute) AG_SWIFT_CC(swift), const void *function_context); // MARK: Tree diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 487f542..dd695ec 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -873,7 +873,7 @@ void Subgraph::notify_observers() { #pragma mark - Cache data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata &metadata, void *body, - ClosureFunctionCI closure) { + ClosureFunctionCI closure) { if (_cache == nullptr) { _cache = (data::ptr)alloc_bytes(sizeof(NodeCache), 7); new (&_cache) NodeCache(); @@ -891,7 +891,8 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata type->equatable = equatable; type->last_item = nullptr; type->first_item = nullptr; - type->type_id = closure(reinterpret_cast(_graph)); + auto graph_context = AGUnownedGraphContext(_graph); + type->type_id = closure(&graph_context); _cache->types().insert(&metadata, type); } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 45e1789..ad6a148 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -205,7 +205,7 @@ class Subgraph : public data::zone { // FIXME: not AGUnownedGraphContextRef data::ptr cache_fetch(uint64_t identifier, const swift::metadata &type, void *body, - ClosureFunctionCI closure); + ClosureFunctionCI closure); void cache_insert(data::ptr node); void cache_collect(); diff --git a/Sources/ComputeCxx/Swift/AGTuple.cpp b/Sources/ComputeCxx/Swift/AGTuple.cpp index 3156264..f76fcad 100644 --- a/Sources/ComputeCxx/Swift/AGTuple.cpp +++ b/Sources/ComputeCxx/Swift/AGTuple.cpp @@ -189,8 +189,10 @@ void AGTupleDestroyElement(AGTupleType tuple_type, void *tuple_value, uint32_t i } void AGTupleWithBuffer(AGTupleType tuple_type, size_t count, - const void (*function)(const AGUnsafeMutableTuple mutable_tuple, const void *context), + void (*function)(const void *context AG_SWIFT_CONTEXT, const AGUnsafeMutableTuple mutable_tuple) + AG_SWIFT_CC(swift), const void *context) { + // TODO: why didn't I call function from implementation? auto metadata = reinterpret_cast(tuple_type); auto buffer_size = metadata->vw_stride() * count; if (buffer_size <= 0x1000) { diff --git a/Sources/ComputeCxx/Swift/AGTuple.h b/Sources/ComputeCxx/Swift/AGTuple.h index f0c5ac2..bf88187 100644 --- a/Sources/ComputeCxx/Swift/AGTuple.h +++ b/Sources/ComputeCxx/Swift/AGTuple.h @@ -63,7 +63,7 @@ size_t AGTupleElementOffsetChecked(AGTupleType tuple_type, uint32_t index, AGTyp CF_EXPORT CF_REFINED_FOR_SWIFT -void *AGTupleGetElement(AGTupleType tuple_type, void *tuple_value, uint32_t index, const void *element_value, +void *AGTupleGetElement(AGTupleType tuple_type, void *tuple_value, uint32_t index, void *element_value, AGTypeID element_type, AGTupleCopyOptions options); CF_EXPORT @@ -83,7 +83,8 @@ void AGTupleDestroyElement(AGTupleType tuple_type, void *tuple_value, uint32_t i CF_EXPORT CF_REFINED_FOR_SWIFT void AGTupleWithBuffer(AGTupleType tuple_type, size_t count, - const void (*function)(const AGUnsafeMutableTuple mutable_tuple, const void *context), + void (*function)(const void *context AG_SWIFT_CONTEXT, const AGUnsafeMutableTuple mutable_tuple) + AG_SWIFT_CC(swift), const void *context); CF_EXTERN_C_END diff --git a/Sources/ComputeCxx/Swift/AGType.cpp b/Sources/ComputeCxx/Swift/AGType.cpp index d151c73..41d18b6 100644 --- a/Sources/ComputeCxx/Swift/AGType.cpp +++ b/Sources/ComputeCxx/Swift/AGType.cpp @@ -74,16 +74,18 @@ const char *AGTypeNominalDescriptorName(AGTypeID typeID) { } void AGTypeApplyFields(AGTypeID typeID, - void (*body)(const char *field_name, size_t field_size, AGTypeID field_type, void *context), - void *context) { + void (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *context) { class Visitor : public AG::swift::metadata_visitor { private: - void (*_body)(const char *field_name, size_t field_size, AGTypeID field_type, void *context); - void *_context; + void (*_body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, + AGTypeID field_type) AG_SWIFT_CC(swift); + const void *_context; public: - Visitor(void (*body)(const char *field_name, size_t field_size, AGTypeID field_type, void *context), - void *context) + Visitor(void (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, AGTypeID field_type) AG_SWIFT_CC(swift), + const void *context) : _body(body), _context(context) {} bool unknown_result() const override { return true; } @@ -93,7 +95,7 @@ void AGTypeApplyFields(AGTypeID typeID, auto field_type = type.mangled_type_name_ref(mangled_name, true, nullptr); if (field_type) { auto field_name = field.FieldName.get(); - _body(field_name, field_offset, AGTypeID(field_type), _context); + _body(_context, field_name, field_offset, AGTypeID(field_type)); } return true; } @@ -106,18 +108,21 @@ void AGTypeApplyFields(AGTypeID typeID, } bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, - bool (*body)(const char *field_name, size_t field_size, AGTypeID field_type, void *context), - void *context) { + bool (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *context) { class Visitor : public AG::swift::metadata_visitor { private: AGTypeApplyOptions _options; - bool (*_body)(const char *field_name, size_t field_size, AGTypeID field_type, void *context); - void *_context; + bool (*_body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, + AGTypeID field_type) AG_SWIFT_CC(swift); + const void *_context; public: Visitor(AGTypeApplyOptions options, - bool (*body)(const char *field_name, size_t field_size, AGTypeID field_type, void *context), - void *context) + bool (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *context) : _options(options), _body(body), _context(context) {} bool unknown_result() const override { return _options & 2; } @@ -129,7 +134,7 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, return unknown_result(); } auto field_name = field.FieldName.get(); - _body(field_name, field_offset, AGTypeID(field_type), _context); + _body(_context, field_name, field_offset, AGTypeID(field_type)); return field_name != nullptr; } bool visit_case(const AG::swift::metadata &type, const AG::swift::field_record &field, @@ -140,7 +145,7 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, return unknown_result(); } auto field_name = field.FieldName.get(); - _body(field_name, index, AGTypeID(field_type), _context); + _body(_context, field_name, index, AGTypeID(field_type)); return field_name != nullptr; } }; @@ -176,7 +181,8 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, } bool AGTypeApplyEnumData(AGTypeID typeID, void *value, - void (*body)(const void *context, uint32_t tag, AGTypeID field_type, void *field_value), + void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, + void *field_value) AG_SWIFT_CC(swift), const void *context) { auto type = reinterpret_cast(typeID); auto value_witness = type->getValueWitnesses(); @@ -219,7 +225,7 @@ bool AGTypeApplyEnumData(AGTypeID typeID, void *value, } bool AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, - void (*body)(const void *context, uint32_t tag, AGTypeID field_type, void *field_value), + void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, void *field_value) AG_SWIFT_CC(swift), const void *context) { auto type = reinterpret_cast(typeID); auto value_witness = type->getValueWitnesses(); diff --git a/Sources/ComputeCxx/Swift/AGType.h b/Sources/ComputeCxx/Swift/AGType.h index e100ddd..7f716e7 100644 --- a/Sources/ComputeCxx/Swift/AGType.h +++ b/Sources/ComputeCxx/Swift/AGType.h @@ -63,16 +63,16 @@ typedef CF_OPTIONS(uint32_t, AGTypeApplyOptions) { CF_EXPORT CF_REFINED_FOR_SWIFT void AGTypeApplyFields(AGTypeID typeID, - void (*body)(const char *field_name, size_t field_size, AGTypeID field_type, - void *_Nullable context), - void *_Nullable context); + void (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *context); CF_EXPORT CF_REFINED_FOR_SWIFT bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, - bool (*body)(const char *field_name, size_t field_size, AGTypeID field_type, - void *_Nullable context), - void *_Nullable context); + bool (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *context); CF_EXPORT CF_REFINED_FOR_SWIFT From cd4ed52ce45efbb784fb8d9ffa9c82f8c4aef959 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 11 Apr 2025 14:37:53 +0200 Subject: [PATCH 60/74] Add default implementations for AttributeIDList --- Sources/ComputeCxx/Attribute/AttributeIDList.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/AttributeIDList.h b/Sources/ComputeCxx/Attribute/AttributeIDList.h index 96e7dca..54457a6 100644 --- a/Sources/ComputeCxx/Attribute/AttributeIDList.h +++ b/Sources/ComputeCxx/Attribute/AttributeIDList.h @@ -44,8 +44,8 @@ class AttributeIDIterator { class AttributeIDList { public: - virtual AttributeIDIterator begin(); - virtual AttributeIDIterator end(); + virtual AttributeIDIterator begin() {}; + virtual AttributeIDIterator end() {}; }; class AttributeIDList1 : public AttributeIDList { From de5e10dc8be8819bf437372a2d563fb9719bdf53 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 11 Apr 2025 14:49:29 +0200 Subject: [PATCH 61/74] Test fixes --- Sources/Compute/Runtime/Metadata.swift | 4 ---- Sources/ComputeCxx/Swift/AGType.cpp | 22 ++++++++++------------ Sources/ComputeCxx/Swift/AGType.h | 8 ++++---- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Sources/Compute/Runtime/Metadata.swift b/Sources/Compute/Runtime/Metadata.swift index ce5e478..95fd483 100644 --- a/Sources/Compute/Runtime/Metadata.swift +++ b/Sources/Compute/Runtime/Metadata.swift @@ -37,10 +37,6 @@ extension Metadata { public func forEachField(options: ApplyOptions, do body: (UnsafePointer, Int, Any.Type) -> Bool) -> Bool { - struct Context { - var body: (UnsafePointer, Int, Any.Type) -> Bool - } - return withoutActuallyEscaping(body) { escapingClosure in return withUnsafePointer(to: AGTypeApplyFields2Thunk(body: escapingClosure)) { thunkPointer in return __AGTypeApplyFields2( diff --git a/Sources/ComputeCxx/Swift/AGType.cpp b/Sources/ComputeCxx/Swift/AGType.cpp index 41d18b6..aed4cee 100644 --- a/Sources/ComputeCxx/Swift/AGType.cpp +++ b/Sources/ComputeCxx/Swift/AGType.cpp @@ -74,17 +74,16 @@ const char *AGTypeNominalDescriptorName(AGTypeID typeID) { } void AGTypeApplyFields(AGTypeID typeID, - void (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, - AGTypeID field_type) AG_SWIFT_CC(swift), + void (*body)(const void *context, const char *field_name, size_t field_size, + AGTypeID field_type), const void *context) { class Visitor : public AG::swift::metadata_visitor { private: - void (*_body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, - AGTypeID field_type) AG_SWIFT_CC(swift); + void (*_body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type); const void *_context; public: - Visitor(void (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, AGTypeID field_type) AG_SWIFT_CC(swift), + Visitor(void (*body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type), const void *context) : _body(body), _context(context) {} @@ -108,20 +107,18 @@ void AGTypeApplyFields(AGTypeID typeID, } bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, - bool (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, - AGTypeID field_type) AG_SWIFT_CC(swift), + bool (*body)(const void *context, const char *field_name, size_t field_size, + AGTypeID field_type), const void *context) { class Visitor : public AG::swift::metadata_visitor { private: AGTypeApplyOptions _options; - bool (*_body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, - AGTypeID field_type) AG_SWIFT_CC(swift); + bool (*_body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type); const void *_context; public: Visitor(AGTypeApplyOptions options, - bool (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, - AGTypeID field_type) AG_SWIFT_CC(swift), + bool (*body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type), const void *context) : _options(options), _body(body), _context(context) {} @@ -225,7 +222,8 @@ bool AGTypeApplyEnumData(AGTypeID typeID, void *value, } bool AGTypeApplyMutableEnumData(AGTypeID typeID, void *value, - void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, void *field_value) AG_SWIFT_CC(swift), + void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, + void *field_value) AG_SWIFT_CC(swift), const void *context) { auto type = reinterpret_cast(typeID); auto value_witness = type->getValueWitnesses(); diff --git a/Sources/ComputeCxx/Swift/AGType.h b/Sources/ComputeCxx/Swift/AGType.h index 7f716e7..ac6c148 100644 --- a/Sources/ComputeCxx/Swift/AGType.h +++ b/Sources/ComputeCxx/Swift/AGType.h @@ -63,15 +63,15 @@ typedef CF_OPTIONS(uint32_t, AGTypeApplyOptions) { CF_EXPORT CF_REFINED_FOR_SWIFT void AGTypeApplyFields(AGTypeID typeID, - void (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, - AGTypeID field_type) AG_SWIFT_CC(swift), + void (*body)(const void *context, const char *field_name, size_t field_size, + AGTypeID field_type), const void *context); CF_EXPORT CF_REFINED_FOR_SWIFT bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, - bool (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_size, - AGTypeID field_type) AG_SWIFT_CC(swift), + bool (*body)(const void *context, const char *field_name, size_t field_size, + AGTypeID field_type), const void *context); CF_EXPORT From 966b1e8a1f0575e3fa0ffecbafa21386cd99e1e4 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 11 Apr 2025 17:03:46 +0200 Subject: [PATCH 62/74] DebugServer fixes for util::objc_ptr --- Sources/ComputeCxx/Debug/Connection.cpp | 13 +++++++------ Sources/ComputeCxx/Debug/DebugServer.mm | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Sources/ComputeCxx/Debug/Connection.cpp b/Sources/ComputeCxx/Debug/Connection.cpp index 90053eb..ce78a51 100644 --- a/Sources/ComputeCxx/Debug/Connection.cpp +++ b/Sources/ComputeCxx/Debug/Connection.cpp @@ -8,15 +8,16 @@ DebugServer::Connection::Connection(DebugServer *server, int socket) { _server = server; _socket = socket; - _dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket, 0, dispatch_get_main_queue()); - dispatch_set_context(_dispatch_source, this); - dispatch_source_set_event_handler_f(_dispatch_source, handler); - dispatch_resume(_dispatch_source); + _dispatch_source = + util::objc_ptr(dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket, 0, dispatch_get_main_queue())); + dispatch_set_context(_dispatch_source.get(), this); + dispatch_source_set_event_handler_f(_dispatch_source.get(), handler); + dispatch_resume(_dispatch_source.get()); } DebugServer::Connection::~Connection() { - dispatch_source_set_event_handler(_dispatch_source, nullptr); - dispatch_set_context(_dispatch_source, nullptr); + dispatch_source_set_event_handler(_dispatch_source.get(), nullptr); + dispatch_set_context(_dispatch_source.get(), nullptr); _dispatch_source = nullptr; close(_socket); diff --git a/Sources/ComputeCxx/Debug/DebugServer.mm b/Sources/ComputeCxx/Debug/DebugServer.mm index 45323ff..5df50ed 100644 --- a/Sources/ComputeCxx/Debug/DebugServer.mm +++ b/Sources/ComputeCxx/Debug/DebugServer.mm @@ -99,10 +99,11 @@ return this; } - _dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, _socket, 0, dispatch_get_main_queue()); - dispatch_set_context(_dispatch_source, this); - dispatch_source_set_event_handler_f(_dispatch_source, accept_handler); - dispatch_resume(_dispatch_source); + _dispatch_source = + util::objc_ptr(dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, _socket, 0, dispatch_get_main_queue())); + dispatch_set_context(_dispatch_source.get(), this); + dispatch_source_set_event_handler_f(_dispatch_source.get(), accept_handler); + dispatch_resume(_dispatch_source.get()); char ip_str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &_ip_address, ip_str, sizeof(ip_str)); @@ -115,8 +116,8 @@ void DebugServer::shutdown() { if (auto dispatch_source = _dispatch_source) { - dispatch_source_set_event_handler(dispatch_source, nullptr); - dispatch_set_context(dispatch_source, nullptr); + dispatch_source_set_event_handler(dispatch_source.get(), nullptr); + dispatch_set_context(dispatch_source.get(), nullptr); _dispatch_source = nullptr; } if (_socket >= 0) { From b42e9cc0404373f5985e73932beb01ea6b4e85f2 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 11 Apr 2025 17:03:55 +0200 Subject: [PATCH 63/74] Subgraph fixes for util::cf_ptr --- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 6 +++--- Sources/Utilities/include/Utilities/CFPointer.h | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index dd695ec..534ff68 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -465,7 +465,7 @@ void Subgraph::update(uint8_t flags) { AG::vector, 32, uint64_t>>(); auto nodes_to_update = vector, 256, uint64_t>(); - stack.push(std::move(to_cf())); + stack.push(util::cf_ptr(to_cf())); _traversal_seed = _last_traversal_seed; bool thread_is_updating = false; @@ -474,7 +474,7 @@ void Subgraph::update(uint8_t flags) { util::cf_ptr object = stack.top(); stack.pop(); - Subgraph *subgraph = Subgraph::from_cf(object); + Subgraph *subgraph = Subgraph::from_cf(object.get()); if (subgraph) { while (subgraph->is_valid()) { @@ -488,7 +488,7 @@ void Subgraph::update(uint8_t flags) { // TODO: check child has 0x3 pointer tag that needs to be masked... if (flags & (child_subgraph->_flags.value3 | child_subgraph->_flags.value4) && child_subgraph->_traversal_seed != _traversal_seed) { - stack.push(std::move(child_subgraph->to_cf())); + stack.push(util::cf_ptr(child_subgraph->to_cf())); child_subgraph->_traversal_seed = _traversal_seed; } } diff --git a/Sources/Utilities/include/Utilities/CFPointer.h b/Sources/Utilities/include/Utilities/CFPointer.h index 8eda573..5b83548 100644 --- a/Sources/Utilities/include/Utilities/CFPointer.h +++ b/Sources/Utilities/include/Utilities/CFPointer.h @@ -7,12 +7,7 @@ CF_ASSUME_NONNULL_BEGIN namespace util { -template -concept AnyCFTypeRef = std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || std::is_same_v; - -template class cf_ptr { +template class cf_ptr { private: CFTypeRef _storage; From 1964fdeab5bab7863fe681ed3960b5ffe7613c70 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 11 Apr 2025 17:04:19 +0200 Subject: [PATCH 64/74] Delete duplicate code --- Sources/Utilities/include/Utilities/Heap.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Sources/Utilities/include/Utilities/Heap.h b/Sources/Utilities/include/Utilities/Heap.h index 5c99d7d..0c33fb7 100644 --- a/Sources/Utilities/include/Utilities/Heap.h +++ b/Sources/Utilities/include/Utilities/Heap.h @@ -65,14 +65,6 @@ template class InlineHeap : public Heap { InlineHeap() : Heap(_inline_buffer, _inline_size, 0) {} }; -template class InlineHeap : public Heap { - private: - char _inline_buffer[_inline_size] = {}; - - public: - InlineHeap() : Heap(_inline_buffer, _inline_size, 0) {} -}; - } // namespace util CF_ASSUME_NONNULL_END From 7f07c564602e84732cac4aa605cd9486f207592d Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 13 Apr 2025 20:23:46 +0200 Subject: [PATCH 65/74] Prevent implicit conversion of data::ptr to bool --- Sources/ComputeCxx/Attribute/AttributeID.cpp | 2 +- Sources/ComputeCxx/Attribute/AttributeID.h | 8 ++-- Sources/ComputeCxx/Attribute/Node/Node.cpp | 4 +- Sources/ComputeCxx/Data/Pointer.h | 10 ++-- Sources/ComputeCxx/Data/Table.cpp | 4 +- Sources/ComputeCxx/Data/Zone.cpp | 36 +++++++-------- Sources/ComputeCxx/Data/Zone.h | 5 +- Sources/ComputeCxx/External/ExternalTrace.cpp | 8 ++-- Sources/ComputeCxx/Graph/AGGraph.cpp | 5 +- Sources/ComputeCxx/Graph/Graph+Description.mm | 6 +-- Sources/ComputeCxx/Graph/Graph.cpp | 19 ++++---- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 32 ++++++------- .../ComputeCxx/Graph/Tree/AGTreeElement.cpp | 46 +++++++++---------- Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp | 8 ++-- Sources/ComputeCxx/Graph/Tree/TreeElement.h | 20 ++++++-- Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 4 +- Sources/ComputeCxx/Subgraph/AGSubgraph.h | 3 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 16 +++---- 18 files changed, 128 insertions(+), 108 deletions(-) diff --git a/Sources/ComputeCxx/Attribute/AttributeID.cpp b/Sources/ComputeCxx/Attribute/AttributeID.cpp index e6ed1bd..44c65a4 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.cpp +++ b/Sources/ComputeCxx/Attribute/AttributeID.cpp @@ -14,7 +14,7 @@ namespace AG { AttributeID AttributeIDNil = AttributeID::from_storage(2); RelativeAttributeID AttributeID::to_relative() const { - return RelativeAttributeID(((_value & ~KindMask) - page_ptr()) | kind()); + return RelativeAttributeID(((_value & ~KindMask) - page_ptr().offset()) | kind()); } std::optional AttributeID::size() const { diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index c78f143..faed16c 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -64,9 +64,9 @@ class AttributeID { }; explicit constexpr AttributeID() : _value(0) {}; - explicit AttributeID(data::ptr node) : _value(node | Kind::Direct) {}; - explicit AttributeID(data::ptr indirect_node) : _value(indirect_node | Kind::Indirect) {}; - explicit AttributeID(data::ptr indirect_node) : _value(indirect_node | Kind::Indirect) {}; + explicit AttributeID(data::ptr node) : _value(node.offset() | Kind::Direct) {}; + explicit AttributeID(data::ptr indirect_node) : _value(indirect_node.offset() | Kind::Indirect) {}; + explicit AttributeID(data::ptr indirect_node) : _value(indirect_node.offset() | Kind::Indirect) {}; constexpr uint32_t to_storage() const { return _value; } static constexpr AttributeID from_storage(uint32_t value) { return AttributeID(value); } @@ -154,7 +154,7 @@ class RelativeAttributeID { bool operator==(const RelativeAttributeID &other) const { return _value == other._value; } bool operator!=(const RelativeAttributeID &other) const { return _value != other._value; } - AttributeID resolve(data::ptr page_ptr) { return AttributeID(page_ptr + _value); } + AttributeID resolve(data::ptr page_ptr) { return AttributeID(page_ptr.offset() + _value); } }; extern AttributeID AttributeIDNil; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.cpp b/Sources/ComputeCxx/Attribute/Node/Node.cpp index 0fe4a29..4fcc4d3 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.cpp +++ b/Sources/ComputeCxx/Attribute/Node/Node.cpp @@ -62,8 +62,8 @@ void Node::allocate_value(Graph &graph, data::zone &zone) { if (_flags.has_indirect_value()) { _value = zone.alloc_bytes_recycle(sizeof(void *), sizeof(void *) - 1); - void *value = zone.alloc_persistent(size); - *(static_cast>(_value)).get() = value; + void *persistent_buffer = zone.alloc_persistent(size); + *_value.unsafe_cast().get() = persistent_buffer; } else { if (size <= 0x10) { _value = zone.alloc_bytes_recycle(uint32_t(size), uint32_t(alignment)); diff --git a/Sources/ComputeCxx/Data/Pointer.h b/Sources/ComputeCxx/Data/Pointer.h index 4ca4e69..5529e48 100644 --- a/Sources/ComputeCxx/Data/Pointer.h +++ b/Sources/ComputeCxx/Data/Pointer.h @@ -50,7 +50,7 @@ template class ptr { return ptr((_offset + alignment_mask) & ~alignment_mask); }; - operator bool() const noexcept { return _offset != 0; }; + explicit operator bool() const noexcept { return _offset != 0; }; std::add_lvalue_reference_t operator*() const noexcept { return *get(); }; T *_Nonnull operator->() const noexcept { return get(); }; @@ -64,12 +64,16 @@ template class ptr { bool operator>(difference_type offset) const noexcept { return _offset > offset; }; bool operator>=(difference_type offset) const noexcept { return _offset >= offset; }; - template ptr operator+(difference_type shift) const noexcept { return ptr(_offset + shift); }; - template ptr operator-(difference_type shift) const noexcept { return ptr(_offset - shift); }; + template ptr operator+(difference_type shift) const noexcept { return ptr(_offset + shift); }; + template ptr operator-(difference_type shift) const noexcept { return ptr(_offset - shift); }; template difference_type operator-(const ptr &other) const noexcept { return _offset - other._offset; }; + + template ptr advanced(difference_type shift) const noexcept { return ptr(_offset + shift); }; + + template ptr unsafe_cast() const { return ptr(_offset); } }; } // namespace data diff --git a/Sources/ComputeCxx/Data/Table.cpp b/Sources/ComputeCxx/Data/Table.cpp index fa397ac..de60af4 100644 --- a/Sources/ComputeCxx/Data/Table.cpp +++ b/Sources/ComputeCxx/Data/Table.cpp @@ -207,7 +207,7 @@ void table::dealloc_page_locked(ptr page) { _num_used_pages -= num_pages; // convert the page address (starts at 512) to an index (starts at 0) - int32_t page_index = (page / page_size) - 1; + int32_t page_index = (page.offset() / page_size) - 1; for (int32_t i = 0; i != num_pages; i += 1) { int32_t next_page_index = page_index + i; @@ -254,7 +254,7 @@ uint64_t table::raw_page_seed(ptr page) { lock(); - uint32_t page_index = (page / page_size) - 1; + uint32_t page_index = (page.offset() / page_size) - 1; uint32_t map_index = page_index / pages_per_map; uint64_t result = 0; diff --git a/Sources/ComputeCxx/Data/Zone.cpp b/Sources/ComputeCxx/Data/Zone.cpp index d6762fe..e3d9fb0 100644 --- a/Sources/ComputeCxx/Data/Zone.cpp +++ b/Sources/ComputeCxx/Data/Zone.cpp @@ -27,7 +27,7 @@ void zone::clear() { void zone::realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask) { if (new_size > size && *buffer) { auto page = buffer->page_ptr(); - uint32_t buffer_offset_from_page = buffer->offset() - page; + uint32_t buffer_offset_from_page = buffer->offset() - page.offset(); if ((page->in_use == buffer_offset_from_page + size && page->total >= buffer_offset_from_page + new_size)) { page->in_use += new_size - size; } else { @@ -48,6 +48,19 @@ void zone::realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, ui } } +ptr zone::alloc_bytes(uint32_t size, uint32_t alignment_mask) { + if (_first_page) { + uint32_t aligned_in_use = (_first_page->in_use + alignment_mask) & ~alignment_mask; + uint32_t new_used_size = aligned_in_use + size; + if (new_used_size <= _first_page->total) { + _first_page->in_use = new_used_size; + return _first_page.advanced(aligned_in_use); + } + } + + return alloc_slow(size, alignment_mask); +} + ptr zone::alloc_bytes_recycle(uint32_t size, uint32_t alignment_mask) { for (ptr bytes = _free_bytes; bytes; bytes = bytes->next) { if (size > bytes->size) { @@ -67,8 +80,8 @@ ptr zone::alloc_bytes_recycle(uint32_t size, uint32_t alignment_mask) { } // check if there will be some bytes remaining within the same page - ptr end = aligned_bytes + size; - if ((uint32_t(aligned_bytes) ^ uint32_t(end)) <= page_alignment_mask) { + auto end = aligned_bytes.advanced(size); + if ((aligned_bytes.offset() ^ end.offset()) <= page_alignment_mask) { ptr aligned_end = end.aligned(); uint32_t remaining_size = usable_size - size + (end - aligned_end); if (remaining_size >= sizeof(bytes_info)) { @@ -85,24 +98,11 @@ ptr zone::alloc_bytes_recycle(uint32_t size, uint32_t alignment_mask) { return alloc_bytes(size, alignment_mask); } -ptr zone::alloc_bytes(uint32_t size, uint32_t alignment_mask) { - if (_first_page) { - uint32_t aligned_in_use = (_first_page->in_use + alignment_mask) & ~alignment_mask; - uint32_t new_used_size = aligned_in_use + size; - if (new_used_size <= _first_page->total) { - _first_page->in_use = new_used_size; - return _first_page + aligned_in_use; - } - } - - return alloc_slow(size, alignment_mask); -} - ptr zone::alloc_slow(uint32_t size, uint32_t alignment_mask) { if (_first_page) { // check if we can use remaining bytes in this page - ptr next_bytes = _first_page + _first_page->in_use; + ptr next_bytes = _first_page.advanced(_first_page->in_use); if (next_bytes.page_ptr() == _first_page) { ptr aligned_next_bytes = next_bytes.aligned(); @@ -145,7 +145,7 @@ ptr zone::alloc_slow(uint32_t size, uint32_t alignment_mask) { } new_page->in_use = aligned_used_bytes + size; - return new_page + aligned_used_bytes; + return new_page.advanced(aligned_used_bytes); }; #pragma mark - Persistent memory diff --git a/Sources/ComputeCxx/Data/Zone.h b/Sources/ComputeCxx/Data/Zone.h index deae108..1ed61ac 100644 --- a/Sources/ComputeCxx/Data/Zone.h +++ b/Sources/ComputeCxx/Data/Zone.h @@ -44,6 +44,8 @@ class zone { ptr _free_bytes; info _info; + ptr alloc_slow(uint32_t size, uint32_t alignment_mask); + public: zone(); ~zone(); @@ -57,9 +59,8 @@ class zone { void realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask); // Paged memory - ptr alloc_bytes_recycle(uint32_t size, uint32_t alignment_mask); ptr alloc_bytes(uint32_t size, uint32_t alignment_mask); - ptr alloc_slow(uint32_t size, uint32_t alignment_mask); + ptr alloc_bytes_recycle(uint32_t size, uint32_t alignment_mask); // Persistent memory void *alloc_persistent(size_t size); diff --git a/Sources/ComputeCxx/External/ExternalTrace.cpp b/Sources/ComputeCxx/External/ExternalTrace.cpp index 69d4e0e..0e33be9 100644 --- a/Sources/ComputeCxx/External/ExternalTrace.cpp +++ b/Sources/ComputeCxx/External/ExternalTrace.cpp @@ -35,7 +35,7 @@ void ExternalTrace::end_update(const AG::Subgraph &subgraph) { void ExternalTrace::begin_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, uint32_t options) { if (auto callback = _interface->begin_update_stack) { - callback(_trace, node); + callback(_trace, AG::AttributeID(node).to_storage()); } } @@ -102,7 +102,7 @@ void ExternalTrace::begin_event(AG::data::ptr node, uint32_t event_id) if (auto callback = _interface->begin_event) { if (auto subgraph = AG::AttributeID(node).subgraph()) { const char *event_name = subgraph->graph()->key_name(event_id); - callback(_trace, node, event_name); + callback(_trace, AG::AttributeID(node).to_storage(), event_name); } } } @@ -111,7 +111,7 @@ void ExternalTrace::end_event(AG::data::ptr node, uint32_t event_id) { if (auto callback = _interface->end_event) { if (auto subgraph = AG::AttributeID(node).subgraph()) { const char *event_name = subgraph->graph()->key_name(event_id); - callback(_trace, node, event_name); + callback(_trace, AG::AttributeID(node).to_storage(), event_name); } } } @@ -306,7 +306,7 @@ void ExternalTrace::compare_failed(AG::data::ptr node, const void *lhs if (_interface->options > 3) { AGComparisonStateStorage storage = {lhs, rhs, range_offset, range_size, AGTypeID(&type)}; if (auto callback = _interface->compare_failed) { - callback(_trace, node, &storage); + callback(_trace, AG::AttributeID(node).to_storage(), &storage); } } } diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index 31ea72f..dcd9054 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -184,7 +184,8 @@ AGAttribute AGGraphCreateAttribute(uint32_t type_id, const void *body, const voi if (!current_subgraph) { AG::precondition_failure("no subgraph active while adding attribute"); } - return current_subgraph->graph()->add_attribute(*current_subgraph, type_id, body, value); + auto attribute = current_subgraph->graph()->add_attribute(*current_subgraph, type_id, body, value); + return AG::AttributeID(attribute).to_storage(); } AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute) { @@ -355,7 +356,7 @@ AGAttribute AGGraphGetCurrentAttribute() { if (auto update_stack = update.get()) { auto frame = update_stack->frames().back(); if (frame.attribute) { - return frame.attribute; + return AG::AttributeID(frame.attribute).to_storage(); } } } diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index b2bce5c..4c1d49f 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -127,7 +127,7 @@ int trap_cycles() { collect_stack(nodes); NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; for (auto node : nodes) { - [indexSet addIndex:node]; + [indexSet addIndex:node.offset()]; } NSMutableDictionary *dict = [NSMutableDictionary @@ -966,7 +966,7 @@ int trap_cycles() { auto frames = update.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { - [nodes addObject:[NSNumber numberWithUnsignedInt:frame->attribute]]; + [nodes addObject:[NSNumber numberWithUnsignedInt:frame->attribute.offset()]]; frame_count += 1; if (frame_count >= max_frames) { @@ -995,7 +995,7 @@ int trap_cycles() { if (i == frame_index || frame.attribute == frame_node) { dictionary[@"index"] = [NSNumber numberWithUnsignedLong:i]; - dictionary[@"node-id"] = [NSNumber numberWithUnsignedInt:frame.attribute]; + dictionary[@"node-id"] = [NSNumber numberWithUnsignedInt:frame.attribute.offset()]; const AttributeType &type = attribute_type(frame.attribute->type_id()); AGSetTypeForKey((__bridge CFMutableDictionaryRef)dictionary, (__bridge CFStringRef) @"self-type", diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 5336cf0..0b5dc64 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -481,9 +481,10 @@ data::ptr Graph::add_attribute(Subgraph &subgraph, uint32_t type_id, const data::ptr node; if (total_size > 0x10) { - node = (data::ptr)subgraph.alloc_bytes_recycle(uint32_t(total_size), uint32_t(alignment_mask | 3)); + node = subgraph.alloc_bytes_recycle(uint32_t(total_size), uint32_t(alignment_mask | 3)).unsafe_cast(); } else { - node = (data::ptr)subgraph.alloc_bytes(uint32_t(total_size), uint32_t(alignment_mask | 3)); + node = (data::ptr)subgraph.alloc_bytes(uint32_t(total_size), uint32_t(alignment_mask | 3)) + .unsafe_cast(); } bool main_thread = type.main_thread(); *node = Node(Node::State().with_main_thread(main_thread).with_main_thread_only(main_thread), type_id, @@ -767,7 +768,7 @@ data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, Attrib } if (is_mutable) { - auto indirect_node = (data::ptr)subgraph.alloc_bytes(sizeof(MutableIndirectNode), 3); + auto indirect_node = subgraph.alloc_bytes(sizeof(MutableIndirectNode), 3).unsafe_cast(); // TODO: check accessing zone_id directly or through raw_page_seed // check references of raw_page_seed in ghidra @@ -778,10 +779,10 @@ data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, Attrib *indirect_node = MutableIndirectNode(source, traverses_contexts, offset, node_size, source, offset); add_input_dependencies(AttributeID(indirect_node).with_kind(AttributeID::Kind::Indirect), attribute); - subgraph.add_indirect((data::ptr)indirect_node, true); - return (data::ptr)indirect_node; + subgraph.add_indirect(indirect_node.unsafe_cast(), true); + return indirect_node.unsafe_cast(); } else { - auto indirect_node = (data::ptr)subgraph.alloc_bytes_recycle(sizeof(Node), 3); + auto indirect_node = subgraph.alloc_bytes_recycle(sizeof(Node), 3).unsafe_cast(); uint32_t zone_id = attribute.has_value() ? attribute.subgraph()->info().zone_id() : 0; auto source = WeakAttributeID(attribute, zone_id); @@ -1447,7 +1448,7 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni } precondition_failure("reading from invalid source attribute: %u", input); } - if (resolved.attribute() == node) { + if (resolved.attribute() == AttributeID(node)) { precondition_failure("cyclic edge: %u -> %u", resolved.attribute(), node); } @@ -1782,7 +1783,7 @@ void Graph::mark_changed(data::ptr node, AttributeType *_Nullable type, co return; } for (auto input : output.value.to_node().inputs()) { - if (input.value.resolve(TraversalOptions::None).attribute() != node) { + if (input.value.resolve(TraversalOptions::None).attribute() != AttributeID(node)) { continue; } if (input.is_changed()) { @@ -2145,7 +2146,7 @@ void Graph::encode_tree(Encoder &encoder, data::ptr tree) { } if (node->second) { encoder.encode_varint(0x30); - encoder.encode_varint(node->second); + encoder.encode_varint(node->second.offset()); } } } diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 4c5dfcd..9cd4acb 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -151,7 +151,7 @@ void Graph::TraceRecorder::encode_stack() { if (frame->attribute) { _encoder.encode_varint(8); - _encoder.encode_varint(frame->attribute); + _encoder.encode_varint(frame->attribute.offset()); } if (frame->needs_update) { _encoder.encode_varint(0x10); @@ -492,7 +492,7 @@ void Graph::TraceRecorder::begin_update(const Graph::UpdateStack &update_stack, if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (options) { _encoder.encode_varint(0x20); @@ -516,7 +516,7 @@ void Graph::TraceRecorder::end_update(const Graph::UpdateStack &update_stack, da if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (update_status == Graph::UpdateStatus::Changed) { _encoder.encode_varint(0x20); @@ -539,7 +539,7 @@ void Graph::TraceRecorder::begin_update(data::ptr node) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } _encoder.end_length_delimited(); @@ -558,7 +558,7 @@ void Graph::TraceRecorder::end_update(data::ptr node, bool changed) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (changed) { _encoder.encode_varint(0x20); @@ -674,7 +674,7 @@ void Graph::TraceRecorder::begin_modify(data::ptr node) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } _encoder.end_length_delimited(); @@ -715,7 +715,7 @@ void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event_id) if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (event_id) { _encoder.encode_varint(0x20); @@ -738,7 +738,7 @@ void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event_id) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (event_id) { _encoder.encode_varint(0x20); @@ -941,7 +941,7 @@ void Graph::TraceRecorder::added(data::ptr node) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } auto zone_id = AttributeID(node).subgraph()->info().zone_id(); if (zone_id) { @@ -971,7 +971,7 @@ void Graph::TraceRecorder::add_edge(data::ptr node, AttributeID input, uin if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (input) { _encoder.encode_varint(0x20); @@ -995,7 +995,7 @@ void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_inde if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } auto input_edge = node->inputs()[input_index]; if (input_edge.value) { @@ -1023,7 +1023,7 @@ void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } auto input_edge = node->inputs()[input_index]; if (input_edge.value) { @@ -1053,7 +1053,7 @@ void Graph::TraceRecorder::set_dirty(data::ptr node, bool dirty) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (dirty) { _encoder.encode_varint(0x20); @@ -1078,7 +1078,7 @@ void Graph::TraceRecorder::set_pending(data::ptr node, bool pending) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } if (pending) { _encoder.encode_varint(0x20); @@ -1103,7 +1103,7 @@ void Graph::TraceRecorder::set_value(data::ptr node, const void *value) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } _encoder.end_length_delimited(); @@ -1124,7 +1124,7 @@ void Graph::TraceRecorder::mark_value(data::ptr node) { if (node) { _encoder.encode_varint(0x18); - _encoder.encode_varint(node); + _encoder.encode_varint(node.offset()); } _encoder.end_length_delimited(); diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp index 39d7a4a..b4afe29 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp @@ -4,41 +4,41 @@ #include "TreeElement.h" AGTypeID AGTreeElementGetType(AGTreeElement tree_element) { - auto tree_element_id = AG::TreeElementID(tree_element); - auto type = tree_element_id.to_element_ptr()->type; + auto tree_element_id = AG::TreeElementID::from_storage(tree_element); + auto type = tree_element_id.to_ptr()->type; return AGTypeID(type); } // TODO: rename to value AGAttribute AGTreeElementGetValue(AGTreeElement tree_element) { - auto tree_element_id = AG::TreeElementID(tree_element); - auto node = tree_element_id.to_element_ptr()->node; + auto tree_element_id = AG::TreeElementID::from_storage(tree_element); + auto node = tree_element_id.to_ptr()->node; return AGAttribute(node); } uint32_t AGTreeElementGetFlags(AGTreeElement tree_element) { - auto tree_element_id = AG::TreeElementID(tree_element); - return tree_element_id.to_element_ptr()->flags; + auto tree_element_id = AG::TreeElementID::from_storage(tree_element); + return tree_element_id.to_ptr()->flags; } AGTreeElement AGTreeElementGetParent(AGTreeElement tree_element) { - auto tree_element_id = AG::TreeElementID(tree_element); - return tree_element_id.to_element_ptr()->parent; + auto tree_element_id = AG::TreeElementID::from_storage(tree_element); + return AG::TreeElementID(tree_element_id.to_ptr()->parent).to_storage(); } #pragma mark - Iterating values AGTreeElementValueIterator AGTreeElementMakeValueIterator(AGTreeElement tree_element) { - auto tree_element_id = AG::TreeElementID(tree_element); - auto tree_value = tree_element_id.to_element_ptr()->first_value; - return AGTreeElementValueIterator(tree_element, tree_value); + auto tree_element_id = AG::TreeElementID::from_storage(tree_element); + auto tree_value = AG::TreeValueID(tree_element_id.to_ptr()->first_value); + return AGTreeElementValueIterator(tree_element, tree_value.to_storage()); } AGTreeValue AGTreeElementGetNextValue(AGTreeElementValueIterator iter) { AGTreeValue tree_value = iter.next_value; if (tree_value) { - auto tree_value_id = AG::TreeValueID(tree_value); - iter.next_value = tree_value_id.to_tree_value().next; + auto tree_value_id = AG::TreeValueID::from_storage(tree_value); + iter.next_value = AG::TreeValueID(tree_value_id.to_tree_value().next).to_storage(); } return tree_value; } @@ -48,9 +48,9 @@ AGTreeValue AGTreeElementGetNextValue(AGTreeElementValueIterator iter) { AGTreeElementNodeIterator AGTreeElementMakeNodeIterator(AGTreeElement tree_element) { return {tree_element, 0}; } AGAttribute AGTreeElementGetNextNode(AGTreeElementNodeIterator *iter) { - auto tree_element_id = AG::TreeElementID(iter->tree_element); + auto tree_element_id = AG::TreeElementID::from_storage(iter->tree_element); AG::AttributeID node = - tree_element_id.subgraph()->tree_node_at_index(tree_element_id.to_element_ptr(), iter->index); + tree_element_id.subgraph()->tree_node_at_index(tree_element_id.to_ptr(), iter->index); if (!node.has_value()) { return AGAttributeNil; } @@ -61,26 +61,26 @@ AGAttribute AGTreeElementGetNextNode(AGTreeElementNodeIterator *iter) { #pragma mark - Iterating children AGTreeElementChildIterator AGTreeElementMakeChildIterator(AGTreeElement tree_element) { - auto tree_element_id = AG::TreeElementID(tree_element); - auto child = tree_element_id.to_element_ptr()->first_child; - return AGTreeElementChildIterator(tree_element, child, 0); + auto tree_element_id = AG::TreeElementID::from_storage(tree_element); + auto child = AG::TreeElementID(tree_element_id.to_ptr()->first_child); + return AGTreeElementChildIterator(tree_element, child.to_storage(), 0); } AGTreeElement AGTreeElementGetNextChild(AGTreeElementChildIterator *iter) { AGTreeElement next_child = iter->next_child; if (next_child) { - iter->next_child = AG::TreeElementID(next_child).to_element_ptr()->next_sibling; + iter->next_child = AG::TreeElementID(AG::TreeElementID::from_storage(next_child).to_ptr()->next_sibling).to_storage(); return next_child; } if (!iter->iterated_subgraph) { iter->iterated_subgraph = true; - auto tree_element_id = AG::TreeElementID(iter->tree_element); + auto tree_element_id = AG::TreeElementID::from_storage(iter->tree_element); auto subgraph = tree_element_id.subgraph(); - auto next_child = subgraph->tree_subgraph_child(tree_element_id.to_element_ptr()); + auto next_child = subgraph->tree_subgraph_child(tree_element_id.to_ptr()); if (next_child) { - iter->next_child = AG::TreeElementID(next_child).to_element_ptr()->next_sibling; - return next_child; + iter->next_child = AG::TreeElementID(AG::TreeElementID(next_child).to_ptr()->next_sibling).to_storage(); + return AG::TreeElementID(next_child).to_storage(); } } diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp index b14dc2b..f4ed63d 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp @@ -5,22 +5,22 @@ #include "TreeElement.h" AGTypeID AGTreeValueGetType(AGTreeValue tree_value) { - auto tree_value_id = AG::TreeValueID(tree_value); + auto tree_value_id = AG::TreeValueID::from_storage(tree_value); return AGTypeID(tree_value_id.to_tree_value().type); } AGAttribute AGTreeValueGetValue(AGTreeValue tree_value) { - auto tree_value_id = AG::TreeValueID(tree_value); + auto tree_value_id = AG::TreeValueID::from_storage(tree_value); return AGAttribute(tree_value_id.to_tree_value().value); } const char *AGTreeValueGetKey(AGTreeValue tree_value) { - auto tree_value_id = AG::TreeValueID(tree_value); + auto tree_value_id = AG::TreeValueID::from_storage(tree_value); auto key_id = tree_value_id.to_tree_value().value; return tree_value_id.subgraph()->graph()->key_name(key_id); } uint32_t AGTreeValueGetFlags(AGTreeValue tree_value) { - auto tree_value_id = AG::TreeValueID(tree_value); + auto tree_value_id = AG::TreeValueID::from_storage(tree_value); return tree_value_id.to_tree_value().flags; } diff --git a/Sources/ComputeCxx/Graph/Tree/TreeElement.h b/Sources/ComputeCxx/Graph/Tree/TreeElement.h index 94f66bc..2bafac8 100644 --- a/Sources/ComputeCxx/Graph/Tree/TreeElement.h +++ b/Sources/ComputeCxx/Graph/Tree/TreeElement.h @@ -20,7 +20,7 @@ struct Graph::TreeElement { data::ptr parent; data::ptr first_child; data::ptr next_sibling; - + data::ptr first_value; }; static_assert(sizeof(Graph::TreeElement) == 0x20); @@ -29,13 +29,20 @@ class TreeElementID { private: uint32_t _value; + explicit constexpr TreeElementID(uint32_t value) : _value(value) {}; + public: - TreeElementID(uint32_t value) : _value(value){}; + explicit TreeElementID(data::ptr tree_element) : _value(tree_element.offset()) {}; + + constexpr uint32_t to_storage() const { return _value; } + static constexpr TreeElementID from_storage(uint32_t value) { return TreeElementID(value); } - data::ptr to_element_ptr() const { return data::ptr(_value); }; + data::ptr to_ptr() const { return data::ptr(_value); }; Subgraph *_Nullable subgraph() const { return reinterpret_cast(page_ptr()->zone); } data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; + + bool has_value() const { return _value != 0; } }; struct Graph::TreeValue { @@ -51,8 +58,13 @@ class TreeValueID { private: uint32_t _value; + explicit constexpr TreeValueID(uint32_t value) : _value(value) {}; + public: - TreeValueID(uint32_t value) : _value(value){}; + explicit TreeValueID(data::ptr tree_value) : _value(tree_value.offset()) {}; + + constexpr uint32_t to_storage() const { return _value; } + static constexpr TreeValueID from_storage(uint32_t value) { return TreeValueID(value); } const Graph::TreeValue &to_tree_value() const { return *data::ptr(_value); }; diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index 887f763..d5c014e 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -265,12 +265,12 @@ void AGSubgraphApply(AGSubgraphRef subgraph, AGAttributeFlags flags, #pragma mark - Tree -uint32_t AGSubgraphGetTreeRoot(AGSubgraphRef subgraph) { +AGTreeElement AGSubgraphGetTreeRoot(AGSubgraphRef subgraph) { if (subgraph->subgraph == nullptr) { return 0; // TODO: nullptr } - return subgraph->subgraph->tree_root(); + return AG::TreeElementID(subgraph->subgraph->tree_root()).to_storage(); } void AGSubgraphSetTreeOwner(AGSubgraphRef subgraph, AGAttribute owner) { diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index 67dc938..1dee134 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -4,6 +4,7 @@ #include "Attribute/AGAttribute.h" #include "Graph/AGGraph.h" +#include "Graph/Tree/AGTreeElement.h" CF_ASSUME_NONNULL_BEGIN @@ -117,7 +118,7 @@ void AGSubgraphApply(AGSubgraphRef subgraph, AGAttributeFlags flags, CF_EXPORT CF_REFINED_FOR_SWIFT -uint32_t AGSubgraphGetTreeRoot(AGSubgraphRef subgraph); +AGTreeElement AGSubgraphGetTreeRoot(AGSubgraphRef subgraph); CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 534ff68..ebc3e46 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -633,7 +633,7 @@ void Subgraph::apply(Flags flags, ClosureFunctionAV body) { void Subgraph::begin_tree(AttributeID value, const swift::metadata *type, uint32_t flags) { - data::ptr tree = (data::ptr)alloc_bytes(sizeof(Graph::TreeElement), 7); + data::ptr tree = alloc_bytes(sizeof(Graph::TreeElement), 7).unsafe_cast(); tree->type = type; tree->node = value; tree->flags = flags; @@ -672,7 +672,7 @@ void Subgraph::add_tree_value(AttributeID value, const swift::metadata *type, co auto key_id = graph()->intern_key(key); - data::ptr tree_value = (data::ptr)alloc_bytes(sizeof(Graph::TreeValue), 7); + data::ptr tree_value = alloc_bytes(sizeof(Graph::TreeValue), 7).unsafe_cast(); tree_value->type = type; tree_value->value = value; tree_value->key_id = key_id; @@ -723,7 +723,7 @@ data::ptr Subgraph::tree_subgraph_child(data::ptr bool { return iter.first.offset() < value; }); + [](auto iter, auto value) -> bool { return iter.first.offset() < value.offset(); }); if (iter == nodes.end()) { return; } @@ -755,8 +755,8 @@ data::ptr Subgraph::tree_subgraph_child(data::ptr(); + auto last_old_parent = data::ptr(); for (auto subgraph : subgraph_vector) { result = subgraph->_tree_root; subgraph->_tree_root->next_sibling = last_old_parent; @@ -836,7 +836,7 @@ bool Subgraph::intersects(uint8_t mask) const { return ((_flags.value1 | _flags. uint64_t Subgraph::add_observer(ClosureFunctionVV &&callback) { if (!_observers) { _observers = - (data::ptr *>)alloc_bytes(sizeof(vector *), 7); + alloc_bytes(sizeof(vector *), 7).unsafe_cast *>(); *_observers = new vector(); } @@ -875,7 +875,7 @@ void Subgraph::notify_observers() { data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata &metadata, void *body, ClosureFunctionCI closure) { if (_cache == nullptr) { - _cache = (data::ptr)alloc_bytes(sizeof(NodeCache), 7); + _cache = alloc_bytes(sizeof(NodeCache), 7).unsafe_cast(); new (&_cache) NodeCache(); } @@ -886,7 +886,7 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata precondition_failure("cache key must be equatable: %s", metadata.name(false)); } - type = (data::ptr)alloc_bytes(sizeof(NodeCache::Type), 7); + type = alloc_bytes(sizeof(NodeCache::Type), 7).unsafe_cast(); type->type = &metadata; type->equatable = equatable; type->last_item = nullptr; From bc769130e28e4ae3ca7284dd5c8b8a5a00e68c2a Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 24 Apr 2025 13:36:14 +0200 Subject: [PATCH 66/74] Refine flags property for Swift --- Sources/Compute/Attribute/AnyAttribute.swift | 13 +------------ Sources/ComputeCxx/Graph/AGGraph.h | 4 ++-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Sources/Compute/Attribute/AnyAttribute.swift b/Sources/Compute/Attribute/AnyAttribute.swift index b698b5e..2894648 100644 --- a/Sources/Compute/Attribute/AnyAttribute.swift +++ b/Sources/Compute/Attribute/AnyAttribute.swift @@ -71,19 +71,8 @@ extension AnyAttribute { } } - public var flags: AGAttributeFlags { - get { - return __AGGraphGetFlags(self) - } - set { - __AGGraphSetFlags(self, newValue) - } - } - public func setFlags(_ newFlags: AGAttributeFlags, mask: AGAttributeFlags) { - let oldFlags = __AGGraphGetFlags(self) - let newFlags = oldFlags.subtracting(mask).union(newFlags.intersection(mask)) - __AGGraphSetFlags(self, newFlags) + flags = flags.subtracting(mask).union(newFlags.intersection(mask)) } public func addInput(_ input: AnyAttribute, options: AGInputOptions, token: Int) { diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 0ba79b1..8712d8c 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -110,11 +110,11 @@ AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute); CF_EXPORT CF_REFINED_FOR_SWIFT -AGAttributeFlags AGGraphGetFlags(AGAttribute attribute); +AGAttributeFlags AGGraphGetFlags(AGAttribute attribute) CF_SWIFT_NAME(getter:AGAttribute.flags(self:)); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags); +void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags) CF_SWIFT_NAME(setter:AGAttribute.flags(self:_:)); CF_EXPORT CF_REFINED_FOR_SWIFT From 8f1db566ab74b35f7911151221950ffa87a09dd5 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 24 Apr 2025 13:44:06 +0200 Subject: [PATCH 67/74] Change #include to #import for Objective-C dependencies --- Sources/ComputeCxx/Debug/DebugServer.mm | 3 ++- Sources/ComputeCxx/Graph/Graph+Description.mm | 3 ++- Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm | 2 +- Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Sources/ComputeCxx/Debug/DebugServer.mm b/Sources/ComputeCxx/Debug/DebugServer.mm index 5df50ed..092beed 100644 --- a/Sources/ComputeCxx/Debug/DebugServer.mm +++ b/Sources/ComputeCxx/Debug/DebugServer.mm @@ -1,6 +1,7 @@ #include "DebugServer.h" -#include +#import + #include #include #include diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 4c1d49f..9405c3a 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -1,6 +1,7 @@ #include "Graph.h" -#include +#import + #include #include "AGGraph.h" diff --git a/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm index df79302..bcc0e69 100644 --- a/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm +++ b/Sources/ComputeCxx/Graph/Profile/AGAppObserver.mm @@ -1,6 +1,6 @@ #include "AGAppObserver.h" -#include +#import #include "Graph/Graph.h" diff --git a/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm index a90558c..c01eb52 100644 --- a/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm +++ b/Sources/ComputeCxx/Graph/Profile/ProfileData+JSON.mm @@ -1,6 +1,7 @@ #include "ProfileData.h" -#include +#import + #include #include "Graph/Graph.h" From 49df0b86fb004c5a847444eab678a0bd317eff49 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 24 Apr 2025 21:53:10 +0200 Subject: [PATCH 68/74] Refine info property --- Sources/Compute/Attribute/AnyAttribute.swift | 10 +++------- Sources/ComputeCxx/Graph/AGGraph.h | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Sources/Compute/Attribute/AnyAttribute.swift b/Sources/Compute/Attribute/AnyAttribute.swift index 2894648..23b25b1 100644 --- a/Sources/Compute/Attribute/AnyAttribute.swift +++ b/Sources/Compute/Attribute/AnyAttribute.swift @@ -29,8 +29,7 @@ extension AnyAttribute { } public func visitBody(_ visitor: inout Visitor) { - let info = __AGGraphGetAttributeInfo(self) - let type = info.type.pointee.body_type_id.type as! _AttributeBody.Type + let type = info.type.pointee.typeID.type as! _AttributeBody.Type type._visitSelf(info.body, visitor: &visitor) } @@ -94,18 +93,15 @@ extension AnyAttribute { } public var _bodyType: Any.Type { - let info = __AGGraphGetAttributeInfo(self) - return info.type.pointee.body_type_id.type + return info.type.pointee.typeID.type } public var _bodyPointer: UnsafeRawPointer { - let info = __AGGraphGetAttributeInfo(self) return info.body } public var valueType: Any.Type { - let info = __AGGraphGetAttributeInfo(self) - return info.type.pointee.value_type_id.type + return info.type.pointee.valueTypeID.type } } diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 8712d8c..8bfac56 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -106,7 +106,7 @@ AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute); CF_EXPORT CF_REFINED_FOR_SWIFT -AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute); +AGAttributeInfo AGGraphGetAttributeInfo(AGAttribute attribute) CF_SWIFT_NAME(getter:AGAttribute.info(self:)); CF_EXPORT CF_REFINED_FOR_SWIFT From c18ab8b105c3e862f6bc2989129bf8334bc6585c Mon Sep 17 00:00:00 2001 From: James Moschou Date: Mon, 28 Apr 2025 12:18:50 +0200 Subject: [PATCH 69/74] Tests WIP --- Compute.xctestplan | 64 ++ Package.resolved | 10 +- Package.swift | 43 +- Sources/Compute/Attribute/AnyAttribute.swift | 41 +- Sources/Compute/Attribute/Attribute.swift | 30 +- Sources/Compute/Attribute/AttributeType.swift | 96 +-- .../Attribute/Body/AttributeBody.swift | 2 +- Sources/Compute/Attribute/External.swift | 2 +- Sources/Compute/Attribute/PointerOffset.swift | 4 +- .../Attribute/Rule/AnyRuleContext.swift | 17 +- Sources/Compute/Attribute/Rule/Rule.swift | 61 +- Sources/Compute/Graph/Graph.swift | 121 ++-- Sources/Compute/Graph/Subgraph.swift | 47 +- Sources/Compute/Runtime/CompareValues.swift | 6 +- Sources/Compute/Runtime/Enum.swift | 44 +- Sources/Compute/Runtime/Metadata.swift | 45 +- Sources/Compute/Runtime/Tuple.swift | 22 +- Sources/ComputeCxx/Attribute/AGAttribute.cpp | 2 +- Sources/ComputeCxx/Attribute/AGAttribute.h | 20 +- .../ComputeCxx/Attribute/AGWeakAttribute.cpp | 2 +- Sources/ComputeCxx/Attribute/AttributeID.h | 23 +- Sources/ComputeCxx/Attribute/Node/Node.h | 21 +- Sources/ComputeCxx/Closure/AGClosure.h | 13 +- Sources/ComputeCxx/Closure/ClosureFunction.h | 3 +- .../Containers/IndirectPointerVector.h | 2 +- Sources/ComputeCxx/Data/Pointer.h | 5 +- Sources/ComputeCxx/Data/Table.cpp | 8 +- Sources/ComputeCxx/Data/Table.h | 1 + Sources/ComputeCxx/External/ExternalTrace.cpp | 18 +- Sources/ComputeCxx/Graph/AGGraph-Private.h | 8 +- Sources/ComputeCxx/Graph/AGGraph.cpp | 45 +- Sources/ComputeCxx/Graph/AGGraph.h | 21 +- Sources/ComputeCxx/Graph/Context.cpp | 14 +- Sources/ComputeCxx/Graph/Graph+Description.mm | 13 +- Sources/ComputeCxx/Graph/Graph.cpp | 15 +- .../ComputeCxx/Graph/Tree/AGTreeElement.cpp | 8 +- Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp | 2 +- Sources/ComputeCxx/Layout/AGComparison.h | 8 +- .../ComputeCxx/Layout/LayoutDescriptor.cpp | 3 +- Sources/ComputeCxx/Layout/LayoutDescriptor.h | 2 +- Sources/ComputeCxx/Subgraph/AGSubgraph.cpp | 35 +- Sources/ComputeCxx/Subgraph/AGSubgraph.h | 14 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 36 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 9 +- Sources/ComputeCxx/Swift/AGType.cpp | 61 +- Sources/ComputeCxx/Swift/AGType.h | 28 +- Sources/ComputeCxx/Swift/Metadata.cpp | 8 +- Sources/ComputeCxx/UniqueID/AGUniqueID.h | 2 +- Sources/ComputeCxx/include/Compute.h | 2 + Sources/Utilities/HashTable.cpp | 4 +- Sources/Utilities/Heap.cpp | 6 +- .../Utilities/include/Utilities/HashTable.h | 6 +- Sources/Utilities/include/Utilities/Heap.h | 4 +- Sources/Utilities/include/Utilities/List.h | 7 +- Tests/ComputeCompatibilityTests/Shared | 1 + Tests/ComputeCompatibilityTests/Shims.swift | 4 + Tests/ComputeCxxTests/Data/ZoneTests.mm | 47 ++ .../Vector/IndirectPointerVectorTests.mm | 83 +++ Tests/ComputeTests/MetadataTests.swift | 242 ------- .../Shared/Attribute/AnyAttributeTests.swift | 91 +++ .../Shared/Attribute/AttributeTests.swift | 99 +++ .../Shared/Attribute/AttributeTypeTests.swift | 77 +++ .../Shared/Attribute/ExternalTests.swift | 29 + .../Shared/Attribute/FocusTests.swift | 36 + .../Indirect/IndirectAttributeTests.swift | 28 + .../Optional/AnyOptionalAttributeTests.swift | 67 ++ .../Optional/OptionalAttributeTests.swift | 52 ++ .../Shared/Attribute/PointerOffsetTests.swift | 175 +++++ .../Shared/Attribute/Rule/RuleTests.swift | 15 + .../Attribute/Weak/WeakAttributeTests.swift | 28 + .../Shared/Data/UniqueIDTests.swift | 11 + .../Shared/Graph/GraphContextTests.swift | 18 + .../Shared/Graph/GraphTests.swift | 85 +++ .../Shared/Graph/SubgraphTests.swift | 27 + .../Shared/Runtime/CompareValuesTests.swift | 137 ++++ .../Shared/Runtime/MetadataTests.swift | 635 ++++++++++++++++++ .../Shared/Runtime/TupleTypeTests.swift | 147 ++++ Tests/ComputeTests/Shared/TestTypes.swift | 182 +++++ Tests/ComputeTests/Shims.swift | 1 + Tests/UtilitiesTests/HashTableTests.swift | 18 +- Tests/UtilitiesTests/HeapTests.swift | 18 +- Tests/UtilitiesTests/ListTests.swift | 24 +- 82 files changed, 2695 insertions(+), 816 deletions(-) create mode 100644 Compute.xctestplan create mode 120000 Tests/ComputeCompatibilityTests/Shared create mode 100644 Tests/ComputeCompatibilityTests/Shims.swift create mode 100644 Tests/ComputeCxxTests/Data/ZoneTests.mm create mode 100644 Tests/ComputeCxxTests/Vector/IndirectPointerVectorTests.mm delete mode 100644 Tests/ComputeTests/MetadataTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/AnyAttributeTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/AttributeTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/AttributeTypeTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/ExternalTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/FocusTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/Indirect/IndirectAttributeTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/Optional/AnyOptionalAttributeTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/Optional/OptionalAttributeTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/PointerOffsetTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/Rule/RuleTests.swift create mode 100644 Tests/ComputeTests/Shared/Attribute/Weak/WeakAttributeTests.swift create mode 100644 Tests/ComputeTests/Shared/Data/UniqueIDTests.swift create mode 100644 Tests/ComputeTests/Shared/Graph/GraphContextTests.swift create mode 100644 Tests/ComputeTests/Shared/Graph/GraphTests.swift create mode 100644 Tests/ComputeTests/Shared/Graph/SubgraphTests.swift create mode 100644 Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift create mode 100644 Tests/ComputeTests/Shared/Runtime/MetadataTests.swift create mode 100644 Tests/ComputeTests/Shared/Runtime/TupleTypeTests.swift create mode 100644 Tests/ComputeTests/Shared/TestTypes.swift create mode 100644 Tests/ComputeTests/Shims.swift diff --git a/Compute.xctestplan b/Compute.xctestplan new file mode 100644 index 0000000..f462977 --- /dev/null +++ b/Compute.xctestplan @@ -0,0 +1,64 @@ +{ + "configurations" : [ + { + "id" : "EF776845-F06A-498A-A7EE-EA72E31616F9", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "environmentVariableEntries" : [ + { + "key" : "AG_PREFETCH_LAYOUTS", + "value" : "1" + }, + { + "key" : "AG_ASYNC_LAYOUTS", + "value" : "0" + } + ] + }, + "testTargets" : [ + { + "skippedTests" : { + "suites" : [ + { + "name" : "MetadataTests", + "testFunctions" : [ + "description(of:equals:)" + ] + } + ] + }, + "target" : { + "containerPath" : "container:", + "identifier" : "ComputeTests", + "name" : "ComputeTests" + } + }, + { + "target" : { + "containerPath" : "container:", + "identifier" : "UtilitiesTests", + "name" : "UtilitiesTests" + } + }, + { + "target" : { + "containerPath" : "container:", + "identifier" : "ComputeCxxTests", + "name" : "ComputeCxxTests" + } + }, + { + "target" : { + "containerPath" : "container:", + "identifier" : "ComputeCompatibilityTests", + "name" : "ComputeCompatibilityTests" + } + } + ], + "version" : 1 +} diff --git a/Package.resolved b/Package.resolved index 89597d7..1fc7070 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "61413c97c58a2fee6b5ea96bcd7492c6adb3b2f91f38345446ffd26b19e1e7af", + "originHash" : "8210536fc04b6250c5a09ccd177be5a174e3601edb343f5709f8ce94514f2a42", "pins" : [ { "identity" : "swift-algorithms", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-algorithms", "state" : { - "revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42", - "version" : "1.2.0" + "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", + "version" : "1.2.1" } }, { @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-numerics.git", "state" : { - "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b", - "version" : "1.0.2" + "revision" : "e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8", + "version" : "1.0.3" } } ], diff --git a/Package.swift b/Package.swift index 431bb54..b59acfd 100644 --- a/Package.swift +++ b/Package.swift @@ -45,7 +45,8 @@ let package = Package( .library(name: "Compute", targets: ["Compute"]) ], dependencies: [ - .package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0") + .package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"), + .package(path: "../DarwinPrivateFrameworks"), ], targets: [ .target(name: "Utilities"), @@ -59,12 +60,28 @@ let package = Package( name: "Compute", dependencies: ["ComputeCxx"], cxxSettings: [.headerSearchPath("../ComputeCxx")], - swiftSettings: [.interoperabilityMode(.Cxx)] + swiftSettings: [.interoperabilityMode(.Cxx), .enableExperimentalFeature("Extern")] ), .testTarget( name: "ComputeTests", - dependencies: ["Compute", .product(name: "Algorithms", package: "swift-algorithms")], - swiftSettings: [.interoperabilityMode(.Cxx)], + dependencies: [ + "Compute", + .product(name: "Algorithms", package: "swift-algorithms"), + ], + swiftSettings: [ + .interoperabilityMode(.Cxx) + ], + linkerSettings: [.linkedLibrary("swiftDemangle")] + ), + .testTarget( + name: "ComputeCompatibilityTests", + dependencies: [ + .product(name: "AttributeGraph", package: "DarwinPrivateFrameworks"), + .product(name: "Algorithms", package: "swift-algorithms"), + ], + swiftSettings: [ + .interoperabilityMode(.Cxx) + ], linkerSettings: [.linkedLibrary("swiftDemangle")] ), .swiftRuntimeTarget( @@ -73,6 +90,24 @@ let package = Package( cxxSettings: [.headerSearchPath("")] ), .target(name: "ComputeCxxSwiftSupport"), + .testTarget( + name: "ComputeCxxTests", + dependencies: ["ComputeCxx"], + cSettings: [ + .headerSearchPath("../../Sources/ComputeCxx"), + .unsafeFlags([ + "-isystem", "\(swiftProjectPath)/swift/include", + "-isystem", "\(swiftProjectPath)/swift/stdlib/public/SwiftShims", + "-isystem", "\(swiftProjectPath)/swift/stdlib/public/runtime", + "-isystem", "\(swiftProjectPath)/llvm-project/llvm/include", + "-isystem", "\(swiftProjectPath)/llvm-project/clang/include", + "-isystem", "\(swiftProjectPath)/build/Default/swift/include", + "-isystem", "\(swiftProjectPath)/build/Default/llvm/include", + "-isystem", "\(swiftProjectPath)/build/Default/llvm/tools/clang/include", + ]), + ], + linkerSettings: [.linkedLibrary("swiftDemangle")] + ), ], cxxLanguageStandard: .cxx20 ) diff --git a/Sources/Compute/Attribute/AnyAttribute.swift b/Sources/Compute/Attribute/AnyAttribute.swift index 23b25b1..0e5f529 100644 --- a/Sources/Compute/Attribute/AnyAttribute.swift +++ b/Sources/Compute/Attribute/AnyAttribute.swift @@ -1,13 +1,5 @@ import ComputeCxx -struct AGGraphMutateAttributeThunk { - let body: (UnsafeMutableRawPointer) -> Void -} - -struct AGGraphSearchThunk { - let body: (AnyAttribute) -> Bool -} - extension AnyAttribute { public static var current: AnyAttribute? { @@ -33,41 +25,14 @@ extension AnyAttribute { type._visitSelf(info.body, visitor: &visitor) } - // XXX: Swift compiler crashes when capturing a generic type public func mutateBody(as type: Body.Type, invalidating: Bool, _ mutator: (inout Body) -> Void) { - withoutActuallyEscaping(mutator) { escapingMutator in - let thunk = AGGraphMutateAttributeThunk(body: { bodyPointer in - escapingMutator(&bodyPointer.assumingMemoryBound(to: Body.self).pointee) - }) - withUnsafePointer(to: thunk) { thunkPointer in - __AGGraphMutateAttribute( - self, - Metadata(type), - invalidating, - { - $0.assumingMemoryBound(to: AGGraphMutateAttributeThunk.self).pointee.body($1) - }, - thunkPointer - ) - } + Graph.mutateAttribute(self, type: Metadata(type), invalidating: invalidating) { pointer in + mutator(&pointer.assumingMemoryBound(to: Body.self).pointee) } - } public func breadthFirstSearch(options: AGSearchOptions, _ predicate: (AnyAttribute) -> Bool) -> Bool { - // TODO: @silgen? - return withoutActuallyEscaping(predicate) { escapingPredicate in - return withUnsafePointer(to: AGGraphSearchThunk(body: escapingPredicate)) { thunkPointer in - return __AGGraphSearch( - self, - options, - { - $0.assumingMemoryBound(to: AGGraphSearchThunk.self).pointee.body($1) - }, - thunkPointer - ) - } - } + return Graph.search(attribute: self, options: options, predicate: predicate) } public func setFlags(_ newFlags: AGAttributeFlags, mask: AGAttributeFlags) { diff --git a/Sources/Compute/Attribute/Attribute.swift b/Sources/Compute/Attribute/Attribute.swift index 7c95e01..220b7f9 100644 --- a/Sources/Compute/Attribute/Attribute.swift +++ b/Sources/Compute/Attribute/Attribute.swift @@ -39,20 +39,24 @@ public struct Attribute { flags: AGAttributeTypeFlags, update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) { - - guard let subgraph = Subgraph.current else { - // or preconditionFailure... - fatalError("attempting to create attribute with no subgraph: \(Body.self)") + guard let graphContext = Subgraph.currentGraphContext else { + preconditionFailure("attempting to create attribute with no subgraph: \(Body.self)") } - let graph = subgraph.graph - let typeID = graph.internAttributeType( - selfType: Body.self, - bodyType: Body.self, // TODO: make this function generic, don't need to pass witness tables - valueType: Value.self, - flags: flags, - update: update - ) + let typeID = graphContext.internAttributeType( + type: Metadata(Body.self) + ) { + let attributeType = AGAttributeType( + selfType: Body.self, + bodyType: Body.self, + valueType: Value.self, + flags: flags, + update: update() + ) + let pointer = UnsafeMutablePointer.allocate(capacity: 1) + pointer.initialize(to: attributeType) + return UnsafeRawPointer(pointer) + } identifier = __AGGraphCreateAttribute(typeID, body, value) } @@ -125,7 +129,7 @@ public struct Attribute { get { return identifier.flags } - set { + nonmutating set { identifier.flags = newValue } } diff --git a/Sources/Compute/Attribute/AttributeType.swift b/Sources/Compute/Attribute/AttributeType.swift index 050075a..9d09741 100644 --- a/Sources/Compute/Attribute/AttributeType.swift +++ b/Sources/Compute/Attribute/AttributeType.swift @@ -1,51 +1,26 @@ import ComputeCxx -struct AGGraphInternAttributeTypeThunk { - let body: () -> UnsafeRawPointer -} +extension Graph { + + @_extern(c, "AGGraphInternAttributeType") + fileprivate static func internAttributeType( + _ graph: UnsafeRawPointer, + type: Metadata, + makeAttributeType: () -> UnsafeRawPointer + ) + -> UInt32 -struct AGAttributeTypeUpdateThunk { - let body: (UnsafeMutableRawPointer, AnyAttribute) -> Void } -extension Graph { +extension AGUnownedGraphRef { - func internAttributeType( - selfType: Any.Type, - bodyType: _AttributeBody.Type, - valueType: Any.Type, - flags: AGAttributeTypeFlags, - update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void - ) -> UInt32 { - return withoutActuallyEscaping(update) { escapingUpdate in - let thunk = AGGraphInternAttributeTypeThunk(body: { - return withUnsafePointer(to: AGAttributeTypeUpdateThunk(body: escapingUpdate())) { updateThunkPointer in - let attributeType = AGAttributeType( - selfType: selfType, - bodyType: bodyType, - valueType: valueType, - flags: flags, - update: { - $0.assumingMemoryBound(to: AGAttributeTypeUpdateThunk.self).pointee.body($1, $2) - }, - updateContext: updateThunkPointer - ) - let pointer = UnsafeMutablePointer.allocate(capacity: 1) - pointer.initialize(to: attributeType) - return UnsafeRawPointer(pointer) - } - }) - return withUnsafePointer(to: thunk) { thunkPointer in - return __AGGraphInternAttributeType( - self, - Metadata(selfType), - { - return $0.assumingMemoryBound(to: AGGraphInternAttributeTypeThunk.self).pointee.body() - }, - thunkPointer - ) - } - } + @inline(__always) + func internAttributeType(type: Metadata, makeAttributeType: () -> UnsafeRawPointer) -> UInt32 { + return Graph.internAttributeType( + unsafeBitCast(self, to: UnsafeRawPointer.self), + type: type, + makeAttributeType: makeAttributeType + ) } } @@ -60,21 +35,18 @@ extension AGAttributeType { flags: AGAttributeTypeFlags, update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) -> UnsafeMutablePointer { - return withUnsafePointer(to: AGAttributeTypeUpdateThunk(body: update())) { updateThunkPointer in - let attributeType = AGAttributeType( - selfType: selfType, - bodyType: bodyType, - valueType: valueType, - flags: flags, - update: { - $0.assumingMemoryBound(to: AGAttributeTypeUpdateThunk.self).pointee.body($1, $2) - }, - updateContext: updateThunkPointer - ) - let pointer = UnsafeMutablePointer.allocate(capacity: 1) - pointer.initialize(to: attributeType) - return pointer - } + + let attributeType = AGAttributeType( + selfType: selfType, + bodyType: bodyType, + valueType: valueType, + flags: flags, + update: update() + ) + let pointer = UnsafeMutablePointer.allocate(capacity: 1) + pointer.initialize(to: attributeType) + return pointer + } // sub_1AFE86960 @@ -83,8 +55,7 @@ extension AGAttributeType { bodyType: _AttributeBody.Type, // witness table valueType: Any.Type, flags: AGAttributeTypeFlags, - update: @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer, AnyAttribute) -> Void, - updateContext: UnsafeRawPointer? + update: @escaping (UnsafeMutableRawPointer, AnyAttribute) -> Void, ) { self.init() @@ -95,10 +66,9 @@ extension AGAttributeType { effectiveFlags.insert(.option4) // TODO: is this flag reallyHasDestroySelf?? } - self.body_type_id = Metadata(selfType) - self.value_type_id = Metadata(valueType) - self.update_function = update - self.update_function_context = updateContext + self.typeID = Metadata(selfType) + self.valueTypeID = Metadata(valueType) + self.update_function = unsafeBitCast(update, to: AGClosureStorage2.self) self.callbacks = nil self.flags = effectiveFlags diff --git a/Sources/Compute/Attribute/Body/AttributeBody.swift b/Sources/Compute/Attribute/Body/AttributeBody.swift index 68331bb..ce3f32c 100644 --- a/Sources/Compute/Attribute/Body/AttributeBody.swift +++ b/Sources/Compute/Attribute/Body/AttributeBody.swift @@ -27,7 +27,7 @@ extension _AttributeBody { } public static var comparisonMode: AGComparisonMode { - return .option2 + return ._2 } public static var flags: AGAttributeTypeFlags { diff --git a/Sources/Compute/Attribute/External.swift b/Sources/Compute/Attribute/External.swift index 89d1efe..1997075 100644 --- a/Sources/Compute/Attribute/External.swift +++ b/Sources/Compute/Attribute/External.swift @@ -13,7 +13,7 @@ public struct External { extension External: _AttributeBody { public static var comparisonMode: AGComparisonMode { - return [.option1, .option2] + return [._1, ._2] } public static var flags: AGAttributeTypeFlags { diff --git a/Sources/Compute/Attribute/PointerOffset.swift b/Sources/Compute/Attribute/PointerOffset.swift index d25aaf7..99f633c 100644 --- a/Sources/Compute/Attribute/PointerOffset.swift +++ b/Sources/Compute/Attribute/PointerOffset.swift @@ -56,7 +56,7 @@ extension UnsafePointer { .assumingMemoryBound(to: Member.self) } - public subscript(offset: PointerOffset) -> Member { + public subscript(offset offset: PointerOffset) -> Member { unsafeAddress { return UnsafeRawPointer(self) .advanced(by: offset.byteOffset) @@ -83,7 +83,7 @@ extension UnsafeMutablePointer { .advanced(by: offset.byteOffset) .assumingMemoryBound(to: Member.self) } - unsafeMutableAddress { + nonmutating unsafeMutableAddress { return UnsafeMutableRawPointer(self) .advanced(by: offset.byteOffset) .assumingMemoryBound(to: Member.self) diff --git a/Sources/Compute/Attribute/Rule/AnyRuleContext.swift b/Sources/Compute/Attribute/Rule/AnyRuleContext.swift index 1a5c1a8..afb8862 100644 --- a/Sources/Compute/Attribute/Rule/AnyRuleContext.swift +++ b/Sources/Compute/Attribute/Rule/AnyRuleContext.swift @@ -1,9 +1,5 @@ import ComputeCxx -struct AGGraphWithUpdateThunk { - let body: () -> Void -} - public struct AnyRuleContext { public var attribute: AnyAttribute @@ -17,18 +13,7 @@ public struct AnyRuleContext { } public func update(body: () -> Void) { - // TODO: use silgen? - withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: AGGraphWithUpdateThunk(body: escapingBody)) { thunkPointer in - __AGGraphWithUpdate( - attribute, - { - $0.assumingMemoryBound(to: AGGraphWithUpdateThunk.self).pointee.body() - }, - thunkPointer - ) - } - } + Graph.withUpdate(attribute: attribute, body: body) } public func changedValue(of input: Attribute, options: AGValueOptions) -> ( diff --git a/Sources/Compute/Attribute/Rule/Rule.swift b/Sources/Compute/Attribute/Rule/Rule.swift index b0ea682..0cbd228 100644 --- a/Sources/Compute/Attribute/Rule/Rule.swift +++ b/Sources/Compute/Attribute/Rule/Rule.swift @@ -54,8 +54,20 @@ extension Rule { } -struct AGGraphReadCachedAttributeThunk { - let body: (AGUnownedGraphContextRef) -> UInt32 +extension Graph { + + @_extern(c, "AGGraphReadCachedAttribute") + static func readCachedAttribute( + identifier: UInt64, + type: Metadata, + body: UnsafeMutableRawPointer, + valueType: Metadata, + options: AGCachedValueOptions, + attribute: AnyAttribute, + changed: UnsafeMutablePointer?, + internAttributeType: (AGUnownedGraphRef) -> UInt32 + ) + -> UnsafeRawPointer } extension Rule where Self: Hashable { @@ -94,38 +106,33 @@ extension Rule where Self: Hashable { bodyPtr: UnsafeRawPointer, update: () -> (UnsafeMutableRawPointer, AnyAttribute) -> Void ) -> UnsafePointer { - - let result = withUnsafePointer(to: self) { selfPointer in - return withoutActuallyEscaping(update) { escapingUpdate in - let thunk = AGGraphReadCachedAttributeThunk { graphContextRef in - let graph = __AGGraphContextGetGraph(graphContextRef) - return graph.takeUnretainedValue().internAttributeType( + return withUnsafePointer(to: self) { selfPointer in + let result = Graph.readCachedAttribute( + identifier: UInt64(hashValue), + type: Metadata(Self.self), + body: UnsafeMutablePointer(mutating: selfPointer), + valueType: Metadata(Value.self), + options: options, + attribute: owner ?? .nil, + changed: nil + ) { graphContext in + return graphContext.internAttributeType( + type: Metadata(Self.self) + ) { + let attributeType = AGAttributeType( selfType: Self.self, bodyType: Self.self, valueType: Value.self, - flags: [], - update: escapingUpdate - ) - } - return withUnsafePointer(to: thunk) { thunkPointer in - return __AGGraphReadCachedAttribute( - UInt64(hashValue), // TODO: bitPattern? - Metadata(Self.self), - UnsafeMutablePointer(mutating: selfPointer), - Metadata(Value.self), - options, - owner ?? .nil, - nil, - { - return $0.assumingMemoryBound(to: AGGraphReadCachedAttributeThunk.self).pointee.body($1) - }, - thunkPointer + flags: [], // TODO: check flags are empty + update: update() ) + let pointer = UnsafeMutablePointer.allocate(capacity: 1) + pointer.initialize(to: attributeType) + return UnsafeRawPointer(pointer) } } + return result.assumingMemoryBound(to: Value.self) } - let pointer = result.assumingMemoryBound(to: Value.self) - return UnsafePointer(pointer) } } diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index 2aa454a..cfe239b 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -1,44 +1,64 @@ import ComputeCxx -struct AGGraphSetUpdateCallbackThunk { - let body: () -> Void -} +extension Graph { + + @_extern(c, "AGGraphWithUpdate") + static func withUpdate(attribute: AnyAttribute, body: () -> Void) -struct AGGraphSetInvalidationCallbackThunk { - let body: (AnyAttribute) -> Void } -struct AGGraphWithMainThreadHandlerThunk { - let body: () -> Void +extension Graph { + + @_extern(c, "AGGraphSearch") + static func search(attribute: AnyAttribute, options: AGSearchOptions, predicate: (AnyAttribute) -> Bool) -> Bool + } -struct MainThreadHandlerThunk { - let body: ((UnsafeRawPointer) -> Void, UnsafeRawPointer) -> Void +extension Graph { + + @_extern(c, "AGGraphMutateAttribute") + static func mutateAttribute( + _ attribute: AnyAttribute, + type: Metadata, + invalidating: Bool, + body: (UnsafeMutableRawPointer) -> Void + ) + } extension Graph { + @_extern(c, "AGGraphSetUpdateCallback") + private static func setUpdateCallback(_ graph: UnsafeRawPointer, callback: (() -> Void)?) + + public static func setUpdateCallback(_ graph: Graph, callback: (() -> Void)?) { + withUnsafePointer(to: self) { selfPointer in + Graph.setUpdateCallback(selfPointer, callback: callback) + } + } + public func onUpdate(_ handler: @escaping () -> Void) { - withUnsafePointer(to: AGGraphSetUpdateCallbackThunk(body: handler)) { thunkPointer in - __AGGraphSetUpdateCallback( - self, - { - $0.assumingMemoryBound(to: AGGraphSetUpdateCallbackThunk.self).pointee.body() - }, - thunkPointer - ) + withUnsafePointer(to: self) { graphPointer in + Graph.setUpdateCallback(graphPointer, callback: handler) + } + } + +} + +extension Graph { + + @_extern(c, "AGGraphSetInvalidationCallback") + private static func setInvalidationCallback(_ graph: UnsafeRawPointer, callback: ((AnyAttribute) -> Void)?) + + public static func setInvalidationCallback(_ graph: Graph, callback: ((AnyAttribute) -> Void)?) { + withUnsafePointer(to: self) { selfPointer in + Graph.setInvalidationCallback(selfPointer, callback: callback) } } public func onInvalidation(_ handler: @escaping (AnyAttribute) -> Void) { - withUnsafePointer(to: AGGraphSetInvalidationCallbackThunk(body: handler)) { thunkPointer in - __AGGraphSetInvalidationCallback( - self, - { - $0.assumingMemoryBound(to: AGGraphSetInvalidationCallbackThunk.self).pointee.body($1) - }, - thunkPointer - ) + withUnsafePointer(to: self) { graphPointer in + Graph.setInvalidationCallback(graphPointer, callback: handler) } } @@ -64,32 +84,19 @@ extension Graph { return result } + @_extern(c, "AGGraphWithMainThreadHandler") + private static func withMainThreadHandler( + _ graph: UnsafeRawPointer, + body: () -> Void, + mainThreadHandler: (() -> Void) -> Void + ) + public func withMainThreadHandler( - // TODO: does this need to be escaping? - _ mainThreadHandler: @escaping (() -> Void) -> Void, + _ mainThreadHandler: (() -> Void) -> Void, do body: () -> Void ) { - withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: AGGraphWithMainThreadHandlerThunk(body: escapingBody)) { thunkPointer in - let mainThreadHandlerThunk = MainThreadHandlerThunk(body: { handler, context in - mainThreadHandler({ - handler(context) - }) - }) - withUnsafePointer(to: mainThreadHandlerThunk) { mainThreadHandlerThunkPointer in - __AGGraphWithMainThreadHandler( - self, - { - $0.assumingMemoryBound(to: AGGraphWithMainThreadHandlerThunk.self).pointee.body() - }, - thunkPointer, - { - $0.assumingMemoryBound(to: MainThreadHandlerThunk.self).pointee.body($1, $2) - }, - mainThreadHandlerThunkPointer - ) - } - } + withUnsafePointer(to: self) { selfPointer in + Graph.withMainThreadHandler(selfPointer, body: body, mainThreadHandler: mainThreadHandler) } } @@ -130,20 +137,26 @@ extension Graph { } +extension Graph { + + public var mainUpdates: Int { numericCast(counter(for: .mainThreadUpdateCount)) } + +} + extension Graph { public func print(includeValues: Bool) { Swift.print(graphvizDescription(includeValues: includeValues)) } - public func archiveJSON(name: String?) { + public static func archiveJSON(name: String?) { __AGGraphArchiveJSON(name?.cString(using: .utf8)) } public func graphvizDescription(includeValues: Bool) -> String { - let result = __AGGraphDescription( + let result = Graph.description( self, - [AGDescriptionFormat: "graph/dot", AGDescriptionIncludeValues: includeValues] as CFDictionary + options: [AGDescriptionFormat: "graph/dot", AGDescriptionIncludeValues: includeValues] as CFDictionary ).takeUnretainedValue() guard let description = result as? String else { preconditionFailure() @@ -156,9 +169,9 @@ extension Graph { } public static func stackDescription(maxFrames: Int) -> String { - let result = __AGGraphDescription( + let result = Graph.description( nil, - [AGDescriptionFormat: "stack/text", AGDescriptionMaxFrames: maxFrames] as CFDictionary + options: [AGDescriptionFormat: "stack/text", AGDescriptionMaxFrames: maxFrames] as CFDictionary ).takeUnretainedValue() guard let description = result as? String else { preconditionFailure() @@ -171,7 +184,7 @@ extension Graph { extension Graph: @retroactive Equatable { public static func == (_ lhs: Graph, _ rhs: Graph) -> Bool { - return __AGGraphGetCounter(lhs, .graphID) == __AGGraphGetCounter(rhs, .graphID) + return lhs.counter(for: .graphID) == rhs.counter(for: .graphID) } } diff --git a/Sources/Compute/Graph/Subgraph.swift b/Sources/Compute/Graph/Subgraph.swift index 067ba6b..eddbe77 100644 --- a/Sources/Compute/Graph/Subgraph.swift +++ b/Sources/Compute/Graph/Subgraph.swift @@ -1,26 +1,14 @@ import ComputeCxx -struct AGSubgraphAddObserverThunk { - let body: () -> Void -} - -struct AGSubgraphApplyThunk { - let body: (AnyAttribute) -> Void -} - extension Subgraph { - public func addObserver(_ observer: @escaping () -> Void) -> Int { - let result = withUnsafePointer(to: AGSubgraphAddObserverThunk(body: observer)) { thunkPointer in - return __AGSubgraphAddObserver( - self, - { - $0.assumingMemoryBound(to: AGSubgraphAddObserverThunk.self).pointee.body() - }, - thunkPointer - ) + @_extern(c, "AGSubgraphAddObserver") + private static func addObserver(_ subgraph: UnsafeRawPointer, observer: () -> Void) -> Int + + func addObserver(_ observer: () -> Void) -> Int { + return withUnsafePointer(to: self) { selfPointer in + return Subgraph.addObserver(selfPointer, observer: observer) } - return Int(result) // TODO: where is this converted? } public func apply(_ body: () -> T) -> T { @@ -34,20 +22,13 @@ extension Subgraph { return body() } + @_extern(c, "AGSubgraphApply") + private static func apply(_ subgraph: UnsafeRawPointer, flags: AGAttributeFlags, body: (AnyAttribute) -> Void) + public func forEach(_ flags: AGAttributeFlags, _ body: (AnyAttribute) -> Void) { - withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: AGSubgraphApplyThunk(body: escapingBody)) { thunkPointer in - __AGSubgraphApply( - self, - flags, - { - $0.assumingMemoryBound(to: AGSubgraphApplyThunk.self).pointee.body($1) - }, - thunkPointer - ) - } + withUnsafePointer(to: self) { selfPointer in + Subgraph.apply(selfPointer, flags: flags, body: body) } - } } @@ -55,19 +36,19 @@ extension Subgraph { extension Subgraph { public static func addTreeValue(_ value: Attribute, forKey key: UnsafePointer, flags: UInt32) { - if __AGSubgraphShouldRecordTree() { + if shouldRecordTree { __AGSubgraphAddTreeValue(value.identifier, Metadata(Value.self), key, flags) } } public static func beginTreeElement(value: Attribute, flags: UInt32) { - if __AGSubgraphShouldRecordTree() { + if shouldRecordTree { __AGSubgraphBeginTreeElement(value.identifier, Metadata(Value.self), flags) } } public static func endTreeElement(value: Attribute) { - if __AGSubgraphShouldRecordTree() { + if shouldRecordTree { __AGSubgraphEndTreeElement(value.identifier) } } diff --git a/Sources/Compute/Runtime/CompareValues.swift b/Sources/Compute/Runtime/CompareValues.swift index 795c7d0..ed958b2 100644 --- a/Sources/Compute/Runtime/CompareValues.swift +++ b/Sources/Compute/Runtime/CompareValues.swift @@ -8,11 +8,11 @@ extension AGComparisonOptions { } -func compareValues(_ lhs: Value, _ rhs: Value, mode: AGComparisonMode) -> Bool { +public func compareValues(_ lhs: Value, _ rhs: Value, mode: AGComparisonMode = [._1, ._2]) -> Bool { return compareValues(lhs, rhs, options: AGComparisonOptions(mode: mode)) } -func compareValues(_ lhs: Value, _ rhs: Value, options: AGComparisonOptions) -> Bool { +public func compareValues(_ lhs: Value, _ rhs: Value, options: AGComparisonOptions) -> Bool { return withUnsafePointer(to: lhs) { lhsPointer in return withUnsafePointer(to: rhs) { rhsPointer in return __AGCompareValues(lhsPointer, rhsPointer, Metadata(Value.self), options.union(.copyOnWrite)) @@ -20,3 +20,5 @@ func compareValues(_ lhs: Value, _ rhs: Value, options: AGComparisonOptio } } + + diff --git a/Sources/Compute/Runtime/Enum.swift b/Sources/Compute/Runtime/Enum.swift index 465d23c..fc3cb47 100644 --- a/Sources/Compute/Runtime/Enum.swift +++ b/Sources/Compute/Runtime/Enum.swift @@ -1,45 +1,33 @@ import ComputeCxx -struct AGTypeApplyEnumDataThunk { - let body: (Int, Any.Type, UnsafeRawPointer) -> Void -} +@_extern(c, "AGTypeApplyEnumData") +func applyEnumData( + type: Metadata, + value: UnsafeRawPointer, + body: (Int, Metadata, UnsafeRawPointer) -> Void +) -> Bool public func withUnsafePointerToEnumCase( of enumValue: UnsafeMutablePointer, do body: (Int, Any.Type, UnsafeRawPointer) -> Void ) -> Bool { - return withoutActuallyEscaping(body) { escapingBody in - return withUnsafePointer(to: AGTypeApplyEnumDataThunk(body: escapingBody)) { thunkPointer in - return __AGTypeApplyEnumData( - Metadata(Value.self), - enumValue, - { - $0.assumingMemoryBound(to: AGTypeApplyEnumDataThunk.self).pointee.body(Int($1), $2.type, $3) - }, - thunkPointer - ) - } + return applyEnumData(type: Metadata(Value.self), value: enumValue) { tag, fieldType, fieldValue in + body(tag, fieldType.type, fieldValue) } } -struct AGTypeApplyMutableEnumDatThunk { - let body: (Int, Any.Type, UnsafeMutableRawPointer) -> Void -} +@_extern(c, "AGTypeApplyMutableEnumData") +func applyMutableEnumData( + type: Metadata, + value: UnsafeRawPointer, + body: (Int, Metadata, UnsafeMutableRawPointer) -> Void +) -> Bool public func withUnsafeMutablePointerToEnumCase( of enumValue: UnsafeMutablePointer, do body: (Int, Any.Type, UnsafeMutableRawPointer) -> Void ) -> Bool { - return withoutActuallyEscaping(body) { escapingBody in - return withUnsafePointer(to: AGTypeApplyMutableEnumDatThunk(body: escapingBody)) { thunkPointer in - return __AGTypeApplyMutableEnumData( - Metadata(Value.self), - enumValue, - { - $0.assumingMemoryBound(to: AGTypeApplyMutableEnumDatThunk.self).pointee.body(Int($1), $2.type, $3) - }, - thunkPointer - ) - } + return applyMutableEnumData(type: Metadata(Value.self), value: enumValue) { tag, fieldType, fieldValue in + body(tag, fieldType.type, fieldValue) } } diff --git a/Sources/Compute/Runtime/Metadata.swift b/Sources/Compute/Runtime/Metadata.swift index 95fd483..bae4084 100644 --- a/Sources/Compute/Runtime/Metadata.swift +++ b/Sources/Compute/Runtime/Metadata.swift @@ -1,26 +1,16 @@ import ComputeCxx import Foundation -struct AGTypeApplyFieldsThunk { - var body: (UnsafePointer, Int, Any.Type) -> Void -} +extension Metadata { + + @_extern(c, "AGTypeApplyFields") + static func applyFields(type: Metadata, body: (UnsafePointer, Int, Metadata) -> Void) -struct AGTypeApplyFields2Thunk { - var body: (UnsafePointer, Int, Any.Type) -> Bool } public func forEachField(of type: Any.Type, do body: (UnsafePointer, Int, Any.Type) -> Void) { - - withoutActuallyEscaping(body) { escapingClosure in - withUnsafePointer(to: AGTypeApplyFieldsThunk(body: escapingClosure)) { thunkPointer in - __AGTypeApplyFields( - Metadata(type), - { - $0.assumingMemoryBound(to: AGTypeApplyFieldsThunk.self).pointee.body($1, $2, $3.type) - }, - thunkPointer - ) - } + Metadata.applyFields(type: Metadata(type)) { fieldName, fieldOffset, fieldType in + body(fieldName, fieldOffset, fieldType.type) } } @@ -34,20 +24,19 @@ extension Metadata { return unsafeBitCast(rawValue, to: Any.Type.self) } - public func forEachField(options: ApplyOptions, do body: (UnsafePointer, Int, Any.Type) -> Bool) + @_extern(c, "AGTypeApplyFields2") + static func applyFields2( + type: Metadata, + options: AGTypeApplyOptions, + body: (UnsafePointer, Int, Metadata) -> Bool + ) + -> Bool + + public func forEachField(options: AGTypeApplyOptions, do body: (UnsafePointer, Int, Any.Type) -> Bool) -> Bool { - return withoutActuallyEscaping(body) { escapingClosure in - return withUnsafePointer(to: AGTypeApplyFields2Thunk(body: escapingClosure)) { thunkPointer in - return __AGTypeApplyFields2( - self, - options, - { - return $0.assumingMemoryBound(to: AGTypeApplyFields2Thunk.self).pointee.body($1, $2, $3.type) - }, - thunkPointer - ) - } + return Metadata.applyFields2(type: self, options: options) { fieldName, fieldOffset, fieldType in + return body(fieldName, fieldOffset, fieldType.type) } } diff --git a/Sources/Compute/Runtime/Tuple.swift b/Sources/Compute/Runtime/Tuple.swift index 1e68e83..5e44d7c 100644 --- a/Sources/Compute/Runtime/Tuple.swift +++ b/Sources/Compute/Runtime/Tuple.swift @@ -1,21 +1,5 @@ -struct AGTupleWithBufferThunk { - let body: (UnsafeMutableTuple) -> Void -} - -public func withUnsafeTuple(of type: TupleType, count: Int, _ body: (UnsafeMutableTuple) -> Void) { - withoutActuallyEscaping(body) { escapingBody in - withUnsafePointer(to: AGTupleWithBufferThunk(body: escapingBody)) { thunkPointer in - __AGTupleWithBuffer( - type, - count, - { - $0.assumingMemoryBound(to: AGTupleWithBufferThunk.self).pointee.body($1) - }, - thunkPointer - ) - } - } -} +@_extern(c, "AGTupleWithBuffer") +public func withUnsafeTuple(of type: TupleType, count: Int, _ body: (UnsafeMutableTuple) -> Void) extension TupleType { @@ -24,7 +8,7 @@ extension TupleType { } public init(_ type: Any.Type) { - self.init(rawValue: OpaquePointer.init(unsafeBitCast(type, to: UnsafePointer.self))) + self.init(rawValue: unsafeBitCast(type, to: OpaquePointer.self)) } public var type: Any.Type { diff --git a/Sources/ComputeCxx/Attribute/AGAttribute.cpp b/Sources/ComputeCxx/Attribute/AGAttribute.cpp index 983409d..665a1ca 100644 --- a/Sources/ComputeCxx/Attribute/AGAttribute.cpp +++ b/Sources/ComputeCxx/Attribute/AGAttribute.cpp @@ -2,4 +2,4 @@ #include "AttributeID.h" -const AGAttribute AGAttributeNil = AGAttribute(AG::AttributeIDNil.to_storage()); +const AGAttribute AGAttributeNil = AGAttribute(AG::AttributeIDNil); diff --git a/Sources/ComputeCxx/Attribute/AGAttribute.h b/Sources/ComputeCxx/Attribute/AGAttribute.h index 80aa8e9..63e45f2 100644 --- a/Sources/ComputeCxx/Attribute/AGAttribute.h +++ b/Sources/ComputeCxx/Attribute/AGAttribute.h @@ -16,8 +16,12 @@ typedef uint32_t AGAttribute AG_SWIFT_STRUCT AG_SWIFT_NAME(AnyAttribute); CF_EXPORT const AGAttribute AGAttributeNil; +// TODO: validate where these come from typedef CF_OPTIONS(uint8_t, AGAttributeFlags) { - AGAttributeFlagsNone = 0, + AGAttributeFlagsDefault = 0, + AGAttributeFlagsActive = 1 << 0, + AGAttributeFlagsRemovable = 1 << 1, + AGAttributeFlagsInvalidatable = 1 << 2, }; typedef CF_OPTIONS(uint32_t, AGAttributeTypeFlags) { @@ -36,11 +40,17 @@ typedef struct AGAttributeVTable { void *callback5; } AGAttributeVTable; +struct AGClosureStorage2 { + void *a; + void *b; +}; + typedef struct AGAttributeType { - AGTypeID body_type_id; - AGTypeID value_type_id; - void (*update_function)(const void *, void *, AGAttribute); - const void *_Nullable update_function_context; + AGTypeID typeID; + AGTypeID valueTypeID; + + AGClosureStorage2 update_function; + AGAttributeVTable *_Nullable callbacks; AGAttributeTypeFlags flags; diff --git a/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp b/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp index 61b5cd2..59efbbf 100644 --- a/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp +++ b/Sources/ComputeCxx/Attribute/AGWeakAttribute.cpp @@ -16,5 +16,5 @@ AGWeakAttribute AGCreateWeakAttribute(AGAttribute attribute) { AGAttribute AGWeakAttributeGetAttribute(AGWeakAttribute attribute) { auto attribute_id = AG::WeakAttributeID::from_cf(attribute); - return attribute_id.evaluate().to_storage(); + return attribute_id.evaluate(); } diff --git a/Sources/ComputeCxx/Attribute/AttributeID.h b/Sources/ComputeCxx/Attribute/AttributeID.h index faed16c..8f9a607 100644 --- a/Sources/ComputeCxx/Attribute/AttributeID.h +++ b/Sources/ComputeCxx/Attribute/AttributeID.h @@ -5,6 +5,7 @@ #include #include +#include "AGAttribute.h" #include "Data/Page.h" #include "Data/Pointer.h" #include "Data/Zone.h" @@ -41,10 +42,11 @@ enum TraversalOptions : uint32_t { /// if any weak references evaluate to nil. EvaluateWeakReferences = 1 << 4, }; - -inline TraversalOptions operator|(TraversalOptions lhs, TraversalOptions rhs) { - return static_cast(lhs | rhs); +inline TraversalOptions &operator|=(TraversalOptions &lhs, TraversalOptions rhs) { + lhs = TraversalOptions(uint32_t(lhs) | uint32_t(rhs)); + return lhs; } +inline TraversalOptions operator|(TraversalOptions lhs, TraversalOptions rhs) { return (lhs |= rhs); } class AttributeID { friend RelativeAttributeID; @@ -66,9 +68,10 @@ class AttributeID { explicit constexpr AttributeID() : _value(0) {}; explicit AttributeID(data::ptr node) : _value(node.offset() | Kind::Direct) {}; explicit AttributeID(data::ptr indirect_node) : _value(indirect_node.offset() | Kind::Indirect) {}; - explicit AttributeID(data::ptr indirect_node) : _value(indirect_node.offset() | Kind::Indirect) {}; + explicit AttributeID(data::ptr indirect_node) + : _value(indirect_node.offset() | Kind::Indirect) {}; - constexpr uint32_t to_storage() const { return _value; } + operator AGAttribute() const { return _value; } static constexpr AttributeID from_storage(uint32_t value) { return AttributeID(value); } // @@ -80,16 +83,20 @@ class AttributeID { bool operator<(const AttributeID &other) const { return _value < other._value; } - operator bool() const { return _value != 0; } + explicit operator bool() const { return _value != 0; } // MARK: Accessing zone data // data::ptr as_ptr() const { return data::ptr(_value); }; // uint32_t value() const { return _value; } - // operator bool() const { return _value == 0; }; + // explicit operator bool() const { return _value == 0; }; + + data::page &page() const { + assert(_value); + return *data::ptr(_value).page_ptr(); + }; - data::page &page() const { return *data::ptr(_value).page_ptr(); }; data::ptr page_ptr() const { return data::ptr(_value).page_ptr(); }; void validate_data_offset() const { data::ptr(_value).assert_valid(); }; diff --git a/Sources/ComputeCxx/Attribute/Node/Node.h b/Sources/ComputeCxx/Attribute/Node/Node.h index ca1cb22..0a924c7 100644 --- a/Sources/ComputeCxx/Attribute/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/Node/Node.h @@ -16,19 +16,6 @@ class zone; class AttributeType; class Graph; -// AGAttributeFlags -class AttributeFlags { - private: - uint8_t _data; - - public: - AttributeFlags() : _data(0) {} - AttributeFlags(uint8_t data) : _data(data) {} - uint8_t data() const { return _data; } - - operator bool() const { return _data == 0; }; -}; - class Node { public: class State { @@ -131,7 +118,7 @@ class Node { State _state; unsigned int _type_id : 24; RelativeAttributeID _relative_offset; - AttributeFlags _subgraph_flags; + AGAttributeFlags _subgraph_flags; Flags _flags; data::ptr _value; @@ -156,9 +143,9 @@ class Node { RelativeAttributeID relative_offset() const { return _relative_offset; }; void set_relative_offset(RelativeAttributeID relative_offset) { _relative_offset = relative_offset; }; - AttributeFlags &subgraph_flags() { return _subgraph_flags; }; - const AttributeFlags &subgraph_flags() const { return _subgraph_flags; }; - void set_subgraph_flags(AttributeFlags subgraph_flags) { _subgraph_flags = subgraph_flags; }; + AGAttributeFlags &subgraph_flags() { return _subgraph_flags; }; + const AGAttributeFlags &subgraph_flags() const { return _subgraph_flags; }; + void set_subgraph_flags(AGAttributeFlags subgraph_flags) { _subgraph_flags = subgraph_flags; }; Flags &flags() { return _flags; }; const Flags &flags() const { return _flags; }; diff --git a/Sources/ComputeCxx/Closure/AGClosure.h b/Sources/ComputeCxx/Closure/AGClosure.h index 299550d..f63588b 100644 --- a/Sources/ComputeCxx/Closure/AGClosure.h +++ b/Sources/ComputeCxx/Closure/AGClosure.h @@ -11,20 +11,17 @@ CF_EXTERN_C_BEGIN struct AGOpaqueValue; -struct AGClosureStorage { +class AGClosureStorage { + public: const AGOpaqueValue *_Nullable function; const AGOpaqueValue *_Nullable context; - - AGClosureStorage() : function(nullptr), context(nullptr){}; - AGClosureStorage(const AGOpaqueValue *fun, const AGOpaqueValue *ctx) : function(fun), context(ctx){}; } SWIFT_SHARED_REFERENCE(AGRetainClosure, AGReleaseClosure); -void AGRetainClosure(AGClosureStorage *closure); -void AGReleaseClosure(AGClosureStorage *closure); - - typedef struct AGClosureStorage *AGClosureRef AG_SWIFT_NAME(Closure); +void AGRetainClosure(AGClosureRef closure); +void AGReleaseClosure(AGClosureRef closure); + CF_EXTERN_C_END CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Closure/ClosureFunction.h b/Sources/ComputeCxx/Closure/ClosureFunction.h index 76b5135..72e3886 100644 --- a/Sources/ComputeCxx/Closure/ClosureFunction.h +++ b/Sources/ComputeCxx/Closure/ClosureFunction.h @@ -24,11 +24,12 @@ template class ClosureFunction { inline ClosureFunction(std::nullptr_t) : _function(nullptr), _context(nullptr) {} inline ~ClosureFunction() { ::swift::swift_release((::swift::HeapObject *)_context); } - operator bool() { return _function != nullptr; } + explicit operator bool() { return _function != nullptr; } const ReturnType operator()(Args... args) const noexcept { return _function(_context, std::forward(args)...); } + }; template diff --git a/Sources/ComputeCxx/Containers/IndirectPointerVector.h b/Sources/ComputeCxx/Containers/IndirectPointerVector.h index 8f08afe..7cfbb6e 100644 --- a/Sources/ComputeCxx/Containers/IndirectPointerVector.h +++ b/Sources/ComputeCxx/Containers/IndirectPointerVector.h @@ -214,7 +214,7 @@ void indirect_pointer_vector::resize(size_type count) { } if (count == 1) { if (_data == 0) { - _data = NullElement; + _data = NullElement; // How does this affect size,empty and capacity? } return; } diff --git a/Sources/ComputeCxx/Data/Pointer.h b/Sources/ComputeCxx/Data/Pointer.h index 5529e48..d6963f2 100644 --- a/Sources/ComputeCxx/Data/Pointer.h +++ b/Sources/ComputeCxx/Data/Pointer.h @@ -52,7 +52,10 @@ template class ptr { explicit operator bool() const noexcept { return _offset != 0; }; std::add_lvalue_reference_t operator*() const noexcept { return *get(); }; - T *_Nonnull operator->() const noexcept { return get(); }; + T *_Nonnull operator->() const noexcept { + assert(_offset != 0); + return get(); + }; bool operator==(const ptr &other) const noexcept { return _offset == other._offset; }; bool operator!=(const ptr &other) const noexcept { return _offset != other._offset; }; diff --git a/Sources/ComputeCxx/Data/Table.cpp b/Sources/ComputeCxx/Data/Table.cpp index de60af4..4756695 100644 --- a/Sources/ComputeCxx/Data/Table.cpp +++ b/Sources/ComputeCxx/Data/Table.cpp @@ -59,6 +59,12 @@ table::table() { } } +table::~table() { + if (_malloc_zone) { + malloc_destroy_zone(_malloc_zone); + } +} + void table::lock() { os_unfair_lock_lock(&_lock); } void table::unlock() { os_unfair_lock_unlock(&_lock); } @@ -259,7 +265,7 @@ uint64_t table::raw_page_seed(ptr page) { uint64_t result = 0; if (map_index < _page_metadata_maps.size() && _page_metadata_maps[map_index].test(page_index % page_size)) { - auto raw_zone_info = page->zone->info().to_raw_value(); + auto raw_zone_info = page->zone->info().to_raw_value(); // TODO: check includes deleted flag result = raw_zone_info | (1 < 8); } diff --git a/Sources/ComputeCxx/Data/Table.h b/Sources/ComputeCxx/Data/Table.h index b1b81cd..6b55d19 100644 --- a/Sources/ComputeCxx/Data/Table.h +++ b/Sources/ComputeCxx/Data/Table.h @@ -55,6 +55,7 @@ class table { static table &shared(); table(); + ~table(); // only called during testing void lock(); void unlock(); diff --git a/Sources/ComputeCxx/External/ExternalTrace.cpp b/Sources/ComputeCxx/External/ExternalTrace.cpp index 0e33be9..d11b84c 100644 --- a/Sources/ComputeCxx/External/ExternalTrace.cpp +++ b/Sources/ComputeCxx/External/ExternalTrace.cpp @@ -35,7 +35,7 @@ void ExternalTrace::end_update(const AG::Subgraph &subgraph) { void ExternalTrace::begin_update(const AG::Graph::UpdateStack &update_stack, AG::data::ptr node, uint32_t options) { if (auto callback = _interface->begin_update_stack) { - callback(_trace, AG::AttributeID(node).to_storage()); + callback(_trace, AGAttribute(AG::AttributeID(node))); } } @@ -75,14 +75,14 @@ void ExternalTrace::end_update(const AG::Graph::Context &context) { void ExternalTrace::begin_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute) { auto cf_context = context.to_cf(); if (auto callback = _interface->begin_invalidation) { - callback(_trace, cf_context, attribute); + callback(_trace, cf_context, AGAttribute(attribute)); } } void ExternalTrace::end_invalidation(const AG::Graph::Context &context, AG::AttributeID attribute) { auto cf_context = context.to_cf(); if (auto callback = _interface->end_invalidation) { - callback(_trace, cf_context, attribute); + callback(_trace, cf_context, AGAttribute(attribute)); } } @@ -102,7 +102,7 @@ void ExternalTrace::begin_event(AG::data::ptr node, uint32_t event_id) if (auto callback = _interface->begin_event) { if (auto subgraph = AG::AttributeID(node).subgraph()) { const char *event_name = subgraph->graph()->key_name(event_id); - callback(_trace, AG::AttributeID(node).to_storage(), event_name); + callback(_trace, AGAttribute(AG::AttributeID(node)), event_name); } } } @@ -111,7 +111,7 @@ void ExternalTrace::end_event(AG::data::ptr node, uint32_t event_id) { if (auto callback = _interface->end_event) { if (auto subgraph = AG::AttributeID(node).subgraph()) { const char *event_name = subgraph->graph()->key_name(event_id); - callback(_trace, AG::AttributeID(node).to_storage(), event_name); + callback(_trace, AGAttribute(AG::AttributeID(node)), event_name); } } } @@ -225,19 +225,19 @@ void ExternalTrace::mark_value(AG::data::ptr node) { void ExternalTrace::added(AG::data::ptr indirect_node) { if (auto callback = _interface->added_indirect_node) { - callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind + callback(_trace, AGAttribute(AG::AttributeID(indirect_node))); // TODO: check sets kind } } void ExternalTrace::set_source(AG::data::ptr indirect_node, AG::AttributeID source) { if (auto callback = _interface->set_source) { - callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind + callback(_trace, AGAttribute(AG::AttributeID(indirect_node))); // TODO: check sets kind } } void ExternalTrace::set_dependency(AG::data::ptr indirect_node, AG::AttributeID dependency) { if (auto callback = _interface->set_dependency) { - callback(_trace, AG::AttributeID(indirect_node)); // TODO: check sets kind + callback(_trace, AGAttribute(AG::AttributeID(indirect_node))); // TODO: check sets kind } } @@ -306,7 +306,7 @@ void ExternalTrace::compare_failed(AG::data::ptr node, const void *lhs if (_interface->options > 3) { AGComparisonStateStorage storage = {lhs, rhs, range_offset, range_size, AGTypeID(&type)}; if (auto callback = _interface->compare_failed) { - callback(_trace, AG::AttributeID(node).to_storage(), &storage); + callback(_trace, AGAttribute(AG::AttributeID(node)), &storage); } } } diff --git a/Sources/ComputeCxx/Graph/AGGraph-Private.h b/Sources/ComputeCxx/Graph/AGGraph-Private.h index 7939330..321e2de 100644 --- a/Sources/ComputeCxx/Graph/AGGraph-Private.h +++ b/Sources/ComputeCxx/Graph/AGGraph-Private.h @@ -8,13 +8,13 @@ CF_ASSUME_NONNULL_BEGIN -struct AGUnownedGraphContext { - AG::Graph *graph; -}; - struct AGGraphStorage { CFRuntimeBase base; AG::Graph::Context context; }; +struct AGGraphContextStorage { + AG::Graph::Context context; +}; + CF_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/Graph/AGGraph.cpp b/Sources/ComputeCxx/Graph/AGGraph.cpp index dcd9054..a1ec960 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.cpp +++ b/Sources/ComputeCxx/Graph/AGGraph.cpp @@ -63,24 +63,29 @@ AGGraphRef AGGraphCreateShared(AGGraphRef original) { graph = &original->context.graph(); AG::Graph::will_add_to_context(graph); } else { - graph = new AG::Graph(); + graph = new AG::Graph(); // ref = 1 } - new (&instance->context) AG::Graph::Context(graph); + new (&instance->context) AG::Graph::Context(graph); // ref + 1 - AG::Graph::did_remove_from_context(graph); + AG::Graph::did_remove_from_context(graph); // ref - 1 instance->context.set_invalidated(false); return instance; }; -AGUnownedGraphContextRef AGGraphGetGraphContext(AGGraphRef graph) { - return reinterpret_cast(AG::Graph::Context::from_cf(graph)); +AGUnownedGraphRef AGGraphGetGraphContext(AGGraphRef graph) { + AG::Graph::Context *graph_context = AG::Graph::Context::from_cf(graph); + AG::Graph *unowned_graph = &graph_context->graph(); + fprintf(stdout, "AGGraphGetGraphContext %p -> %p", graph, unowned_graph); + return reinterpret_cast(unowned_graph); } -AGGraphRef AGGraphContextGetGraph(AGUnownedGraphContextRef context) { - return reinterpret_cast(reinterpret_cast(context) - sizeof(CFRuntimeBase)); +AGGraphRef AGGraphContextGetGraph(AGUnownedGraphContextRef storage) { + auto graph_context = reinterpret_cast(storage); + fprintf(stdout, "AGGraphContextGetGraph %p -> %p", storage, graph_context->to_cf()); + return graph_context->to_cf(); } const void *AGGraphGetContext(AGGraphRef graph) { @@ -150,12 +155,13 @@ void AGGraphEndDeferringSubgraphInvalidation(AGGraphRef graph, bool was_deferrin #pragma mark - Attribute types -// TODO: is this AGGraphRef or AG::Graph ? -uint32_t AGGraphInternAttributeType(AGGraphRef graph, AGTypeID type, - const void *(*intern)(const void *context AG_SWIFT_CONTEXT)AG_SWIFT_CC(swift), - const void *context) { +uint32_t +AGGraphInternAttributeType(AGUnownedGraphRef unowned_graph, AGTypeID type, + const void *(*intern_function)(const void *context AG_SWIFT_CONTEXT)AG_SWIFT_CC(swift), + const void *intern_function_context) { auto metadata = reinterpret_cast(type); - return graph->context.graph().intern_type(metadata, AG::ClosureFunctionVP(intern, context)); + AG::Graph *graph = reinterpret_cast(unowned_graph); + return graph->intern_type(metadata, AG::ClosureFunctionVP(intern_function, intern_function_context)); } void AGGraphVerifyType(AGAttribute attribute, AGTypeID type) { @@ -184,8 +190,8 @@ AGAttribute AGGraphCreateAttribute(uint32_t type_id, const void *body, const voi if (!current_subgraph) { AG::precondition_failure("no subgraph active while adding attribute"); } - auto attribute = current_subgraph->graph()->add_attribute(*current_subgraph, type_id, body, value); - return AG::AttributeID(attribute).to_storage(); + auto node = current_subgraph->graph()->add_attribute(*current_subgraph, type_id, body, value); + return AGAttribute(AG::AttributeID(node)); } AGGraphRef AGGraphGetAttributeGraph(AGAttribute attribute) { @@ -225,8 +231,7 @@ AGAttributeFlags AGGraphGetFlags(AGAttribute attribute) { if (!attribute_id.is_direct()) { AG::precondition_failure("non-direct attribute id: %u", attribute); } - auto flags = attribute_id.to_node().subgraph_flags(); - return AGAttributeFlags(flags.data()); + return attribute_id.to_node().subgraph_flags(); } void AGGraphSetFlags(AGAttribute attribute, AGAttributeFlags flags) { @@ -278,7 +283,7 @@ namespace { void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &metadata, void *body, const AG::swift::metadata &value_type, AGCachedValueOptions options, AG::AttributeID attribute, uint8_t *state_out, - AG::ClosureFunctionCI closure) { + AG::ClosureFunctionCI closure) { auto current_update = AG::Graph::current_update(); AG::Graph::UpdateStack *stack = current_update.tag() == 0 ? current_update.get() : nullptr; @@ -319,7 +324,7 @@ void *read_cached_attribute(uint64_t identifier, const AG::swift::metadata &meta void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, AGCachedValueOptions options, AGAttribute attribute, bool *changed_out, uint32_t (*closure)(const void *context AG_SWIFT_CONTEXT, - AGUnownedGraphContextRef graph) AG_SWIFT_CC(swift), + AGUnownedGraphRef graph) AG_SWIFT_CC(swift), const void *closure_context) { auto metadata = reinterpret_cast(type); auto value_metadata = reinterpret_cast(value_type); @@ -327,7 +332,7 @@ void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, uint8_t state = 0; void *value = read_cached_attribute( identifier, *metadata, body, *value_metadata, options, AG::AttributeID::from_storage(attribute), &state, - AG::ClosureFunctionCI(closure, closure_context)); + AG::ClosureFunctionCI(closure, closure_context)); if (changed_out) { *changed_out = state & 1 ? true : false; } @@ -356,7 +361,7 @@ AGAttribute AGGraphGetCurrentAttribute() { if (auto update_stack = update.get()) { auto frame = update_stack->frames().back(); if (frame.attribute) { - return AG::AttributeID(frame.attribute).to_storage(); + return AGAttribute(AG::AttributeID(frame.attribute)); } } } diff --git a/Sources/ComputeCxx/Graph/AGGraph.h b/Sources/ComputeCxx/Graph/AGGraph.h index 8bfac56..84a6b43 100644 --- a/Sources/ComputeCxx/Graph/AGGraph.h +++ b/Sources/ComputeCxx/Graph/AGGraph.h @@ -16,7 +16,8 @@ CF_EXTERN_C_BEGIN // MARK: CFType typedef struct CF_BRIDGED_TYPE(id) AGGraphStorage *AGGraphRef AG_SWIFT_NAME(Graph); -typedef struct AGUnownedGraphContext *AGUnownedGraphContextRef; +typedef void *AGUnownedGraphRef AG_SWIFT_STRUCT; +typedef struct AGGraphContextStorage *AGUnownedGraphContextRef AG_SWIFT_STRUCT; CF_EXPORT CF_REFINED_FOR_SWIFT @@ -26,19 +27,20 @@ CFTypeID AGGraphGetTypeID(); CF_EXPORT CF_REFINED_FOR_SWIFT -AGGraphRef AGGraphCreate(); +AGGraphRef AGGraphCreate() CF_SWIFT_NAME(AGGraphRef.init()); CF_EXPORT CF_REFINED_FOR_SWIFT -AGGraphRef AGGraphCreateShared(AGGraphRef _Nullable graph); +AGGraphRef AGGraphCreateShared(AGGraphRef _Nullable graph) CF_SWIFT_NAME(AGGraphRef.init(shared:)); CF_EXPORT CF_REFINED_FOR_SWIFT -AGUnownedGraphContextRef AGGraphGetGraphContext(AGGraphRef graph); +AGUnownedGraphRef AGGraphGetGraphContext(AGGraphRef graph) CF_SWIFT_NAME(getter:AGGraphRef.graphContext(self:)); CF_EXPORT CF_REFINED_FOR_SWIFT -AGGraphRef AGGraphContextGetGraph(AGUnownedGraphContextRef context); +AGGraphRef AGGraphContextGetGraph(AGUnownedGraphContextRef context) + CF_SWIFT_NAME(getter:AGUnownedGraphContextRef.graph(self:)); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -69,7 +71,7 @@ typedef CF_ENUM(uint32_t, AGGraphCounterQuery) { CF_EXPORT CF_REFINED_FOR_SWIFT -uint64_t AGGraphGetCounter(AGGraphRef graph, AGGraphCounterQuery query); +uint64_t AGGraphGetCounter(AGGraphRef graph, AGGraphCounterQuery query) CF_SWIFT_NAME(AGGraphRef.counter(self:for:)); // MARK: Subgraphs @@ -85,7 +87,7 @@ void AGGraphEndDeferringSubgraphInvalidation(AGGraphRef graph, bool was_deferrin CF_EXPORT CF_REFINED_FOR_SWIFT -uint32_t AGGraphInternAttributeType(AGGraphRef graph, AGTypeID type, +uint32_t AGGraphInternAttributeType(AGUnownedGraphRef graph, AGTypeID type, const void *_Nonnull (*_Nonnull intern)(const void *context AG_SWIFT_CONTEXT) AG_SWIFT_CC(swift), const void *context); @@ -143,7 +145,7 @@ CF_REFINED_FOR_SWIFT void *AGGraphReadCachedAttribute(uint64_t identifier, AGTypeID type, void *body, AGTypeID value_type, AGCachedValueOptions options, AGAttribute attribute, bool *_Nullable changed_out, uint32_t (*closure)(const void *context AG_SWIFT_CONTEXT, - AGUnownedGraphContextRef graph_context) AG_SWIFT_CC(swift), + AGUnownedGraphRef graph_context) AG_SWIFT_CC(swift), const void *closure_context); CF_EXPORT @@ -484,7 +486,8 @@ void AGGraphResetProfile(AGGraphRef _Nullable graph); CF_EXPORT CF_REFINED_FOR_SWIFT -CFTypeRef AGGraphDescription(AGGraphRef _Nullable graph, CFDictionaryRef options); +CFTypeRef AGGraphDescription(AGGraphRef _Nullable graph, CFDictionaryRef options) + CF_SWIFT_NAME(AGGraphRef.description(_:options:)); CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Graph/Context.cpp b/Sources/ComputeCxx/Graph/Context.cpp index e0a53a2..47f136c 100644 --- a/Sources/ComputeCxx/Graph/Context.cpp +++ b/Sources/ComputeCxx/Graph/Context.cpp @@ -15,7 +15,7 @@ Graph::Context::Context(Graph *graph) { _unique_id = AGMakeUniqueID(); _deadline = UINT64_MAX; - graph->_num_contexts += 1; + Graph::will_add_to_context(graph); graph->_contexts_by_id.insert(_unique_id, this); _graph->foreach_trace([this](Trace &trace) { trace.created(*this); }); @@ -47,13 +47,15 @@ Graph::Context::~Context() { // batch.~without_invalidating called here } - _graph->_num_contexts -= 1; - if (_graph && _graph->_num_contexts == 0) { - _graph->~Graph(); - } + Graph::did_remove_from_context(_graph); +// _graph->_num_contexts -= 1; +// if (_graph && _graph->_num_contexts == 0) { +// _graph->~Graph(); +// } + } -// TODO: AGUnknownedGraphContextRef ? + Graph::Context *Graph::Context::from_cf(AGGraphStorage *storage) { if (storage->context._invalidated) { precondition_failure("invalidated graph"); diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 9405c3a..82b64ff 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -520,7 +520,7 @@ int trap_cycles() { break; } - AttributeFlags subgraph_flags = attribute.to_node().subgraph_flags(); + AGAttributeFlags subgraph_flags = attribute.to_node().subgraph_flags(); auto found_node_index = node_indices_by_id.find(attribute.to_ptr()); if (found_node_index != node_indices_by_id.end()) { [nodes addObject:[NSNumber numberWithUnsignedLong:found_node_index->second]]; @@ -750,7 +750,7 @@ int trap_cycles() { if (!attribute_ids || [attribute_ids containsIndex:attribute]) { - [result appendFormat:@" _%d[label=\"%d", attribute.to_storage(), attribute.to_storage()]; + [result appendFormat:@" _%d[label=\"%d", AGAttribute(attribute), AGAttribute(attribute)]; Node &node = attribute.to_node(); const AttributeType &node_type = attribute_type(node.type_id()); @@ -833,11 +833,10 @@ int trap_cycles() { AttributeID resolved_input_attribute = input_edge.value.resolve(TraversalOptions::None).attribute(); if (resolved_input_attribute.is_direct() && - (!attribute_ids || - [attribute_ids containsIndex:resolved_input_attribute.to_storage()])) { + (!attribute_ids || [attribute_ids containsIndex:resolved_input_attribute])) { [result appendFormat:@" _%d -> _%d[", input_edge.value.to_ptr().offset(), - attribute.to_storage()]; + AGAttribute(attribute)]; // collect source inputs AttributeID intermediate = input_edge.value; @@ -892,7 +891,7 @@ int trap_cycles() { if (indirect_node->is_mutable()) { if (auto dependency = indirect_node->to_mutable().dependency()) { [result - appendFormat:@" _%d -> _%d[color=blue];\n", dependency.to_storage(), indirect_node.offset()]; + appendFormat:@" _%d -> _%d[color=blue];\n", AGAttribute(dependency), indirect_node.offset()]; } } } @@ -923,7 +922,7 @@ int trap_cycles() { [description appendString:@" -- inputs:\n"]; for (auto input_edge : frame->attribute->inputs()) { OffsetAttributeID resolved = input_edge.value.resolve(TraversalOptions::ReportIndirectionInOffset); - [description appendFormat:@" %u", resolved.attribute().to_storage()]; + [description appendFormat:@" %u", AGAttribute(resolved.attribute())]; if (resolved.offset() != 0) { [description appendFormat:@"[@%d]", resolved.offset() - 1]; } diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 0b5dc64..2f53e01 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -796,16 +796,15 @@ data::ptr Graph::add_indirect_attribute(Subgraph &subgraph, Attrib } void Graph::remove_indirect_node(data::ptr indirect_node_ptr) { - IndirectNode &indirect_node = *indirect_node_ptr; if (indirect_node_ptr->is_mutable()) { AttributeID attribute = AttributeID(indirect_node_ptr); - remove_removed_input(attribute, indirect_node.source().attribute()); - AttributeID dependency = indirect_node.to_mutable().dependency(); + remove_removed_input(attribute, indirect_node_ptr->source().attribute()); + AttributeID dependency = indirect_node_ptr->to_mutable().dependency(); if (dependency != 0) { // TODO: == nullptr operator... remove_removed_input(attribute, dependency); } - for (auto output_edge : indirect_node.to_mutable().outputs()) { + for (auto output_edge : indirect_node_ptr->to_mutable().outputs()) { remove_removed_output(attribute, output_edge.value, false); } return; @@ -1336,7 +1335,7 @@ void *Graph::input_value_ref_slow(data::ptr node, AttributeID input_at if ((input_flags >> 1) & 1) { auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, InputEdge::Flags::Unprefetched | InputEdge::Flags::AlwaysEnabled); - index = index_of_input(*node, comparator); + index = index_of_input(*node.get(), comparator); } if (index < 0) { @@ -1433,7 +1432,7 @@ void Graph::input_value_add(data::ptr node, AttributeID input_attribute, u // TODO: check flag and mask are right way around auto comparator = InputEdge::Comparator(input_attribute, input_flags & 1, InputEdge::Flags::Unprefetched | InputEdge::Flags::AlwaysEnabled); - auto index = index_of_input(*node, comparator); + auto index = index_of_input(*node.get(), comparator); if (index >= 0) { auto input_edge = node->inputs()[index]; input_edge.set_unknown4(true); @@ -1497,7 +1496,7 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni void Graph::remove_input(data::ptr node, uint32_t index) { remove_input_dependencies(AttributeID(node), node->inputs()[index].value); - remove_input_edge(node, *node, index); + remove_input_edge(node, *node.get(), index); } void Graph::remove_all_inputs(data::ptr node) { @@ -1882,7 +1881,7 @@ void Graph::mark_pending(data::ptr node_ptr, Node *node) { foreach_trace([&node_ptr](Trace &trace) { trace.set_dirty(node_ptr, true); }); node->set_state(node->state().with_dirty(true)); - AttributeFlags subgraph_flags = node->subgraph_flags(); + AGAttributeFlags subgraph_flags = node->subgraph_flags(); Subgraph *subgraph = AttributeID(node_ptr).subgraph(); if (subgraph_flags && subgraph != nullptr) { subgraph->add_dirty_flags(subgraph_flags); diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp index b4afe29..0699f5d 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeElement.cpp @@ -49,13 +49,12 @@ AGTreeElementNodeIterator AGTreeElementMakeNodeIterator(AGTreeElement tree_eleme AGAttribute AGTreeElementGetNextNode(AGTreeElementNodeIterator *iter) { auto tree_element_id = AG::TreeElementID::from_storage(iter->tree_element); - AG::AttributeID node = - tree_element_id.subgraph()->tree_node_at_index(tree_element_id.to_ptr(), iter->index); + AG::AttributeID node = tree_element_id.subgraph()->tree_node_at_index(tree_element_id.to_ptr(), iter->index); if (!node.has_value()) { return AGAttributeNil; } iter->index += 1; - return node; + return AGAttribute(node); } #pragma mark - Iterating children @@ -69,7 +68,8 @@ AGTreeElementChildIterator AGTreeElementMakeChildIterator(AGTreeElement tree_ele AGTreeElement AGTreeElementGetNextChild(AGTreeElementChildIterator *iter) { AGTreeElement next_child = iter->next_child; if (next_child) { - iter->next_child = AG::TreeElementID(AG::TreeElementID::from_storage(next_child).to_ptr()->next_sibling).to_storage(); + iter->next_child = + AG::TreeElementID(AG::TreeElementID::from_storage(next_child).to_ptr()->next_sibling).to_storage(); return next_child; } diff --git a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp index f4ed63d..dbbe314 100644 --- a/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp +++ b/Sources/ComputeCxx/Graph/Tree/AGTreeValue.cpp @@ -16,7 +16,7 @@ AGAttribute AGTreeValueGetValue(AGTreeValue tree_value) { const char *AGTreeValueGetKey(AGTreeValue tree_value) { auto tree_value_id = AG::TreeValueID::from_storage(tree_value); - auto key_id = tree_value_id.to_tree_value().value; + auto key_id = tree_value_id.to_tree_value().key_id; return tree_value_id.subgraph()->graph()->key_name(key_id); } diff --git a/Sources/ComputeCxx/Layout/AGComparison.h b/Sources/ComputeCxx/Layout/AGComparison.h index 5db7274..507d7e8 100644 --- a/Sources/ComputeCxx/Layout/AGComparison.h +++ b/Sources/ComputeCxx/Layout/AGComparison.h @@ -42,9 +42,9 @@ typedef CF_OPTIONS(uint32_t, AGComparisonOptions) { }; typedef CF_OPTIONS(uint16_t, AGComparisonMode) { - AGComparisonModeDefault = 0, - AGComparisonModeOption1 = 1, - AGComparisonModeOption2 = 2 + AGComparisonModeNone = 0, + AGComparisonMode_1 = 1, + AGComparisonMode_2 = 2 }; CF_EXPORT @@ -53,7 +53,7 @@ bool AGCompareValues(const void *destination, const void *source, AGTypeID type_ CF_EXPORT CF_REFINED_FOR_SWIFT -const unsigned char *AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority); +const unsigned char *_Nullable AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority) CF_SWIFT_NAME(prefetchCompareValues(type:options:priority:)); CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp index 945b226..76772db 100644 --- a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp +++ b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp @@ -120,13 +120,14 @@ ValueLayout TypeDescriptorCache::fetch(const swift::metadata &type, LayoutDescri if (result) { return atoi(result) != 0; } - return false; + return true; }(); if (options.fetch_layouts_synchronously() || !async_layouts) { // insert layout synchronously double start_time = current_time(); layout = LayoutDescriptor::make_layout(type, comparison_mode, heap_mode); + assert(layout); double end_time = current_time(); if (comparison_mode < 0) { diff --git a/Sources/ComputeCxx/Layout/LayoutDescriptor.h b/Sources/ComputeCxx/Layout/LayoutDescriptor.h index 13a8cff..0688f59 100644 --- a/Sources/ComputeCxx/Layout/LayoutDescriptor.h +++ b/Sources/ComputeCxx/Layout/LayoutDescriptor.h @@ -14,7 +14,7 @@ class context_descriptor; } // namespace swift /// A string that encodes an object's layout in memory. -using ValueLayout = const unsigned char *_Nullable; +using ValueLayout = const unsigned char *; extern const ValueLayout ValueLayoutEmpty; diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp index d5c014e..e6340f0 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.cpp @@ -2,8 +2,9 @@ #include -#include "Graph/AGGraph.h" +#include "Graph/AGGraph-Private.h" #include "Graph/Context.h" +#include "Graph/Tree/TreeElement.h" #include "Subgraph.h" namespace { @@ -13,7 +14,7 @@ CFRuntimeClass &subgraph_type_id() { AGSubgraphStorage *storage = (AGSubgraphStorage *)subgraph_ref; AG::Subgraph *subgraph = storage->subgraph; if (subgraph) { - // TODO: should call destructor? + // TODO: should call destructor????x subgraph->clear_object(); subgraph->invalidate_and_delete_(false); } @@ -52,8 +53,9 @@ AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute) { AG::Graph::Context *context = AG::Graph::Context::from_cf(graph); - AG::Subgraph *subgraph = new (&instance->subgraph) - AG::Subgraph((AG::SubgraphObject *)instance, *context, AG::AttributeID::from_storage(attribute)); + instance->subgraph = + new AG::Subgraph((AG::SubgraphObject *)instance, *context, AG::AttributeID::from_storage(attribute)); + ; return instance; }; @@ -85,6 +87,16 @@ void AGSubgraphSetCurrent(AGSubgraphRef subgraph) { #pragma mark - Graph +AGUnownedGraphRef AGSubgraphGetCurrentGraphContext() { + AG::Subgraph *current = AG::Subgraph::current_subgraph(); + if (current == nullptr) { + return nullptr; + } + + AG::Graph *graph = current->graph(); + return reinterpret_cast(graph); +} + AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph) { if (subgraph->subgraph == nullptr) { AG::precondition_failure("accessing invalidated subgraph"); @@ -93,22 +105,11 @@ AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph) { auto context_id = subgraph->subgraph->context_id(); if (context_id != 0) { if (auto context = subgraph->subgraph->graph()->context_with_id(context_id)) { - return AGGraphContextGetGraph( - reinterpret_cast(context)); // TODO: proper conversion + return AGGraphContextGetGraph(reinterpret_cast(context)); } } - AG::precondition_failure("accessing invalidated contex"); -} - -AGUnownedGraphContextRef AGSubgraphGetCurrentGraphContext() { - AG::Subgraph *current = AG::Subgraph::current_subgraph(); - if (current == nullptr) { - return nullptr; - } - - // FIXME: this is not the context - return (AGUnownedGraphContextRef)current->graph(); + AG::precondition_failure("accessing invalidated context"); } AGSubgraphRef AGGraphGetAttributeSubgraph(AGAttribute attribute) { diff --git a/Sources/ComputeCxx/Subgraph/AGSubgraph.h b/Sources/ComputeCxx/Subgraph/AGSubgraph.h index 1dee134..20dab3e 100644 --- a/Sources/ComputeCxx/Subgraph/AGSubgraph.h +++ b/Sources/ComputeCxx/Subgraph/AGSubgraph.h @@ -18,11 +18,12 @@ CFTypeID AGSubgraphGetTypeID(); CF_EXPORT CF_REFINED_FOR_SWIFT -AGSubgraphRef AGSubgraphCreate(AGGraphRef graph); +AGSubgraphRef AGSubgraphCreate(AGGraphRef graph) CF_SWIFT_NAME(AGSubgraphRef.init(graph:)); CF_EXPORT CF_REFINED_FOR_SWIFT -AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute); +AGSubgraphRef AGSubgraphCreate2(AGGraphRef graph, AGAttribute attribute) + CF_SWIFT_NAME(AGSubgraphRef.init(graph:attribute:)); // MARK: Current subgraph @@ -38,11 +39,12 @@ void AGSubgraphSetCurrent(AGSubgraphRef _Nullable subgraph) CF_SWIFT_NAME(setter CF_EXPORT CF_REFINED_FOR_SWIFT -AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph) CF_SWIFT_NAME(getter:AGSubgraphRef.graph(self:)); +AGUnownedGraphRef _Nullable AGSubgraphGetCurrentGraphContext() + CF_SWIFT_NAME(getter:AGSubgraphRef.currentGraphContext()); CF_EXPORT CF_REFINED_FOR_SWIFT -AGUnownedGraphContextRef AGSubgraphGetCurrentGraphContext(AGSubgraphRef subgraph); +AGGraphRef AGSubgraphGetGraph(AGSubgraphRef subgraph) CF_SWIFT_NAME(getter:AGSubgraphRef.graph(self:)); CF_EXPORT CF_REFINED_FOR_SWIFT @@ -138,11 +140,11 @@ void AGSubgraphEndTreeElement(AGAttribute value); CF_EXPORT CF_REFINED_FOR_SWIFT -bool AGSubgraphShouldRecordTree(); +bool AGSubgraphShouldRecordTree() CF_SWIFT_NAME(getter:AGSubgraphRef.shouldRecordTree()); CF_EXPORT CF_REFINED_FOR_SWIFT -void AGSubgraphSetShouldRecordTree(); +void AGSubgraphSetShouldRecordTree() CF_SWIFT_NAME(AGSubgraphRef.setShouldRecordTree()); // MARK: Observers diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index ebc3e46..4ed7e69 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -26,7 +26,10 @@ pthread_key_t Subgraph::_current_subgraph_key; void Subgraph::make_current_subgraph_key() { pthread_key_create(&Subgraph::_current_subgraph_key, 0); } -Subgraph *Subgraph::current_subgraph() { return (Subgraph *)pthread_getspecific(Subgraph::_current_subgraph_key); } +Subgraph *Subgraph::current_subgraph() { + assert(Subgraph::_current_subgraph_key); + return (Subgraph *)pthread_getspecific(Subgraph::_current_subgraph_key); +} void Subgraph::set_current_subgraph(Subgraph *subgraph) { pthread_setspecific(Subgraph::_current_subgraph_key, subgraph); @@ -59,13 +62,16 @@ Subgraph::Subgraph(SubgraphObject *object, Graph::Context &context, AttributeID begin_tree(owner, nullptr, 0); } - context.graph().foreach_trace([*this](Trace &trace) { trace.created(*this); }); + context.graph().foreach_trace([this](Trace &trace) { + trace.created(*this); + // fprintf(stdout, "trace created subgraph ref"); + }); } Subgraph::~Subgraph() { if (_observers) { notify_observers(); - delete _observers.get(); // what pointer is deleted? + delete _observers.get(); // TODO: what pointer is deleted? } if (_cache) { _cache->~NodeCache(); @@ -767,7 +773,7 @@ data::ptr Subgraph::tree_subgraph_child(data::ptr node, AttributeFlags flags) { +void Subgraph::set_flags(data::ptr node, AGAttributeFlags flags) { if (node->subgraph_flags() == flags) { return; } @@ -786,7 +792,7 @@ void Subgraph::set_flags(data::ptr node, AttributeFlags flags) { } } -void Subgraph::add_flags(AttributeFlags flags) { +void Subgraph::add_flags(AGAttributeFlags flags) { // Status: doesn't exist in decompile if (flags & ~_flags.value1) { _flags.value1 |= flags; @@ -794,7 +800,7 @@ void Subgraph::add_flags(AttributeFlags flags) { } } -void Subgraph::add_dirty_flags(AttributeFlags dirty_flags) { +void Subgraph::add_dirty_flags(AGAttributeFlags dirty_flags) { // Status: Verified if (dirty_flags & ~_flags.value3) { _flags.value3 |= dirty_flags; @@ -842,14 +848,14 @@ uint64_t Subgraph::add_observer(ClosureFunctionVV &&callback) { auto observer_id = AGMakeUniqueID(); - auto observers = *_observers; auto observer = Observer(callback, observer_id); - observers->push_back(observer); + (*_observers)->push_back(observer); return observer_id; } void Subgraph::remove_observer(uint64_t observer_id) { - if (auto observers = *_observers) { + if (auto observers_ptr = _observers.get()) { + auto observers = *observers_ptr; auto iter = std::remove_if(observers->begin(), observers->end(), [&observer_id](auto observer) -> bool { if (observer.observer_id == observer_id) { return true; @@ -861,7 +867,8 @@ void Subgraph::remove_observer(uint64_t observer_id) { } void Subgraph::notify_observers() { - if (auto observers = *_observers) { + if (auto observers_ptr = _observers.get()) { + auto observers = *observers_ptr; while (!observers->empty()) { auto &observer = observers->back(); observer.callback(); @@ -873,7 +880,7 @@ void Subgraph::notify_observers() { #pragma mark - Cache data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata &metadata, void *body, - ClosureFunctionCI closure) { + ClosureFunctionCI closure) { if (_cache == nullptr) { _cache = alloc_bytes(sizeof(NodeCache), 7).unsafe_cast(); new (&_cache) NodeCache(); @@ -891,8 +898,7 @@ data::ptr Subgraph::cache_fetch(uint64_t identifier, const swift::metadata type->equatable = equatable; type->last_item = nullptr; type->first_item = nullptr; - auto graph_context = AGUnownedGraphContext(_graph); - type->type_id = closure(&graph_context); + type->type_id = closure(reinterpret_cast(_graph)); _cache->types().insert(&metadata, type); } @@ -1116,9 +1122,9 @@ void Subgraph::print(uint32_t indent_level) { if (!attribute.is_direct()) { continue; } - fprintf(stdout, "%s%u", first ? "" : " ", attribute.to_storage()); + fprintf(stdout, "%s%u", first ? "" : " ", AGAttribute(attribute)); if (attribute.to_node().subgraph_flags()) { - fprintf(stdout, "(%u)", attribute.to_node().subgraph_flags().data()); + fprintf(stdout, "(%u)", attribute.to_node().subgraph_flags()); } first = false; } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index ad6a148..7728842 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -179,10 +179,10 @@ class Subgraph : public data::zone { // flags 1 and 3 are the values themselvs // flags 3 and 4 are the dirty subset of 1 and 2 - void set_flags(data::ptr node, AttributeFlags flags); + void set_flags(data::ptr node, AGAttributeFlags flags); - void add_flags(AttributeFlags flags); - void add_dirty_flags(AttributeFlags dirty_flags); + void add_flags(AGAttributeFlags flags); + void add_dirty_flags(AGAttributeFlags dirty_flags); void propagate_flags(); void propagate_dirty_flags(); @@ -203,9 +203,8 @@ class Subgraph : public data::zone { // MARK: Cache - // FIXME: not AGUnownedGraphContextRef data::ptr cache_fetch(uint64_t identifier, const swift::metadata &type, void *body, - ClosureFunctionCI closure); + ClosureFunctionCI closure); void cache_insert(data::ptr node); void cache_collect(); diff --git a/Sources/ComputeCxx/Swift/AGType.cpp b/Sources/ComputeCxx/Swift/AGType.cpp index aed4cee..6bee24d 100644 --- a/Sources/ComputeCxx/Swift/AGType.cpp +++ b/Sources/ComputeCxx/Swift/AGType.cpp @@ -2,6 +2,7 @@ #include +#include "Closure/ClosureFunction.h" #include "ContextDescriptor.h" #include "Metadata.h" #include "MetadataVisitor.h" @@ -74,18 +75,20 @@ const char *AGTypeNominalDescriptorName(AGTypeID typeID) { } void AGTypeApplyFields(AGTypeID typeID, - void (*body)(const void *context, const char *field_name, size_t field_size, - AGTypeID field_type), - const void *context) { + void (*apply)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_offset, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *apply_context) { class Visitor : public AG::swift::metadata_visitor { private: - void (*_body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type); - const void *_context; + void (*_body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_offset, + AGTypeID field_type) AG_SWIFT_CC(swift); + const void *_body_context; public: - Visitor(void (*body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type), - const void *context) - : _body(body), _context(context) {} + Visitor(void (*body)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_offset, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *body_context) + : _body(body), _body_context(body_context) {} bool unknown_result() const override { return true; } bool visit_field(const AG::swift::metadata &type, const AG::swift::field_record &field, size_t field_offset, @@ -94,35 +97,32 @@ void AGTypeApplyFields(AGTypeID typeID, auto field_type = type.mangled_type_name_ref(mangled_name, true, nullptr); if (field_type) { auto field_name = field.FieldName.get(); - _body(_context, field_name, field_offset, AGTypeID(field_type)); + _body(_body_context, field_name, field_offset, AGTypeID(field_type)); } return true; } }; - Visitor visitor = Visitor(body, context); + Visitor visitor = Visitor(apply, apply_context); auto type = reinterpret_cast(typeID); type->visit(visitor); } bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, - bool (*body)(const void *context, const char *field_name, size_t field_size, - AGTypeID field_type), - const void *context) { + bool (*apply)(const void *context AG_SWIFT_CONTEXT, const char *field_name, size_t field_offset, + AGTypeID field_type) AG_SWIFT_CC(swift), + const void *apply_context) { class Visitor : public AG::swift::metadata_visitor { private: AGTypeApplyOptions _options; - bool (*_body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type); - const void *_context; + AG::ClosureFunction *_body; public: - Visitor(AGTypeApplyOptions options, - bool (*body)(const void *context, const char *field_name, size_t field_size, AGTypeID field_type), - const void *context) - : _options(options), _body(body), _context(context) {} + Visitor(AGTypeApplyOptions options, AG::ClosureFunction *body) + : _options(options), _body(body) {} - bool unknown_result() const override { return _options & 2; } + bool unknown_result() const override { return _options & AGTypeApplyOptionsContinueAfterUnknownField; } bool visit_field(const AG::swift::metadata &type, const AG::swift::field_record &field, size_t field_offset, size_t field_size) const override { auto mangled_name = field.MangledTypeName.get(); @@ -131,8 +131,8 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, return unknown_result(); } auto field_name = field.FieldName.get(); - _body(_context, field_name, field_offset, AGTypeID(field_type)); - return field_name != nullptr; + bool result = (*_body)(field_name, field_offset, AGTypeID(field_type)); + return result != 0; } bool visit_case(const AG::swift::metadata &type, const AG::swift::field_record &field, uint32_t index) const override { @@ -142,33 +142,34 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, return unknown_result(); } auto field_name = field.FieldName.get(); - _body(_context, field_name, index, AGTypeID(field_type)); - return field_name != nullptr; + bool result = (*_body)(field_name, index, AGTypeID(field_type)); + return result != 0; } }; - Visitor visitor = Visitor(options, body, context); + auto closure_function = AG::ClosureFunction(apply, apply_context); + Visitor visitor = Visitor(options, &closure_function); auto type = reinterpret_cast(typeID); switch (type->getKind()) { case ::swift::MetadataKind::Class: - if (options & AGTypeApplyOptionsHeapClasses) { + if (options & AGTypeApplyOptionsEnumerateClassFields) { return type->visit_heap(visitor, AG::swift::metadata::visit_options::heap_class); } return false; case ::swift::MetadataKind::Struct: - if (!(options & AGTypeApplyOptionsHeapClasses) && !(options & AGTypeApplyOptionsEnumCases)) { + if (!(options & AGTypeApplyOptionsEnumerateClassFields) && !(options & AGTypeApplyOptionsEnumerateEnumCases)) { return type->visit(visitor); } return false; case ::swift::MetadataKind::Enum: case ::swift::MetadataKind::Optional: - if (options & AGTypeApplyOptionsEnumCases) { + if (options & AGTypeApplyOptionsEnumerateEnumCases) { return type->visit(visitor); } return false; case ::swift::MetadataKind::Tuple: - if (!(options & AGTypeApplyOptionsHeapClasses) && !(options & AGTypeApplyOptionsEnumCases)) { + if (!(options & AGTypeApplyOptionsEnumerateClassFields) && !(options & AGTypeApplyOptionsEnumerateEnumCases)) { return type->visit(visitor); } return false; @@ -179,7 +180,7 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, bool AGTypeApplyEnumData(AGTypeID typeID, void *value, void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, - void *field_value) AG_SWIFT_CC(swift), + const void *field_value) AG_SWIFT_CC(swift), const void *context) { auto type = reinterpret_cast(typeID); auto value_witness = type->getValueWitnesses(); diff --git a/Sources/ComputeCxx/Swift/AGType.h b/Sources/ComputeCxx/Swift/AGType.h index ac6c148..da6dce8 100644 --- a/Sources/ComputeCxx/Swift/AGType.h +++ b/Sources/ComputeCxx/Swift/AGType.h @@ -12,9 +12,9 @@ CF_EXTERN_C_BEGIN typedef const struct AGSwiftMetadata *AGTypeID AG_SWIFT_STRUCT AG_SWIFT_NAME(Metadata); -typedef struct AG_SWIFT_NAME(Signature) AGTypeSignature { +typedef struct AGTypeSignature { uint32_t data[5]; -} AGTypeSignature; +} AG_SWIFT_NAME(Signature) AGTypeSignature; typedef CF_CLOSED_ENUM(uint32_t, AGTypeKind) { AGTypeKindNone, @@ -54,31 +54,31 @@ const char *_Nullable AGTypeNominalDescriptorName(AGTypeID typeID) CF_SWIFT_NAME(getter:Metadata.nominalDescriptorName(self:)); typedef CF_OPTIONS(uint32_t, AGTypeApplyOptions) { - AGTypeApplyOptionsNone = 0, - AGTypeApplyOptionsHeapClasses = 1 << 0, - AGTypeApplyOptions_2 = 1 << 1, - AGTypeApplyOptionsEnumCases = 1 << 2, -} CF_SWIFT_NAME(Metadata.ApplyOptions); + AGTypeApplyOptionsEnumerateStructFields = 0, + AGTypeApplyOptionsEnumerateClassFields = 1 << 0, + AGTypeApplyOptionsContinueAfterUnknownField = 1 << 1, + AGTypeApplyOptionsEnumerateEnumCases = 1 << 2, +}; CF_EXPORT CF_REFINED_FOR_SWIFT void AGTypeApplyFields(AGTypeID typeID, - void (*body)(const void *context, const char *field_name, size_t field_size, - AGTypeID field_type), - const void *context); + void (*apply)(const void *_Nullable context AG_SWIFT_CONTEXT, const char *field_name, + size_t field_offset, AGTypeID field_type) AG_SWIFT_CC(swift), + const void *apply_context); CF_EXPORT CF_REFINED_FOR_SWIFT bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, - bool (*body)(const void *context, const char *field_name, size_t field_size, - AGTypeID field_type), - const void *context); + bool (*_Nonnull apply)(const void *context AG_SWIFT_CONTEXT, const char *field_name, + size_t field_offset, AGTypeID field_type) AG_SWIFT_CC(swift), + const void *apply_context); CF_EXPORT CF_REFINED_FOR_SWIFT bool AGTypeApplyEnumData(AGTypeID typeID, void *value, void (*body)(const void *context AG_SWIFT_CONTEXT, uint32_t tag, AGTypeID field_type, - void *field_value) AG_SWIFT_CC(swift), + const void *field_value) AG_SWIFT_CC(swift), const void *context); CF_EXPORT diff --git a/Sources/ComputeCxx/Swift/Metadata.cpp b/Sources/ComputeCxx/Swift/Metadata.cpp index fc47e6e..b6d8f5f 100644 --- a/Sources/ComputeCxx/Swift/Metadata.cpp +++ b/Sources/ComputeCxx/Swift/Metadata.cpp @@ -126,7 +126,7 @@ void metadata::append_description(CFMutableStringRef description) const { } if (all_parents.empty()) { all_parents.push_back({ - name(true), + name(false), 0, }); } @@ -518,11 +518,11 @@ bool metadata::visit(metadata_visitor &visitor) const { auto enum_context = reinterpret_cast(context); if (enum_context->Fields) { if (enum_context->getNumPayloadCases() != 0) { - if (enum_context->Fields->NumFields == 0) { - return true; - } unsigned index = 0; for (auto &field : enum_context->Fields->getFields()) { + if (!field.hasMangledTypeName()) { + continue; + } if (!visitor.visit_case(*this, field, index)) { return false; } diff --git a/Sources/ComputeCxx/UniqueID/AGUniqueID.h b/Sources/ComputeCxx/UniqueID/AGUniqueID.h index 9d4df3c..2877cdc 100644 --- a/Sources/ComputeCxx/UniqueID/AGUniqueID.h +++ b/Sources/ComputeCxx/UniqueID/AGUniqueID.h @@ -7,7 +7,7 @@ CF_ASSUME_NONNULL_BEGIN CF_EXTERN_C_BEGIN -uint64_t AGMakeUniqueID(); +uint64_t AGMakeUniqueID() CF_SWIFT_NAME(makeUniqueID()); CF_EXTERN_C_END diff --git a/Sources/ComputeCxx/include/Compute.h b/Sources/ComputeCxx/include/Compute.h index 0008ae8..89bf443 100644 --- a/Sources/ComputeCxx/include/Compute.h +++ b/Sources/ComputeCxx/include/Compute.h @@ -1,4 +1,5 @@ #include "Attribute/AGAttribute.h" +#include "Closure/AGClosure.h" #include "Graph/AGDescription.h" #include "Graph/AGGraph.h" #include "Graph/Tree/AGTreeElement.h" @@ -6,3 +7,4 @@ #include "Subgraph/AGSubgraph.h" #include "Swift/AGTuple.h" #include "Swift/AGType.h" +#include "UniqueID/AGUniqueID.h" diff --git a/Sources/Utilities/HashTable.cpp b/Sources/Utilities/HashTable.cpp index 1780836..a0b63aa 100644 --- a/Sources/Utilities/HashTable.cpp +++ b/Sources/Utilities/HashTable.cpp @@ -28,7 +28,9 @@ uint64_t string_hash(char const *str) { constexpr uint32_t initial_bucket_mask_width = 4; -std::shared_ptr UntypedTable::make_shared() { return std::make_shared(); } +UntypedTable *UntypedTable::create() { return new UntypedTable(); } + +void UntypedTable::destroy(UntypedTable *value) { delete value; } UntypedTable::UntypedTable() { _hash = pointer_hash; diff --git a/Sources/Utilities/Heap.cpp b/Sources/Utilities/Heap.cpp index bf7abd9..4205268 100644 --- a/Sources/Utilities/Heap.cpp +++ b/Sources/Utilities/Heap.cpp @@ -6,10 +6,12 @@ namespace util { constexpr size_t default_increment = 0x2000; -std::shared_ptr Heap::make_shared(char *_Nullable start, size_t capacity, size_t increment) { - return std::make_shared(start, capacity, increment); +Heap *Heap::create(char *_Nullable start, size_t capacity, size_t increment) { + return new Heap(start, capacity, increment); } +void Heap::destroy(Heap *value) { delete value; } + Heap::Heap(char *start, size_t capacity, size_t increment) { // enforce minimum but treat 0 as the default size_t effective_increment = increment > 0 ? std::max(increment, minimum_increment) : default_increment; diff --git a/Sources/Utilities/include/Utilities/HashTable.h b/Sources/Utilities/include/Utilities/HashTable.h index 749f41b..55104f8 100644 --- a/Sources/Utilities/include/Utilities/HashTable.h +++ b/Sources/Utilities/include/Utilities/HashTable.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include CF_ASSUME_NONNULL_BEGIN @@ -51,7 +50,8 @@ class UntypedTable { void grow_buckets(); public: - static std::shared_ptr make_shared(); + static UntypedTable *create(); + static void destroy(UntypedTable *value); UntypedTable(); UntypedTable(hasher _Nullable custom_hasher, key_equal _Nullable custom_compare, @@ -62,7 +62,7 @@ class UntypedTable { // non-copyable UntypedTable(const UntypedTable &) = delete; UntypedTable &operator=(const UntypedTable &) = delete; - + // non-movable UntypedTable(UntypedTable &&) = delete; UntypedTable &operator=(UntypedTable &&) = delete; diff --git a/Sources/Utilities/include/Utilities/Heap.h b/Sources/Utilities/include/Utilities/Heap.h index 0c33fb7..f7fd0bd 100644 --- a/Sources/Utilities/include/Utilities/Heap.h +++ b/Sources/Utilities/include/Utilities/Heap.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include CF_ASSUME_NONNULL_BEGIN @@ -25,7 +24,8 @@ class Heap { public: static constexpr size_t minimum_increment = 0x400; - static std::shared_ptr make_shared(char *_Nullable start, size_t capacity, size_t increment); + static Heap *create(char *_Nullable start, size_t capacity, size_t increment); + static void destroy(Heap *value); Heap(char *_Nullable start, size_t capacity, size_t increment); ~Heap(); diff --git a/Sources/Utilities/include/Utilities/List.h b/Sources/Utilities/include/Utilities/List.h index 8aeb39f..2e2adc2 100644 --- a/Sources/Utilities/include/Utilities/List.h +++ b/Sources/Utilities/include/Utilities/List.h @@ -136,7 +136,8 @@ template void ForwardList::pop_front() { class UInt64ForwardList : public ForwardList { public: - static std::shared_ptr make_shared(); + static UInt64ForwardList *create(); + static void destroy(UInt64ForwardList *value); bool empty() const noexcept; @@ -149,7 +150,9 @@ class UInt64ForwardList : public ForwardList { } SWIFT_UNSAFE_REFERENCE; -std::shared_ptr UInt64ForwardList::make_shared() { return std::make_shared(); } +UInt64ForwardList *UInt64ForwardList::create() { return new UInt64ForwardList(); } + +void UInt64ForwardList::destroy(UInt64ForwardList *value) { delete value; } bool UInt64ForwardList::empty() const noexcept { return ForwardList::empty(); } diff --git a/Tests/ComputeCompatibilityTests/Shared b/Tests/ComputeCompatibilityTests/Shared new file mode 120000 index 0000000..4b2f6a9 --- /dev/null +++ b/Tests/ComputeCompatibilityTests/Shared @@ -0,0 +1 @@ +../ComputeTests/Shared \ No newline at end of file diff --git a/Tests/ComputeCompatibilityTests/Shims.swift b/Tests/ComputeCompatibilityTests/Shims.swift new file mode 100644 index 0000000..740306e --- /dev/null +++ b/Tests/ComputeCompatibilityTests/Shims.swift @@ -0,0 +1,4 @@ +@_exported public import AttributeGraph + +typealias Graph = AGGraph +typealias Subgraph = AGSubgraph diff --git a/Tests/ComputeCxxTests/Data/ZoneTests.mm b/Tests/ComputeCxxTests/Data/ZoneTests.mm new file mode 100644 index 0000000..22db81b --- /dev/null +++ b/Tests/ComputeCxxTests/Data/ZoneTests.mm @@ -0,0 +1,47 @@ +#import +#import + +#include "Data/Table.h" +#include "Data/Zone.h" + +@interface ZoneTests : XCTestCase +@end + +@implementation ZoneTests + +- (void)setUp { + [super setUp]; + + AG::data::table::ensure_shared(); +} + +// MARK: Zones + +- (void)testZoneLifecycle { + // auto zone = AG::data::zone(); + // XCTAssertEqual(zone.info().zone_id(), 1); + // + // for (auto page : zone.pages()) { + // XCTFail(@"New zone shouldn't contain any pages"); + // } + // + // zone.clear(); +} + +// MARK: Alloc bytes + +- (void)testAllocBytes { + + auto zone = AG::data::zone(); + + auto bytes = zone.alloc_bytes(0x10, 3); + XCTAssert(bytes != nullptr); +} + +- (void)testAllocBytesWithExistingPage { +} + +- (void)testAllocBytesWithExistingPageInsufficientCapacity { +} + +@end diff --git a/Tests/ComputeCxxTests/Vector/IndirectPointerVectorTests.mm b/Tests/ComputeCxxTests/Vector/IndirectPointerVectorTests.mm new file mode 100644 index 0000000..6e4ec1f --- /dev/null +++ b/Tests/ComputeCxxTests/Vector/IndirectPointerVectorTests.mm @@ -0,0 +1,83 @@ +#import +#import + +#include "Containers/IndirectPointerVector.h" +#include "Data/Table.h" + +@interface IndirectPointerVectorTests : XCTestCase +@end + +@implementation IndirectPointerVectorTests + +- (void)testInit { + auto vector = AG::indirect_pointer_vector(); + XCTAssertTrue(vector.empty()); + XCTAssertEqual(vector.size(), 0); + + XCTAssert(vector.begin() == vector.end()); + + for (auto element : vector) { + XCTFail(@"New vector should be empty"); + } +} + +- (void)testClearWhenEmpty { + auto vector = AG::indirect_pointer_vector(); + + vector.clear(); +} + +- (void)testOneElement { + uint64_t testElement = 1; + + auto vector = AG::indirect_pointer_vector(); + vector.push_back(&testElement); + + XCTAssertFalse(vector.empty()); + XCTAssertEqual(vector.size(), 1); + + NSInteger i = 0; + for (auto element : vector) { + if (i == 0) { + XCTAssertEqual(element, &testElement); + } else { + XCTFail(@"Vector contains too many elements"); + } + ++i; + } + + vector.erase(vector.begin()); + XCTAssertTrue(vector.empty()); + XCTAssertEqual(vector.size(), 0); +} + +- (void)testTwoElements { + uint64_t testElement1 = 1; + uint64_t testElement2 = 1; + + auto vector = AG::indirect_pointer_vector(); + vector.push_back(&testElement1); + vector.push_back(&testElement2); + + XCTAssertFalse(vector.empty()); + XCTAssertEqual(vector.size(), 2); + + NSInteger i = 0; + for (auto element : vector) { + if (i == 0) { + XCTAssertEqual(element, &testElement1); + } else if (i == 1) { + XCTAssertEqual(element, &testElement2); + } else { + XCTFail(@"Vector contains too many elements"); + } + ++i; + } + + vector.erase(vector.begin()); + vector.erase(vector.begin()); + XCTAssertTrue(vector.empty()); + XCTAssertEqual(vector.size(), 0); +} + +@end diff --git a/Tests/ComputeTests/MetadataTests.swift b/Tests/ComputeTests/MetadataTests.swift deleted file mode 100644 index a7efacc..0000000 --- a/Tests/ComputeTests/MetadataTests.swift +++ /dev/null @@ -1,242 +0,0 @@ -import Algorithms -import Compute -import Testing - -@Suite("Metadata tests") -struct MetadataTests { - - class CustomClass {} - - struct CustomStruct {} - - enum CustomEnum {} - - typealias CustomTuple = (String, Int) - - typealias CustomFunction = (String) -> Int - - struct CustomGeneric { - struct Nested {} - } - - @Test("Metadata description matches declaration name") - func hasDescription() { - #expect(Metadata(CustomClass.self).description == "MetadataTests.CustomClass") - #expect(Metadata(CustomStruct.self).description == "MetadataTests.CustomStruct") - #expect(Metadata(CustomEnum.self).description == "MetadataTests.CustomEnum") - #expect(Metadata(Optional.self).description == "Optional") - #expect(Metadata(CustomTuple.self).description == "(String, Int)") - #expect(Metadata(CustomFunction.self).description == "(String) -> Int") - #expect(Metadata(Any.self).description == "Any") - #expect(Metadata(Void.Type.self).description == "().Type") - #expect( - Metadata(CustomGeneric.Nested.self).description - == "MetadataTests.CustomGeneric.Nested") - } - - @Test("Metadata kind matches type kind") - func hasKind() { - #expect(Metadata(CustomClass.self).kind == .class) - #expect(Metadata(CustomStruct.self).kind == .struct) - #expect(Metadata(CustomEnum.self).kind == .enum) - #expect(Metadata(Optional.self).kind == .optional) - #expect(Metadata(CustomTuple.self).kind == .tuple) - #expect(Metadata(CustomFunction.self).kind == .function) - #expect(Metadata(Any.self).kind == .existential) - #expect(Metadata(Void.Type.self).kind == .metatype) - #expect(Metadata(CustomGeneric.Nested.self).kind == .struct) - } - - @Suite - struct SignatureTests { - - @Test( - "Metadata for nominal type has a valid signature", - arguments: [ - Metadata(CustomClass.self), - Metadata(CustomStruct.self), - Metadata(CustomEnum.self), - Metadata(Optional.self), - ] - ) - func nominalTypeSignatureIsValid(metadata: Metadata) { - #expect(metadata.signature != Signature()) - } - - @Test( - "Metadata for non-nominal type does not have a signature", - arguments: [ - Metadata(CustomTuple.self), - Metadata(CustomFunction.self), - Metadata(Any.self), - Metadata(Void.Type.self), - ] - ) - func nonNominalTypeSignatureIsNull(metadata: Metadata) { - #expect(metadata.signature == Signature(), "\(metadata)") - } - - func signaturesAreUnique() { - var signatures: [Signature] = [] - - signatures.append(Metadata(CustomClass.self).signature) - signatures.append(Metadata(CustomStruct.self).signature) - signatures.append(Metadata(CustomEnum.self).signature) - signatures.append(Metadata(Optional.self).signature) - - #expect(signatures.count == 4) - signatures.combinations(ofCount: 2).forEach { elements in - #expect(elements[0] != elements[1]) - } - } - - } - - @Suite - struct DescriptorTests { - - @Test( - "Metadata for nominal type has a descriptor", - arguments: [ - Metadata(CustomClass.self), - Metadata(CustomStruct.self), - Metadata(CustomEnum.self), - Metadata(Optional.self), - ] - ) - func hasDescriptor(metadata: Metadata) { - #expect(metadata.descriptor != nil) - } - - @Test( - "Metadata for non-class nominal type has a nominal descriptor", - arguments: [ - Metadata(CustomStruct.self), - Metadata(CustomEnum.self), - Metadata(Optional.self), - ] - ) - func nominalDescriptorIsValid(metadata: Metadata) { - #expect(metadata.nominalDescriptor != nil) - } - - @Test( - "Metadata for class does not have a nominal descriptor" - ) - func classNominalDescriptorIsNil() { - #expect(Metadata(CustomClass.self).nominalDescriptor == nil) - } - - @Test("Metadata for nominal type has a nominal descriptor name") - func hasNominalDescriptorName() throws { - if let customStructName = Metadata(CustomStruct.self).nominalDescriptorName { - #expect(String(cString: customStructName) == "CustomStruct") - } else { - #expect(Bool(false)) - } - if let customEnumName = Metadata(CustomEnum.self).nominalDescriptorName { - #expect(String(cString: customEnumName) == "CustomEnum") - } else { - #expect(Bool(false)) - } - if let optionalName = Metadata(Optional.self).nominalDescriptorName { - #expect(String(cString: optionalName) == "Optional") - } else { - #expect(Bool(false)) - } - - } - - } - - // These tests are taken from https://github.com/OpenSwiftUIProject/OpenGraph/blob/main/Tests/OpenGraphCompatibilityTests/Runtime/MetadataTests.swift - @Suite - struct ApplyFieldsTests { - class T1 { - var a = 0 - var b: Double = 0 - } - - struct T2 { - var a: Int - var b: Double - } - - enum T3 { - case a, b - } - - @Test - func forEachField() throws { - for options in [Metadata.ApplyOptions.heapClasses] { - let result = Metadata(T1.self).forEachField(options: options) { name, offset, type in - if offset == 16 { - #expect(type is Int.Type) - #expect(String(cString: name) == "a") - return true - } else if offset == 24 { - #expect(type is Double.Type) - #expect(String(cString: name) == "b") - return true - } else { - return false - } - } - #expect(result == true) - } - for options in [Metadata.ApplyOptions._2, .enumCases, []] { - let result = Metadata(T1.self).forEachField(options: options) { name, offset, type in - if offset == 16 { - #expect(type is Int.Type) - #expect(String(cString: name) == "a") - return true - } else if offset == 24 { - #expect(type is Double.Type) - #expect(String(cString: name) == "b") - return true - } else { - return false - } - } - #expect(result == false) - } - for options in [Metadata.ApplyOptions._2, []] { - let result = Metadata(T2.self).forEachField(options: options) { name, offset, type in - if offset == 0 { - #expect(type is Int.Type) - return true - } else if offset == 8 { - #expect(type is Double.Type) - return true - } else { - return false - } - } - #expect(result == true) - } - for options in [Metadata.ApplyOptions.heapClasses, .enumCases] { - let result = Metadata(T2.self).forEachField(options: options) { name, offset, type in - if offset == 0 { - #expect(type is Int.Type) - #expect(String(cString: name) == "a") - return true - } else if offset == 8 { - #expect(type is Double.Type) - #expect(String(cString: name) == "b") - return true - } else { - return false - } - } - #expect(result == false) - } - for options in [Metadata.ApplyOptions.heapClasses, ._2, .enumCases, []] { - let result = Metadata(T3.self).forEachField(options: options) { _, _, _ in - true - } - #expect(result == false) - } - } - } - -} diff --git a/Tests/ComputeTests/Shared/Attribute/AnyAttributeTests.swift b/Tests/ComputeTests/Shared/Attribute/AnyAttributeTests.swift new file mode 100644 index 0000000..f21398c --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/AnyAttributeTests.swift @@ -0,0 +1,91 @@ +import Testing + +@Suite +final class AnyAttributeTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func constantValue() throws { + let attributeNil = AnyAttribute.nil + #expect(attributeNil.rawValue == 2) + } + + @Test + func description() throws { + let attribute = AnyAttribute(rawValue: 0) + #expect(attribute.description == "#0") + + let attributeNil = AnyAttribute.nil + #expect(attributeNil.description == "#2") + } + + @Test + func current() { + #expect(AnyAttribute.current == nil) + } + + @Test + func setFlags() throws { + let attribute = AnyAttribute(Attribute(value: 0)) + #expect(attribute.flags == []) + + // Test mask = [] + attribute.flags = [] + + attribute.setFlags([.active], mask: []) + #expect(attribute.flags == []) + + attribute.setFlags([.removable], mask: []) + #expect(attribute.flags == []) + + attribute.setFlags([.active, .invalidatable], mask: []) + #expect(attribute.flags == []) + + // Test mask + attribute.flags = [] + attribute.setFlags([.active], mask: [.active]) + #expect(attribute.flags == [.active]) + + attribute.setFlags([.removable], mask: [.removable]) + #expect(attribute.flags == [.active, .removable]) + + attribute.setFlags([.invalidatable], mask: [.active]) + #expect(attribute.flags == [.removable]) + + attribute.setFlags([.active, .invalidatable], mask: [.active, .removable, .invalidatable]) + #expect(attribute.flags == [.active, .invalidatable]) + } + + @Test + func visitBody() async { + let attribute = Attribute(value: "string value") + + struct Visitor: AttributeBodyVisitor { + let confirm: Confirmation + func visit(body: UnsafePointer) where Body: _AttributeBody { + if body.pointee is External { + confirm() + } + } + } + + await confirmation(expectedCount: 1) { confirm in + var visitor = Visitor(confirm: confirm) + attribute.visitBody(&visitor) + } + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/AttributeTests.swift b/Tests/ComputeTests/Shared/Attribute/AttributeTests.swift new file mode 100644 index 0000000..dd69a6d --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/AttributeTests.swift @@ -0,0 +1,99 @@ +import Testing + +struct Triple { + var first: A + var second: B + var third: C +} + +extension Triple: Sendable where A: Sendable, B: Sendable, C: Sendable {} + +@Suite +final class AttributeTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func initWithValue() { + let intAttribute = Attribute(value: 0) + #expect(intAttribute.value == 0) + } + + @Test + func hashableAndEquatable() { + let a = Attribute(identifier: .nil) + let b = Attribute(identifier: .nil) + #expect(a == b) + #expect(a.hashValue == b.hashValue) + } + + @Test + func propertyWrapper() { + @Attribute(value: 0) var value + #expect(value == 0) + value = 3 + #expect(value == 3) + #expect(_value.setValue(4) == true) + #expect(value == 4) + let newAttribute = $value + value = 5 + #expect(newAttribute.wrappedValue == 5) + } + + @Test(arguments: [ + Triple(first: 0, second: 1, third: 2), + Triple(first: 3, second: 4, third: 5), + ]) + func attributeWithSubscript(_ value: Triple) { + let attribute = Attribute(value: value) + let offsetValue = attribute[offset: { _ in + PointerOffset, Int>(byteOffset: 8) + }] + #expect(offsetValue.wrappedValue == value.second) + #expect(attribute.first.wrappedValue == value.first) + #expect(attribute[keyPath: \.third].wrappedValue == value.third) + } + + @Test + func value() { + let attribute = Attribute(value: 5) + #expect(attribute.hasValue == true) + #expect(attribute.changedValue(options: []) == (5, false)) + + #expect(attribute.setValue(3) == true) + #expect(attribute.changedValue(options: []) == (3, false)) // TODO: How to test for changed == true + + // Unknown effect and untested. + // Just add here for linkage test + attribute.updateValue() + attribute.prefetchValue() + attribute.invalidateValue() + } + + @Test + func mutateBodyAPI() { + let attribute = Attribute(value: 5) + attribute.mutateBody(as: External.self, invalidating: true) { _ in + + } + } + + @Test + func flagSetter() { + let attribute = Attribute(value: ()) + attribute.flags = .active + #expect(attribute.flags == .active) + } +} diff --git a/Tests/ComputeTests/Shared/Attribute/AttributeTypeTests.swift b/Tests/ComputeTests/Shared/Attribute/AttributeTypeTests.swift new file mode 100644 index 0000000..ca197fc --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/AttributeTypeTests.swift @@ -0,0 +1,77 @@ +import Foundation +import Testing + +private struct CustomStruct { + var property: Int +} + +private enum CustomEnum { + case first + case second(Int) + case third(String) +} + +private indirect enum CustomRecursiveEnum { + case none + case recursive(CustomRecursiveEnum) +} + +private struct CustomType { + var string: String? + var integer: Int? + var structProperty: CustomStruct? + var existential: (any Equatable)? + var function: (() -> Void)? + var enumProperty: CustomEnum? + var recursiveEnumProperty: CustomRecursiveEnum? +} + +@Suite +final class AttributeTypeTests { + + static let prefetchLayouts = + Int(ProcessInfo.processInfo.environment["AG_PREFETCH_LAYOUTS", default: "0"], radix: 10) != 0 + static let asyncLayouts = Int(ProcessInfo.processInfo.environment["AG_ASYNC_LAYOUTS", default: "1"], radix: 10) != 0 + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + // TODO: use setenv + @Test(.enabled(if: prefetchLayouts && !asyncLayouts)) + func layout() { + let attribute = Attribute( + value: CustomType( + string: nil, + integer: nil, + structProperty: nil, + existential: nil, + function: nil, + enumProperty: nil, + recursiveEnumProperty: nil + ) + ) + let attributeType = attribute.identifier.info.type.pointee + + #expect(attributeType.typeID == Metadata(External.self)) + #expect(attributeType.valueTypeID == Metadata(CustomType.self)) + #expect(attributeType.flags == AGAttributeTypeFlags(rawValue: 19)) + + #expect(attributeType.layout != nil) + if let layout = attributeType.layout { + #expect(String(cString: layout) == "\u{1}\u{fffd}\u{fffd}D\u{4}\u{2}") + } + + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/ExternalTests.swift b/Tests/ComputeTests/Shared/Attribute/ExternalTests.swift new file mode 100644 index 0000000..a444c00 --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/ExternalTests.swift @@ -0,0 +1,29 @@ +import Testing + +@Suite +final class ExternalTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func example() throws { + let type = External.self + let externalInt = type.init() + #expect(externalInt.description == "Int") + #expect(type.comparisonMode == [._1, ._2]) + #expect(type.flags == []) + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/FocusTests.swift b/Tests/ComputeTests/Shared/Attribute/FocusTests.swift new file mode 100644 index 0000000..b6509c3 --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/FocusTests.swift @@ -0,0 +1,36 @@ +import Testing + +struct Demo { + var a: Int + var b: Double +} + +@Suite +final class FocusTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func example() throws { + let root = Attribute(value: Demo(a: 0, b: 1.0)) + let type = Focus.self + let focus = type.init(root: root, keyPath: \.a) + let d = focus.description + #expect(d == "• Int") + #expect(focus.value == 0) + #expect(type.flags == []) + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/Indirect/IndirectAttributeTests.swift b/Tests/ComputeTests/Shared/Attribute/Indirect/IndirectAttributeTests.swift new file mode 100644 index 0000000..c8f4840 --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/Indirect/IndirectAttributeTests.swift @@ -0,0 +1,28 @@ +import Testing + +@Suite +final class IndirectAttributeTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func basic() { + let source = Attribute(value: 0) + let indirect = IndirectAttribute(source: source) + #expect(indirect.identifier != source.identifier) + #expect(indirect.source.identifier == source.identifier) + #expect(indirect.dependency == .init(rawValue: 0)) + } +} diff --git a/Tests/ComputeTests/Shared/Attribute/Optional/AnyOptionalAttributeTests.swift b/Tests/ComputeTests/Shared/Attribute/Optional/AnyOptionalAttributeTests.swift new file mode 100644 index 0000000..80a944c --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/Optional/AnyOptionalAttributeTests.swift @@ -0,0 +1,67 @@ +import Testing + +@Suite +final class AnyOptionalAttributeTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func basicInit() { + let o1 = AnyOptionalAttribute() + #expect(o1.identifier == .nil) + + let attr = AnyAttribute(rawValue: 0x1) + let o2 = AnyOptionalAttribute(attr) + #expect(o2.identifier == attr) + + let o3 = AnyOptionalAttribute(nil) + #expect(o3.identifier == .nil) + } + + @Test + func attribute() { + let o1 = AnyOptionalAttribute() + #expect(o1.attribute == nil) + + let attr = AnyAttribute(rawValue: 0x1) + let o2 = AnyOptionalAttribute(attr) + #expect(o2.attribute != nil) + } + + @Test + func current() { + let currentNil = AnyOptionalAttribute.current + #expect(currentNil.identifier == .nil) + } + + @Test + func description() { + let o1 = AnyOptionalAttribute() + #expect(o1.description == "nil") + + let attr = AnyAttribute(rawValue: 0x1) + let o2 = AnyOptionalAttribute(attr) + #expect(o2.description == "#1") + } + + @Test("Test symbol link") + func other() { + let optional = OptionalAttribute() + let anyOptional = AnyOptionalAttribute(optional) + _ = anyOptional.unsafeCast(to: Int.self) + _ = anyOptional.map { _ in 0 } + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/Optional/OptionalAttributeTests.swift b/Tests/ComputeTests/Shared/Attribute/Optional/OptionalAttributeTests.swift new file mode 100644 index 0000000..8005262 --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/Optional/OptionalAttributeTests.swift @@ -0,0 +1,52 @@ +import Testing + +@Suite +final class OptionalAttributeTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func basicInit() { + let ao1 = AnyOptionalAttribute() + let o1 = OptionalAttribute() + #expect(o1 == OptionalAttribute(base: ao1)) + + let attr = Attribute(identifier: .init(rawValue: 0x1)) + let ao2 = AnyOptionalAttribute(attr.identifier) + let o2 = OptionalAttribute(attr) + #expect(o2 == OptionalAttribute(base: ao2)) + + let o3 = OptionalAttribute(nil) + #expect(o3.base.identifier == .nil) + } + + @Test(.disabled("crash for invalid data offset")) + func initWithWeak() { + let attr = Attribute(value: 0) + let weakAttr = WeakAttribute(attr) + let _ = OptionalAttribute(weakAttr) + } + + @Test + func description() { + let o1 = OptionalAttribute() + #expect(o1.description == "nil") + + let attr = AnyAttribute(rawValue: 0x1) + let o2 = OptionalAttribute(Attribute(identifier: attr)) + #expect(o2.description == "#1") + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/PointerOffsetTests.swift b/Tests/ComputeTests/Shared/Attribute/PointerOffsetTests.swift new file mode 100644 index 0000000..cf1b57a --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/PointerOffsetTests.swift @@ -0,0 +1,175 @@ +import Testing + +struct Tuple { + var first: A + var second: B +} + +@Suite +struct PointerOffsetTests { + + @Test + func plainInitAndProperty() { + typealias Base = Tuple + var offset = PointerOffset(byteOffset: 8) + #expect(offset.byteOffset == 8) + offset.byteOffset = 0 + #expect(offset.byteOffset == 0) + } + + @Test + func emptyInit() { + #expect(PointerOffset().byteOffset == 0) + #expect(PointerOffset().byteOffset == 0) + #expect(PointerOffset().byteOffset == 0) + } + + @Test + func plusOperator() { + typealias Base = Tuple + typealias Base2 = Tuple + let offset1 = PointerOffset(byteOffset: 8) + let offset2 = PointerOffset(byteOffset: 8) + let result = offset1 + offset2 + #expect(result.byteOffset == 16) + #expect(type(of: result) == PointerOffset.self) + } + + @Test + func invalidScenePointer() { + typealias Base = Tuple + let invalidPointer = PointerOffset.invalidScenePointer() + let stride = MemoryLayout.stride + #expect(stride == 16) + + // if compatibilityTestEnabled { + // if #available(iOS 18, macOS 15, *) { + // #expect(invalidPointer == UnsafeMutablePointer(bitPattern: 0x1)) + // } else { + // #expect(invalidPointer == UnsafeMutablePointer(bitPattern: stride)) + // } + // } else { + // #if OPENGRAPH_RELEASE_2024 + // #expect(invalidPointer == UnsafeMutablePointer(bitPattern: 0x1)) + // #else + // + // #endif + // } + #expect(invalidPointer == UnsafeMutablePointer(bitPattern: 0x1)) + } + + @Test(.bug("https://github.com/OpenSwiftUIProject/OpenGraph/issues/70", id: 70, "Verify fix of #70")) + func ofAndOffset() { + struct Empty { + var value: Void + } + + func helper( + expectedByteOffset: Int, + _: Base.Type, + _: Member.Type, + _ body: (inout Base) -> PointerOffset + ) { + let pointerOffsetType = PointerOffset.self + let offset = pointerOffsetType.offset { invalid in + withUnsafeMutablePointer(to: &invalid) { pointer in + #expect(pointer == PointerOffset.invalidScenePointer()) + } + return body(&invalid) + } + #expect(offset.byteOffset == expectedByteOffset) + } + + helper(expectedByteOffset: 0, Tuple.self, Void.self) { _ in fatalError("Unreachable") } + helper(expectedByteOffset: 0, Tuple.self, Empty.self) { _ in fatalError("Unreachable") } + + typealias Base = Triple + helper(expectedByteOffset: 0, Base.self, Int.self) { invalid in + .of(&invalid.first) + } + helper(expectedByteOffset: 8, Base.self, Int.self) { invalid in + .of(&invalid.second) + } + helper(expectedByteOffset: 0, Base.self, Empty.self) { invalid in + .of(&invalid.third) + } + } + + @Test("Extension API between UnsafePointer/UnsafeMutablePointer and PointerOffset") + func unsafePointerAndUnsafeMutablePointerExtension() { + do { + var tuple = Tuple(first: 1, second: 2.0) + typealias Base = Tuple + let firstOffset = PointerOffset(byteOffset: 0) + let secondOffset = PointerOffset(byteOffset: 8) + withUnsafePointer(to: tuple) { pointer in + #expect(pointer[offset: firstOffset] == 1) + #expect(pointer[offset: secondOffset].isApproximatelyEqual(to: 2.0)) + } + withUnsafeMutablePointer(to: &tuple) { pointer in + #expect(pointer[offset: firstOffset] == 1) + #expect(pointer[offset: secondOffset].isApproximatelyEqual(to: 2.0)) + + pointer[offset: firstOffset] = 3 + pointer[offset: secondOffset] = 4.0 + + #expect(pointer[offset: firstOffset] == 3) + #expect(pointer[offset: secondOffset].isApproximatelyEqual(to: 4.0)) + } + withUnsafePointer(to: &tuple) { pointer in + #expect(pointer[offset: firstOffset] == 3) + #expect(pointer[offset: secondOffset].isApproximatelyEqual(to: 4.0)) + } + #if !(!canImport(Darwin) && !DEBUG) + withUnsafePointer(to: tuple) { pointer in + #expect(pointer[offset: firstOffset] == 3) + #expect(pointer[offset: secondOffset].isApproximatelyEqual(to: 4.0)) + } + #expect(tuple.first == 3) + #expect(tuple.second.isApproximatelyEqual(to: 4.0)) + #endif + } + + do { + var triple = Triple(first: 0, second: 1, third: 2) + typealias Base = Triple + + let firstOffset = PointerOffset.offset { .of(&$0.first) } + let secondOffset = PointerOffset.offset { .of(&$0.second) } + let thirdOffset = PointerOffset.offset { .of(&$0.third) } + + withUnsafePointer(to: triple) { pointer in + #expect((pointer + firstOffset).pointee == 0) + #expect((pointer + secondOffset).pointee == 1) + #expect((pointer + thirdOffset).pointee == 2) + } + withUnsafeMutablePointer(to: &triple) { pointer in + #expect((pointer + firstOffset).pointee == 0) + #expect((pointer + secondOffset).pointee == 1) + #expect((pointer + thirdOffset).pointee == 2) + + (pointer + firstOffset).pointee = 3 + (pointer + secondOffset).pointee = 4 + (pointer + thirdOffset).pointee = 5 + + #expect((pointer + firstOffset).pointee == 3) + #expect((pointer + secondOffset).pointee == 4) + #expect((pointer + thirdOffset).pointee == 5) + } + withUnsafePointer(to: &triple) { pointer in + #expect((pointer + firstOffset).pointee == 3) + #expect((pointer + secondOffset).pointee == 4) + #expect((pointer + thirdOffset).pointee == 5) + } + withUnsafePointer(to: triple) { pointer in + #expect((pointer + firstOffset).pointee == 3) + #expect((pointer + secondOffset).pointee == 4) + #expect((pointer + thirdOffset).pointee == 5) + } + #expect(triple.first == 3) + #expect(triple.second == 4) + #expect(triple.third == 5) + } + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/Rule/RuleTests.swift b/Tests/ComputeTests/Shared/Attribute/Rule/RuleTests.swift new file mode 100644 index 0000000..a48c1fa --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/Rule/RuleTests.swift @@ -0,0 +1,15 @@ +import Testing + +@Suite +struct RuleTests { + + @Test + func ruleInitialValue() throws { + struct A: Rule { + typealias Value = Int + var value: Int + } + #expect(A.initialValue == nil) + } + +} diff --git a/Tests/ComputeTests/Shared/Attribute/Weak/WeakAttributeTests.swift b/Tests/ComputeTests/Shared/Attribute/Weak/WeakAttributeTests.swift new file mode 100644 index 0000000..da32791 --- /dev/null +++ b/Tests/ComputeTests/Shared/Attribute/Weak/WeakAttributeTests.swift @@ -0,0 +1,28 @@ +import Testing + +@Suite +final class WeakAttributeTests { + + nonisolated(unsafe) private static let sharedGraph = Graph() + private var graph: Graph + private var subgraph: Subgraph + + init() { + graph = Graph(shared: Self.sharedGraph) + subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + } + + deinit { + Subgraph.current = nil + } + + @Test + func initTest() { + let _ = WeakAttribute() + let _ = WeakAttribute(nil) + let attr = Attribute(value: 0) + let _ = WeakAttribute(attr) + } + +} diff --git a/Tests/ComputeTests/Shared/Data/UniqueIDTests.swift b/Tests/ComputeTests/Shared/Data/UniqueIDTests.swift new file mode 100644 index 0000000..637cca8 --- /dev/null +++ b/Tests/ComputeTests/Shared/Data/UniqueIDTests.swift @@ -0,0 +1,11 @@ +import Testing + +struct UniqueIDTests { + + @Test + func uniqueID() throws { + let initialID = makeUniqueID() + #expect(makeUniqueID() == initialID + 1) + } + +} diff --git a/Tests/ComputeTests/Shared/Graph/GraphContextTests.swift b/Tests/ComputeTests/Shared/Graph/GraphContextTests.swift new file mode 100644 index 0000000..d8b1b1d --- /dev/null +++ b/Tests/ComputeTests/Shared/Graph/GraphContextTests.swift @@ -0,0 +1,18 @@ +import Testing + +@Suite +struct GraphContextTests { + + @Test + func currentGraphContext() { + let graph = Graph() + + let subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + + let currentGraphContext = Subgraph.currentGraphContext + #expect(currentGraphContext != nil) + #expect(currentGraphContext == graph.graphContext) + } + +} diff --git a/Tests/ComputeTests/Shared/Graph/GraphTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTests.swift new file mode 100644 index 0000000..5a6eb46 --- /dev/null +++ b/Tests/ComputeTests/Shared/Graph/GraphTests.swift @@ -0,0 +1,85 @@ +import Foundation +import Testing + +@Suite +struct GraphTests { + + @Test + func graphCreate() throws { + _ = Graph() + } + + @Test + func graphCreateShared() throws { + let graph = Graph() + _ = Graph(shared: graph) + _ = Graph(shared: nil) + } + + @Test + func graphArchiveJSON() throws { + struct Graphs: Codable { + var version: Int + var graphs: [Graph] + struct Graph: Codable {} + } + let name = "empty_graph.json" + Graph.archiveJSON(name: name) + // let name = "empty_graph.json" + // name.withCString { Graph.archiveJSON(name: $0) } + let url = + if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) { + URL(filePath: NSTemporaryDirectory().appending(name)) + } else { + URL(fileURLWithPath: NSTemporaryDirectory().appending(name)) + } + let data = try Data(contentsOf: url) + let graphs = try JSONDecoder().decode(Graphs.self, from: data) + #expect(graphs.version == 2) + } + + @Test + func graphDescriptionDict() throws { + let description = try #require(Graph.description(nil, options: ["format": "graph/dict"] as NSDictionary)) + let dic = description.takeUnretainedValue() as! [String: AnyHashable] + #expect(dic["version"] as? UInt32 == 2) + #expect((dic["graphs"] as? NSArray)?.count == 0) + } + + @Test + func graphDescriptionDot() throws { + let options = NSMutableDictionary() + options["format"] = "graph/dot" + #expect(Graph.description(nil, options: options) == nil) + let graph = Graph() + let description = try #require(Graph.description(graph, options: options)) + let dotGraph = description.takeUnretainedValue() as! String + let expectedEmptyDotGraph = #""" + digraph { + } + + """# + #expect(dotGraph == expectedEmptyDotGraph) + } + + @Test + func graphCallback() { + let graph = Graph() + Graph.setUpdateCallback(graph, callback: nil) + Graph.setUpdateCallback(graph) { + print("Update") + } + Graph.setInvalidationCallback(graph, callback: nil) + Graph.setInvalidationCallback(graph) { attr in + print("Invalidate \(attr)") + } + + } + + @Test + func counter() { + let graph = Graph() + #expect(graph.mainUpdates == 0) + } + +} diff --git a/Tests/ComputeTests/Shared/Graph/SubgraphTests.swift b/Tests/ComputeTests/Shared/Graph/SubgraphTests.swift new file mode 100644 index 0000000..2f6999e --- /dev/null +++ b/Tests/ComputeTests/Shared/Graph/SubgraphTests.swift @@ -0,0 +1,27 @@ +import Testing + +@Suite +struct SubgraphTests { + + @Test + func shouldRecordTree() { + setenv("AG_TREE", "0", 1) + #expect(Subgraph.shouldRecordTree == false) + + Subgraph.setShouldRecordTree() + #expect(Subgraph.shouldRecordTree == true) + } + + @Test + func treeElementAPICheck() { + let graph = Graph() + let subgraph = Subgraph(graph: graph) + subgraph.apply { + let value = Attribute(value: ()) + Subgraph.beginTreeElement(value: value, flags: 0) + Subgraph.addTreeValue(value, forKey: "", flags: 0) + Subgraph.endTreeElement(value: value) + } + } + +} diff --git a/Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift b/Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift new file mode 100644 index 0000000..4521c4e --- /dev/null +++ b/Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift @@ -0,0 +1,137 @@ +import Testing + +@Suite +struct PrefetchCompareValuesTests { + + enum LayoutExpectation: ExpressibleByStringLiteral { + case empty + case layout(String) + + init(stringLiteral value: StringLiteralType) { + self = .layout(value) + } + } + + @Test( + arguments: [ + // POD +// (Void.self, .empty), +// (Bool.self, .empty), +// (Int.self, .empty), +// (Double.self, .empty), +// (Float.self, .empty), +// (PODStruct.self, .empty), + + // Non-POD + (String.self, .empty), +// +// // Tuple +// ((Int, String).self, .empty), +// +// // Enum +// (CEnum.self, nil), +// (TaggedEnum.self, "\t\u{fffd}\u{fffd}W\u{4}\u{1}"), +// (IndirectEnum.self, "\t0\u{fffd}W\u{4}\u{1}"), +// +// // Heap +// (HeapClass.self, nil), +// +// // Product types +// (TestStruct.self, "\u{fffd}F\u{fffd}C\u{fffd}F\t\u{fffd}\u{fffd}W\u{4}\u{1}"), +// (TestClass.self, nil), + ] as [(Any.Type, LayoutExpectation?)] + ) + func layout(of type: Any.Type, matches layoutExpectation: LayoutExpectation?) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + + let layout = prefetchCompareValues(type: Metadata(type), options: [], priority: 0) + + if let layoutExpectation { + #expect(layout != nil) + if let layout { + switch layoutExpectation { + case .empty: + #expect(layout == UnsafePointer(bitPattern: 1)) + case .layout(let expectedLayout): + #expect(String(cString: layout) == expectedLayout) + } + } + } else { + #expect(layout == nil) + } + } + + @Test(arguments: [ + Triple(first: 0, second: 1, third: 2), + Triple(first: 3, second: 4, third: 5), + ]) + func attributeWithSubscript(_ value: Triple) { + let attribute = Attribute(value: value) + let offsetValue = attribute[offset: { _ in + PointerOffset, Int>(byteOffset: 8) + }] + #expect(offsetValue.wrappedValue == value.second) + #expect(attribute.first.wrappedValue == value.first) + #expect(attribute[keyPath: \.third].wrappedValue == value.third) + } + +} + +@Suite +struct CompareValuesTests { + + @Test + func intCompare() throws { + #expect(compareValues(1, 1) == true) + #expect(compareValues(1, 2) == false) + } + + @Test + func enumCompare() throws { + enum A { case a, b } + #expect(compareValues(A.a, A.a) == true) + #expect(compareValues(A.a, A.b) == false) + + enum B { case a, b, c } + let b = B.b + withUnsafePointer(to: b) { p in + p.withMemoryRebound(to: A.self, capacity: MemoryLayout.size) { pointer in + #expect(compareValues(pointer.pointee, A.b) == true) + } + } + withUnsafePointer(to: b) { p in + p.withMemoryRebound(to: A.self, capacity: MemoryLayout.size) { pointer in + #expect(compareValues(pointer.pointee, A.a) == false) + } + } + } + + @Test + func testStructCompare() throws { + struct A1 { + var a: Int + var b: Bool + } + struct A2 { + var a: Int + var b: Bool + } + let a = A1(a: 1, b: true) + let b = A1(a: 1, b: true) + let c = A1(a: 1, b: false) + #expect(compareValues(b, a) == true) + #expect(compareValues(c, a) == false) + let d = A2(a: 1, b: true) + withUnsafePointer(to: d) { p in + p.withMemoryRebound(to: A1.self, capacity: MemoryLayout.size) { pointer in + #expect(compareValues(pointer.pointee, a) == true) + } + } + withUnsafePointer(to: d) { p in + p.withMemoryRebound(to: A1.self, capacity: MemoryLayout.size) { pointer in + #expect(compareValues(pointer.pointee, c) == false) + } + } + } + +} diff --git a/Tests/ComputeTests/Shared/Runtime/MetadataTests.swift b/Tests/ComputeTests/Shared/Runtime/MetadataTests.swift new file mode 100644 index 0000000..987192f --- /dev/null +++ b/Tests/ComputeTests/Shared/Runtime/MetadataTests.swift @@ -0,0 +1,635 @@ +import Algorithms +import Testing + +@Suite("Metadata tests") +struct MetadataTests { + + @Test( + arguments: [ + (TestClass.self, "TestClass"), + (TestStruct.self, "TestStruct"), + (TestEnum.self, "TestEnum"), + (TestTaggedEnum.self, "TestTaggedEnum"), + (TestIndirectEnum.self, "TestIndirectEnum"), + (TestOptionalClass.self, "Optional"), + (TestOptionalStruct.self, "Optional"), + (TestForeignClass.self, "CFDateRef"), + (TestTuple.self, "(String, Int)"), + (TestFunction.self, "(String) -> Int"), + (TestExistential.self, "Hashable"), + (TestConstrainedExistential.self, "<<< invalid type >>>"), + (TestComposedExistential.self, "CustomStringConvertible & Hashable"), + (TestMetatype.self, "TestClass.Type"), + (TestObjCClass.self, "NSDate"), + (TestExistentialMetatype.self, "Hashable.Protocol"), + (TestNamespace.TestNestedStruct.self, "TestNamespace.TestNestedStruct"), + ( + TestGenericStruct.TestNestedGenericStruct.self, + "TestGenericStruct.TestNestedGenericStruct" + ), + ] as [(Any.Type, String)] + ) + func description(of type: Any.Type, equals expectedDescription: String) { + #expect(Metadata(type).description == expectedDescription) + } + + @Test( + arguments: [ + (TestClass.self, .class), + (TestStruct.self, .struct), + (TestEnum.self, .enum), + (TestTaggedEnum.self, .enum), + (TestIndirectEnum.self, .enum), + (TestOptionalClass.self, .optional), + (TestOptionalStruct.self, .optional), + (TestForeignClass.self, .none), + (TestTuple.self, .tuple), + (TestFunction.self, .function), + (TestExistential.self, .existential), + (TestConstrainedExistential.self, .none), + (TestComposedExistential.self, .existential), + (TestMetatype.self, .metatype), + (TestObjCClass.self, .none), + (TestExistentialMetatype.self, .metatype), + (TestNamespace.TestNestedStruct.self, .struct), + ( + TestGenericStruct.TestNestedGenericStruct.self, + .struct + ), + ] as [(Any.Type, Metadata.Kind)] + ) + func kind(of type: Any.Type, equals expectedKind: Metadata.Kind) { + #expect(Metadata(type).kind == expectedKind) + } + + @Suite + struct SignatureTests { + + @Test( + "Metadata for nominal type has a valid signature", + arguments: [ + (TestClass.self, true), + (TestStruct.self, true), + (TestEnum.self, true), + (TestTaggedEnum.self, true), + (TestIndirectEnum.self, true), + (TestOptionalClass.self, true), + (TestOptionalStruct.self, true), + (TestForeignClass.self, false), + (TestTuple.self, false), + (TestFunction.self, false), + (TestExistential.self, false), + (TestConstrainedExistential.self, false), + (TestComposedExistential.self, false), + (TestMetatype.self, false), + (TestObjCClass.self, false), + (TestExistentialMetatype.self, false), + (TestNamespace.TestNestedStruct.self, true), + ( + TestGenericStruct.TestNestedGenericStruct.self, + true + ), + ] as [(Any.Type, Bool)] + ) + func signature(of type: Any.Type, isValid: Bool) { + if isValid { + #expect(Metadata(type).signature != Signature()) + } else { + #expect(Metadata(type).signature == Signature()) + } + } + + func signaturesAreUnique() { + var signatures: [Signature] = [] + + signatures.append(Metadata(TestClass.self).signature) + signatures.append(Metadata(TestStruct.self).signature) + signatures.append(Metadata(TestEnum.self).signature) + signatures.append(Metadata(TestTaggedEnum.self).signature) + signatures.append(Metadata(TestIndirectEnum.self).signature) + signatures.append(Metadata(TestOptionalClass.self).signature) + signatures.append(Metadata(TestOptionalStruct.self).signature) + signatures.append(Metadata(TestNamespace.TestNestedStruct.self).signature) + signatures.append(Metadata(TestGenericStruct.TestNestedGenericStruct.self).signature) + + #expect(signatures.count == 9) + signatures.combinations(ofCount: 2).forEach { elements in + #expect(elements[0] != elements[1]) + } + } + + } + + @Suite + struct DescriptorTests { + + @Test( + "Metadata for nominal type has a descriptor", + arguments: [ + (TestClass.self, true), + (TestStruct.self, true), + (TestEnum.self, true), + (TestTaggedEnum.self, true), + (TestIndirectEnum.self, true), + (TestOptionalClass.self, true), + (TestOptionalStruct.self, true), + (TestForeignClass.self, false), + (TestTuple.self, false), + (TestFunction.self, false), + (TestExistential.self, false), + (TestConstrainedExistential.self, false), + (TestComposedExistential.self, false), + (TestMetatype.self, false), + (TestObjCClass.self, false), + (TestExistentialMetatype.self, false), + (TestNamespace.TestNestedStruct.self, true), + ( + TestGenericStruct.TestNestedGenericStruct.self, + true + ), + ] as [(Any.Type, Bool)] + ) + func descriptor(of type: Any.Type, hasDescriptor: Bool) { + if hasDescriptor { + #expect(Metadata(type).descriptor != nil) + } else { + #expect(Metadata(type).descriptor == nil) + } + } + + @Test( + "Metadata for non-class nominal type has a nominal descriptor", + arguments: [ + (TestClass.self, false), + (TestStruct.self, true), + (TestEnum.self, true), + (TestTaggedEnum.self, true), + (TestIndirectEnum.self, true), + (TestOptionalClass.self, true), + (TestOptionalStruct.self, true), + (TestForeignClass.self, false), + (TestTuple.self, false), + (TestFunction.self, false), + (TestExistential.self, false), + (TestConstrainedExistential.self, false), + (TestComposedExistential.self, false), + (TestMetatype.self, false), + (TestObjCClass.self, false), + (TestExistentialMetatype.self, false), + (TestNamespace.TestNestedStruct.self, true), + ( + TestGenericStruct.TestNestedGenericStruct.self, + true + ), + ] as [(Any.Type, Bool)] + ) + func nominalDescriptor(of type: Any.Type, hasNominalDescriptor: Bool) { + if hasNominalDescriptor { + #expect(Metadata(type).nominalDescriptor != nil) + } else { + #expect(Metadata(type).nominalDescriptor == nil) + } + } + + @Test( + "Metadata for nominal type has a nominal descriptor name", + arguments: [ + (TestClass.self, nil), + (TestStruct.self, "TestStruct"), + (TestEnum.self, "TestEnum"), + (TestTaggedEnum.self, "TestTaggedEnum"), + (TestIndirectEnum.self, "TestIndirectEnum"), + (TestOptionalClass.self, "Optional"), + (TestOptionalStruct.self, "Optional"), + (TestForeignClass.self, nil), + (TestTuple.self, nil), + (TestFunction.self, nil), + (TestExistential.self, nil), + (TestConstrainedExistential.self, nil), + (TestComposedExistential.self, nil), + (TestMetatype.self, nil), + (TestObjCClass.self, nil), + (TestExistentialMetatype.self, nil), + (TestNamespace.TestNestedStruct.self, "TestNestedStruct"), + ( + TestGenericStruct.TestNestedGenericStruct.self, + "TestNestedGenericStruct" + ), + ] as [(Any.Type, String?)] + ) + func nominalDescriptorName(of type: Any.Type, equals expectedNominalDescriptorName: String?) { + if let expectedNominalDescriptorName { + let nominalDescriptorName = Metadata(type).nominalDescriptorName.map { String(cString: $0) } + #expect(nominalDescriptorName == expectedNominalDescriptorName) + } else { + #expect(Metadata(type).nominalDescriptorName == nil) + } + } + } + + @Suite + struct GlobalForEachFieldTests { + + struct StructFields { + var first: Int = 0 + var second: Float = 0.0 + var third: Double = 0.0 + } + + @Test + func enumeratesStructFields() { + var fields: [(String, Int, any Any.Type)] = [] + forEachField(of: StructFields.self) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + } + + #expect(fields.count == 3) + + #expect(fields[0].0 == "first") + #expect(fields[0].1 == 0) + #expect(fields[0].2 as Any.Type == Int.self) + #expect(fields[1].0 == "second") + #expect(fields[1].1 == 8) + #expect(fields[1].2 as Any.Type == Float.self) + #expect(fields[2].0 == "third") + #expect(fields[2].1 == 16) + #expect(fields[2].2 as Any.Type == Double.self) + } + + struct NestedStructFields { + var nested: StructFields + } + + @Test + func doesNotEnumerateNestedStructFields() { + var fields: [(String, Int, any Any.Type)] = [] + forEachField(of: NestedStructFields.self) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + } + + #expect(fields.count == 1) + + #expect(fields[0].0 == "nested") + #expect(fields[0].1 == 0) + #expect(fields[0].2 as Any.Type == StructFields.self) + } + + class ClassFields { + var first: Int = 0 + var second: Float = 0.0 + var third: Double = 0.0 + } + + @Test + func doesNotEnumerateClassFields() { + var fields: [(String, Int, any Any.Type)] = [] + forEachField(of: ClassFields.self) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + } + + #expect(fields.count == 0) + } + + class NestedClassFields { + var nested: ClassFields = ClassFields() + } + + @Test + func doesNotEnumerateNestedClassFields() { + var fields: [(String, Int, any Any.Type)] = [] + forEachField(of: NestedClassFields.self) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + } + + #expect(fields.count == 0) + } + + enum EnumFields { + case first + case second(Int) + case third(Double) + } + + @Test + func doesNotEnumerateEnumFields() { + var fields: [(String, Int, any Any.Type)] = [] + forEachField(of: EnumFields.self) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + } + + #expect(fields.count == 0) + } + + enum NestedEnumFields { + case nested(EnumFields) + } + + @Test + func doesNotEnumerateNestedEnumFields() { + var fields: [(String, Int, any Any.Type)] = [] + forEachField(of: EnumFields.self) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + } + + #expect(fields.count == 0) + } + + typealias TupleFields = (Int, Float, Double) + + @Test + func doesNotEnumeratesTupleFields() { + var fields: [(String, Int, any Any.Type)] = [] + forEachField(of: TupleFields.self) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + } + + #expect(fields.count == 0) + } + + } + + @Suite + struct ForEachFieldTests { + + struct StructFields { + var first: Int = 0 + var second: Float = 0.0 + var third: Double = 0.0 + } + + @Test + func enumeratesStructFields() { + let options: AGTypeApplyOptions = [] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(StructFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 3) + + #expect(fields[0].0 == "first") + #expect(fields[0].1 == 0) + #expect(fields[0].2 as Any.Type == Int.self) + #expect(fields[1].0 == "second") + #expect(fields[1].1 == 8) + #expect(fields[1].2 as Any.Type == Float.self) + #expect(fields[2].0 == "third") + #expect(fields[2].1 == 16) + #expect(fields[2].2 as Any.Type == Double.self) + } + + @Test + func doesNotEnumerateStructFieldsWhenEnumerateClassFieldsIsSpecified() { + let options: AGTypeApplyOptions = [.enumerateClassFields] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(StructFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == false) + #expect(fields.count == 0) + } + + @Test + func doesNotEnumerateStructFieldsWhenEnumerateEnumCasesIsSpecified() { + let options: AGTypeApplyOptions = [.enumerateEnumCases] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(StructFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == false) + #expect(fields.count == 0) + } + + struct NestedStructFields { + var nested: StructFields + } + + @Test + func doesNotEnumerateNestedStructFields() { + let options: AGTypeApplyOptions = [] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(NestedStructFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 1) + + #expect(fields[0].0 == "nested") + #expect(fields[0].1 == 0) + #expect(fields[0].2 as Any.Type == StructFields.self) + } + + class ClassFields { + var first: Int = 0 + var second: Float = 0.0 + var third: Double = 0.0 + } + + @Test + func doesNotEnumeratesClassFields() { + let options: AGTypeApplyOptions = [] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(ClassFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == false) + #expect(fields.count == 0) + } + + @Test + func enumeratesClassFieldsWhenEnumerateClassFieldsIsSpecified() { + let options: AGTypeApplyOptions = [.enumerateClassFields] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(ClassFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 3) + + #expect(fields[0].0 == "first") + #expect(fields[0].1 == 16) + #expect(fields[0].2 as Any.Type == Int.self) + #expect(fields[1].0 == "second") + #expect(fields[1].1 == 24) + #expect(fields[1].2 as Any.Type == Float.self) + #expect(fields[2].0 == "third") + #expect(fields[2].1 == 32) + #expect(fields[2].2 as Any.Type == Double.self) + } + + class NestedClassFields { + var nested: ClassFields = ClassFields() + } + + @Test + func doesNotEnumerateNestedClassFields() { + let options: AGTypeApplyOptions = [.enumerateClassFields] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(NestedClassFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 1) + + #expect(fields[0].0 == "nested") + #expect(fields[0].1 == 16) + #expect(fields[0].2 as Any.Type == ClassFields.self) + } + + enum EnumFields { + case first + case second(Int) + case third(Double) + } + + @Test + func doesNotEnumerateEnumFields() { + let options: AGTypeApplyOptions = [] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(EnumFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == false) + #expect(fields.count == 0) + } + + @Test + func enumeratesEnumFieldsWhenEnumerateEnumCasesIsSpecified() { + let options: AGTypeApplyOptions = [.enumerateEnumCases] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(EnumFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 2) + + #expect(fields[0].0 == "second") + #expect(fields[0].1 == 0) + #expect(fields[0].2 as Any.Type == Int.self) + #expect(fields[1].0 == "third") + #expect(fields[1].1 == 1) + #expect(fields[1].2 as Any.Type == Double.self) + } + + enum NestedEnumFields { + case nested(EnumFields) + } + + @Test + func doesNotEnumerateNestedEnumFields() { + let options: AGTypeApplyOptions = [.enumerateEnumCases] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(NestedEnumFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 1) + + #expect(fields[0].0 == "nested") + #expect(fields[0].1 == 0) + #expect(fields[0].2 as Any.Type == EnumFields.self) + } + + typealias TupleFields = (Int, Float, Double) + + @Test + func doesNotEnumeratesTupleFields() { + let options: AGTypeApplyOptions = [] + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(TupleFields.self).forEachField(options: options) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == false) + #expect(fields.count == 0) + } + + } + +} diff --git a/Tests/ComputeTests/Shared/Runtime/TupleTypeTests.swift b/Tests/ComputeTests/Shared/Runtime/TupleTypeTests.swift new file mode 100644 index 0000000..c232baf --- /dev/null +++ b/Tests/ComputeTests/Shared/Runtime/TupleTypeTests.swift @@ -0,0 +1,147 @@ +import Testing + +@Suite +struct TupleTypeTests { + + class T1 { + var a = 0 + var b: Double = 0 + } + + struct T2 { + var a: Int = 0 + var b: Double = 0 + } + + enum T3 { + case a, b + } + + @Test + func initType() { + #expect(TupleType(T1.self).rawValue == Metadata(T1.self).rawValue) + #expect(TupleType([T1.self, T2.self]).rawValue == Metadata((T1, T2).self).rawValue) + #expect(TupleType([T1.self, T2.self, T3.self]).rawValue == Metadata((T1, T2, T3).self).rawValue) + } + + + @Test + func type() { + #expect(TupleType([T1.self, T2.self, T3.self]).type == (T1, T2, T3).self) + } + + @Test(arguments:[ + (TupleType(Void.self), true, 0 ..< 0), + (TupleType(T1.self), false, 0 ..< 1), + (TupleType([T1.self, T2.self]), false, 0 ..< 2), + ]) + func collectionAPI(tupleType: TupleType, expectedIsEmpty: Bool, expectedIndices: Range) { + #expect(tupleType.isEmpty == expectedIsEmpty) + #expect(tupleType.indices == expectedIndices) + } + + @Test + func elementTypeAndOffset() { + let tupleType = TupleType([T1.self, T2.self, T3.self]) + #expect(tupleType.type(at: 0) == T1.self) + #expect(tupleType.type(at: 1) == T2.self) + #expect(tupleType.type(at: 2) == T3.self) + + #expect(tupleType.offset(at: 0, as: T1.self) == 0) + #expect(tupleType.offset(at: 1, as: T2.self) == 8) + #expect(tupleType.offset(at: 2, as: T3.self) == 24) + } + + @Test + func getAndSetElement() { + let tupleType = TupleType([T1.self, T2.self]) + var tuple = (T1(), T2()) + #expect(tuple.0.a == 0) + #expect(tuple.0.b == 0) + #expect(tuple.1.a == 0) + #expect(tuple.1.b == 0) + + var newT1 = T1() + newT1.a = 1 + var newT2 = T2(a: 2) + + withUnsafeMutablePointer(to: &tuple) { tuplePointer in + withUnsafePointer(to: newT1) { + tupleType.setElement(in: tuplePointer, at: 0, from: $0, options: .assignCopy) + } + #expect(tuplePointer.pointee.0.a == 1) + #expect(tuplePointer.pointee.1.a == 0) + withUnsafePointer(to: newT2) { + tupleType.setElement(in: tuplePointer, at: 1, from: $0, options: .assignCopy) + } + #expect(tuplePointer.pointee.0.a == 1) + #expect(tuplePointer.pointee.1.a == 2) + } + + tuple.0.a = 3 + tuple.1.a = 4 + + withUnsafeMutablePointer(to: &tuple) { tuplePointer in + withUnsafeMutablePointer(to: &newT1) { + tupleType.getElement(in: tuplePointer, at: 0, to: $0, options: .assignCopy) + } + #expect(newT1.a == 3) + #expect(newT2.a == 2) + + withUnsafeMutablePointer(to: &newT2) { + tupleType.getElement(in: tuplePointer, at: 1, to: $0, options: .assignCopy) + } + #expect(newT2.a == 4) + } + } +} + +@Suite +struct UnsafeMutableTupleTests { + + class T1 { + var a = 0 + } + + struct T2 { + var a = 0 + } + + @Test + func buffer() { + withUnsafeTuple(of: TupleType([T1.self, T2.self]), count: 1) { mutableTuple in + let ref = T1() + ref.a = 1 + mutableTuple.initialize(at: 0, to: ref) + mutableTuple.initialize(at: 1, to: T2(a: 2)) + + let tuple = UnsafeTuple(type: mutableTuple.type, value: mutableTuple.value) + let t1 = tuple[0] as T1 + let t2 = tuple[1] as T2 + + #expect(t1 === ref) + #expect(t1.a == 1) + #expect(t2.a == 2) + } + } + + @Test + func initialize() { + let mutableTuple = UnsafeMutableTuple(with: TupleType([T1.self, T2.self])) + + #expect(mutableTuple.count == 2) + #expect(mutableTuple.isEmpty == false) + + let t1 = T1() + t1.a = 1 + let t2 = T2(a: 2) + mutableTuple.initialize(at: 0, to: t1) + mutableTuple.initialize(at: 1, to: t2) + + #expect((mutableTuple[0] as T1).a == 1) + #expect((mutableTuple[1] as T2).a == 2) + + mutableTuple.deinitialize() + } + +} diff --git a/Tests/ComputeTests/Shared/TestTypes.swift b/Tests/ComputeTests/Shared/TestTypes.swift new file mode 100644 index 0000000..9effa5a --- /dev/null +++ b/Tests/ComputeTests/Shared/TestTypes.swift @@ -0,0 +1,182 @@ +import Foundation + +// Class + +class TestClass { + var a: Bool = false +} + +// Struct + +struct TestStruct { + var a: Bool = false +} + +// Enum + +enum TestEnum { + case a + case b +} + +enum TestTaggedEnum { + case a(TestClass) + case b(TestStruct) +} + +indirect enum TestIndirectEnum { + case a + case b(TestIndirectEnum) +} + +// Optional + +typealias TestOptionalClass = TestClass? +typealias TestOptionalStruct = TestStruct? + +// Foreign Class + +typealias TestForeignClass = CFDate + +// ForeignReferenceType (non-swift non-objc-c class type) +//typealias TestForeignReferenceType = util.UntypedTable + +// Opaque, type not exposed in metadata system + +// Tuple + +typealias TestTuple = (String, Int) + +// Function + +typealias TestFunction = (String) -> Int + +// Existentialtype + +typealias TestExistential = any Hashable +typealias TestConstrainedExistential = any Sequence +typealias TestComposedExistential = any Hashable & CustomStringConvertible + +// Metatype + +typealias TestMetatype = TestClass.Type + +// ObjC + +typealias TestObjCClass = NSDate + +// Existential metatype + +typealias TestExistentialMetatype = (any Hashable).Type + +// Nesting + +enum TestNamespace { + struct TestNestedStruct {} +} + +// Generic + +struct TestGenericStruct { + struct TestNestedGenericStruct {} +} + +// extended existential type + +// heap-allocated local variable using statically generated metadata +// heap-allocated local variable using runtime instantiated meatdta + +// native error object + +// heap allocated task +// non-task async ob + +// POD types + +struct PODStruct { + var a: Int + var b: Double + var c: Float +} + +// Enum types + +enum CEnum { + case a + case b + case c +} + +enum TaggedEnum { + case a(Int) + case b(Double) + case c(Float) +} + +indirect enum IndirectEnum { + case a + case b + case c(IndirectEnum) +} + +// Heap types + +class HeapClass { + var a: Int = 0 + var b: Double = 0.0 + var c: Float = 0.0 +} + +// Product types + +//struct TestStruct { +// var a: Void +// +// var b: Bool +// +// var c: Int +// var d: Double +// var e: Float +// +// var f: String +// var g: Character +// +// var h: CEnum +// var i: TaggedEnum +// var j: IndirectEnum +// +// var k: [String] +// var l: [String: String] +// var m: Set +// +// var n: (String, String) +// +// var o: (Int) -> String +// var p: any Equatable +//} +// +//class TestClass { +// var a: Void = () +// +// var b: Bool = false +// +// var c: Int = 0 +// var d: Double = 0.0 +// var e: Float = 0.0 +// +// var f: String = "" +// var g: Character = "\0" +// +// var h: CEnum = .a +// var i: TaggedEnum = .a(0) +// var j: IndirectEnum = .a +// +// var k: [String] = [] +// var l: [String: String] = [:] +// var m: Set = [] +// +// var n: (String, String) = ("", "") +// +// var o: (Int) -> String = { _ in "" } +// var p: any Equatable = 0 +//} diff --git a/Tests/ComputeTests/Shims.swift b/Tests/ComputeTests/Shims.swift new file mode 100644 index 0000000..5c0effc --- /dev/null +++ b/Tests/ComputeTests/Shims.swift @@ -0,0 +1 @@ +@_exported public import Compute diff --git a/Tests/UtilitiesTests/HashTableTests.swift b/Tests/UtilitiesTests/HashTableTests.swift index 22b85ff..6d410d1 100644 --- a/Tests/UtilitiesTests/HashTableTests.swift +++ b/Tests/UtilitiesTests/HashTableTests.swift @@ -6,8 +6,10 @@ struct HashTableTests { @Test("Initialize empty table") func initEmpty() { - let tablePointer = util.UntypedTable.make_shared() - let table = tablePointer.pointee + let table = util.UntypedTable.create() + defer { + util.UntypedTable.destroy(table) + } #expect(table.empty()) #expect(table.count() == 0) @@ -22,8 +24,10 @@ struct HashTableTests { } } - let tablePointer = util.UntypedTable.make_shared() - let table = tablePointer.pointee + let table = util.UntypedTable.create() + defer { + util.UntypedTable.destroy(table) + } let key = "key1" let value = Value(prop: "valueProp") @@ -49,8 +53,10 @@ struct HashTableTests { } } - let tablePointer = util.UntypedTable.make_shared() - let table = tablePointer.pointee + let table = util.UntypedTable.create() + defer { + util.UntypedTable.destroy(table) + } let key = "key1" let value = Value(prop: "valueProp") diff --git a/Tests/UtilitiesTests/HeapTests.swift b/Tests/UtilitiesTests/HeapTests.swift index ad89576..33bd4ba 100644 --- a/Tests/UtilitiesTests/HeapTests.swift +++ b/Tests/UtilitiesTests/HeapTests.swift @@ -8,8 +8,10 @@ struct HeapTests { @Test("Initializing with default arguments") func initDefault() { - let heapPointer = util.Heap.make_shared(nil, 0, 0) - let heap = heapPointer.pointee + let heap = util.Heap.create(nil, 0, 0) + defer { + util.Heap.destroy(heap) + } #expect(heap.capacity() == 0) #expect(heap.increment() == 0x2000) @@ -18,8 +20,10 @@ struct HeapTests { @Test("Allocating small object uses node") func allocateSmallObjects() { - let heapPointer = util.Heap.make_shared(nil, 0, 0) - let heap = heapPointer.pointee + let heap = util.Heap.create(nil, 0, 0) + defer { + util.Heap.destroy(heap) + } let _ = heap.__alloc_uint64_tUnsafe() @@ -36,8 +40,10 @@ struct HeapTests { @Test("Allocating large object creates new node") func allocateLargeObject() { - let heapPointer = util.Heap.make_shared(nil, 0, 0) - let heap = heapPointer.pointee + let heap = util.Heap.create(nil, 0, 0) + defer { + util.Heap.destroy(heap) + } // larger than minimum increment let _ = heap.__alloc_uint64_tUnsafe(500) diff --git a/Tests/UtilitiesTests/ListTests.swift b/Tests/UtilitiesTests/ListTests.swift index bfc4a5e..01bd4ec 100644 --- a/Tests/UtilitiesTests/ListTests.swift +++ b/Tests/UtilitiesTests/ListTests.swift @@ -6,16 +6,20 @@ struct ListTests { @Test("Initialize empty list") func initEmpty() { - let listPointer = util.UInt64ForwardList.make_shared() - let list = listPointer.pointee + let list = util.UInt64ForwardList.create() + defer { + util.UInt64ForwardList.destroy(list) + } #expect(list.empty()) } @Test("Push element") func pushElement() { - let listPointer = util.UInt64ForwardList.make_shared() - let list = listPointer.pointee + let list = util.UInt64ForwardList.create() + defer { + util.UInt64ForwardList.destroy(list) + } list.push_front(1) @@ -27,8 +31,10 @@ struct ListTests { @Test("Push multiple elements") func pushMultipleElements() { - let listPointer = util.UInt64ForwardList.make_shared() - let list = listPointer.pointee + let list = util.UInt64ForwardList.create() + defer { + util.UInt64ForwardList.destroy(list) + } list.push_front(1) list.push_front(2) @@ -40,8 +46,10 @@ struct ListTests { @Test("Remove element") func removeElement() { - let listPointer = util.UInt64ForwardList.make_shared() - let list = listPointer.pointee + let list = util.UInt64ForwardList.create() + defer { + util.UInt64ForwardList.destroy(list) + } list.push_front(1) list.push_front(2) From 453c44c4710e8bea2e0191601174c8877fc7cd47 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 4 May 2025 14:03:05 +0200 Subject: [PATCH 70/74] Fix passing Graph and Subgraph to closure --- Sources/Compute/Graph/Graph.swift | 16 ++++------------ Sources/Compute/Graph/Subgraph.swift | 8 ++------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index cfe239b..e630c48 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -32,15 +32,11 @@ extension Graph { private static func setUpdateCallback(_ graph: UnsafeRawPointer, callback: (() -> Void)?) public static func setUpdateCallback(_ graph: Graph, callback: (() -> Void)?) { - withUnsafePointer(to: self) { selfPointer in - Graph.setUpdateCallback(selfPointer, callback: callback) - } + Graph.setUpdateCallback(unsafeBitCast(graph, to: UnsafeRawPointer.self), callback: callback) } public func onUpdate(_ handler: @escaping () -> Void) { - withUnsafePointer(to: self) { graphPointer in - Graph.setUpdateCallback(graphPointer, callback: handler) - } + Graph.setUpdateCallback(self, callback: handler) } } @@ -51,15 +47,11 @@ extension Graph { private static func setInvalidationCallback(_ graph: UnsafeRawPointer, callback: ((AnyAttribute) -> Void)?) public static func setInvalidationCallback(_ graph: Graph, callback: ((AnyAttribute) -> Void)?) { - withUnsafePointer(to: self) { selfPointer in - Graph.setInvalidationCallback(selfPointer, callback: callback) - } + Graph.setInvalidationCallback(unsafeBitCast(graph, to: UnsafeRawPointer.self), callback: callback) } public func onInvalidation(_ handler: @escaping (AnyAttribute) -> Void) { - withUnsafePointer(to: self) { graphPointer in - Graph.setInvalidationCallback(graphPointer, callback: handler) - } + Graph.setInvalidationCallback(self, callback: handler) } public func withDeadline(_ deadline: UInt64, _ body: () -> T) -> T { diff --git a/Sources/Compute/Graph/Subgraph.swift b/Sources/Compute/Graph/Subgraph.swift index eddbe77..4f8ec11 100644 --- a/Sources/Compute/Graph/Subgraph.swift +++ b/Sources/Compute/Graph/Subgraph.swift @@ -6,9 +6,7 @@ extension Subgraph { private static func addObserver(_ subgraph: UnsafeRawPointer, observer: () -> Void) -> Int func addObserver(_ observer: () -> Void) -> Int { - return withUnsafePointer(to: self) { selfPointer in - return Subgraph.addObserver(selfPointer, observer: observer) - } + return Subgraph.addObserver(unsafeBitCast(self, to: UnsafeRawPointer.self), observer: observer) } public func apply(_ body: () -> T) -> T { @@ -26,9 +24,7 @@ extension Subgraph { private static func apply(_ subgraph: UnsafeRawPointer, flags: AGAttributeFlags, body: (AnyAttribute) -> Void) public func forEach(_ flags: AGAttributeFlags, _ body: (AnyAttribute) -> Void) { - withUnsafePointer(to: self) { selfPointer in - Subgraph.apply(selfPointer, flags: flags, body: body) - } + Subgraph.apply(unsafeBitCast(self, to: UnsafeRawPointer.self), flags: flags, body: body) } } From 4ef880d1a3d3d39e9c6a04e5eae7530d6e69dc97 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Sun, 4 May 2025 14:03:22 +0200 Subject: [PATCH 71/74] Ensure unique indices are not zero --- Sources/ComputeCxx/UniqueID/AGUniqueID.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/UniqueID/AGUniqueID.cpp b/Sources/ComputeCxx/UniqueID/AGUniqueID.cpp index d1b97dc..6b1747a 100644 --- a/Sources/ComputeCxx/UniqueID/AGUniqueID.cpp +++ b/Sources/ComputeCxx/UniqueID/AGUniqueID.cpp @@ -2,5 +2,5 @@ uint64_t AGMakeUniqueID() { static uint64_t counter = 0; - return counter++; + return ++counter; } From 314716fc1ae15f0875f60b390131b3849384d548 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 14 May 2025 23:53:31 +0200 Subject: [PATCH 72/74] Fix build warnings --- Sources/Compute/Runtime/Tuple.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Compute/Runtime/Tuple.swift b/Sources/Compute/Runtime/Tuple.swift index 5e44d7c..a5e207c 100644 --- a/Sources/Compute/Runtime/Tuple.swift +++ b/Sources/Compute/Runtime/Tuple.swift @@ -91,23 +91,23 @@ extension UnsafeTuple { } -@_silgen_name("swift_slowAlloc") -private func slowAlloc(_ size: Int, _ alignMask: Int) -> UnsafeMutableRawPointer - -@_silgen_name("swift_slowDealloc") -private func slowDealloc(_ ptr: UnsafeMutableRawPointer, _ size: Int, _ alignMask: Int) - extension UnsafeMutableTuple { public init(with tupleType: TupleType) { - self.init(type: tupleType, value: slowAlloc(tupleType.size, -1)) + self.init( + type: tupleType, + value: UnsafeMutableRawPointer.allocate( + byteCount: tupleType.size, + alignment: -1 + ) + ) } public func deallocate(initialized: Bool) { if initialized { deinitialize() } - slowDealloc(value, -1, -1) + value.deallocate() } public func initialize(at index: Int, to element: T) { From b8061a2e0ad5d558a242bedabe432936da0c9394 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 14 May 2025 23:59:47 +0200 Subject: [PATCH 73/74] Make AG::vector non copyable and other code hygiene --- Package.resolved | 2 +- .../ComputeCxx/Attribute/AttributeIDList.h | 12 +- Sources/ComputeCxx/Containers/Vector.h | 542 +++++++++++++++++- Sources/ComputeCxx/Containers/Vector.tpp | 451 --------------- Sources/ComputeCxx/Data/Table.h | 6 +- Sources/ComputeCxx/Graph/Graph+Description.mm | 20 +- Sources/ComputeCxx/Graph/Graph.cpp | 22 +- Sources/ComputeCxx/Graph/Graph.h | 4 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 2 +- Sources/ComputeCxx/Graph/TraceRecorder.h | 91 +-- Sources/ComputeCxx/Graph/UpdateStack.cpp | 2 +- .../ComputeCxx/Layout/LayoutDescriptor.cpp | 22 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 16 +- Sources/ComputeCxx/Subgraph/Subgraph.h | 2 +- Sources/ComputeCxx/Swift/AGTuple.cpp | 2 +- Sources/ComputeCxx/Swift/AGType.cpp | 10 +- Sources/ComputeCxx/Swift/MetadataVisitor.cpp | 18 +- Sources/ComputeCxx/Swift/MetadataVisitor.h | 17 +- .../Shared/Graph/GraphTests.swift | 2 - 19 files changed, 659 insertions(+), 584 deletions(-) delete mode 100644 Sources/ComputeCxx/Containers/Vector.tpp diff --git a/Package.resolved b/Package.resolved index 1fc7070..7d861c3 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "8210536fc04b6250c5a09ccd177be5a174e3601edb343f5709f8ce94514f2a42", + "originHash" : "d5a74a2bc3e7341b5a26dd2a67cc7e3657b4adc4fec92d36cc0ae8cd14c0fa09", "pins" : [ { "identity" : "swift-algorithms", diff --git a/Sources/ComputeCxx/Attribute/AttributeIDList.h b/Sources/ComputeCxx/Attribute/AttributeIDList.h index 54457a6..098a5bc 100644 --- a/Sources/ComputeCxx/Attribute/AttributeIDList.h +++ b/Sources/ComputeCxx/Attribute/AttributeIDList.h @@ -55,8 +55,10 @@ class AttributeIDList1 : public AttributeIDList { public: AttributeIDList1(data::ptr page) : _page(page) {} - AttributeIDIterator begin() { return AttributeIDIterator(_page, RelativeAttributeID(_page->first_child_1)); } - AttributeIDIterator end() { return AttributeIDIterator(nullptr, nullptr); } + virtual AttributeIDIterator begin() override { + return AttributeIDIterator(_page, RelativeAttributeID(_page->first_child_1)); + } + virtual AttributeIDIterator end() override { return AttributeIDIterator(nullptr, nullptr); } }; class AttributeIDList2 : public AttributeIDList { @@ -66,8 +68,10 @@ class AttributeIDList2 : public AttributeIDList { public: AttributeIDList2(data::ptr page) : _page(page) {} - AttributeIDIterator begin() { return AttributeIDIterator(_page, RelativeAttributeID(_page->first_child_2)); } - AttributeIDIterator end() { return AttributeIDIterator(nullptr, nullptr); } + virtual AttributeIDIterator begin() override { + return AttributeIDIterator(_page, RelativeAttributeID(_page->first_child_2)); + } + virtual AttributeIDIterator end() override { return AttributeIDIterator(nullptr, nullptr); } }; } // namespace AG diff --git a/Sources/ComputeCxx/Containers/Vector.h b/Sources/ComputeCxx/Containers/Vector.h index 90cbb36..de41e2c 100644 --- a/Sources/ComputeCxx/Containers/Vector.h +++ b/Sources/ComputeCxx/Containers/Vector.h @@ -1,15 +1,21 @@ #pragma once #include +#include +#include #include #include +#include #include +#include + +#include "Errors/Errors.h" CF_ASSUME_NONNULL_BEGIN namespace AG { -template +template requires std::unsigned_integral<_size_type> class vector { public: @@ -23,16 +29,27 @@ class vector { using size_type = _size_type; private: - T _stack_buffer[_stack_size]; + T _inline_buffer[_inline_capacity]; T *_Nullable _buffer = nullptr; size_type _size = 0; - size_type _capacity = _stack_size; + size_type _capacity = _inline_capacity; void reserve_slow(size_type new_cap); public: + vector() {}; ~vector(); + // Non-copyable + + vector(const vector &) = delete; + vector &operator=(const vector &) = delete; + + // Move + + vector(vector &&); + vector &operator=(vector &&); + // Element access reference operator[](size_type pos) { return data()[pos]; }; @@ -43,8 +60,8 @@ class vector { reference back() { return *&data()[_size - 1]; }; const_reference back() const { return *&data()[_size - 1]; }; - T *_Nonnull data() { return _buffer != nullptr ? _buffer : _stack_buffer; }; - const T *_Nonnull data() const { return _buffer != nullptr ? _buffer : _stack_buffer; }; + T *_Nonnull data() { return _buffer != nullptr ? _buffer : _inline_buffer; }; + const T *_Nonnull data() const { return _buffer != nullptr ? _buffer : _inline_buffer; }; // Iterators @@ -91,6 +108,196 @@ class vector { static_assert(std::contiguous_iterator::iterator>); static_assert(std::contiguous_iterator::const_iterator>); +namespace details { + +template + requires std::unsigned_integral +void *_Nullable realloc_vector(void *buffer, void *_inline_buffer, size_type stack_size, size_type *_Nonnull size, + size_type preferred_new_size) { + // copy data from heap buffer buffer into stack buffer if possible + if (preferred_new_size <= stack_size) { + if (buffer) { + memcpy(_inline_buffer, buffer, preferred_new_size * element_size_bytes); + free(buffer); + *size = stack_size; + } + return nullptr; + } + + size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size_bytes); + size_type new_size = (size_type)(new_size_bytes / element_size_bytes); + if (new_size == *size) { + // nothing to do + return buffer; + } + + void *new_buffer = realloc(buffer, new_size_bytes); + if (!new_buffer) { + precondition_failure("allocation failure"); + } + + // copy data from inline buffer into heap buffer + if (!buffer) { + memcpy(new_buffer, _inline_buffer, (*size) * element_size_bytes); + } + + *size = new_size; + return new_buffer; +} + +} // namespace details + +template + requires std::unsigned_integral +vector::~vector() { + for (auto i = 0; i < _size; i++) { + data()[i].~T(); + } + if (_buffer) { + free((void *)_buffer); + } +} + +template + requires std::unsigned_integral +void vector::reserve_slow(size_type new_cap) { + size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); + _buffer = reinterpret_cast(details::realloc_vector( + (void *)_buffer, (void *)_inline_buffer, _inline_capacity, &_capacity, effective_new_cap)); +} + +template + requires std::unsigned_integral +void vector::reserve(size_type new_cap) { + if (new_cap <= capacity()) { + return; + } + reserve_slow(new_cap); +} + +template + requires std::unsigned_integral +void vector::shrink_to_fit() { + if (capacity() > _size) { + _buffer = reinterpret_cast( + details::realloc_vector(_buffer, _inline_buffer, _inline_capacity, &_capacity, _size)); + } +} + +template + requires std::unsigned_integral +void vector::clear() { + for (auto i = 0; i < _size; i++) { + data()[i].~T(); + } + _size = 0; +} + +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, + const T &value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (mutable_pos) value_type(value); + _size += 1; + return end(); +} + +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, T &&value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (pos) value_type(std::move(value)); + _size += 1; + return end(); +} + +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator first, iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + for (auto iter = first; iter != last; iter++) { + iter->~T(); + } + for (auto iter = last, old_end = end(); iter != old_end; iter++) { + std::swap(*(iter - count), *iter); + } + _size -= count; + return end(); +} + +template + requires std::unsigned_integral +void vector::push_back(const T &value) { + reserve(_size + 1); + new (&data()[_size]) value_type(value); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::push_back(T &&value) { + reserve(_size + 1); + new (&data()[_size]) value_type(std::move(value)); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::pop_back() { + assert(size() > 0); + data()[_size - 1].~T(); + _size -= 1; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (&data()[i]) value_type(); + } + } + _size = count; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count, const value_type &value) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (&data()[i]) value_type(value); + } + } + _size = count; +} + // MARK: Specialization for empty stack buffer template @@ -114,8 +321,19 @@ class vector { void reserve_slow(size_type new_cap); public: + vector() {}; ~vector(); + // Non-copyable + + vector(const vector &) = delete; + vector &operator=(const vector &) = delete; + + // Move + + vector(vector &&other) noexcept; + vector &operator=(vector &&other) noexcept; + // Element access reference operator[](size_type pos) { return data()[pos]; }; @@ -171,6 +389,211 @@ class vector { void resize(size_type count, const value_type &value); }; +namespace details { + +template + requires std::unsigned_integral +void *_Nullable realloc_vector(void *buffer, size_type *_Nonnull size, size_type preferred_new_size) { + if (preferred_new_size == 0) { + *size = 0; + free(buffer); + return nullptr; + } + + size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size); + size_type new_size = (size_type)(new_size_bytes / element_size); + if (new_size == *size) { + // nothing to do + return buffer; + } + + void *new_buffer = realloc(buffer, new_size_bytes); + if (!new_buffer) { + precondition_failure("allocation failure"); + } + *size = new_size; + return new_buffer; +} + +} // namespace details + +template + requires std::unsigned_integral +vector::~vector() { + for (auto i = 0; i < _size; i++) { + _buffer[i].~T(); + } + if (_buffer) { + free(_buffer); + } +} + +template + requires std::unsigned_integral +vector::vector(vector &&other) noexcept + : _buffer(std::exchange(other._buffer, nullptr)), _size(other._size), _capacity(other._capacity) { + other._size = 0; + other._capacity = 0; +} + +template + requires std::unsigned_integral +vector &vector::operator=(vector &&other) noexcept { + if (this != &other) { + for (auto i = 0; i < _size; i++) { + _buffer[i].~T(); + } + if (_buffer) { + free(_buffer); + } + _buffer = other._buffer; + _size = other._size; + _capacity = other._capacity; + other._buffer = nullptr; + other._size = 0; + other._capacity = 0; + } + return *this; +} + +template + requires std::unsigned_integral +void vector::reserve_slow(size_type new_cap) { + size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); + _buffer = + reinterpret_cast(details::realloc_vector(_buffer, &_capacity, effective_new_cap)); +} + +template + requires std::unsigned_integral +void vector::reserve(size_type new_cap) { + if (new_cap <= capacity()) { + return; + } + reserve_slow(new_cap); +} + +template + requires std::unsigned_integral +void vector::shrink_to_fit() { + if (capacity() > size()) { + _buffer = reinterpret_cast(details::realloc_vector(_buffer, &_capacity, 0)); + } +} + +template + requires std::unsigned_integral +void vector::clear() { + for (auto i = 0; i < _size; i++) { + data()[i].~T(); + } + _size = 0; +} + +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, const T &value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (mutable_pos) value_type(value); + _size += 1; + return end(); +} + +template + requires std::unsigned_integral +vector::iterator vector::insert(const_iterator pos, T &&value) { + reserve(_size + 1); + iterator mutable_pos = begin() + (pos - begin()); + std::move_backward(mutable_pos, end(), end() + 1); + new (mutable_pos) value_type(std::move(value)); + _size += 1; + return end(); +} + +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +vector::iterator vector::erase(iterator first, iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + for (auto iter = first; iter != last; iter++) { + iter->~T(); + } + for (auto iter = last, old_end = end(); iter != old_end; iter++) { + std::swap(*(iter - count), *iter); + } + _size -= count; + return end(); +} + +template + requires std::unsigned_integral +void vector::push_back(const T &value) { + reserve(_size + 1); + new (&_buffer[_size]) value_type(value); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::push_back(T &&value) { + reserve(_size + 1); + new (&_buffer[_size]) value_type(std::move(value)); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::pop_back() { + assert(size() > 0); + data()[_size - 1].~T(); + _size -= 1; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (&data()[i]) value_type(); + } + } + _size = count; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count, const value_type &value) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (&data()[i]) value_type(value); + } + } + _size = count; +} + // MARK: Specialization for unique_ptr template @@ -194,8 +617,19 @@ class vector, 0, _size_type> { void reserve_slow(size_type new_cap); public: + vector() {}; ~vector(); + // Non-copyable + + vector(const vector &) = delete; + vector &operator=(const vector &) = delete; + + // Move + + vector(vector &&) noexcept; + vector &operator=(vector &&) noexcept; + // Element access reference operator[](size_type pos) { return data()[pos]; }; @@ -239,7 +673,7 @@ class vector, 0, _size_type> { iterator insert(const_iterator pos, const std::unique_ptr &value); iterator insert(const_iterator pos, std::unique_ptr &&value); - + iterator erase(iterator pos); iterator erase(iterator first, iterator last); @@ -251,8 +685,100 @@ class vector, 0, _size_type> { void resize(size_type count, const value_type &value); }; +template + requires std::unsigned_integral +vector, 0, size_type>::~vector() { + for (auto i = 0; i < _size; i++) { + _buffer[i].reset(); + } + if (_buffer) { + free(_buffer); + } +} + +template + requires std::unsigned_integral +vector, 0, size_type>::vector(vector &&other) noexcept + : _buffer(std::exchange(other._buffer, nullptr)), _size(other._size), _capacity(other._capacity) { + other._size = 0; + other._capacity = 0; +} + +template + requires std::unsigned_integral +vector, 0, size_type> & +vector, 0, size_type>::operator=(vector &&other) noexcept { + if (this != &other) { + for (auto i = 0; i < _size; i++) { + _buffer[i].~T(); + } + if (_buffer) { + free(_buffer); + } + _buffer = other._buffer; + _size = other._size; + _capacity = other._capacity; + other._buffer = nullptr; + other._size = 0; + other._capacity = 0; + } + return *this; +} + +template + requires std::unsigned_integral +void vector, 0, size_type>::reserve_slow(size_type new_cap) { + size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); + _buffer = reinterpret_cast *>( + details::realloc_vector)>(_buffer, &_capacity, + effective_new_cap)); +} + +template + requires std::unsigned_integral +void vector, 0, size_type>::reserve(size_type new_cap) { + if (new_cap <= capacity()) { + return; + } + reserve_slow(new_cap); +} + +template + requires std::unsigned_integral +vector, 0, size_type>::iterator +vector, 0, size_type>::erase(iterator pos) { + if (pos == end()) { + return end(); + } + return erase(pos, pos + 1); +} + +template + requires std::unsigned_integral +vector, 0, size_type>::iterator +vector, 0, size_type>::erase(iterator first, iterator last) { + auto count = last - first; + if (count == 0) { + return last; + } + for (auto iter = first; iter != last; iter++) { + iter->reset(); + } + for (auto iter = last, old_end = end(); iter != old_end; iter++) { + std::swap(*(iter - count), *iter); + } + _size -= count; + return end(); +} + +template + requires std::unsigned_integral +void vector, 0, size_type>::push_back(std::unique_ptr &&value) { + reserve(_size + 1); + new (&_buffer[_size]) value_type(std::move(value)); + _size += 1; +} + } // namespace AG CF_ASSUME_NONNULL_END - -#include "Vector.tpp" diff --git a/Sources/ComputeCxx/Containers/Vector.tpp b/Sources/ComputeCxx/Containers/Vector.tpp deleted file mode 100644 index a98ad4d..0000000 --- a/Sources/ComputeCxx/Containers/Vector.tpp +++ /dev/null @@ -1,451 +0,0 @@ -#include "Vector.h" - -#include -#include -#include -#include -#include - -#include "Errors/Errors.h" - -namespace AG { - -#pragma mark - Base implementation - -namespace details { - -template - requires std::unsigned_integral -void *realloc_vector(void *buffer, void *stack_buffer, size_type stack_size, size_type *size, - size_type preferred_new_size) { - // copy data from heap buffer buffer into stack buffer if possible - if (preferred_new_size <= stack_size) { - if (buffer) { - memcpy(stack_buffer, buffer, preferred_new_size * element_size_bytes); - free(buffer); - *size = stack_size; - } - return nullptr; - } - - size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size_bytes); - size_type new_size = (size_type)(new_size_bytes / element_size_bytes); - if (new_size == *size) { - // nothing to do - return buffer; - } - - void *new_buffer = realloc(buffer, new_size_bytes); - if (!new_buffer) { - precondition_failure("allocation failure"); - } - - // copy data from stack buffer into heap buffer - if (!buffer) { - memcpy(new_buffer, stack_buffer, (*size) * element_size_bytes); - } - - *size = new_size; - return new_buffer; -} - -} // namespace details - -template - requires std::unsigned_integral -vector::~vector() { - for (auto i = 0; i < _size; i++) { - data()[i].~T(); - } - if (_buffer) { - free((void *)_buffer); - } -} - -template - requires std::unsigned_integral -void vector::reserve_slow(size_type new_cap) { - size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); - _buffer = reinterpret_cast(details::realloc_vector( - (void *)_buffer, (void *)_stack_buffer, _stack_size, &_capacity, effective_new_cap)); -} - -template - requires std::unsigned_integral -void vector::reserve(size_type new_cap) { - if (new_cap <= capacity()) { - return; - } - reserve_slow(new_cap); -} - -template - requires std::unsigned_integral -void vector::shrink_to_fit() { - if (capacity() > _size) { - _buffer = reinterpret_cast( - details::realloc_vector(_buffer, _stack_buffer, _stack_size, &_capacity, _size)); - } -} - -template - requires std::unsigned_integral -void vector::clear() { - for (auto i = 0; i < _size; i++) { - data()[i].~T(); - } - _size = 0; -} - -template - requires std::unsigned_integral -vector::iterator vector::insert(const_iterator pos, - const T &value) { - reserve(_size + 1); - iterator mutable_pos = begin() + (pos - begin()); - std::move_backward(mutable_pos, end(), end() + 1); - new (mutable_pos) value_type(value); - _size += 1; - return end(); -} - -template - requires std::unsigned_integral -vector::iterator vector::insert(const_iterator pos, T &&value) { - reserve(_size + 1); - iterator mutable_pos = begin() + (pos - begin()); - std::move_backward(mutable_pos, end(), end() + 1); - new (pos) value_type(std::move(value)); - _size += 1; - return end(); -} - -template - requires std::unsigned_integral -vector::iterator vector::erase(iterator pos) { - if (pos == end()) { - return end(); - } - return erase(pos, pos + 1); -} - -template - requires std::unsigned_integral -vector::iterator vector::erase(iterator first, iterator last) { - auto count = last - first; - if (count == 0) { - return last; - } - for (auto iter = first; iter != last; iter++) { - iter->~T(); - } - for (auto iter = last, old_end = end(); iter != old_end; iter++) { - std::swap(*(iter - count), *iter); - } - _size -= count; - return end(); -} - -template - requires std::unsigned_integral -void vector::push_back(const T &value) { - reserve(_size + 1); - new (&data()[_size]) value_type(value); - _size += 1; -} - -template - requires std::unsigned_integral -void vector::push_back(T &&value) { - reserve(_size + 1); - new (&data()[_size]) value_type(std::move(value)); - _size += 1; -} - -template - requires std::unsigned_integral -void vector::pop_back() { - assert(size() > 0); - data()[_size - 1].~T(); - _size -= 1; -} - -template - requires std::unsigned_integral -void vector::resize(size_type count) { - reserve(count); - if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } - } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (this[i]) value_type(); - } - } - _size = count; -} - -template - requires std::unsigned_integral -void vector::resize(size_type count, const value_type &value) { - reserve(count); - if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } - } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (this[i]) value_type(value); - } - } - _size = count; -} - -#pragma mark - Specialization for empty stack buffer - -namespace details { - -template - requires std::unsigned_integral -void *realloc_vector(void *buffer, size_type *size, size_type preferred_new_size) { - if (preferred_new_size == 0) { - *size = 0; - free(buffer); - return nullptr; - } - - size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size); - size_type new_size = (size_type)(new_size_bytes / element_size); - if (new_size == *size) { - // nothing to do - return buffer; - } - - void *new_buffer = realloc(buffer, new_size_bytes); - if (!new_buffer) { - precondition_failure("allocation failure"); - } - *size = new_size; - return new_buffer; -} - -} // namespace details - -template - requires std::unsigned_integral -vector::~vector() { - for (auto i = 0; i < _size; i++) { - _buffer[i].~T(); - } - if (_buffer) { - free(_buffer); - } -} - -template - requires std::unsigned_integral -void vector::reserve_slow(size_type new_cap) { - size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); - _buffer = - reinterpret_cast(details::realloc_vector(_buffer, &_capacity, effective_new_cap)); -} - -template - requires std::unsigned_integral -void vector::reserve(size_type new_cap) { - if (new_cap <= capacity()) { - return; - } - reserve_slow(new_cap); -} - -template - requires std::unsigned_integral -void vector::shrink_to_fit() { - if (capacity() > size()) { - _buffer = reinterpret_cast(details::realloc_vector(_buffer, &_capacity, 0)); - } -} - -template - requires std::unsigned_integral -void vector::clear() { - for (auto i = 0; i < _size; i++) { - data()[i].~T(); - } - _size = 0; -} - -template - requires std::unsigned_integral -vector::iterator vector::insert(const_iterator pos, const T &value) { - reserve(_size + 1); - iterator mutable_pos = begin() + (pos - begin()); - std::move_backward(mutable_pos, end(), end() + 1); - new (mutable_pos) value_type(value); - _size += 1; - return end(); -} - -template - requires std::unsigned_integral -vector::iterator vector::insert(const_iterator pos, T &&value) { - reserve(_size + 1); - iterator mutable_pos = begin() + (pos - begin()); - std::move_backward(mutable_pos, end(), end() + 1); - new (mutable_pos) value_type(std::move(value)); - _size += 1; - return end(); -} - -template - requires std::unsigned_integral -vector::iterator vector::erase(iterator pos) { - if (pos == end()) { - return end(); - } - return erase(pos, pos + 1); -} - -template - requires std::unsigned_integral -vector::iterator vector::erase(iterator first, iterator last) { - auto count = last - first; - if (count == 0) { - return last; - } - for (auto iter = first; iter != last; iter++) { - iter->~T(); - } - for (auto iter = last, old_end = end(); iter != old_end; iter++) { - std::swap(*(iter - count), *iter); - } - _size -= count; - return end(); -} - -template - requires std::unsigned_integral -void vector::push_back(const T &value) { - reserve(_size + 1); - new (&_buffer[_size]) value_type(value); - _size += 1; -} - -template - requires std::unsigned_integral -void vector::push_back(T &&value) { - reserve(_size + 1); - new (&_buffer[_size]) value_type(std::move(value)); - _size += 1; -} - -template - requires std::unsigned_integral -void vector::pop_back() { - assert(size() > 0); - data()[_size - 1].~T(); - _size -= 1; -} - -template - requires std::unsigned_integral -void vector::resize(size_type count) { - reserve(count); - if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } - } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (&this[i]) value_type(); - } - } - _size = count; -} - -template - requires std::unsigned_integral -void vector::resize(size_type count, const value_type &value) { - reserve(count); - if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } - } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (&this[i]) value_type(value); - } - } - _size = count; -} - -#pragma mark - Specialization for unique_ptr - -template - requires std::unsigned_integral -vector, 0, size_type>::~vector() { - for (auto i = 0; i < _size; i++) { - _buffer[i].reset(); - } - if (_buffer) { - free(_buffer); - } -} - -template - requires std::unsigned_integral -void vector, 0, size_type>::reserve_slow(size_type new_cap) { - size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); - _buffer = reinterpret_cast *>( - details::realloc_vector)>(_buffer, &_capacity, - effective_new_cap)); -} - -template - requires std::unsigned_integral -void vector, 0, size_type>::reserve(size_type new_cap) { - if (new_cap <= capacity()) { - return; - } - reserve_slow(new_cap); -} - -template - requires std::unsigned_integral -vector, 0, size_type>::iterator -vector, 0, size_type>::erase(iterator pos) { - if (pos == end()) { - return end(); - } - return erase(pos, pos + 1); -} - -template - requires std::unsigned_integral -vector, 0, size_type>::iterator -vector, 0, size_type>::erase(iterator first, iterator last) { - auto count = last - first; - if (count == 0) { - return last; - } - for (auto iter = first; iter != last; iter++) { - iter->reset(); - } - for (auto iter = last, old_end = end(); iter != old_end; iter++) { - std::swap(*(iter - count), *iter); - } - _size -= count; - return end(); -} - -template - requires std::unsigned_integral -void vector, 0, size_type>::push_back(std::unique_ptr &&value) { - reserve(_size + 1); - new (&_buffer[_size]) value_type(std::move(value)); - _size += 1; -} - -} // namespace AG diff --git a/Sources/ComputeCxx/Data/Table.h b/Sources/ComputeCxx/Data/Table.h index 6b55d19..e2e3653 100644 --- a/Sources/ComputeCxx/Data/Table.h +++ b/Sources/ComputeCxx/Data/Table.h @@ -43,12 +43,12 @@ class table { uint32_t _num_zones = 0; using remapped_region = std::pair; - vector _remapped_regions = {}; + vector _remapped_regions; constexpr static unsigned int pages_per_map = 64; using page_map_type = std::bitset; - vector _page_maps = {}; - vector _page_metadata_maps = {}; + vector _page_maps; + vector _page_metadata_maps; public: static table &ensure_shared(); diff --git a/Sources/ComputeCxx/Graph/Graph+Description.mm b/Sources/ComputeCxx/Graph/Graph+Description.mm index 82b64ff..2a34510 100644 --- a/Sources/ComputeCxx/Graph/Graph+Description.mm +++ b/Sources/ComputeCxx/Graph/Graph+Description.mm @@ -166,7 +166,7 @@ int trap_cycles() { if ([format isEqualToString:@"graph/dict"]) { return (__bridge id)description_graph(graph, options); } - if ([format isEqualToString:@"graph/dot"]) { + if (graph && [format isEqualToString:@"graph/dot"]) { return (__bridge id)graph->description_graph_dot(options); } if ([format hasPrefix:@"stack/"]) { @@ -382,8 +382,8 @@ int trap_cycles() { type_indices_by_id.try_emplace(type_id, index); } } else { - for (auto entry : graph->_profile_data->categories()) { - auto category = entry.second; + for (auto &entry : graph->_profile_data->categories()) { + auto &category = entry.second; auto removed_item = category.removed_items_by_type_id().find(type_id); if (removed_item != category.removed_items_by_type_id().end()) { if (!type_indices_by_id.contains(type_id)) { @@ -630,7 +630,7 @@ int trap_cycles() { auto tree_data_element = map->find(subgraph); if (tree_data_element != map->end()) { - auto data_nodes = tree_data_element->second.nodes(); + auto &data_nodes = tree_data_element->second.nodes(); std::pair, data::ptr> *found = std::find_if( data_nodes.begin(), data_nodes.end(), [&tree](auto node) { return node.first == tree; }); @@ -692,7 +692,7 @@ int trap_cycles() { } if (!profile_data->categories().empty()) { NSMutableDictionary *events = [NSMutableDictionary dictionary]; - for (auto category : profile_data->categories()) { + for (auto &category : profile_data->categories()) { NSDictionary *json = (__bridge NSDictionary *)profile_data->json_data(category.second, *graph); if (json) { events[[NSString stringWithUTF8String:graph->key_name(category.first)]] = json; @@ -776,10 +776,10 @@ int trap_cycles() { double duration_fraction = 0.0; if (auto profile_data = _profile_data.get()) { - auto items_by_attribute = profile_data->all_events().items_by_attribute(); + auto &items_by_attribute = profile_data->all_events().items_by_attribute(); auto found_item = items_by_attribute.find(attribute.to_ptr()); if (found_item != items_by_attribute.end()) { - auto item = found_item->second; + auto &item = found_item->second; if (item.data().update_count) { uint64_t update_count = item.data().update_count; uint64_t update_total = item.data().update_total; @@ -911,7 +911,7 @@ int trap_cycles() { int frame_count = 0; for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { - auto frames = update.get()->frames(); + auto &frames = update.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { const AttributeType &type = attribute_type(frame->attribute->type_id()); @@ -963,7 +963,7 @@ int trap_cycles() { int frame_count = 0; for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { - auto frames = update.get()->frames(); + auto &frames = update.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { [nodes addObject:[NSNumber numberWithUnsignedInt:frame->attribute.offset()]]; @@ -989,7 +989,7 @@ int trap_cycles() { for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { int i = 0; - auto frames = update.get()->frames(); + auto &frames = update.get()->frames(); for (auto frame : frames) { if (i == frame_index || frame.attribute == frame_node) { diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 2f53e01..c05fafd 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -122,7 +122,7 @@ Graph::Graph() } } - return {profiler_flags, trace_flags, trace_subsystems}; + return {profiler_flags, trace_flags, std::move(trace_subsystems)}; }(); if (trace_flags && !trace_subsystems.empty()) { @@ -318,7 +318,7 @@ void Graph::reset_update(data::ptr node) { void Graph::collect_stack(vector, 0, uint64_t> &nodes) { for (auto update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { - auto frames = update_stack.get()->frames(); + auto &frames = update_stack.get()->frames(); for (auto iter = frames.rbegin(), end = frames.rend(); iter != end; ++iter) { nodes.push_back(iter->attribute); } @@ -331,13 +331,13 @@ void Graph::with_update(data::ptr node, ClosureFunctionVV body) UpdateStack _base; public: - scoped_update(UpdateStack base, data::ptr node) : _base(base) { + scoped_update(Graph *graph, uint8_t options, data::ptr node) : _base(graph, options) { _base.frames().push_back({node, node->state().is_pending()}); }; ~scoped_update() { _base.frames().pop_back(); } }; - scoped_update update = scoped_update(UpdateStack(this, 0), node); + scoped_update update = scoped_update(this, 0, node); body(); // ~scoped_update called } @@ -1070,9 +1070,9 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void if (type.layout() == nullptr) { type.set_layout(LayoutDescriptor::fetch(value_type, comparison_options, 0)); } - ValueLayout layout = type.layout() == ValueLayoutEmpty ? nullptr : type.layout(); // TODO: make void * and rename to dest and source + ValueLayout layout = type.layout() == ValueLayoutPOD ? nullptr : type.layout(); if (LayoutDescriptor::compare(layout, (const unsigned char *)value_dest, (const unsigned char *)value_source, value_type.vw_size(), comparison_options)) { return false; @@ -1276,7 +1276,7 @@ void Graph::propagate_dirty(AttributeID attribute) { for (auto update = current_update(); update != nullptr; update = update.get()->previous()) { bool stop = false; - auto frames = update.get()->frames(); + auto &frames = update.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { if (frame->attribute->state().is_main_thread()) { stop = true; @@ -1644,11 +1644,11 @@ bool Graph::compare_edge_values(InputEdge input_edge, const AttributeType *type, LayoutDescriptor::ComparisonOptions(type->comparison_mode()) | LayoutDescriptor::ComparisonOptions::CopyOnWrite; auto layout = type->layout(); - if (layout == 0) { + if (layout == nullptr) { layout = LayoutDescriptor::fetch(type->value_metadata(), options, 0); } - if (layout == ValueLayoutEmpty) { - layout = 0; + if (layout == ValueLayoutPOD) { + layout = nullptr; } return LayoutDescriptor::compare_partial(layout, (unsigned char *)destination_value + resolved_offset, @@ -2135,7 +2135,7 @@ void Graph::encode_tree(Encoder &encoder, data::ptr tree) { if (tree_data_element != map->end()) { tree_data_element->second.sort_nodes(); - auto nodes = tree_data_element->second.nodes(); + auto &nodes = tree_data_element->second.nodes(); std::pair, data::ptr> *found = std::find_if(nodes.begin(), nodes.end(), [&tree](auto node) { return node.first == tree; }); @@ -2367,7 +2367,7 @@ void Graph::end_profile_event(data::ptr node, const char *event_name, uint duration = end_time - start_time - _profile_data->precision(); } - auto category = _profile_data.get()->categories().try_emplace(event_id).first->second; + auto &category = _profile_data.get()->categories().try_emplace(event_id).first->second; category.add_update(node, duration, changed); _profile_data->set_has_unmarked_categories(true); diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index bb5c452..771f001 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -62,7 +62,7 @@ class Graph { bool _sorted; public: - vector nodes() { return _nodes; }; + vector &nodes() { return _nodes; }; void sort_nodes(); void push_back(TreeElementNodePair pair) { _nodes.push_back(pair); }; }; @@ -385,7 +385,7 @@ class Graph { void sync_tracing(); CFStringRef copy_trace_path(); - vector traces() const { return _traces; }; + const vector &traces() const { return _traces; }; template requires std::invocable diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 9cd4acb..6201c5f 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -144,7 +144,7 @@ void Graph::TraceRecorder::encode_stack() { _encoder.begin_length_delimited(); for (auto update = first_update; update != nullptr; update = update.get()->previous()) { - auto frames = update.get()->frames(); + auto &frames = update.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { _encoder.encode_varint(10); _encoder.begin_length_delimited(); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index 7549b9f..587cd0f 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -55,72 +55,73 @@ class Graph::TraceRecorder : public Encoder::Delegate, public Trace { void field_timestamp(Encoder &encoder); void field_backtrace(Encoder &encoder, uint64_t field); - int flush_encoder(Encoder &encoder); + virtual int flush_encoder(Encoder &encoder) override; // MARK: Trace methods - void begin_trace(const Graph &graph); - void end_trace(const Graph &graph); - void sync_trace(); + void begin_trace(const Graph &graph) override; + void end_trace(const Graph &graph) override; + void sync_trace() override; - void log_message_v(const char *format, va_list args); + void log_message_v(const char *format, va_list args) override; - void begin_update(const Subgraph &subgraph, uint32_t options); - void end_update(const Subgraph &subgraph); - void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options); - void end_update(const Graph::UpdateStack &update_stack, data::ptr node, Graph::UpdateStatus update_status); - void begin_update(data::ptr node); - void end_update(data::ptr node, bool changed); - void begin_update(const Graph::Context &context); - void end_update(const Graph::Context &context); + void begin_update(const Subgraph &subgraph, uint32_t options) override; + void end_update(const Subgraph &subgraph) override; + void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) override; + void end_update(const Graph::UpdateStack &update_stack, data::ptr node, + Graph::UpdateStatus update_status) override; + void begin_update(data::ptr node) override; + void end_update(data::ptr node, bool changed) override; + void begin_update(const Graph::Context &context) override; + void end_update(const Graph::Context &context) override; - void begin_invalidation(const Graph::Context &context, AttributeID attribute); - void end_invalidation(const Graph::Context &context, AttributeID attribute); + void begin_invalidation(const Graph::Context &context, AttributeID attribute) override; + void end_invalidation(const Graph::Context &context, AttributeID attribute) override; - void begin_modify(data::ptr node); - void end_modify(data::ptr node); + void begin_modify(data::ptr node) override; + void end_modify(data::ptr node) override; - void begin_event(data::ptr node, uint32_t event_id); - void end_event(data::ptr node, uint32_t event_id); + void begin_event(data::ptr node, uint32_t event_id) override; + void end_event(data::ptr node, uint32_t event_id) override; - void created(const Graph::Context &context); - void destroy(const Graph::Context &context); - void needs_update(const Graph::Context &context); + void created(const Graph::Context &context) override; + void destroy(const Graph::Context &context) override; + void needs_update(const Graph::Context &context) override; - void created(const Subgraph &subgraph); - void invalidate(const Subgraph &subgraph); - void destroy(const Subgraph &subgraph); + void created(const Subgraph &subgraph) override; + void invalidate(const Subgraph &subgraph) override; + void destroy(const Subgraph &subgraph) override; - void add_child(const Subgraph &subgraph, const Subgraph &child); - void remove_child(const Subgraph &subgraph, const Subgraph &child); + void add_child(const Subgraph &subgraph, const Subgraph &child) override; + void remove_child(const Subgraph &subgraph, const Subgraph &child) override; - void added(data::ptr node); + void added(data::ptr node) override; - void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags); - void remove_edge(data::ptr node, uint32_t input_index); - void set_edge_pending(data::ptr node, uint32_t input_index, bool pending); + void add_edge(data::ptr node, AttributeID input, uint8_t input_edge_flags) override; + void remove_edge(data::ptr node, uint32_t input_index) override; + void set_edge_pending(data::ptr node, uint32_t input_index, bool pending) override; - void set_dirty(data::ptr node, bool dirty); - void set_pending(data::ptr node, bool pending); + void set_dirty(data::ptr node, bool dirty) override; + void set_pending(data::ptr node, bool pending) override; - void set_value(data::ptr node, const void *value); - void mark_value(data::ptr node); + void set_value(data::ptr node, const void *value) override; + void mark_value(data::ptr node) override; - void added(data::ptr indirect_node); + void added(data::ptr indirect_node) override; - void set_source(data::ptr indirect_node, AttributeID source); - void set_dependency(data::ptr indirect_node, AttributeID dependency); + void set_source(data::ptr indirect_node, AttributeID source) override; + void set_dependency(data::ptr indirect_node, AttributeID dependency) override; - void set_deadline(uint64_t deadline); - void passed_deadline(); + void set_deadline(uint64_t deadline) override; + void passed_deadline() override; - void mark_profile(const Graph &graph, uint32_t options); + void mark_profile(const Graph &graph, uint32_t options) override; void custom_event(const Graph::Context &context, const char *event_name, const void *value, - const swift::metadata &type); - void named_event(const Graph::Context &context, uint32_t event_id, uint32_t num_event_args, const uint64_t *event_args, - CFDataRef data, uint32_t arg6); - bool named_event_enabled(uint32_t event_id); + const swift::metadata &type) override; + void named_event(const Graph::Context &context, uint32_t event_id, uint32_t num_event_args, + const uint64_t *event_args, CFDataRef data, uint32_t arg6) override; + bool named_event_enabled(uint32_t event_id) override; // compare_failed not overridden }; diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index f9b5ae0..9211775 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -61,7 +61,7 @@ Graph::UpdateStack::Frame *Graph::UpdateStack::global_top() { void Graph::UpdateStack::cancel() { for (util::tagged_ptr update_stack = current_update(); update_stack != nullptr; update_stack = update_stack.get()->previous()) { - auto frames = update_stack.get()->frames(); + auto &frames = update_stack.get()->frames(); for (auto frame = frames.rbegin(), end = frames.rend(); frame != end; ++frame) { if (frame->cancelled) { return; diff --git a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp index 76772db..6d0379d 100644 --- a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp +++ b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp @@ -74,7 +74,7 @@ class TypeDescriptorCache { void lock() { os_unfair_lock_lock(&_lock); }; void unlock() { os_unfair_lock_unlock(&_lock); }; - vector> modes() { return _modes; }; + vector> &modes() { return _modes; }; static void *make_key(const swift::metadata *type, LayoutDescriptor::ComparisonMode comparison_mode, LayoutDescriptor::HeapMode heap_mode); @@ -233,7 +233,7 @@ ComparisonMode mode_for_type(const swift::metadata *type, ComparisonMode default ComparisonMode result = default_mode; TypeDescriptorCache::shared_cache().lock(); - auto modes = TypeDescriptorCache::shared_cache().modes(); + auto &modes = TypeDescriptorCache::shared_cache().modes(); auto iter = std::find_if(modes.begin(), modes.end(), [&](const auto &element) -> bool { return element.first == descriptor; }); if (iter != modes.end()) { @@ -250,7 +250,7 @@ void add_type_descriptor_override(const swift::context_descriptor *_Nullable typ } TypeDescriptorCache::shared_cache().lock(); - auto modes = TypeDescriptorCache::shared_cache().modes(); + auto &modes = TypeDescriptorCache::shared_cache().modes(); auto iter = std::find_if(modes.begin(), modes.end(), [&](const auto &element) -> bool { return element.first == type_descriptor; }); if (iter != modes.end()) { @@ -1040,7 +1040,7 @@ ValueLayout Builder::commit(const swift::metadata &type) { return ValueLayoutEmpty; } } - auto layout_data = emitter.data(); + auto &layout_data = emitter.data(); unsigned char *result; @@ -1131,7 +1131,7 @@ bool Builder::should_visit_fields(const swift::metadata &type, bool no_fetch) { } void Builder::revert(const RevertItemsInfo &info) { - auto items = get_items(); + auto &items = get_items(); while (items.size() > info.item_index) { items.pop_back(); } @@ -1153,7 +1153,7 @@ bool Builder::visit_element(const swift::metadata &type, const swift::metadata:: _current_comparison_mode = mode_for_type(&type, _current_comparison_mode); if (should_visit_fields(type, false)) { - auto items = get_items(); + auto &items = get_items(); auto num_items = items.size(); @@ -1185,7 +1185,7 @@ bool Builder::visit_case(const swift::metadata &type, const swift::field_record return false; } - auto items = get_items(); + auto &items = get_items(); // Add EnumItem if this is the first case if (index == 0) { @@ -1195,9 +1195,9 @@ bool Builder::visit_case(const swift::metadata &type, const swift::field_record &type, {}, }; - items.push_back(item); + items.push_back(std::move(item)); } - EnumItem enum_item = std::get(items.back()); // throws + EnumItem &enum_item = std::get(items.back()); // throws // Add this case to the enum item EnumItem::Case enum_case = { @@ -1205,7 +1205,7 @@ bool Builder::visit_case(const swift::metadata &type, const swift::field_record _current_offset, {}, }; - enum_item.cases.push_back(enum_case); + enum_item.cases.push_back(std::move(enum_case)); bool result; @@ -1240,7 +1240,7 @@ bool Builder::visit_case(const swift::metadata &type, const swift::field_record if (should_visit_fields(*field_type, false)) { // same as visit_element - auto items = get_items(); + auto &items = get_items(); size_t prev_offset = -1; size_t prev_size = 0; if (auto data_item = items.size() > 0 ? std::get_if(&items.back()) : nullptr) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 4ed7e69..692d9b5 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -123,7 +123,7 @@ void Subgraph::invalidate_and_delete_(bool delete_subgraph) { _graph->will_invalidate_subgraph(*this); _validation_state = ValidationState::InvalidationScheduled; if (was_valid) { - _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + _graph->foreach_trace([this](Trace &trace) { trace.invalidate(*this); }); } } } @@ -141,7 +141,7 @@ void Subgraph::invalidate_now(Graph &graph) { if (_validation_state != ValidationState::Invalidated) { _validation_state = ValidationState::Invalidated; if (was_valid) { - _graph->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + _graph->foreach_trace([this](Trace &trace) { trace.invalidate(*this); }); } clear_object(); stack.push(this); @@ -260,7 +260,7 @@ void Subgraph::graph_destroyed() { _validation_state = ValidationState::GraphDestroyed; if (was_valid) { - graph()->foreach_trace([*this](Trace &trace) { trace.invalidate(*this); }); + graph()->foreach_trace([this](Trace &trace) { trace.invalidate(*this); }); } notify_observers(); @@ -364,7 +364,7 @@ void Subgraph::add_node(data::ptr node) { insert_attribute(AttributeID(node), true); if (_tree_root) { - auto tree_data_element = graph()->tree_data_element_for_subgraph(this); + auto &tree_data_element = graph()->tree_data_element_for_subgraph(this); tree_data_element.push_back({ _tree_root, node, @@ -463,7 +463,7 @@ void Subgraph::update(uint8_t flags) { if (is_valid()) { if ((flags & (_flags.value1 | _flags.value2))) { - _graph->foreach_trace([*this, &flags](Trace &trace) { trace.begin_update(*this, flags); }); + _graph->foreach_trace([this, &flags](Trace &trace) { trace.begin_update(*this, flags); }); _last_traversal_seed += 1; // TODO: check atomics @@ -557,7 +557,7 @@ void Subgraph::update(uint8_t flags) { } // while !stack.empty _graph->invalidate_subgraphs(); - _graph->foreach_trace([*this](Trace &trace) { trace.end_update(*this); }); + _graph->foreach_trace([this](Trace &trace) { trace.end_update(*this); }); } } } @@ -695,7 +695,7 @@ AttributeID Subgraph::tree_node_at_index(data::ptr tree_elem if (tree_data_element != map->end()) { tree_data_element->second.sort_nodes(); - auto nodes = tree_data_element->second.nodes(); + auto &nodes = tree_data_element->second.nodes(); std::pair, data::ptr> *found = std::find_if( nodes.begin(), nodes.end(), [&tree_element](auto node) { return node.first == tree_element; }); @@ -722,7 +722,7 @@ data::ptr Subgraph::tree_subgraph_child(data::ptrfind(this); tree_data_element->second.sort_nodes(); - auto nodes = tree_data_element->second.nodes(); + auto &nodes = tree_data_element->second.nodes(); if (nodes.empty()) { return nullptr; } diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index 7728842..29e0beb 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -132,7 +132,7 @@ class Subgraph : public data::zone { void add_child(Subgraph &child, SubgraphChild::Flags flags); void remove_child(Subgraph &child, bool flag); - vector children() { return _children; }; + vector &children() { return _children; }; bool ancestor_of(const Subgraph &other); diff --git a/Sources/ComputeCxx/Swift/AGTuple.cpp b/Sources/ComputeCxx/Swift/AGTuple.cpp index f76fcad..efbc54e 100644 --- a/Sources/ComputeCxx/Swift/AGTuple.cpp +++ b/Sources/ComputeCxx/Swift/AGTuple.cpp @@ -196,7 +196,7 @@ void AGTupleWithBuffer(AGTupleType tuple_type, size_t count, auto metadata = reinterpret_cast(tuple_type); auto buffer_size = metadata->vw_stride() * count; if (buffer_size <= 0x1000) { - char buffer[buffer_size]; + char buffer[buffer_size]; // TODO: use alloca memset(&buffer, 0, buffer_size); // function(tuple_type, buffer); } else { diff --git a/Sources/ComputeCxx/Swift/AGType.cpp b/Sources/ComputeCxx/Swift/AGType.cpp index 6bee24d..064e0a6 100644 --- a/Sources/ComputeCxx/Swift/AGType.cpp +++ b/Sources/ComputeCxx/Swift/AGType.cpp @@ -90,9 +90,9 @@ void AGTypeApplyFields(AGTypeID typeID, const void *body_context) : _body(body), _body_context(body_context) {} - bool unknown_result() const override { return true; } + bool unknown_result() override { return true; } bool visit_field(const AG::swift::metadata &type, const AG::swift::field_record &field, size_t field_offset, - size_t field_size) const override { + size_t field_size) override { auto mangled_name = field.MangledTypeName.get(); auto field_type = type.mangled_type_name_ref(mangled_name, true, nullptr); if (field_type) { @@ -122,9 +122,9 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, Visitor(AGTypeApplyOptions options, AG::ClosureFunction *body) : _options(options), _body(body) {} - bool unknown_result() const override { return _options & AGTypeApplyOptionsContinueAfterUnknownField; } + bool unknown_result() override { return _options & AGTypeApplyOptionsContinueAfterUnknownField; } bool visit_field(const AG::swift::metadata &type, const AG::swift::field_record &field, size_t field_offset, - size_t field_size) const override { + size_t field_size) override { auto mangled_name = field.MangledTypeName.get(); auto field_type = type.mangled_type_name_ref(mangled_name, true, nullptr); if (!field_type) { @@ -135,7 +135,7 @@ bool AGTypeApplyFields2(AGTypeID typeID, AGTypeApplyOptions options, return result != 0; } bool visit_case(const AG::swift::metadata &type, const AG::swift::field_record &field, - uint32_t index) const override { + uint32_t index) override { auto mangled_name = field.MangledTypeName.get(); auto field_type = type.mangled_type_name_ref(mangled_name, true, nullptr); // TODO: _cached or not? if (!field_type) { diff --git a/Sources/ComputeCxx/Swift/MetadataVisitor.cpp b/Sources/ComputeCxx/Swift/MetadataVisitor.cpp index 75e648c..8f76918 100644 --- a/Sources/ComputeCxx/Swift/MetadataVisitor.cpp +++ b/Sources/ComputeCxx/Swift/MetadataVisitor.cpp @@ -5,15 +5,15 @@ namespace AG { namespace swift { -bool metadata_visitor::unknown_result() const { return false; } +bool metadata_visitor::unknown_result() { return false; } bool metadata_visitor::visit_element(const metadata &type, const metadata::ref_kind kind, size_t element_offset, - size_t element_size) const { + size_t element_size) { return unknown_result(); } bool metadata_visitor::visit_field(const metadata &type, const field_record &field, size_t field_offset, - size_t field_size) const { + size_t field_size) { const char *mangled_type_name = field.MangledTypeName ? field.MangledTypeName.get() : nullptr; if (mangled_type_name) { metadata::ref_kind ref_kind = metadata::ref_kind::strong; @@ -29,19 +29,17 @@ bool metadata_visitor::visit_field(const metadata &type, const field_record &fie return unknown_result(); } -bool metadata_visitor::visit_case(const metadata &type, const field_record &field, uint32_t index) const { +bool metadata_visitor::visit_case(const metadata &type, const field_record &field, uint32_t index) { return unknown_result(); } -bool metadata_visitor::metadata_visitor::visit_class(const any_class_type_metadata &type) const { - return unknown_result(); -} +bool metadata_visitor::metadata_visitor::visit_class(const any_class_type_metadata &type) { return unknown_result(); } -bool metadata_visitor::visit_existential(const existential_type_metadata &type) const { return unknown_result(); } +bool metadata_visitor::visit_existential(const existential_type_metadata &type) { return unknown_result(); } -bool metadata_visitor::visit_function(const function_type_metadata &type) const { return unknown_result(); } +bool metadata_visitor::visit_function(const function_type_metadata &type) { return unknown_result(); } -bool metadata_visitor::visit_native_object(const metadata &type) const { return unknown_result(); } +bool metadata_visitor::visit_native_object(const metadata &type) { return unknown_result(); } } // namespace swift } // namespace AG diff --git a/Sources/ComputeCxx/Swift/MetadataVisitor.h b/Sources/ComputeCxx/Swift/MetadataVisitor.h index e2d0c2a..987fda3 100644 --- a/Sources/ComputeCxx/Swift/MetadataVisitor.h +++ b/Sources/ComputeCxx/Swift/MetadataVisitor.h @@ -18,19 +18,18 @@ using field_record = ::swift::reflection::FieldRecord; class metadata_visitor { public: - virtual bool unknown_result() const; + virtual bool unknown_result(); virtual bool visit_element(const metadata &type, const metadata::ref_kind kind, size_t element_offset, - size_t element_size) const; + size_t element_size); - virtual bool visit_field(const metadata &type, const field_record &field, size_t field_offset, - size_t field_size) const; + virtual bool visit_field(const metadata &type, const field_record &field, size_t field_offset, size_t field_size); - virtual bool visit_case(const metadata &type, const field_record &field, uint32_t index) const; - virtual bool visit_class(const any_class_type_metadata &type) const; - virtual bool visit_existential(const existential_type_metadata &type) const; - virtual bool visit_function(const function_type_metadata &type) const; - virtual bool visit_native_object(const metadata &type) const; + virtual bool visit_case(const metadata &type, const field_record &field, uint32_t index); + virtual bool visit_class(const any_class_type_metadata &type); + virtual bool visit_existential(const existential_type_metadata &type); + virtual bool visit_function(const function_type_metadata &type); + virtual bool visit_native_object(const metadata &type); }; } // namespace swift diff --git a/Tests/ComputeTests/Shared/Graph/GraphTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTests.swift index 5a6eb46..a62e338 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTests.swift @@ -25,8 +25,6 @@ struct GraphTests { } let name = "empty_graph.json" Graph.archiveJSON(name: name) - // let name = "empty_graph.json" - // name.withCString { Graph.archiveJSON(name: $0) } let url = if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) { URL(filePath: NSTemporaryDirectory().appending(name)) From f88cd741e0ae5ed09040bdd35b92f96ae6276328 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 15 May 2025 00:03:49 +0200 Subject: [PATCH 74/74] Add layout tests --- Compute.xctestplan | 4 + Sources/ComputeCxx/Graph/Graph.cpp | 4 +- Sources/ComputeCxx/Layout/AGComparison.cpp | 4 +- Sources/ComputeCxx/Layout/AGComparison.h | 9 +- Sources/ComputeCxx/Layout/Builder.h | 45 +- Sources/ComputeCxx/Layout/Compare.cpp | 4 +- .../ComputeCxx/Layout/LayoutDescriptor.cpp | 288 ++-- Sources/ComputeCxx/Layout/LayoutDescriptor.h | 9 +- Sources/ComputeCxx/Swift/Metadata.cpp | 2 +- Sources/ComputeCxx/Swift/Metadata.h | 4 +- .../Shared/Runtime/CompareValuesTests.swift | 77 -- .../Runtime/PrefetchCompareValuesTests.swift | 1190 +++++++++++++++++ .../Shared/Runtime/ReflectionTests.swift | 60 + Tests/ComputeTests/Shared/TestTypes.swift | 6 + .../Shared/readingStandardOutput.swift | 97 ++ 15 files changed, 1547 insertions(+), 256 deletions(-) create mode 100644 Tests/ComputeTests/Shared/Runtime/PrefetchCompareValuesTests.swift create mode 100644 Tests/ComputeTests/Shared/Runtime/ReflectionTests.swift create mode 100644 Tests/ComputeTests/Shared/readingStandardOutput.swift diff --git a/Compute.xctestplan b/Compute.xctestplan index f462977..e85c203 100644 --- a/Compute.xctestplan +++ b/Compute.xctestplan @@ -10,6 +10,10 @@ ], "defaultOptions" : { "environmentVariableEntries" : [ + { + "key" : "AG_PRINT_LAYOUTS", + "value" : "1" + }, { "key" : "AG_PREFETCH_LAYOUTS", "value" : "1" diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index c05fafd..db71bfc 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1072,7 +1072,7 @@ bool Graph::value_set_internal(data::ptr node_ptr, Node &node, const void } // TODO: make void * and rename to dest and source - ValueLayout layout = type.layout() == ValueLayoutPOD ? nullptr : type.layout(); + ValueLayout layout = type.layout() == ValueLayoutTrivial ? nullptr : type.layout(); if (LayoutDescriptor::compare(layout, (const unsigned char *)value_dest, (const unsigned char *)value_source, value_type.vw_size(), comparison_options)) { return false; @@ -1647,7 +1647,7 @@ bool Graph::compare_edge_values(InputEdge input_edge, const AttributeType *type, if (layout == nullptr) { layout = LayoutDescriptor::fetch(type->value_metadata(), options, 0); } - if (layout == ValueLayoutPOD) { + if (layout == ValueLayoutTrivial) { layout = nullptr; } diff --git a/Sources/ComputeCxx/Layout/AGComparison.cpp b/Sources/ComputeCxx/Layout/AGComparison.cpp index 24cf0bb..be68a2e 100644 --- a/Sources/ComputeCxx/Layout/AGComparison.cpp +++ b/Sources/ComputeCxx/Layout/AGComparison.cpp @@ -16,14 +16,14 @@ AGTypeID AGComparisonStateGetFieldType(AGComparisonState state) { return state-> bool AGCompareValues(const void *destination, const void *source, AGTypeID type_id, AGComparisonOptions options) { auto type = reinterpret_cast(type_id); auto layout = AG::LayoutDescriptor::fetch(*type, options, 0); - if (layout == AG::ValueLayoutEmpty) { + if (layout == AG::ValueLayoutTrivial) { layout = nullptr; } return AG::LayoutDescriptor::compare(layout, (const unsigned char *)destination, (const unsigned char *)source, type->vw_size(), options); } -const unsigned char *AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority) { +AGValueLayout AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority) { auto type = reinterpret_cast(type_id); return AG::LayoutDescriptor::fetch(*type, options, priority); } diff --git a/Sources/ComputeCxx/Layout/AGComparison.h b/Sources/ComputeCxx/Layout/AGComparison.h index 507d7e8..fb55cc5 100644 --- a/Sources/ComputeCxx/Layout/AGComparison.h +++ b/Sources/ComputeCxx/Layout/AGComparison.h @@ -36,6 +36,10 @@ AGTypeID AGComparisonStateGetFieldType(AGComparisonState state); typedef CF_OPTIONS(uint32_t, AGComparisonOptions) { AGComparisonOptionsNone = 0, + + AGComparisonOptions_1 = 1 << 0, + AGComparisonOptions_2 = 1 << 1, + AGComparisonOptionsCopyOnWrite = 1 << 8, AGComparisonOptionsFetchLayoutsSynchronously = 1 << 9, AGComparisonOptionsReportFailures = 1ul << 31, @@ -51,9 +55,12 @@ CF_EXPORT CF_REFINED_FOR_SWIFT bool AGCompareValues(const void *destination, const void *source, AGTypeID type_id, AGComparisonOptions options); +typedef const unsigned char *_Nullable AGValueLayout AG_SWIFT_STRUCT AG_SWIFT_NAME(ValueLayout); + CF_EXPORT CF_REFINED_FOR_SWIFT -const unsigned char *_Nullable AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority) CF_SWIFT_NAME(prefetchCompareValues(type:options:priority:)); +AGValueLayout AGPrefetchCompareValues(AGTypeID type_id, AGComparisonOptions options, uint32_t priority) + CF_SWIFT_NAME(prefetchCompareValues(type:options:priority:)); CF_EXPORT CF_REFINED_FOR_SWIFT diff --git a/Sources/ComputeCxx/Layout/Builder.h b/Sources/ComputeCxx/Layout/Builder.h index d7955a3..2886f68 100644 --- a/Sources/ComputeCxx/Layout/Builder.h +++ b/Sources/ComputeCxx/Layout/Builder.h @@ -53,11 +53,8 @@ class Builder : public swift::metadata_visitor { template class Emitter { private: T *_Nonnull _data; - size_t _emitted_size; - - /// Indicates whether iterating the layout descriptor will be less efficient than iterating the object - /// itself. - bool _layout_exceeds_object_size; + size_t _emitted_size = 0; + bool _invalid = false; public: void operator()(const DataItem &item); @@ -70,20 +67,22 @@ class Builder : public swift::metadata_visitor { void enter(const RangeItem &range_item); const vector &data() const { return *_data; }; - bool layout_exceeds_object_size() const { return _layout_exceeds_object_size; }; - void set_layout_exceeds_object_size(bool value) { _layout_exceeds_object_size = value; }; size_t emitted_size() const { return _emitted_size; }; + bool is_invalid() const { return _invalid; }; + void set_invalid(bool invalid) { _invalid = invalid; }; void finish(); }; template <> class Emitter> { private: - vector *_Nonnull _data; - size_t _emitted_size; - bool _layout_exceeds_object_size; + vector *_Nonnull _data; // TODO: why didn't compiler check this was uninitialized? + size_t _emitted_size = 0; + bool _invalid = false; public: + Emitter(vector *data); + void operator()(const DataItem &item); void operator()(const EqualsItem &item); void operator()(const IndirectItem &item); @@ -94,11 +93,19 @@ class Builder : public swift::metadata_visitor { void enter(const RangeItem &range_item); const vector &data() const { return *_data; }; - bool layout_exceeds_object_size() const { return _layout_exceeds_object_size; }; - void set_layout_exceeds_object_size(bool value) { _layout_exceeds_object_size = value; }; size_t emitted_size() const { return _emitted_size; }; + bool is_invalid() const { return _invalid; }; + void set_invalid(bool invalid) { _invalid = invalid; }; void finish(); + + template void emit_value(value_t value) { + // TODO: make not set to zero before overwriting + uint64_t size = _data->size(); + _data->resize(size + sizeof(value_t), 0); + unsigned char *type_pointer = &(*_data)[size]; + *(value_t *)type_pointer = value; + } }; private: @@ -110,9 +117,9 @@ class Builder : public swift::metadata_visitor { ComparisonMode _current_comparison_mode; HeapMode _heap_mode; - size_t _current_offset; + size_t _current_offset = 0; uint64_t _enum_case_depth = 0; - EnumItem::Case *_current_enum_case; + EnumItem::Case *_current_enum_case = nullptr; vector _items; public: @@ -138,12 +145,12 @@ class Builder : public swift::metadata_visitor { void revert(const RevertItemsInfo &info); virtual bool visit_element(const swift::metadata &type, const swift::metadata::ref_kind kind, size_t element_offset, - size_t element_size); + size_t element_size) override; - virtual bool visit_case(const swift::metadata &type, const swift::field_record &field, uint32_t arg); - virtual bool visit_existential(const swift::existential_type_metadata &type); - virtual bool visit_function(const swift::function_type_metadata &type); - virtual bool visit_native_object(const swift::metadata &type); + virtual bool visit_case(const swift::metadata &type, const swift::field_record &field, uint32_t arg) override; + virtual bool visit_existential(const swift::existential_type_metadata &type) override; + virtual bool visit_function(const swift::function_type_metadata &type) override; + virtual bool visit_native_object(const swift::metadata &type) override; }; } // namespace LayoutDescriptor diff --git a/Sources/ComputeCxx/Layout/Compare.cpp b/Sources/ComputeCxx/Layout/Compare.cpp index c8ff7df..2db15c9 100644 --- a/Sources/ComputeCxx/Layout/Compare.cpp +++ b/Sources/ComputeCxx/Layout/Compare.cpp @@ -233,7 +233,7 @@ bool Compare::operator()(ValueLayout layout, const unsigned char *lhs, const uns unsigned shift = 0; enum_tag = 0; - while (*(int *)c < 0) { + while (*c & 0x80) { enum_tag = enum_tag | ((*c & 0x7f) << shift); shift += 7; c += 1; @@ -316,7 +316,7 @@ bool Compare::operator()(ValueLayout layout, const unsigned char *lhs, const uns unsigned shift = 0; enum_tag = 0; - while (*(int *)c < 0) { + while (*c & 0x80) { enum_tag = enum_tag | ((*c & 0x7f) << shift); shift += 7; c += 1; diff --git a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp index 6d0379d..d53532e 100644 --- a/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp +++ b/Sources/ComputeCxx/Layout/LayoutDescriptor.cpp @@ -127,7 +127,6 @@ ValueLayout TypeDescriptorCache::fetch(const swift::metadata &type, LayoutDescri // insert layout synchronously double start_time = current_time(); layout = LayoutDescriptor::make_layout(type, comparison_mode, heap_mode); - assert(layout); double end_time = current_time(); if (comparison_mode < 0) { @@ -216,11 +215,11 @@ void TypeDescriptorCache::drain_queue(void *context) { #pragma mark - LayoutDescriptor -const ValueLayout ValueLayoutEmpty = (ValueLayout)1; +const ValueLayout ValueLayoutTrivial = (ValueLayout)1; namespace LayoutDescriptor { -uintptr_t base_address = 0; +unsigned char base_address = '\0'; ComparisonMode mode_for_type(const swift::metadata *type, ComparisonMode default_mode) { if (!type) { @@ -270,51 +269,53 @@ ValueLayout make_layout(const swift::metadata &type, ComparisonMode default_mode Builder builder = Builder(comparison_mode, heap_mode); - if (heap_mode == HeapMode::Option2) { + if (heap_mode == HeapMode::CaptureRef) { if (!type.visit_heap(builder, swift::metadata::visit_options::heap_locals)) { return nullptr; } - } else { - if (heap_mode == HeapMode::Option1) { - if (type.isClassObject()) { - int mode = type.getValueWitnesses()->isPOD() ? 3 : 2; // TODO: check - if (mode <= builder.current_comparison_mode()) { - if (auto equatable = type.equatable()) { - size_t offset = builder.current_offset(); - size_t size = type.vw_size(); - Builder::EqualsItem item = {offset, size, &type, equatable}; - builder.get_items().push_back(item); - return builder.commit(type); - } + return builder.commit(type); + } + + if (heap_mode == HeapMode::Option1) { + if (type.isClassObject()) { + int mode = type.getValueWitnesses()->isPOD() ? 3 : 2; // TODO: check + if (mode <= builder.current_comparison_mode()) { + if (auto equatable = type.equatable()) { + size_t offset = builder.current_offset(); + size_t size = type.vw_size(); + Builder::EqualsItem item = {offset, size, &type, equatable}; + builder.get_items().push_back(item); + return builder.commit(type); } } - if (!type.visit_heap(builder, swift::metadata::visit_options::heap_class_and_generic_locals)) { - return nullptr; - } } - if (heap_mode != HeapMode::Option0) { + if (!type.visit_heap(builder, swift::metadata::visit_options::heap_class_and_generic_locals)) { return nullptr; } + } - int mode = type.getValueWitnesses()->isPOD() ? 3 : 2; // TODO: check - if (builder.current_comparison_mode() < mode) { - if (!type.visit(builder)) { - return nullptr; - } - } - auto equatable = type.equatable(); - if (equatable == nullptr) { - if (!type.visit(builder)) { - return nullptr; - } + if (heap_mode != HeapMode::Option0) { + return nullptr; + } + + // 2 = ComparisonModeByEquatableconformance + // 3 = ComparisonModeByEquatableConformanceOverBytes + int mode = type.getValueWitnesses()->isPOD() ? 3 : 2; // TODO: check + if (mode <= builder.current_comparison_mode()) { + if (auto equatable = type.equatable()) { + size_t offset = builder.current_offset(); + size_t size = type.vw_size(); + Builder::EqualsItem item = {offset, size, &type, equatable}; + builder.get_items().push_back(item); + + return builder.commit(type); } + } - size_t offset = builder.current_offset(); - size_t size = type.vw_size(); - Builder::EqualsItem item = {offset, size, &type, equatable}; - builder.get_items().push_back(item); - return builder.commit(type); + if (!type.visit(builder)) { + return nullptr; } + return builder.commit(type); } size_t length(ValueLayout layout) { @@ -328,7 +329,7 @@ size_t length(ValueLayout layout) { switch (*c) { case 0: c += 1; - break; + return c - layout; case Controls::EqualsItemBegin: c += 1; c += Controls::EqualsItemTypePointerSize; @@ -350,7 +351,7 @@ size_t length(ValueLayout layout) { case Controls::NestedItemBegin: { c += 1; c += Controls::NestedItemLayoutPointerSize; - while (*(int *)c < 0) { + while (*c & 0x80) { c += 1; } c += 1; @@ -365,7 +366,7 @@ size_t length(ValueLayout layout) { case Controls::EnumItemBeginVariadicCaseIndex: { enum_depth += 1; c += 1; - while (*(int *)c < 0) { + while (*c & 0x80) { c += 1; } c += 1; @@ -382,10 +383,10 @@ size_t length(ValueLayout layout) { } case Controls::EnumItemContinueVariadicCaseIndex: { if (enum_depth == 0) { - break; + return c - layout; } c += 1; - while (*(int *)c < 0) { + while (*c & 0x80) { c += 1; } c += 1; @@ -401,14 +402,14 @@ size_t length(ValueLayout layout) { case Controls::EnumItemContinueCaseIndex7: case Controls::EnumItemContinueCaseIndex8: { if (enum_depth == 0) { - break; + return c - layout; } c += 1; continue; } case Controls::EnumItemEnd: { if (enum_depth == 0) { - break; + return c - layout; } enum_depth -= 1; c += 1; @@ -496,18 +497,20 @@ bool compare_heap_objects(const unsigned char *lhs, const unsigned char *rhs, Co return false; } + // TODO: should this be cast to a HeapObject struct? auto lhs_type = (const swift::metadata *)lhs; auto rhs_type = (const swift::metadata *)rhs; if (lhs_type != rhs_type) { return false; } - HeapMode heap_mode = is_function ? HeapMode::Option2 : HeapMode::Option1; + // only place where heap mode is set to non-zero? + HeapMode heap_mode = is_function ? HeapMode::CaptureRef : HeapMode::Option1; ComparisonOptions fetch_options = ComparisonOptions(options.comparision_mode()); // this has the effect of allowing async fetch ValueLayout layout = TypeDescriptorCache::shared_cache().fetch(*lhs_type, fetch_options, heap_mode, 1); - if (layout > ValueLayoutEmpty) { + if (layout > ValueLayoutTrivial) { return compare(layout, lhs, rhs, -1, options.without_copying_on_write()); } @@ -561,7 +564,7 @@ bool compare_indirect(ValueLayout *layout_ref, const swift::metadata &enum_type, *layout_ref = fetch(layout_type, options.without_copying_on_write(), 0); } - ValueLayout layout = *layout_ref == ValueLayoutEmpty ? nullptr : *layout_ref; + ValueLayout layout = *layout_ref == ValueLayoutTrivial ? nullptr : *layout_ref; static_assert(sizeof(::swift::HeapObject) == 0x10); size_t alignment_mask = layout_type.getValueWitnesses()->getAlignmentMask(); @@ -597,7 +600,7 @@ bool compare_existential_values(const swift::existential_type_metadata &type, co } ValueLayout wrapped_layout = fetch(reinterpret_cast(type), options, 0); - ValueLayout layout = wrapped_layout == ValueLayoutEmpty ? nullptr : layout; + ValueLayout layout = wrapped_layout == ValueLayoutTrivial ? nullptr : layout; return compare(layout, lhs_value, rhs_value, type.vw_size(), options); } @@ -626,6 +629,7 @@ bool compare_partial(ValueLayout layout, const unsigned char *lhs, const unsigne Compare compare_object = Compare(); return compare_object(partial.layout, lhs, rhs, partial.location, remaining_size, options); } + // TODO: no return here? } return compare_bytes_top_level(lhs, rhs, size, options); @@ -697,7 +701,7 @@ Partial find_partial(ValueLayout layout, size_t range_location, size_t range_siz unsigned shift = 0; size_t item_size = 0; - while (*(int *)c < 0) { + while (*c & 0x80) { item_size = item_size | ((*c & 0x7f) << shift); shift += 7; c += 1; @@ -720,7 +724,7 @@ Partial find_partial(ValueLayout layout, size_t range_location, size_t range_siz case Controls::CompactNestedItemBegin: { c += 1; - auto item_layout = reinterpret_cast(base_address + (uint32_t *)c); + ValueLayout item_layout = reinterpret_cast(/*&base_address*/ 0x1e3e6ab60 + *(uint32_t *)c); c += Controls::CompactNestedItemLayoutRelativePointerSize; size_t item_size = *(uint16_t *)(c); @@ -744,7 +748,7 @@ Partial find_partial(ValueLayout layout, size_t range_location, size_t range_siz case Controls::EnumItemBeginCaseIndex2: { if (*c == Controls::EnumItemBeginVariadicCaseIndex) { c += 1; - while (*(int *)c < 0) { + while (*c & 0x80) { c += 1; } c += 1; @@ -770,7 +774,7 @@ Partial find_partial(ValueLayout layout, size_t range_location, size_t range_siz case Controls::EnumItemContinueCaseIndex8: { if (*c == Controls::EnumItemContinueVariadicCaseIndex) { c += 1; - while (*(int *)c < 0) { + while (*c & 0x80) { c += 1; } c += 1; @@ -816,11 +820,13 @@ void print(std::string &output, ValueLayout layout) { const unsigned char *c = layout; while (true) { if (*c == '\0') { + output.push_back(')'); + output.push_back('\n'); return; } if (*c >= 0x40 && *c < 0x80) { - size_t length = *c & 0x3f + 1; // Convert 0-63 to 1-64 + size_t length = (*c & 0x3f) + 1; // Convert 0-63 to 1-64 c += 1; output.push_back('\n'); output.append(indent * 2, ' '); @@ -829,7 +835,7 @@ void print(std::string &output, ValueLayout layout) { } if (*c >= 0x80) { - size_t length = *c & 0x7f + 1; // Convert 0-127 to 1-128 + size_t length = (*c & 0x7f) + 1; // Convert 0-127 to 1-128 c += 1; output.push_back('\n'); output.append(indent * 2, ' '); @@ -841,36 +847,36 @@ void print(std::string &output, ValueLayout layout) { case Controls::EqualsItemBegin: { c += 1; - auto type = reinterpret_cast(c); + auto type = *reinterpret_cast(c); c += Controls::EqualsItemTypePointerSize; c += Controls::EqualsItemEquatablePointerSize; output.push_back('\n'); output.append(indent * 2, ' '); - print_format("(== #:size %d #:type %s)", type->vw_size(), type); + print_format("(== #:size %d #:type %s)", type->vw_size(), type->name(false)); continue; } case Controls::IndirectItemBegin: { c += 1; - auto type = reinterpret_cast(c); + auto type = *reinterpret_cast(c); c += Controls::IndirectItemTypePointerSize; c += Controls::IndirectItemLayoutPointerSize; output.push_back('\n'); output.append(indent * 2, ' '); - print_format("(indirect #:size %d #:type %s)", type->vw_size(), type); + print_format("(indirect #:size %d #:type %s)", type->vw_size(), type->name(false)); continue; } case Controls::ExistentialItemBegin: { c += 1; - auto type = reinterpret_cast(c); + auto type = *reinterpret_cast(c); c += Controls::ExistentialItemTypePointerSize; output.push_back('\n'); output.append(indent * 2, ' '); - print_format("(existential #:size %d #:type %s)", type->vw_size(), type); + print_format("(existential #:size %d #:type %s)", type->vw_size(), type->name(false)); continue; } case Controls::HeapRefItemBegin: @@ -886,12 +892,12 @@ void print(std::string &output, ValueLayout layout) { case Controls::NestedItemBegin: { c += 1; - auto item_layout = reinterpret_cast(c); + ValueLayout item_layout = *(ValueLayout *)c; c += Controls::NestedItemLayoutPointerSize; unsigned shift = 0; size_t item_size = 0; - while (*(int *)c < 0) { + while (*c & 0x80) { item_size = item_size | ((*c & 0x7f) << shift); shift += 7; c += 1; @@ -901,13 +907,13 @@ void print(std::string &output, ValueLayout layout) { output.push_back('\n'); output.append(indent * 2, ' '); - print_format("(nested%s #:size %d #:layout %p)", item_size, item_layout); + print_format("(nested #:size %d #:layout %p)", item_size, item_layout); continue; } case Controls::CompactNestedItemBegin: { c += 1; - auto item_layout = reinterpret_cast(base_address + *(uint32_t *)c); + ValueLayout item_layout = reinterpret_cast(/*&base_address*/ 0x1e3e6ab60 + *(uint32_t *)c); c += Controls::CompactNestedItemLayoutRelativePointerSize; size_t item_size = *(uint16_t *)(c); @@ -915,7 +921,7 @@ void print(std::string &output, ValueLayout layout) { output.push_back('\n'); output.append(indent * 2, ' '); - print_format("(nested%s #:size %d #:layout %p)", item_size, item_layout); + print_format("(nested #:size %d #:layout %p)", item_size, item_layout); continue; } case Controls::EnumItemBeginVariadicCaseIndex: @@ -929,7 +935,7 @@ void print(std::string &output, ValueLayout layout) { unsigned shift = 0; enum_tag = 0; - while (*(int *)c < 0) { + while (*c & 0x80) { enum_tag = enum_tag | ((*c & 0x7f) << shift); shift += 7; c += 1; @@ -937,19 +943,19 @@ void print(std::string &output, ValueLayout layout) { enum_tag = enum_tag | ((*c & 0x7f) << shift); c += 1; - type = reinterpret_cast(c); + type = *reinterpret_cast(c); c += Controls::EnumItemTypePointerSize; } else { enum_tag = *c - Controls::EnumItemBeginCaseIndexFirst; c += 1; - type = reinterpret_cast(c); + type = *reinterpret_cast(c); c += Controls::EnumItemTypePointerSize; } output.push_back('\n'); - output.append(indent * 2 + 4, ' '); - print_format("(enum #:size %d #:type %s", type->vw_size(), type); + output.append(indent * 2, ' '); + print_format("(enum #:size %d #:type %s", type->vw_size(), type->name(false)); output.push_back('\n'); output.append(indent * 2 + 4, ' '); @@ -974,7 +980,7 @@ void print(std::string &output, ValueLayout layout) { unsigned shift = 0; enum_tag = 0; - while (*(int *)c < 0) { + while (*c & 0x80) { enum_tag = enum_tag | ((*c & 0x7f) << shift); shift += 7; c += 1; @@ -986,6 +992,7 @@ void print(std::string &output, ValueLayout layout) { c += 1; } + output.push_back(')'); output.push_back('\n'); output.append(indent * 2 - 4, ' '); print_format("(case %d", enum_tag); @@ -995,6 +1002,7 @@ void print(std::string &output, ValueLayout layout) { c += 1; indent -= 4; output.push_back(')'); + output.push_back(')'); continue; } @@ -1011,10 +1019,10 @@ unsigned char *Builder::_buffer = nullptr; ValueLayout Builder::commit(const swift::metadata &type) { if (_heap_mode == HeapMode(0)) { if (_items.size() == 0) { - return ValueLayoutEmpty; + return ValueLayoutTrivial; } if (_items.size() == 1 && _items[0].index() == 0) { - return ValueLayoutEmpty; + return ValueLayoutTrivial; } } if (_items.size() == 1) { @@ -1025,19 +1033,21 @@ ValueLayout Builder::commit(const swift::metadata &type) { } } - auto emitter = Emitter>(); + auto data = vector(); + auto emitter = Emitter>(&data); for (auto &&item : _items) { std::visit(emitter, item); } emitter.finish(); - if (_heap_mode != HeapMode(0) && emitter.layout_exceeds_object_size()) { + if (_heap_mode != HeapMode(0) && emitter.is_invalid()) { return nullptr; } if (_heap_mode == HeapMode(0)) { - emitter.set_layout_exceeds_object_size(type.vw_size() < emitter.emitted_size()); - if (emitter.layout_exceeds_object_size()) { - return ValueLayoutEmpty; + emitter.set_invalid(type.vw_size() < emitter.emitted_size()); + if (emitter.is_invalid()) { + return ValueLayoutTrivial; // TODO: check does _heap_mode=0 mean that we only encountered POD fields? Does + // _heap_mode ever mutate? } } auto &layout_data = emitter.data(); @@ -1067,14 +1077,16 @@ ValueLayout Builder::commit(const swift::metadata &type) { if (_heap_mode == HeapMode(0)) { const char *name = type.name(false); if (name) { - fprintf(stdout, "== %s, %d bytes ==\n%s", name, (int)layout_data.size(), message.data()); + fprintf(stderr, "== %s, %d bytes ==\n%s", name, type.vw_size(), message.data()); } else { - fprintf(stdout, "== Unknown type %p ==\n%s", &type, message.data()); + fprintf(stderr, "== Unknown type %p ==\n%s", &type, message.data()); } } else { - fprintf(stdout, "== Unknown heap type %p ==\n%s", &type, message.data()); + fprintf(stderr, "== Unknown heap type %p ==\n%s", &type, message.data()); } } + + return result; } void Builder::add_field(size_t field_size) { @@ -1082,9 +1094,9 @@ void Builder::add_field(size_t field_size) { return; } - auto items = get_items(); + auto &items = get_items(); if (auto data_item = !items.empty() ? std::get_if(&items.back()) : nullptr) { - if (data_item->offset == _current_offset) { + if (data_item->offset + data_item->size == _current_offset) { data_item->size += field_size; return; } @@ -1099,7 +1111,7 @@ bool Builder::should_visit_fields(const swift::metadata &type, bool no_fetch) { ComparisonOptions(_current_comparison_mode) | ComparisonOptions::ReportFailures | ComparisonOptions::FetchLayoutsSynchronously, true)) { - if ((uintptr_t)layout == 1) { + if (layout == ValueLayoutTrivial) { add_field(type.vw_size()); } else { NestedItem item = { @@ -1217,8 +1229,9 @@ bool Builder::visit_case(const swift::metadata &type, const swift::field_record add_field(sizeof(void *)); result = true; } else { - auto mangled_name = field.FieldName.get(); // TODO: check this is FieldName of MangledTypeName - auto field_type = mangled_name != nullptr ? type.mangled_type_name_ref(mangled_name, false, nullptr) : nullptr; + auto mangled_type_name = field.MangledTypeName.get(); + auto field_type = + mangled_type_name != nullptr ? type.mangled_type_name_ref(mangled_type_name, false, nullptr) : nullptr; if (field_type == nullptr) { // bail out if we can't get a type for the enum case payload items.pop_back(); @@ -1276,8 +1289,7 @@ bool Builder::visit_case(const swift::metadata &type, const swift::field_record } if (_current_enum_case->children.empty()) { - auto last_case = enum_item.cases.back(); - enum_item.cases.pop_back(); // make call destructor + enum_item.cases.pop_back(); } _enum_case_depth -= 1; @@ -1307,7 +1319,7 @@ bool Builder::visit_function(const swift::function_type_metadata &type) { } bool Builder::visit_native_object(const swift::metadata &type) { - if (_heap_mode != HeapMode::Option2) { + if (_heap_mode != HeapMode::CaptureRef) { return false; } @@ -1317,6 +1329,9 @@ bool Builder::visit_native_object(const swift::metadata &type) { #pragma mark - Builder::Emitter +Builder::Emitter>::Emitter(vector *data) + : _data(data) {} + void Builder::Emitter>::operator()(const DataItem &item) { enter(item); size_t item_size = item.size; @@ -1333,38 +1348,23 @@ void Builder::Emitter>::operator()(const Da void Builder::Emitter>::operator()(const EqualsItem &item) { enter(item); _data->push_back(Controls::EqualsItemBegin); - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(*((unsigned char *)item.type + i)); - } - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(*((unsigned char *)item.equatable + i)); - } + emit_value((void *)item.type); + emit_value((void *)item.equatable); _emitted_size += item.size; } void Builder::Emitter>::operator()(const IndirectItem &item) { enter(item); _data->push_back(Controls::IndirectItemBegin); - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(*((unsigned char *)item.type + i)); - } - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(0); - } + emit_value((void *)item.type); + emit_value(nullptr); _emitted_size += item.size; } void Builder::Emitter>::operator()(const ExistentialItem &item) { enter(item); _data->push_back(Controls::ExistentialItemBegin); - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(*((unsigned char *)item.type + i)); - } + emit_value((void *)item.type); _emitted_size += item.size; } @@ -1378,31 +1378,29 @@ void Builder::Emitter>::operator()(const Ne enter(item); size_t item_length = length(item.layout); - if (item_length <= 0x20) { + if (item_length < 0x20) { // append nested layout directly - // _data->push_back(item.layout); // append length - 1 bytes from nested layout to _data + _data->reserve(_data->size() + item_length - 1); + for (auto i = 0; i < item_length - 1; ++i) { + unsigned char c = item.layout[i]; + _data->push_back(c); + } } else { - uintptr_t layout_relative_address = (uintptr_t)item.layout - (uintptr_t)&base_address; + + uintptr_t layout_relative_address = (uintptr_t)item.layout - 0x1e3e6ab60; // (uintptr_t)&base_address; if ((uint32_t)layout_relative_address == layout_relative_address && item.size < 0xffff) { _data->push_back(Controls::CompactNestedItemBegin); - // layout addressin 4 bytes - _data->reserve(_data->size() + 4); - for (int i = 0; i < sizeof(uint32_t); ++i) { - _data->push_back(*((unsigned char *)layout_relative_address + i)); - } + // layout address in 4 bytes + emit_value((uint32_t)layout_relative_address); // size in two bytes - _data->push_back(*((unsigned char *)item.size + 0)); - _data->push_back(*((unsigned char *)item.size + 1)); + emit_value((uint16_t)item.size); } else { _data->push_back(Controls::NestedItemBegin); // full pointer to layout - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(*((unsigned char *)item.layout + i)); // TODO: how to split the pointer into 8 bytes - } + emit_value(item.layout); // Emit length 7 bits at a time, using the 8th bit as a "has more" flag size_t length = item.size; @@ -1420,28 +1418,25 @@ void Builder::Emitter>::operator()(const En enter(item); if (item.cases.empty()) { - _data->push_back('\t'); - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(*((unsigned char *)item.type + i)); - } + _data->push_back(EnumItemBeginCaseIndexFirst); + emit_value((void *)item.type); } else { bool is_first = true; - for (auto enum_case : item.cases) { + for (auto &enum_case : item.cases) { /* Case indices are encoding using the numbers 8 through 21 - 8 through 11 encode the first case index with a payload - 9, 10, 11 correspond to indices 0, 1, 2 respectively - 8 indicates the index is encoded in the following bytes - - 12 through 21 encode the first case index with a payload + - 12 through 21 encode a subsequent case index with a payload - 13-21 correspond to indices 0-8 respectively - 12 indicates the index is encoded in the following bytes */ uint64_t direct_encoded_index = - enum_case.item_index + - (is_first ? Controls::EnumItemBeginCaseIndexFirst : Controls::EnumItemContinueCaseIndexFirst); + (is_first ? Controls::EnumItemBeginCaseIndexFirst : Controls::EnumItemContinueCaseIndexFirst) + + enum_case.item_index; uint64_t last_direct = is_first ? Controls::EnumItemBeginCaseIndexLast : Controls::EnumItemContinueCaseIndexLast; if (direct_encoded_index <= last_direct) { @@ -1460,20 +1455,19 @@ void Builder::Emitter>::operator()(const En } if (is_first) { - _data->reserve(_data->size() + 8); - for (int i = 0; i < sizeof(void *); ++i) { - _data->push_back(*((unsigned char *)item.type + i)); - } + emit_value((void *)item.type); } + auto children_emitter = Emitter>(_data); for (auto &&child : enum_case.children) { - std::visit(*this, child); + std::visit(children_emitter, child); } is_first = false; - size_t new_size = _emitted_size + item.type->vw_size(); - _layout_exceeds_object_size = (_layout_exceeds_object_size || new_size < _emitted_size); + if (!_invalid && !children_emitter.is_invalid()) { + _invalid = _emitted_size + item.type->vw_size() < children_emitter.emitted_size(); + } } } @@ -1483,20 +1477,20 @@ void Builder::Emitter>::operator()(const En } void Builder::Emitter>::enter(const RangeItem &item) { - if (!_layout_exceeds_object_size) { - _layout_exceeds_object_size = item.offset <= _emitted_size; - if (!_layout_exceeds_object_size) { - + if (!_invalid) { + if (item.offset >= _emitted_size) { // Emit number of bytes until item offset // encode as a series of numbers from 0-63, indicating chunks of bytes 1-64 - size_t length = item.offset - _emitted_size; - while (length > 0x40) { + size_t skip = item.offset - _emitted_size; + while (skip > 0x40) { _data->push_back('\x7f'); - length -= 0x40; + skip -= 0x40; } - if (length > 0) { - _data->push_back((length - 1) | 0x40); + if (skip > 0) { + _data->push_back((skip - 1) | 0x40); } + } else { + _invalid = true; } } _emitted_size = item.offset; diff --git a/Sources/ComputeCxx/Layout/LayoutDescriptor.h b/Sources/ComputeCxx/Layout/LayoutDescriptor.h index 0688f59..c3df0fd 100644 --- a/Sources/ComputeCxx/Layout/LayoutDescriptor.h +++ b/Sources/ComputeCxx/Layout/LayoutDescriptor.h @@ -16,17 +16,17 @@ class context_descriptor; /// A string that encodes an object's layout in memory. using ValueLayout = const unsigned char *; -extern const ValueLayout ValueLayoutEmpty; +extern const ValueLayout ValueLayoutTrivial; namespace LayoutDescriptor { enum class HeapMode : uint16_t { Option0 = 0, Option1 = 1, - Option2 = 2, + CaptureRef = 2, // something to do with function }; -extern uintptr_t base_address; +extern unsigned char base_address; enum ComparisonMode : uint16_t { @@ -37,6 +37,7 @@ struct ComparisonOptions { uint32_t _value; public: + // TODO: delete in favour of AG... enum { ComparisonModeMask = 0xff, CopyOnWrite = 1 << 8, @@ -86,7 +87,7 @@ bool compare_bytes_top_level(const unsigned char *lhs, const unsigned char *rhs, bool compare_bytes(char unsigned const *lhs, char unsigned const *rhs, size_t size, size_t *_Nullable failure_location); bool compare_heap_objects(char unsigned const *lhs, char unsigned const *rhs, ComparisonOptions options, bool is_function); -bool compare_indirect(ValueLayout *_Nullable layout_ref, const swift::metadata &lhs_type, +bool compare_indirect(ValueLayout _Nullable *_Nullable layout_ref, const swift::metadata &lhs_type, const swift::metadata &rhs_type, ComparisonOptions options, const unsigned char *lhs, const unsigned char *rhs); bool compare_existential_values(const swift::existential_type_metadata &type, const unsigned char *lhs, diff --git a/Sources/ComputeCxx/Swift/Metadata.cpp b/Sources/ComputeCxx/Swift/Metadata.cpp index b6d8f5f..8754314 100644 --- a/Sources/ComputeCxx/Swift/Metadata.cpp +++ b/Sources/ComputeCxx/Swift/Metadata.cpp @@ -505,7 +505,7 @@ bool metadata::visit(metadata_visitor &visitor) const { } index += 1; } - return true; + return struct_context->NumFields > 0; } } return visitor.unknown_result(); diff --git a/Sources/ComputeCxx/Swift/Metadata.h b/Sources/ComputeCxx/Swift/Metadata.h index 2b5afe9..1259c9d 100644 --- a/Sources/ComputeCxx/Swift/Metadata.h +++ b/Sources/ComputeCxx/Swift/Metadata.h @@ -66,7 +66,7 @@ class metadata : public ::swift::Metadata { enum visit_options { heap_class = 1 << 0, - heap_locals = 1 << 1, + heap_locals = 1 << 1, // visit captured refs heap_generic_locals = 1 << 2, heap_class_and_generic_locals = heap_class | heap_generic_locals, @@ -74,6 +74,8 @@ class metadata : public ::swift::Metadata { bool visit(metadata_visitor &visitor) const; bool visit_heap(metadata_visitor &visitor, visit_options options) const; + + private: bool visit_heap_class(metadata_visitor &visitor) const; bool visit_heap_locals(metadata_visitor &visitor) const; }; diff --git a/Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift b/Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift index 4521c4e..c68e480 100644 --- a/Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift +++ b/Tests/ComputeTests/Shared/Runtime/CompareValuesTests.swift @@ -1,82 +1,5 @@ import Testing -@Suite -struct PrefetchCompareValuesTests { - - enum LayoutExpectation: ExpressibleByStringLiteral { - case empty - case layout(String) - - init(stringLiteral value: StringLiteralType) { - self = .layout(value) - } - } - - @Test( - arguments: [ - // POD -// (Void.self, .empty), -// (Bool.self, .empty), -// (Int.self, .empty), -// (Double.self, .empty), -// (Float.self, .empty), -// (PODStruct.self, .empty), - - // Non-POD - (String.self, .empty), -// -// // Tuple -// ((Int, String).self, .empty), -// -// // Enum -// (CEnum.self, nil), -// (TaggedEnum.self, "\t\u{fffd}\u{fffd}W\u{4}\u{1}"), -// (IndirectEnum.self, "\t0\u{fffd}W\u{4}\u{1}"), -// -// // Heap -// (HeapClass.self, nil), -// -// // Product types -// (TestStruct.self, "\u{fffd}F\u{fffd}C\u{fffd}F\t\u{fffd}\u{fffd}W\u{4}\u{1}"), -// (TestClass.self, nil), - ] as [(Any.Type, LayoutExpectation?)] - ) - func layout(of type: Any.Type, matches layoutExpectation: LayoutExpectation?) { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - - let layout = prefetchCompareValues(type: Metadata(type), options: [], priority: 0) - - if let layoutExpectation { - #expect(layout != nil) - if let layout { - switch layoutExpectation { - case .empty: - #expect(layout == UnsafePointer(bitPattern: 1)) - case .layout(let expectedLayout): - #expect(String(cString: layout) == expectedLayout) - } - } - } else { - #expect(layout == nil) - } - } - - @Test(arguments: [ - Triple(first: 0, second: 1, third: 2), - Triple(first: 3, second: 4, third: 5), - ]) - func attributeWithSubscript(_ value: Triple) { - let attribute = Attribute(value: value) - let offsetValue = attribute[offset: { _ in - PointerOffset, Int>(byteOffset: 8) - }] - #expect(offsetValue.wrappedValue == value.second) - #expect(attribute.first.wrappedValue == value.first) - #expect(attribute[keyPath: \.third].wrappedValue == value.third) - } - -} - @Suite struct CompareValuesTests { diff --git a/Tests/ComputeTests/Shared/Runtime/PrefetchCompareValuesTests.swift b/Tests/ComputeTests/Shared/Runtime/PrefetchCompareValuesTests.swift new file mode 100644 index 0000000..be1a51c --- /dev/null +++ b/Tests/ComputeTests/Shared/Runtime/PrefetchCompareValuesTests.swift @@ -0,0 +1,1190 @@ +import Testing + +extension ValueLayout: @retroactive CustomStringConvertible { + + static var trivial: ValueLayout { + return unsafeBitCast(UnsafePointer(bitPattern: 1), to: ValueLayout.self) + } + + public var description: String { + return "\(rawValue)".replacing(/^0x0+/, with: "0x") + } + +} + +extension Optional: @retroactive CustomStringConvertible where Wrapped == ValueLayout { + + public var description: String { + switch self { + case .none: + return "0x00000000" + case .some(let wrapped): + return wrapped.description + } + } + +} + +let allOptions: [AGComparisonOptions] = [[], [._1], [._2], [._1, ._2]] + +// The code being tested uses a shared global cache, hence this test suite may +// exhibit different behavior depending on whether a single test or the entire +// suite is run. To prevent this, avoid referencing metadata for a non-trivial +// type more than once within the entire suite. +@Suite(.serialized) +struct PrefetchCompareValuesTests { + + @Suite + struct FundamentalTypeTests { + + @Test + func layoutForNever() async { + let layout0 = prefetchCompareValues(type: Metadata(Never.self), options: [], priority: 0) + #expect(layout0 == nil) + + let layout1 = prefetchCompareValues(type: Metadata(Never.self), options: [._1], priority: 0) + #expect(layout1 == nil) + + let layout2 = prefetchCompareValues(type: Metadata(Never.self), options: [._2], priority: 0) + #expect(layout2 == nil) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(Never.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == Never, 0 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 0 #:type Never)) + + """ + ) + } + + @Test(arguments: allOptions) + func layoutForVoid(with options: AGComparisonOptions) { + let layout = prefetchCompareValues(type: Metadata(Void.self), options: options, priority: 0) + #expect(layout == .trivial) + } + + @Test + func layoutForBool() async { + let layout0 = prefetchCompareValues(type: Metadata(Bool.self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(Bool.self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + let layout2 = prefetchCompareValues(type: Metadata(Bool.self), options: [._2], priority: 0) + #expect(layout2 == .trivial) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(Bool.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == Bool, 1 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 1 #:type Bool)) + + """ + ) + } + + @Test( + arguments: [ + (Int.self, MemoryLayout.size), + (Double.self, MemoryLayout.size), + (Float.self, MemoryLayout.size), + ] as [(Any.Type, Int)] + ) + func layoutForNumeric(of type: Any.Type, size: Int) async { + let layout0 = prefetchCompareValues(type: Metadata(type), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(type), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + let layout2 = prefetchCompareValues(type: Metadata(type), options: [._2], priority: 0) + #expect(layout2 == .trivial) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(type), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == \(type), \(size) bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size \(size) #:type \(type))) + + """ + ) + } + + @Test + func layoutForString() async { + let layout0 = prefetchCompareValues(type: Metadata(String.self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(String.self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(String.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == String, 16 bytes == + (layout #:length 18 #:address \(String(describing: layout2)) + (== #:size 16 #:type String)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(String.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == String, 16 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 16 #:type String)) + + """ + ) + } + + @Test(arguments: allOptions) + func layoutForStaticString(with options: AGComparisonOptions) { + let layout = prefetchCompareValues(type: Metadata(StaticString.self), options: options, priority: 0) + #expect(layout == .trivial) + } + + } + + @Suite + struct StructTests { + + @Test(arguments: allOptions) + func layoutForEmptyStruct(with options: AGComparisonOptions) { + struct EmptyStruct {} + + let layout = prefetchCompareValues(type: Metadata(EmptyStruct.self), options: options, priority: 0) + #expect(layout == nil) + } + + @Test("Layout for struct enclosing single element equals the layout of the enclosed element") + func layoutForStructEnclosingSingleElement() async { + struct StructEnclosingSingleElement { + var property: Int + } + + let layout0 = prefetchCompareValues( + type: Metadata(StructEnclosingSingleElement.self), + options: [], + priority: 0 + ) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues( + type: Metadata(StructEnclosingSingleElement.self), + options: [._1], + priority: 0 + ) + #expect(layout1 == .trivial) + + let layout2 = prefetchCompareValues( + type: Metadata(StructEnclosingSingleElement.self), + options: [._2], + priority: 0 + ) + #expect(layout2 == .trivial) + + let innerLayout = prefetchCompareValues(type: Metadata(Int.self), options: [._1, ._2], priority: 0) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues( + type: Metadata(StructEnclosingSingleElement.self), + options: [._1, ._2], + priority: 0 + ) + } + #expect(layout3 == innerLayout) + } + + @Test + func layoutForTrivialStruct() async { + struct TrivialStruct { + var first: Int + var second: Int + } + + let layout0 = prefetchCompareValues(type: Metadata(TrivialStruct.self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(TrivialStruct.self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + let layout2 = prefetchCompareValues(type: Metadata(TrivialStruct.self), options: [._2], priority: 0) + #expect(layout2 == .trivial) + + let _ = prefetchCompareValues(type: Metadata(Int.self), options: [._1, ._2], priority: 0) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(TrivialStruct.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == TrivialStruct, 16 bytes == + (layout #:length 35 #:address \(String(describing: layout3)) + (== #:size 8 #:type Int) + (== #:size 8 #:type Int)) + + """ + ) + } + + @Test + func layoutForStructWithAlignedElement() async { + struct StructWithAlignedElement { + var int8: Int8 + var int64: Int64 + } + + var output0 = "" + let layout0 = await printingStandardError(to: &output0) { + prefetchCompareValues(type: Metadata(StructWithAlignedElement.self), options: [], priority: 0) + } + #expect(layout0 != nil) + #expect( + output0 == """ + == StructWithAlignedElement, 16 bytes == + (layout #:length 4 #:address \(String(describing: layout0)) + (read 1) + (skip 7) + (read 8)) + + """ + ) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata(StructWithAlignedElement.self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == StructWithAlignedElement, 16 bytes == + (layout #:length 4 #:address \(String(describing: layout1)) + (read 1) + (skip 7) + (read 8)) + + """ + ) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(StructWithAlignedElement.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == StructWithAlignedElement, 16 bytes == + (layout #:length 4 #:address \(String(describing: layout2)) + (read 1) + (skip 7) + (read 8)) + + """ + ) + + let _ = prefetchCompareValues(type: Metadata(Int8.self), options: [._1, ._2], priority: 0) + let _ = prefetchCompareValues(type: Metadata(Int64.self), options: [._1, ._2], priority: 0) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(StructWithAlignedElement.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == StructWithAlignedElement, 16 bytes == + (layout #:length 36 #:address \(String(describing: layout3)) + (== #:size 1 #:type Int8) + (skip 7) + (== #:size 8 #:type Int64)) + + """ + ) + } + + @Test + func layoutForStructWithComplexProperty() async { + typealias ComplexType = Int64??? + struct StructWithComplexProperty { + var int8: Int8 // force layout to be non-nil + var complexProperty: ComplexType + } + + let nestedLayout0 = prefetchCompareValues(type: Metadata(ComplexType.self), options: [], priority: 0) + + var output0 = "" + let layout0 = await printingStandardError(to: &output0) { + prefetchCompareValues(type: Metadata(StructWithComplexProperty.self), options: [], priority: 0) + } + #expect(layout0 != nil) + #expect( + output0 == """ + == StructWithComplexProperty, 19 bytes == + (layout #:length 13 #:address \(String(describing: layout0)) + (read 1) + (skip 7) + (nested #:size 11 #:layout \(String(describing: nestedLayout0)))) + + """ + ) + + let nestedLayout1 = prefetchCompareValues(type: Metadata(ComplexType.self), options: [._1], priority: 0) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata(StructWithComplexProperty.self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == StructWithComplexProperty, 19 bytes == + (layout #:length 13 #:address \(String(describing: layout1)) + (read 1) + (skip 7) + (nested #:size 11 #:layout \(String(describing: nestedLayout1)))) + + """ + ) + + let nestedLayout2 = prefetchCompareValues(type: Metadata(ComplexType.self), options: [._2], priority: 0) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(StructWithComplexProperty.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == StructWithComplexProperty, 19 bytes == + (layout #:length 13 #:address \(String(describing: layout2)) + (read 1) + (skip 7) + (nested #:size 11 #:layout \(String(describing: nestedLayout2)))) + + """ + ) + + let _ = prefetchCompareValues(type: Metadata(Int8.self), options: [._1, ._2], priority: 0) + let _ = prefetchCompareValues(type: Metadata(ComplexType.self), options: [._1, ._2], priority: 0) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(StructWithComplexProperty.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == StructWithComplexProperty, 19 bytes == + (layout #:length 36 #:address \(String(describing: layout3)) + (== #:size 1 #:type Int8) + (skip 7) + (== #:size 11 #:type Optional>>)) + + """ + ) + } + + } + + @Suite + struct ClassTests { + + @Test(arguments: allOptions) + func layoutForEmptyClass(with options: AGComparisonOptions) { + class EmptyClass {} + + let layout = prefetchCompareValues(type: Metadata(EmptyClass.self), options: options, priority: 0) + #expect(layout == nil) + } + + @Test(arguments: allOptions) + func layoutForTrivialClass(with options: AGComparisonOptions) { + class TrivialClass { + var property: Int = 0 + } + + let layout = prefetchCompareValues(type: Metadata(TrivialClass.self), options: options, priority: 0) + #expect(layout == nil) + } + + @Test(arguments: allOptions) + func layoutForStructWithWeakVar(with options: AGComparisonOptions) { + class EmptyClass {} + struct StructWithWeakVar { + weak var property: EmptyClass? + } + + let layout = prefetchCompareValues(type: Metadata(StructWithWeakVar.self), options: options, priority: 0) + #expect(layout == .trivial) + } + + } + + @Suite + struct EnumTests { + + @Test(arguments: allOptions) + func layoutForEmptyEnum(with options: AGComparisonOptions) { + enum EmptyEnum {} + + let layout = prefetchCompareValues(type: Metadata(EmptyEnum.self), options: options, priority: 0) + #expect(layout == nil) + } + + @Test + func layoutForBasicEnum() async { + enum BasicEnum { + case first + case second + } + + let layout0 = prefetchCompareValues(type: Metadata(BasicEnum.self), options: [], priority: 0) + #expect(layout0 == nil) + + let layout1 = prefetchCompareValues(type: Metadata(BasicEnum.self), options: [._1], priority: 0) + #expect(layout1 == nil) + + let layout2 = prefetchCompareValues(type: Metadata(BasicEnum.self), options: [._2], priority: 0) + #expect(layout2 == nil) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(BasicEnum.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == BasicEnum, 1 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 1 #:type BasicEnum)) + + """ + ) + } + + @Test + func layoutForIntEnum() async { + enum IntEnum: Int { + case first = 1 + case second = 2 + } + + let layout0 = prefetchCompareValues(type: Metadata(IntEnum.self), options: [], priority: 0) + #expect(layout0 == nil) + + let layout1 = prefetchCompareValues(type: Metadata(IntEnum.self), options: [._1], priority: 0) + #expect(layout1 == nil) + + let layout2 = prefetchCompareValues(type: Metadata(IntEnum.self), options: [._2], priority: 0) + #expect(layout2 == nil) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(IntEnum.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == IntEnum, 1 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 1 #:type IntEnum)) + + """ + ) + } + + @Test + func layoutForTaggedUnionEnum() async { + enum TaggedUnionEnum { + case first + case second(Int) + case third(String) + } + + var output0 = "" + let layout0 = await printingStandardError(to: &output0) { + prefetchCompareValues(type: Metadata(TaggedUnionEnum.self), options: [], priority: 0) + } + #expect(layout0 != nil) + #expect( + output0 == """ + == TaggedUnionEnum, 17 bytes == + (layout #:length 14 #:address \(String(describing: layout0)) + (enum #:size 17 #:type TaggedUnionEnum + (case 0 + (read 8)) + (case 1 + (read 16)))) + + """ + ) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata(TaggedUnionEnum.self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == TaggedUnionEnum, 17 bytes == + (layout #:length 14 #:address \(String(describing: layout1)) + (enum #:size 17 #:type TaggedUnionEnum + (case 0 + (read 8)) + (case 1 + (read 16)))) + + """ + ) + + let _ = prefetchCompareValues(type: Metadata(String.self), options: [._2], priority: 0) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(TaggedUnionEnum.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == TaggedUnionEnum, 17 bytes == + (layout #:length 30 #:address \(String(describing: layout2)) + (enum #:size 17 #:type TaggedUnionEnum + (case 0 + (read 8)) + (case 1 + (== #:size 16 #:type String)))) + + """ + ) + + let _ = prefetchCompareValues(type: Metadata(Int.self), options: [._1, ._2], priority: 0) + let _ = prefetchCompareValues(type: Metadata(String.self), options: [._1, ._2], priority: 0) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(TaggedUnionEnum.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == TaggedUnionEnum, 17 bytes == + (layout #:length 46 #:address \(String(describing: layout3)) + (enum #:size 17 #:type TaggedUnionEnum + (case 0 + (== #:size 8 #:type Int)) + (case 1 + (== #:size 16 #:type String)))) + + """ + ) + } + + @Test + func layoutForIndirectEnum() async { + indirect enum IndirectEnum { + case string(String) + case indirectEnum(child: IndirectEnum) + } + + var output0 = "" + let layout0 = await printingStandardError(to: &output0) { + prefetchCompareValues(type: Metadata(IndirectEnum.self), options: [], priority: 0) + } + #expect(layout0 != nil) + #expect( + output0 == """ + == IndirectEnum, 8 bytes == + (layout #:length 14 #:address \(String(describing: layout0)) + (enum #:size 8 #:type IndirectEnum + (case 0 + (read 8)) + (case 1 + (read 8)))) + + """ + ) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata(IndirectEnum.self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == IndirectEnum, 8 bytes == + (layout #:length 46 #:address \(String(describing: layout1)) + (enum #:size 8 #:type IndirectEnum + (case 0 + (indirect #:size 16 #:type String)) + (case 1 + (indirect #:size 8 #:type (child: IndirectEnum))))) + + """ + ) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(IndirectEnum.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == IndirectEnum, 8 bytes == + (layout #:length 46 #:address \(String(describing: layout2)) + (enum #:size 8 #:type IndirectEnum + (case 0 + (indirect #:size 16 #:type String)) + (case 1 + (indirect #:size 8 #:type (child: IndirectEnum))))) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(IndirectEnum.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == IndirectEnum, 8 bytes == + (layout #:length 46 #:address \(String(describing: layout3)) + (enum #:size 8 #:type IndirectEnum + (case 0 + (indirect #:size 16 #:type String)) + (case 1 + (indirect #:size 8 #:type (child: IndirectEnum))))) + + """ + ) + } + + } + + @Suite + struct TupleTests { + + @Test + func layoutForTuple() async { + let layout0 = prefetchCompareValues(type: Metadata((Int, String).self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata((Int, String).self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + let _ = prefetchCompareValues(type: Metadata(String.self), options: [._2], priority: 0) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata((Int, String).self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == (Int, String), 24 bytes == + (layout #:length 19 #:address \(String(describing: layout2)) + (read 8) + (== #:size 16 #:type String)) + + """ + ) + + let _ = prefetchCompareValues(type: Metadata(Int.self), options: [._1, ._2], priority: 0) + let _ = prefetchCompareValues(type: Metadata(String.self), options: [._1, ._2], priority: 0) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata((Int, String).self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == (Int, String), 24 bytes == + (layout #:length 35 #:address \(String(describing: layout3)) + (== #:size 8 #:type Int) + (== #:size 16 #:type String)) + + """ + ) + } + + @Test + func layoutForTupleWithAlignedElement() async { + var output0 = "" + let layout0 = await printingStandardError(to: &output0) { + prefetchCompareValues(type: Metadata((Int8, Int64).self), options: [], priority: 0) + } + #expect(layout0 != nil) + #expect( + output0 == """ + == (Int8, Int64), 16 bytes == + (layout #:length 4 #:address \(String(describing: layout0)) + (read 1) + (skip 7) + (read 8)) + + """ + ) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata((Int8, Int64).self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == (Int8, Int64), 16 bytes == + (layout #:length 4 #:address \(String(describing: layout1)) + (read 1) + (skip 7) + (read 8)) + + """ + ) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata((Int8, Int64).self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == (Int8, Int64), 16 bytes == + (layout #:length 4 #:address \(String(describing: layout2)) + (read 1) + (skip 7) + (read 8)) + + """ + ) + + let _ = prefetchCompareValues(type: Metadata(Int8.self), options: [._1, ._2], priority: 0) + let _ = prefetchCompareValues(type: Metadata(Int64.self), options: [._1, ._2], priority: 0) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata((Int8, Int64).self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == (Int8, Int64), 16 bytes == + (layout #:length 36 #:address \(String(describing: layout3)) + (== #:size 1 #:type Int8) + (skip 7) + (== #:size 8 #:type Int64)) + + """ + ) + } + + } + + @Suite + struct CollectionTests { + + @Test + func layoutForArray() async { + let layout0 = prefetchCompareValues(type: Metadata(Array.self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(Array.self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(Array.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == Array, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout2)) + (== #:size 8 #:type Array)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(Array.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == Array, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 8 #:type Array)) + + """ + ) + } + + @Test(arguments: allOptions) + func layoutForArrayOfNotEquatable(with options: AGComparisonOptions) { + class NotEquatable {} + + let layout = prefetchCompareValues(type: Metadata(Array.self), options: options, priority: 0) + #expect(layout == .trivial) + } + + @Test + func layoutForDictionary() async { + let layout0 = prefetchCompareValues(type: Metadata(Dictionary.self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(Dictionary.self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(Dictionary.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == Dictionary, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout2)) + (== #:size 8 #:type Dictionary)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(Dictionary.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == Dictionary, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 8 #:type Dictionary)) + + """ + ) + } + + @Test(arguments: allOptions) + func layoutForDictionaryOfNotEquatable(with options: AGComparisonOptions) { + class NotEquatable {} + + let layout = prefetchCompareValues( + type: Metadata(Dictionary.self), + options: options, + priority: 0 + ) + #expect(layout == .trivial) + } + + @Test + func layoutForSet() async { + let layout0 = prefetchCompareValues(type: Metadata(Set.self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(Set.self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(Set.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == Set, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout2)) + (== #:size 8 #:type Set)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(Set.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == Set, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 8 #:type Set)) + + """ + ) + } + + } + + @Suite + struct EquatableTests { + + @Test + func layoutForEquatableStruct() async { + struct EquatableStruct: Equatable { + var property: Int = 0 + static func == (lhs: EquatableStruct, rhs: EquatableStruct) -> Bool { + return false + } + } + + let layout0 = prefetchCompareValues(type: Metadata(EquatableStruct.self), options: [], priority: 0) + #expect(layout0 == .trivial) + + let layout1 = prefetchCompareValues(type: Metadata(EquatableStruct.self), options: [._1], priority: 0) + #expect(layout1 == .trivial) + + let layout2 = prefetchCompareValues(type: Metadata(EquatableStruct.self), options: [._2], priority: 0) + #expect(layout2 == .trivial) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(EquatableStruct.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == EquatableStruct, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 8 #:type EquatableStruct)) + + """ + ) + } + + @Test + func layoutForEquatableClass() async { + class EquatableClass: Equatable { + var property: Int = 0 + static func == (lhs: EquatableClass, rhs: EquatableClass) -> Bool { + return false + } + } + + let layout0 = prefetchCompareValues(type: Metadata(EquatableClass.self), options: [], priority: 0) + #expect(layout0 == nil) + + let layout1 = prefetchCompareValues(type: Metadata(EquatableClass.self), options: [._1], priority: 0) + #expect(layout1 == nil) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(EquatableClass.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == EquatableClass, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout2)) + (== #:size 8 #:type EquatableClass)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(EquatableClass.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == EquatableClass, 8 bytes == + (layout #:length 18 #:address \(String(describing: layout3)) + (== #:size 8 #:type EquatableClass)) + + """ + ) + } + + } + + @Suite + struct ExistentialTests { + + @Test + func layoutForAny() async { + let layout0 = prefetchCompareValues(type: Metadata(Any.self), options: [], priority: 0) + #expect(layout0 == nil) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata(Any.self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == Any, 32 bytes == + (layout #:length 10 #:address \(String(describing: layout1)) + (existential #:size 32 #:type Any)) + + """ + ) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(Any.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == Any, 32 bytes == + (layout #:length 10 #:address \(String(describing: layout2)) + (existential #:size 32 #:type Any)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(Any.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == Any, 32 bytes == + (layout #:length 10 #:address \(String(describing: layout3)) + (existential #:size 32 #:type Any)) + + """ + ) + } + + @Test + func layoutForAnyError() async { + let layout0 = prefetchCompareValues(type: Metadata((any Error).self), options: [], priority: 0) + #expect(layout0 == nil) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata((any Error).self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == Error, 8 bytes == + (layout #:length 10 #:address \(String(describing: layout1)) + (existential #:size 8 #:type Error)) + + """ + ) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata((any Error).self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == Error, 8 bytes == + (layout #:length 10 #:address \(String(describing: layout2)) + (existential #:size 8 #:type Error)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata((any Error).self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == Error, 8 bytes == + (layout #:length 10 #:address \(String(describing: layout3)) + (existential #:size 8 #:type Error)) + + """ + ) + } + + } + + @Suite + struct FunctionTests { + + @Test + func layoutForFunction() async { + typealias Function = () -> Void + + let layout0 = prefetchCompareValues(type: Metadata(Function.self), options: [], priority: 0) + #expect(layout0 == nil) + + var output1 = "" + let layout1 = await printingStandardError(to: &output1) { + prefetchCompareValues(type: Metadata(Function.self), options: [._1], priority: 0) + } + #expect(layout1 != nil) + #expect( + output1 == """ + == () -> (), 16 bytes == + (layout #:length 3 #:address \(String(describing: layout1)) + (read 8) + (capture-ref)) + + """ + ) + + var output2 = "" + let layout2 = await printingStandardError(to: &output2) { + prefetchCompareValues(type: Metadata(Function.self), options: [._2], priority: 0) + } + #expect(layout2 != nil) + #expect( + output2 == """ + == () -> (), 16 bytes == + (layout #:length 3 #:address \(String(describing: layout2)) + (read 8) + (capture-ref)) + + """ + ) + + var output3 = "" + let layout3 = await printingStandardError(to: &output3) { + prefetchCompareValues(type: Metadata(Function.self), options: [._1, ._2], priority: 0) + } + #expect(layout3 != nil) + #expect( + output3 == """ + == () -> (), 16 bytes == + (layout #:length 3 #:address \(String(describing: layout3)) + (read 8) + (capture-ref)) + + """ + ) + } + + } + +} diff --git a/Tests/ComputeTests/Shared/Runtime/ReflectionTests.swift b/Tests/ComputeTests/Shared/Runtime/ReflectionTests.swift new file mode 100644 index 0000000..a062739 --- /dev/null +++ b/Tests/ComputeTests/Shared/Runtime/ReflectionTests.swift @@ -0,0 +1,60 @@ +import Testing + +@Suite +struct ReflectionTests { + + @Test + func reflectEmptyStruct() { + + struct EmptyStruct {} + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(EmptyStruct.self).forEachField(options: []) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == false) + #expect(fields.count == 0) + } + + @Test + func reflectStaticString() { + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(StaticString.self).forEachField(options: []) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 3) + + #expect(fields[0].1 == 0) + #expect(fields[1].1 == 8) + #expect(fields[2].1 == 16) + } + + @Test + func reflectOptionalInt() { + + var fields: [(String, Int, any Any.Type)] = [] + let finished = Metadata(Optional.self).forEachField(options: [.enumerateEnumCases]) { + fieldName, + fieldOffset, + fieldType in + fields.append((String(cString: fieldName), fieldOffset, fieldType)) + return true + } + + #expect(finished == true) + #expect(fields.count == 1) + } + +} diff --git a/Tests/ComputeTests/Shared/TestTypes.swift b/Tests/ComputeTests/Shared/TestTypes.swift index 9effa5a..37e2e16 100644 --- a/Tests/ComputeTests/Shared/TestTypes.swift +++ b/Tests/ComputeTests/Shared/TestTypes.swift @@ -101,12 +101,18 @@ struct PODStruct { // Enum types +enum EmptyEnum {} + enum CEnum { case a case b case c } +enum TaggedEnum1 { + case a(Int) +} + enum TaggedEnum { case a(Int) case b(Double) diff --git a/Tests/ComputeTests/Shared/readingStandardOutput.swift b/Tests/ComputeTests/Shared/readingStandardOutput.swift new file mode 100644 index 0000000..cc51a85 --- /dev/null +++ b/Tests/ComputeTests/Shared/readingStandardOutput.swift @@ -0,0 +1,97 @@ +import Foundation + +/// Additionally writes any data written to standard output into the given output stream. +/// +/// - Parameters: +/// - output: An output stream to receive the standard output text +/// - encoding: The encoding to use when converting standard output into text. +/// - body: A closure that is executed immediately. +/// - Returns: The return value, if any, of the `body` closure. +func printingStandardOutput( + to output: inout Target, + encoding: String.Encoding = .utf8, + body: () -> Result +) + async -> Result where Target: TextOutputStream +{ + var result: Result? = nil + + let consumer = Pipe() // reads from stdout + let producer = Pipe() // writes to stdout + + let stream = AsyncStream { continuation in + let clonedStandardOutput = dup(STDOUT_FILENO) + defer { + dup2(clonedStandardOutput, STDOUT_FILENO) + close(clonedStandardOutput) + } + + dup2(STDOUT_FILENO, producer.fileHandleForWriting.fileDescriptor) + dup2(consumer.fileHandleForWriting.fileDescriptor, STDOUT_FILENO) + + consumer.fileHandleForReading.readabilityHandler = { fileHandle in + let chunk = fileHandle.availableData + if chunk.isEmpty { + continuation.finish() + } else { + continuation.yield(chunk) + producer.fileHandleForWriting.write(chunk) + } + } + + result = body() + try! consumer.fileHandleForWriting.close() + } + + for await chunk in stream { + output.write(String(data: chunk, encoding: encoding)!) + } + + return result! +} + +func printingStandardError( + to stream: inout Target, + encoding: String.Encoding = .utf8, + body: () -> Result +) async + -> Result where Target: TextOutputStream +{ + var result: Result? = nil + + let consumer = Pipe() // reads from stderr + let producer = Pipe() // writes to stderr + + let chunks = AsyncStream { continuation in + let clonedStandardError = dup(STDERR_FILENO) + defer { + dup2(clonedStandardError, STDERR_FILENO) + close(clonedStandardError) + } + + dup2(STDERR_FILENO, producer.fileHandleForWriting.fileDescriptor) + dup2(consumer.fileHandleForWriting.fileDescriptor, STDERR_FILENO) + + consumer.fileHandleForReading.readabilityHandler = { fileHandle in + let chunk = fileHandle.availableData + if chunk.isEmpty { + continuation.finish() + } else { + continuation.yield(chunk) + producer.fileHandleForWriting.write(chunk) + } + } + + result = body() + try! consumer.fileHandleForWriting.close() + } + + for await chunk in chunks { + guard let chunkString = String(data: chunk, encoding: encoding) else { + continue + } + stream.write(chunkString) + } + + return result! +}