88#include " cinderx/Jit/hir/hir_instr_info_c.h"
99#include " cinderx/Jit/hir/hir_operand_types_c.h"
1010#include " cinderx/Jit/hir/typed_argument_c.h"
11+ #include " cinderx/Jit/hir/phx_ptr_set.h" /* X3b Environment::references_ */
12+ #include " cinderx/Jit/hir/phx_threaded_ref.h" /* X3b incref/decref bridges */
1113#include " cinderx/Jit/threaded_compile.h"
1214
1315#include < algorithm>
@@ -779,15 +781,20 @@ unsigned long TypedArgument::threadSafeTpFlags() const {
779781}
780782
781783Environment::~Environment () {
782- // Phase 4.A W7d Batch 76: STAY C++ per Q-W7-3 stay-C++ exception
783- // (ThreadedCompileSerialize RAII + std::unordered_set::clear + delete
784- // on Register* — all genuinely-can't-port surface). The references_
785- // teardown calls ThreadedRef destructors which run Py_DECREF inside
786- // STL clear; porting requires exposing both the serialize guard +
787- // the ThreadedRef container as C bridges, out of scope for W7d.
788- // Documented as W7d EXCEPTION in commit body.
784+ /* X3b (Batch 96) E-1+E-2+E-3 DISCHARGE: references_ migrated from
785+ * std::unordered_set<ThreadedRef<>> to PhxPtrSet (X2b void*-keyed
786+ * open-address hash). Teardown: serialize guard (ThreadedCompileSerialize
787+ * stays C++ per Phoenix concurrency-infra class) + iterate raw slots +
788+ * phx_threaded_decref each + phx_ptr_set_destroy. Replaces the prior
789+ * STL-clear-via-~ThreadedRef path. */
789790 ThreadedCompileSerialize guard;
790- references_.clear ();
791+ for (size_t i = 0 ; i < phx_ptr_set_capacity (&references_); i++) {
792+ void *obj = phx_ptr_set_at (&references_, i);
793+ if (obj != NULL ) {
794+ phx_threaded_decref (static_cast <PyObject *>(obj));
795+ }
796+ }
797+ phx_ptr_set_destroy (&references_);
791798 for (size_t i = 0 ; i < reg_count_; i++) {
792799 delete reg_data_[i];
793800 }
@@ -808,13 +815,18 @@ Register* Environment::addRegister(std::unique_ptr<Register> reg) {
808815}
809816
810817PyObject* Environment::addReference (PyObject* obj) {
811- // Phase 4.A W7d Batch 76: STAY C++ per Q-W7-3 stay-C++ exception.
812- // ThreadedRef<> + std::unordered_set::emplace are genuinely-can't-port
813- // (RAII + STL container insertion); porting requires exposing both as
814- // C bridges, out of scope for W7d. Serialize as we modify the ref-count
815- // to obj which may be widely accessible. Documented as W7d EXCEPTION.
818+ /* X3b (Batch 96) E-1+E-2+E-3 DISCHARGE: references_ now PhxPtrSet
819+ * (void*-keyed). Dedup semantic preserved via contains-check BEFORE
820+ * phx_threaded_incref (theologian 04:25:09Z watchpoint #1: prevent
821+ * double-incref on duplicate adds; matches prior unordered_set::emplace
822+ * dedup which would discard the new ThreadedRef temporary on dup hit).
823+ * Serialize guard retained for ThreadedRef-class refcount safety. */
816824 ThreadedCompileSerialize guard;
817- return references_.emplace (ThreadedRef<>::create (obj)).first ->get ();
825+ if (!phx_ptr_set_contains (&references_, obj)) {
826+ phx_threaded_incref (obj);
827+ phx_ptr_set_insert (&references_, obj);
828+ }
829+ return obj;
818830}
819831
820832PyObject* Environment::addReference (Ref<> obj) {
@@ -825,8 +837,11 @@ PyObject* Environment::addReference(Ref<> obj) {
825837}
826838
827839const Environment::ReferenceSet& Environment::references () const {
828- return *reinterpret_cast <const ReferenceSet*>(
829- hir_c_env_references (const_cast <Environment*>(this )));
840+ /* X3b: ReferenceSet now PhxPtrSet (POD); direct reference return,
841+ * no opaque-blob bridge cast needed. hir_c_env_references at
842+ * hir_instr_c.h:265 still exposes the field address as void* for
843+ * C-side consumers (offset preserved by HirEnvironmentLayoutVerifier). */
844+ return references_;
830845}
831846
832847bool usesRuntimeFunc ([[maybe_unused]] PyCodeObject* code) {
0 commit comments