From 141c07e09b953ddbd91d82405217fdb52a195cd1 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Wed, 14 Oct 2026 10:25:06 +0200 Subject: [PATCH 1/7] Add scoped suppression of global propagator information --- changelog.in | 10 +++ gecode/kernel/core.cpp | 19 +++- gecode/kernel/core.hpp | 80 ++++++++++++++++- gecode/kernel/gpi.hpp | 9 +- test/afc.cpp | 198 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 308 insertions(+), 8 deletions(-) diff --git a/changelog.in b/changelog.in index 394b936a5d..374a67787c 100755 --- a/changelog.in +++ b/changelog.in @@ -73,6 +73,16 @@ Date: unreleased [DESCRIPTION] This is the development changelog for the next Gecode release. +[ENTRY] +Module: kernel +What: new +Rank: minor +[DESCRIPTION] +Add Space::NoGPI for scoped posting without globally retained propagator +information. Scoped propagators keep their identifiers and groups in the +space, have zero AFC, and copy their information when cloned. Rewriting +preserves the original propagator information. + [ENTRY] Module: int diff --git a/gecode/kernel/core.cpp b/gecode/kernel/core.cpp index 4293cfe6a8..6d5489f761 100644 --- a/gecode/kernel/core.cpp +++ b/gecode/kernel/core.cpp @@ -118,7 +118,7 @@ namespace Gecode { VarImpDisposerBase* Space::vd[AllVarConf::idx_d]; #endif - Space::Space(void) : mm(ssd.data().sm) { + Space::Space(void) : no_gpi(false), mm(ssd.data().sm) { #ifdef GECODE_HAS_CBS var_id_counter = 0; #endif @@ -540,7 +540,8 @@ namespace Gecode { // Process failure failed: // Count failure - ssd.data().gpi.fail(p->gpi()); + if (!p->local_info()) + ssd.data().gpi.fail(p->gpi()); // Mark as failed fail(); // Propagate top priority propagators @@ -722,7 +723,7 @@ namespace Gecode { * */ Space::Space(Space& s) - : ssd(s.ssd), + : no_gpi(false), ssd(s.ssd), mm(ssd.data().sm,s.mm,s.pc.p.n_sub*sizeof(Propagator**)), #ifdef GECODE_HAS_CBS var_id_counter(s.var_id_counter), @@ -920,11 +921,23 @@ namespace Gecode { } + void* + Space::local_info(unsigned int gid) { + static_assert(alignof(Kernel::GPI::Info) >= 4, + "Propagator information needs two tag bits"); + Kernel::GPI::Info* i = alloc(1); + i->init(ssd.data().gpi.next_pid(),gid); + i->afc = 0.0; + return Support::ptrjoin(i,2); + } + void Space::afc_unshare(void) { if (ssd.data().gpi.unshare()) { for (Propagators ps(*this); ps(); ++ps) { Propagator& p = ps.propagator(); + if (p.local_info()) + continue; Kernel::GPI::Info* gpi = ssd.data().gpi.allocate(p.gpi().pid,p.gpi().gid); if (p.disabled()) diff --git a/gecode/kernel/core.hpp b/gecode/kernel/core.hpp index 169aab6599..95f9d93363 100755 --- a/gecode/kernel/core.hpp +++ b/gecode/kernel/core.hpp @@ -1087,12 +1087,19 @@ namespace Gecode { /// A list of advisors (used during cloning) Gecode::ActorLink* advisors; } u; - /// A tagged pointer combining a pointer to global propagator information and whether the propagator is disabled + /** + * \brief Tagged pointer to propagator information + * + * Bit zero marks disabled propagators; bit one marks space-local + * information, which has no accumulated failure count. + */ void* gpi_disabled; /// Static cast for a non-null pointer (to give a hint to optimizer) static Propagator* cast(ActorLink* al); /// Static cast for a non-null pointer (to give a hint to optimizer) static const Propagator* cast(const ActorLink* al); + /// Whether propagator information is local to the space + bool local_info(void) const; /// Disable propagator void disable(Space& home); /// Enable propagator @@ -1104,7 +1111,7 @@ namespace Gecode { Propagator(Space& home, Propagator& p); /// Return forwarding pointer during copying Propagator* fwd(void) const; - /// Provide access to global propagator information + /// Provide access to propagator information Kernel::GPI::Info& gpi(void); public: @@ -1791,6 +1798,10 @@ namespace Gecode { friend GECODE_KERNEL_EXPORT void trace(Home home, TraceFilter tf, int te, Tracer& t); private: + /// Whether new propagator information is local to this space + bool no_gpi; + /// Allocate tagged space-local propagator information + GECODE_KERNEL_EXPORT void* local_info(unsigned int gid); /// Shared data for space Kernel::SharedSpaceData ssd; /// Performs memory management for space @@ -2037,6 +2048,42 @@ namespace Gecode { GECODE_KERNEL_EXPORT void ap_ignore_dispose(Actor* a, bool d); public: + /** + * \brief Suppress global propagator information during scoped posting + * \ingroup TaskModelScript + * + * New propagators posted into this space use space-local information + * instead of records retained by all spaces in a search. Their AFC is + * always zero, including after AFC initialization or unsharing. They + * retain normal identifiers and groups, so tracing and counting-based + * search remain supported. Group changes for these propagators are + * local to the space; clones copy the current group. Local records are + * reclaimed with the space, even if their propagators subsume earlier. + * AFC-based branching therefore only counts ordinary propagators. + * + * Rewriting inherits the original propagator's information, regardless + * of this context. Existing propagators are unaffected. Clones retain + * local information but start with ordinary posting enabled. + * + * Contexts can be nested and restore the previous setting on destruction, + * including exception unwinding. The space must outlive the context, + * and contexts for the same space must be destroyed in reverse order. + */ + class NoGPI { + private: + /// Space in which to suppress global information + Space& home; + /// Previous setting + bool previous; + NoGPI(const NoGPI&) = delete; + NoGPI& operator =(const NoGPI&) = delete; + public: + /// Suppress global information for new propagators in \a home + explicit NoGPI(Space& home); + /// Restore the previous setting + ~NoGPI(void); + }; + /** * \brief Default constructor * \ingroup TaskModelScript @@ -3556,6 +3603,17 @@ namespace Gecode { } + forceinline + Space::NoGPI::NoGPI(Space& h) : home(h), previous(h.no_gpi) { + home.no_gpi = true; + } + + forceinline + Space::NoGPI::~NoGPI(void) { + home.no_gpi = previous; + } + + /* * Propagator * @@ -3581,6 +3639,11 @@ namespace Gecode { return static_cast(prev()); } + forceinline bool + Propagator::local_info(void) const { + return (reinterpret_cast(gpi_disabled) & 2) != 0; + } + forceinline bool Propagator::disabled(void) const { return Support::marked(gpi_disabled); @@ -3600,7 +3663,8 @@ namespace Gecode { forceinline Kernel::GPI::Info& Propagator::gpi(void) { - return *static_cast(Support::funmark(gpi_disabled)); + return *reinterpret_cast + (reinterpret_cast(gpi_disabled) & ~static_cast(3)); } forceinline @@ -3608,6 +3672,9 @@ namespace Gecode { : gpi_disabled((home.propagator() != nullptr) ? // Inherit propagator information home.propagator()->gpi_disabled : + static_cast(home).no_gpi ? + static_cast(home).local_info + (home.propagatorgroup().gid) : // New propagator information static_cast(home).ssd.data().gpi.allocate (home.propagatorgroup().gid)) { @@ -3617,8 +3684,13 @@ namespace Gecode { } forceinline - Propagator::Propagator(Space&, Propagator& p) + Propagator::Propagator(Space& home, Propagator& p) : gpi_disabled(p.gpi_disabled) { + if (p.local_info()) { + Kernel::GPI::Info* i = home.alloc(1); + *i = p.gpi(); + gpi_disabled = Support::ptrjoin(i, p.disabled() ? 3 : 2); + } u.advisors = nullptr; assert((u.med == 0) && (u.size == 0)); // Set forwarding pointer diff --git a/gecode/kernel/gpi.hpp b/gecode/kernel/gpi.hpp index bca28ef4ee..56d18353eb 100755 --- a/gecode/kernel/gpi.hpp +++ b/gecode/kernel/gpi.hpp @@ -92,6 +92,8 @@ namespace Gecode { namespace Kernel { Info* allocate(unsigned int p, unsigned int gid); /// Allocate new actor info Info* allocate(unsigned int gid); + /// Allocate a new propagator id without allocating information + unsigned int next_pid(void); /// Return next free propagator id unsigned int pid(void) const; /// Provide access to unshare info and set to true @@ -141,6 +143,11 @@ namespace Gecode { namespace Kernel { return d; } + forceinline unsigned int + GPI::next_pid(void) { + return npid.fetch_add(1, std::memory_order_seq_cst); + } + forceinline unsigned int GPI::pid(void) const { return npid.load(std::memory_order_acquire); @@ -186,7 +193,7 @@ namespace Gecode { namespace Kernel { } c = &b->info[--b->free]; m.release(); - c->init(npid.fetch_add(1, std::memory_order_seq_cst),gid); + c->init(next_pid(),gid); return c; } diff --git a/test/afc.cpp b/test/afc.cpp index eceff77137..ed9aa620c9 100644 --- a/test/afc.cpp +++ b/test/afc.cpp @@ -36,6 +36,8 @@ #include "test/test.hh" +#include + namespace Test { /// %Test for %AFC infrastructure @@ -132,6 +134,202 @@ namespace Test { } }; + /// Scoped suppression of global propagator information + class NoGPI : public Test::Base { + /// Space with variables for posting and rewriting relations + class TestSpace : public Gecode::Space { + public: + Gecode::IntVar x, y; + Gecode::BoolVar b; + TestSpace(void) : x(*this,0,10), y(*this,0,10), b(*this,0,1) {} + TestSpace(TestSpace& s) : Space(s) { + x.update(*this,s.x); y.update(*this,s.y); b.update(*this,s.b); + } + virtual Space* copy(void) { return new TestSpace(*this); } + /// Find a surviving propagator by its identifier + Gecode::Propagator* find(unsigned int id) { + for (Propagators ps(*this); ps(); ++ps) + if (ps.propagator().id() == id) + return &ps.propagator(); + return nullptr; + } + /// Post a non-subsumed relation and return its propagator + Gecode::Propagator& post(Gecode::PropagatorGroup g = + Gecode::PropagatorGroup::def) { + Gecode::rel((*this)(g), x, Gecode::IRT_LE, y); + Gecode::Propagator* p = nullptr; + for (Propagators ps(*this); ps(); ++ps) + if ((p == nullptr) || (ps.propagator().id() > p->id())) + p = &ps.propagator(); + return *p; + } + }; + + /// Check nesting, unwinding, isolation, and posting in a clone + bool scopes(void) { + using namespace Gecode; + TestSpace s, other; + if (s.post().afc() != 1.0) + return false; + { + Space::NoGPI outer(s); + if ((s.post().afc() != 0.0) || (other.post().afc() != 1.0)) + return false; + try { + Space::NoGPI inner(s); + if (s.post().afc() != 0.0) + return false; + throw 1; + } catch (int) {} + if (s.post().afc() != 0.0) + return false; + (void) s.status(); + std::unique_ptr c(static_cast(s.clone())); + if (c->post().afc() != 1.0) + return false; + } + try { + Space::NoGPI context(s); + throw 1; + } catch (int) {} + return s.post().afc() == 1.0; + } + + /// Check local groups, disabled pointer tags, and lifetime after cloning + bool lifetime(void) { + using namespace Gecode; + std::unique_ptr s(new TestSpace); + PropagatorGroup a, b; + unsigned int id; + { + Space::NoGPI context(*s); + Propagator& p = s->post(a); + id = p.id(); + if ((p.group() != a) || (s->post().id() == id)) + return false; + } + a.disable(*s); + (void) s->status(); + std::unique_ptr c(static_cast(s->clone())); + // The first call marks the shared pool; the second really unshares. + s->afc_unshare(); + c->afc_unshare(); + Propagator* p = c->find(id); + if (!p || !p->disabled() || (p->afc() != 0.0)) + return false; + b.move(*c,id); + if ((p->group() != b) || (s->find(id)->group() != a)) + return false; + s.reset(); + b.enable(*c); + if (p->disabled() || (c->status() == SS_FAILED)) + return false; + b.kill(*c); + return (c->find(id) == nullptr) && (b.size(*c) == 0); + } + + /// Check AFC failure accounting, initialization, and rescaling + bool failure(bool local) { + using namespace Gecode; + TestSpace s; + Propagator* p; + if (local) { + Space::NoGPI context(s); + p = &s.post(); + } else { + p = &s.post(); + } + (void) s.status(); + std::unique_ptr c(static_cast(s.clone())); + // Force rescaling on failure for the ordinary propagator. + IntAFC afc(*c,IntVarArgs() << c->x << c->y,1.0e-300,false); + c->afc_unshare(); + Propagator* q = c->find(p->id()); + if (!q || (q->afc() != (local ? 0.0 : 1.0))) + return false; + rel(*c,c->x,IRT_EQ,5); + rel(*c,c->y,IRT_EQ,4); + if (c->status() != SS_FAILED) + return false; + return local ? ((q->afc() == 0.0) && (p->afc() == 0.0)) : + ((q->afc() > 1.0) && (p->afc() < 1.0)); + } + + /// A real reified propagator must retain its information when rewritten + bool rewrite(bool local) { + using namespace Gecode; + TestSpace s; + PropagatorGroup g; + if (local) { + Space::NoGPI context(s); + rel(s(g),s.x,IRT_EQ,s.y,Reify(s.b)); + } else { + rel(s(g),s.x,IRT_EQ,s.y,Reify(s.b)); + } + (void) s.status(); + Gecode::Propagators ps(s,PropagatorGroup::all); + unsigned int id = ps.propagator().id(); + rel(s,s.b,IRT_EQ,1); + if (local) { + (void) s.status(); + } else { + Space::NoGPI context(s); + (void) s.status(); + } + Propagator* p = s.find(id); + if (!p || (p->group() != g) || + (p->afc() != (local ? 0.0 : 1.0))) + return false; + rel(s,s.x,IRT_EQ,3); + return (s.status() == SS_SOLVED) && s.y.assigned() && (s.y.val() == 3); + } + + /// Collect posting counts and propagated identifiers + class Tracer : public Gecode::Tracer { + public: + unsigned int posted = 0; + std::vector ids; + virtual void propagate(const Gecode::Space&, + const Gecode::PropagateTraceInfo& p) { + ids.push_back(p.id()); + } + virtual void commit(const Gecode::Space&, + const Gecode::CommitTraceInfo&) {} + virtual void post(const Gecode::Space&, + const Gecode::PostTraceInfo& p) { + posted += p.propagators(); + } + }; + + /// Posting traces and propagation traces must retain distinct identities + bool tracing(void) { + using namespace Gecode; + Tracer t; + TestSpace s; + PropagatorGroup g; + trace(s,g,TE_POST | TE_PROPAGATE,t); + t.posted = 0; + unsigned int a, b; + { + Space::NoGPI context(s); + a = s.post(g).id(); + b = s.post(g).id(); + } + (void) s.status(); + return (t.posted == 2) && (a != b) && + (std::find(t.ids.begin(),t.ids.end(),a) != t.ids.end()) && + (std::find(t.ids.begin(),t.ids.end(),b) != t.ids.end()); + } + public: + NoGPI(void) : Test::Base("AFC::NoGPI") {} + virtual bool run(void) { + return scopes() && lifetime() && failure(false) && failure(true) && + rewrite(false) && rewrite(true) && tracing(); + } + }; + + NoGPI no_gpi; + AFC afc; } From ef525f16929838b6f1cf8b86bf622219517d49f9 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Wed, 14 Oct 2026 11:03:07 +0200 Subject: [PATCH 2/7] Add scoped GPI opt-in and fix CBS cache iteration --- changelog.in | 13 ++++++++- gecode/int/branch/cbs.hpp | 16 ++++++----- gecode/kernel/core.hpp | 39 +++++++++++++++++++++++++- test/afc.cpp | 28 +++++++++++++++++++ test/branch/int.cpp | 58 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 9 deletions(-) diff --git a/changelog.in b/changelog.in index 374a67787c..82f35d7e4e 100755 --- a/changelog.in +++ b/changelog.in @@ -81,7 +81,18 @@ Rank: minor Add Space::NoGPI for scoped posting without globally retained propagator information. Scoped propagators keep their identifiers and groups in the space, have zero AFC, and copy their information when cloned. Rewriting -preserves the original propagator information. +preserves the original propagator information. Space::UseGPI enables global +information for selected posting calls inside a NoGPI scope. Both contexts +restore the previous setting when they leave scope. + +[ENTRY] +Module: int +What: bug +Rank: minor +[DESCRIPTION] +Fix iterator invalidation when counting-based search removes expired +propagator cache entries. The invalid iterator could skip remaining entries +and leave the brancher without a choice. [ENTRY] diff --git a/gecode/int/branch/cbs.hpp b/gecode/int/branch/cbs.hpp index 6c21bcf1f4..263d437428 100755 --- a/gecode/int/branch/cbs.hpp +++ b/gecode/int/branch/cbs.hpp @@ -191,13 +191,15 @@ namespace Gecode { namespace Int { namespace Branch { // We delete unvisited propagators from the log and look for the highest // solution density across all propagators. - for (const auto& kv : logProp) { - unsigned int prop_id = kv.first; - const PropInfo& info = kv.second; - if (!info.visited) - logProp.erase(prop_id); - else if (info.dens > maxSD.dens) - maxSD = {info.var_id, info.val, info.dens}; + for (auto i = logProp.begin(); i != logProp.end(); ) { + if (!i->second.visited) { + i = logProp.erase(i); + } else { + const PropInfo& info = i->second; + if (info.dens > maxSD.dens) + maxSD = {info.var_id, info.val, info.dens}; + ++i; + } } assert(maxSD.dens != -1); diff --git a/gecode/kernel/core.hpp b/gecode/kernel/core.hpp index 95f9d93363..44ed428bc5 100755 --- a/gecode/kernel/core.hpp +++ b/gecode/kernel/core.hpp @@ -2065,7 +2065,8 @@ namespace Gecode { * of this context. Existing propagators are unaffected. Clones retain * local information but start with ordinary posting enabled. * - * Contexts can be nested and restore the previous setting on destruction, + * A nested UseGPI context enables global information for selected + * posting calls. Both contexts restore the previous setting on destruction, * including exception unwinding. The space must outlive the context, * and contexts for the same space must be destroyed in reverse order. */ @@ -2084,6 +2085,31 @@ namespace Gecode { ~NoGPI(void); }; + /** + * \brief Enable global propagator information during scoped posting + * \ingroup TaskModelScript + * + * Overrides an enclosing NoGPI context for new propagators posted into + * this space. Rewriting still inherits the original information. + * Destruction restores the previous setting, including exception + * unwinding. The space must outlive the context, and NoGPI and UseGPI + * contexts for the same space must be destroyed in reverse order. + */ + class UseGPI { + private: + /// Space in which to enable global information + Space& home; + /// Previous setting + bool previous; + UseGPI(const UseGPI&) = delete; + UseGPI& operator =(const UseGPI&) = delete; + public: + /// Enable global information for new propagators in \a home + explicit UseGPI(Space& home); + /// Restore the previous setting + ~UseGPI(void); + }; + /** * \brief Default constructor * \ingroup TaskModelScript @@ -3614,6 +3640,17 @@ namespace Gecode { } + forceinline + Space::UseGPI::UseGPI(Space& h) : home(h), previous(h.no_gpi) { + home.no_gpi = false; + } + + forceinline + Space::UseGPI::~UseGPI(void) { + home.no_gpi = previous; + } + + /* * Propagator * diff --git a/test/afc.cpp b/test/afc.cpp index ed9aa620c9..75c2769555 100644 --- a/test/afc.cpp +++ b/test/afc.cpp @@ -181,6 +181,33 @@ namespace Test { return false; throw 1; } catch (int) {} + if (s.post().afc() != 0.0) + return false; + { + Space::UseGPI use(s); + if (s.post().afc() != 1.0) + return false; + { + Space::UseGPI nested(s); + if (s.post().afc() != 1.0) + return false; + } + { + Space::NoGPI suppress(s); + if (s.post().afc() != 0.0) + return false; + } + if (s.post().afc() != 1.0) + return false; + } + if (s.post().afc() != 0.0) + return false; + try { + Space::UseGPI use(s); + if (s.post().afc() != 1.0) + return false; + throw 1; + } catch (int) {} if (s.post().afc() != 0.0) return false; (void) s.status(); @@ -271,6 +298,7 @@ namespace Test { unsigned int id = ps.propagator().id(); rel(s,s.b,IRT_EQ,1); if (local) { + Space::UseGPI context(s); (void) s.status(); } else { Space::NoGPI context(s); diff --git a/test/branch/int.cpp b/test/branch/int.cpp index 33837242ec..ab0ab575c3 100644 --- a/test/branch/int.cpp +++ b/test/branch/int.cpp @@ -36,6 +36,8 @@ #include #include +#include + namespace Test { namespace Branch { /// Space for testing filtered random tie selection @@ -131,6 +133,62 @@ namespace Test { namespace Branch { FilteredRndTies filtered_rnd_ties; +#ifdef GECODE_HAS_CBS + /// Independent permutations leave one CBS cache entry after another expires + class CBSSpace : public Gecode::Space { + public: + Gecode::IntVarArray x; + CBSSpace(bool local) : x(*this,8,0,3) { + if (local) { + NoGPI suppress(*this); + post(); + } else { + post(); + } + Gecode::cbsbranch(*this,x); + } + void post(void) { + using namespace Gecode; + distinct(*this,IntVarArgs() << x[0] << x[1] << x[2] << x[3],IPL_VAL); + distinct(*this,IntVarArgs() << x[4] << x[5] << x[6] << x[7],IPL_VAL); + } + CBSSpace(CBSSpace& s) : Space(s) { + x.update(*this,s.x); + } + virtual Space* copy(void) { + return new CBSSpace(*this); + } + }; + + /// Cache removal must preserve choices from surviving propagators + class CBS : public Base { + public: + CBS(void) : Base("Int::Branch::CBS",TestTags::check()) {} + virtual bool run(void) { + using namespace Gecode; + for (bool local : {false,true}) { + CBSSpace root(local); + Search::Options o; + o.c_d = 2; + o.a_d = 1; + DFS engine(&root,o); + int n = 0; + while (std::unique_ptr s{engine.next()}) { + for (int i=0; ix.size(); i++) + if (!s->x[i].assigned()) + return false; + n++; + } + if (n != 24*24) + return false; + } + return true; + } + }; + + CBS cbs; +#endif + /// %Test brancher with distinct propagator class Int : public IntTest { public: From c65f45fd49b1656bbd9c4f6afe94b645cd5a1fad Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Wed, 14 Oct 2026 11:31:15 +0200 Subject: [PATCH 3/7] Place posting flag in CBS alignment padding --- gecode/kernel/core.cpp | 6 +++--- gecode/kernel/core.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gecode/kernel/core.cpp b/gecode/kernel/core.cpp index 6d5489f761..63b03c7594 100644 --- a/gecode/kernel/core.cpp +++ b/gecode/kernel/core.cpp @@ -118,7 +118,7 @@ namespace Gecode { VarImpDisposerBase* Space::vd[AllVarConf::idx_d]; #endif - Space::Space(void) : no_gpi(false), mm(ssd.data().sm) { + Space::Space(void) : mm(ssd.data().sm), no_gpi(false) { #ifdef GECODE_HAS_CBS var_id_counter = 0; #endif @@ -723,12 +723,12 @@ namespace Gecode { * */ Space::Space(Space& s) - : no_gpi(false), ssd(s.ssd), + : ssd(s.ssd), mm(ssd.data().sm,s.mm,s.pc.p.n_sub*sizeof(Propagator**)), #ifdef GECODE_HAS_CBS var_id_counter(s.var_id_counter), #endif - d_fst(&Actor::sentinel),d_cur(nullptr),d_lst(nullptr) { + no_gpi(false), d_fst(&Actor::sentinel),d_cur(nullptr),d_lst(nullptr) { #ifdef GECODE_HAS_VAR_DISPOSE for (int i=0; i Date: Thu, 15 Oct 2026 14:42:50 +0200 Subject: [PATCH 4/7] Generalize pointer unmarking and trim GPI tests --- changelog.in | 12 +++------ gecode/kernel/core.cpp | 2 +- gecode/kernel/core.hpp | 40 ++++++++++------------------ gecode/support/marked-pointer.hpp | 10 ++++--- test/afc.cpp | 43 +------------------------------ 5 files changed, 26 insertions(+), 81 deletions(-) diff --git a/changelog.in b/changelog.in index 82f35d7e4e..21287db675 100755 --- a/changelog.in +++ b/changelog.in @@ -78,21 +78,15 @@ Module: kernel What: new Rank: minor [DESCRIPTION] -Add Space::NoGPI for scoped posting without globally retained propagator -information. Scoped propagators keep their identifiers and groups in the -space, have zero AFC, and copy their information when cloned. Rewriting -preserves the original propagator information. Space::UseGPI enables global -information for selected posting calls inside a NoGPI scope. Both contexts -restore the previous setting when they leave scope. +Add Space::NoGPI and Space::UseGPI to control global propagator information +for scoped posting. NoGPI keeps information in the space with zero AFC. [ENTRY] Module: int What: bug Rank: minor [DESCRIPTION] -Fix iterator invalidation when counting-based search removes expired -propagator cache entries. The invalid iterator could skip remaining entries -and leave the brancher without a choice. +Fix iterator invalidation during counting-based search cache cleanup. [ENTRY] diff --git a/gecode/kernel/core.cpp b/gecode/kernel/core.cpp index 63b03c7594..c5ea98eb78 100644 --- a/gecode/kernel/core.cpp +++ b/gecode/kernel/core.cpp @@ -928,7 +928,7 @@ namespace Gecode { Kernel::GPI::Info* i = alloc(1); i->init(ssd.data().gpi.next_pid(),gid); i->afc = 0.0; - return Support::ptrjoin(i,2); + return Support::ptrjoin(i,0b10); } void diff --git a/gecode/kernel/core.hpp b/gecode/kernel/core.hpp index 7ad7e9abc0..ed257fdbb9 100755 --- a/gecode/kernel/core.hpp +++ b/gecode/kernel/core.hpp @@ -2052,23 +2052,15 @@ namespace Gecode { * \brief Suppress global propagator information during scoped posting * \ingroup TaskModelScript * - * New propagators posted into this space use space-local information - * instead of records retained by all spaces in a search. Their AFC is - * always zero, including after AFC initialization or unsharing. They - * retain normal identifiers and groups, so tracing and counting-based - * search remain supported. Group changes for these propagators are - * local to the space; clones copy the current group. Local records are - * reclaimed with the space, even if their propagators subsume earlier. - * AFC-based branching therefore only counts ordinary propagators. - * - * Rewriting inherits the original propagator's information, regardless - * of this context. Existing propagators are unaffected. Clones retain - * local information but start with ordinary posting enabled. - * - * A nested UseGPI context enables global information for selected - * posting calls. Both contexts restore the previous setting on destruction, - * including exception unwinding. The space must outlive the context, - * and contexts for the same space must be destroyed in reverse order. + * New propagators keep their information in the space, with normal + * identifiers and groups but zero AFC. Group changes stay local to the + * space. Clones copy this information and start with ordinary posting + * enabled. Rewriting preserves the original propagator's information. + * + * UseGPI enables global information within this scope. Both contexts + * restore the previous setting on destruction, including unwinding. + * The space must outlive them; contexts for the same space must be + * destroyed in reverse order. */ class NoGPI { private: @@ -2089,11 +2081,8 @@ namespace Gecode { * \brief Enable global propagator information during scoped posting * \ingroup TaskModelScript * - * Overrides an enclosing NoGPI context for new propagators posted into - * this space. Rewriting still inherits the original information. - * Destruction restores the previous setting, including exception - * unwinding. The space must outlive the context, and NoGPI and UseGPI - * contexts for the same space must be destroyed in reverse order. + * Overrides NoGPI for new postings and restores the previous setting + * on destruction. The lifetime and nesting rules of NoGPI also apply. */ class UseGPI { private: @@ -3678,7 +3667,7 @@ namespace Gecode { forceinline bool Propagator::local_info(void) const { - return (reinterpret_cast(gpi_disabled) & 2) != 0; + return (reinterpret_cast(gpi_disabled) & 0b10) != 0; } forceinline bool @@ -3700,8 +3689,7 @@ namespace Gecode { forceinline Kernel::GPI::Info& Propagator::gpi(void) { - return *reinterpret_cast - (reinterpret_cast(gpi_disabled) & ~static_cast(3)); + return *static_cast(Support::funmark<2>(gpi_disabled)); } forceinline @@ -3726,7 +3714,7 @@ namespace Gecode { if (p.local_info()) { Kernel::GPI::Info* i = home.alloc(1); *i = p.gpi(); - gpi_disabled = Support::ptrjoin(i, p.disabled() ? 3 : 2); + gpi_disabled = Support::ptrjoin(i, p.disabled() ? 0b11 : 0b10); } u.advisors = nullptr; assert((u.med == 0) && (u.size == 0)); diff --git a/gecode/support/marked-pointer.hpp b/gecode/support/marked-pointer.hpp index 7fa78e514a..9c0f1c117e 100644 --- a/gecode/support/marked-pointer.hpp +++ b/gecode/support/marked-pointer.hpp @@ -32,6 +32,7 @@ */ #include +#include namespace Gecode { namespace Support { @@ -43,7 +44,8 @@ namespace Gecode { namespace Support { void* unmark(void* p); /// Return marked pointer for \a p (possibly already marked) void* fmark(void* p); - /// Return unmarked pointer for a possibly marked pointer \a p + /// Clear the lowest \a bits bits of a possibly marked pointer \a p + template void* funmark(void* p); /// Split possibly marked pointer \a p into mark \a m and unmarked pointer void* ptrsplit(void* p, ptrdiff_t& m); @@ -68,10 +70,12 @@ namespace Gecode { namespace Support { fmark(void* p) { return reinterpret_cast(reinterpret_cast(p)|1); } + template forceinline void* funmark(void* p) { - return reinterpret_cast(reinterpret_cast(p)& - ~static_cast(1)); + static_assert(bits < sizeof(size_t)*CHAR_BIT, "Too many pointer tag bits"); + return reinterpret_cast(reinterpret_cast(p)& + ~((static_cast(1) << bits)-1)); } forceinline void* ptrsplit(void* p, ptrdiff_t& m) { diff --git a/test/afc.cpp b/test/afc.cpp index 75c2769555..a5441e0416 100644 --- a/test/afc.cpp +++ b/test/afc.cpp @@ -187,11 +187,6 @@ namespace Test { Space::UseGPI use(s); if (s.post().afc() != 1.0) return false; - { - Space::UseGPI nested(s); - if (s.post().afc() != 1.0) - return false; - } { Space::NoGPI suppress(s); if (s.post().afc() != 0.0) @@ -312,47 +307,11 @@ namespace Test { return (s.status() == SS_SOLVED) && s.y.assigned() && (s.y.val() == 3); } - /// Collect posting counts and propagated identifiers - class Tracer : public Gecode::Tracer { - public: - unsigned int posted = 0; - std::vector ids; - virtual void propagate(const Gecode::Space&, - const Gecode::PropagateTraceInfo& p) { - ids.push_back(p.id()); - } - virtual void commit(const Gecode::Space&, - const Gecode::CommitTraceInfo&) {} - virtual void post(const Gecode::Space&, - const Gecode::PostTraceInfo& p) { - posted += p.propagators(); - } - }; - - /// Posting traces and propagation traces must retain distinct identities - bool tracing(void) { - using namespace Gecode; - Tracer t; - TestSpace s; - PropagatorGroup g; - trace(s,g,TE_POST | TE_PROPAGATE,t); - t.posted = 0; - unsigned int a, b; - { - Space::NoGPI context(s); - a = s.post(g).id(); - b = s.post(g).id(); - } - (void) s.status(); - return (t.posted == 2) && (a != b) && - (std::find(t.ids.begin(),t.ids.end(),a) != t.ids.end()) && - (std::find(t.ids.begin(),t.ids.end(),b) != t.ids.end()); - } public: NoGPI(void) : Test::Base("AFC::NoGPI") {} virtual bool run(void) { return scopes() && lifetime() && failure(false) && failure(true) && - rewrite(false) && rewrite(true) && tracing(); + rewrite(false) && rewrite(true); } }; From b0fe5d436cf824621fafe355197598a67ead22e2 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Thu, 15 Oct 2026 14:50:14 +0200 Subject: [PATCH 5/7] Use uintptr_t for pointer tag removal --- gecode/support/marked-pointer.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gecode/support/marked-pointer.hpp b/gecode/support/marked-pointer.hpp index 9c0f1c117e..f5f33751ea 100644 --- a/gecode/support/marked-pointer.hpp +++ b/gecode/support/marked-pointer.hpp @@ -33,6 +33,7 @@ #include #include +#include namespace Gecode { namespace Support { @@ -73,9 +74,10 @@ namespace Gecode { namespace Support { template forceinline void* funmark(void* p) { - static_assert(bits < sizeof(size_t)*CHAR_BIT, "Too many pointer tag bits"); - return reinterpret_cast(reinterpret_cast(p)& - ~((static_cast(1) << bits)-1)); + static_assert(bits < sizeof(std::uintptr_t)*CHAR_BIT, + "Too many pointer tag bits"); + return reinterpret_cast(reinterpret_cast(p)& + ~((static_cast(1) << bits)-1)); } forceinline void* ptrsplit(void* p, ptrdiff_t& m) { From e1ca30e97644fa57e6610cbdd2a7bf7b611069bb Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Thu, 15 Oct 2026 14:54:03 +0200 Subject: [PATCH 6/7] Provide a fallback pointer integer type in Support --- gecode/support/marked-pointer.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/gecode/support/marked-pointer.hpp b/gecode/support/marked-pointer.hpp index f5f33751ea..78e5265dfc 100644 --- a/gecode/support/marked-pointer.hpp +++ b/gecode/support/marked-pointer.hpp @@ -32,11 +32,18 @@ */ #include -#include #include +#include namespace Gecode { namespace Support { + /// Integer type for tagged pointers +#ifdef UINTPTR_MAX + using ptrint_t = std::uintptr_t; +#else + using ptrint_t = ptrdiff_t; +#endif + /// Check whether \a p is marked bool marked(void* p); /// Return marked pointer for unmarked pointer \a p @@ -74,10 +81,10 @@ namespace Gecode { namespace Support { template forceinline void* funmark(void* p) { - static_assert(bits < sizeof(std::uintptr_t)*CHAR_BIT, + static_assert(bits < std::numeric_limits::digits, "Too many pointer tag bits"); - return reinterpret_cast(reinterpret_cast(p)& - ~((static_cast(1) << bits)-1)); + return reinterpret_cast(reinterpret_cast(p)& + ~((static_cast(1) << bits)-1)); } forceinline void* ptrsplit(void* p, ptrdiff_t& m) { From 222ede3aa45df97aa154995c8bf608bd9a4f2f70 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Thu, 15 Oct 2026 15:12:40 +0200 Subject: [PATCH 7/7] Centralize unsigned pointer encodings in Support --- gecode/gist/node.cpp | 4 +-- gecode/gist/node.hpp | 14 ++++----- gecode/int/extensional.hh | 2 +- gecode/int/extensional/compact.hpp | 6 ++-- gecode/int/var-imp/int.hpp | 24 ++++++++-------- gecode/int/view-val-graph.hh | 2 +- gecode/int/view-val-graph/comb-ptr-flag.hpp | 16 +++++++---- gecode/kernel/core.hpp | 15 +++++----- gecode/support.hh | 2 +- gecode/support/int-type.hpp | 20 +++++++++++++ gecode/support/marked-pointer.hpp | 32 +++++++++------------ 11 files changed, 78 insertions(+), 59 deletions(-) diff --git a/gecode/gist/node.cpp b/gecode/gist/node.cpp index ff050b5c6c..99f3066745 100644 --- a/gecode/gist/node.cpp +++ b/gecode/gist/node.cpp @@ -48,7 +48,7 @@ namespace Gecode { namespace Gist { case 1: childrenOrFirstChild = reinterpret_cast( - static_cast(na.allocate(getIndex(na)) << 2)); + static_cast(na.allocate(getIndex(na))) << 2); noOfChildren = 1; setTag(TWO_CHILDREN); break; @@ -57,7 +57,7 @@ namespace Gecode { namespace Gist { int idx = getIndex(na); childrenOrFirstChild = reinterpret_cast( - static_cast(na.allocate(idx) << 2)); + static_cast(na.allocate(idx)) << 2); noOfChildren = -na.allocate(idx); setTag(TWO_CHILDREN); } diff --git a/gecode/gist/node.hpp b/gecode/gist/node.hpp index 27bdc0060c..f51b1013fe 100644 --- a/gecode/gist/node.hpp +++ b/gecode/gist/node.hpp @@ -148,27 +148,27 @@ namespace Gecode { namespace Gist { forceinline unsigned int Node::getTag(void) const { return static_cast - (reinterpret_cast(childrenOrFirstChild) & 3); + (reinterpret_cast(childrenOrFirstChild) & 0b11); } forceinline void Node::setTag(unsigned int tag) { - assert(tag <= 3); + assert(tag <= 0b11); assert(getTag() == UNDET); - childrenOrFirstChild = reinterpret_cast - ( (reinterpret_cast(childrenOrFirstChild) & ~(3)) | tag); + childrenOrFirstChild = + Support::ptrjoin(Support::funmark<2>(childrenOrFirstChild),tag); } forceinline void* Node::getPtr(void) const { - return reinterpret_cast - (reinterpret_cast(childrenOrFirstChild) & ~(3)); + return Support::funmark<2>(childrenOrFirstChild); } forceinline int Node::getFirstChild(void) const { return static_cast - ((reinterpret_cast(childrenOrFirstChild) & ~(3)) >> 2); + ((reinterpret_cast(childrenOrFirstChild) & + ~static_cast(0b11)) >> 2); } forceinline diff --git a/gecode/int/extensional.hh b/gecode/int/extensional.hh index 99ddee0577..e1de6da1c5 100755 --- a/gecode/int/extensional.hh +++ b/gecode/int/extensional.hh @@ -470,7 +470,7 @@ namespace Gecode { namespace Int { namespace Extensional { class CompactStatus { protected: /// A tagged advisor pointer or a status value - ptrdiff_t s; + Support::uptrint_t s; public: /// Type of status enum StatusType { diff --git a/gecode/int/extensional/compact.hpp b/gecode/int/extensional/compact.hpp index 1aba8ae137..503ed43737 100755 --- a/gecode/int/extensional/compact.hpp +++ b/gecode/int/extensional/compact.hpp @@ -155,7 +155,7 @@ namespace Gecode { namespace Int { namespace Extensional { template forceinline typename CompactStatus::StatusType CompactStatus::type(void) const { - return static_cast(s & 3); + return static_cast(s & 0b11); } template @@ -169,8 +169,8 @@ namespace Gecode { namespace Int { namespace Extensional { forceinline void CompactStatus::touched(Advisor& a) { if (type() == NONE) { - s = reinterpret_cast(&a); - assert((s & 3) == SINGLE); + s = reinterpret_cast(&a); + assert((s & 0b11) == SINGLE); } else if ((type() == SINGLE) && !single(a)) { s = MULTIPLE; } diff --git a/gecode/int/var-imp/int.hpp b/gecode/int/var-imp/int.hpp index dcadd00d48..5323323cc4 100644 --- a/gecode/int/var-imp/int.hpp +++ b/gecode/int/var-imp/int.hpp @@ -44,8 +44,8 @@ namespace Gecode { namespace Int { * */ -#define GECODE_INT_RL2PD(r) reinterpret_cast(r) -#define GECODE_INT_PD2RL(p) reinterpret_cast(p) +#define GECODE_INT_RL2PI(r) reinterpret_cast(r) +#define GECODE_INT_PI2RL(p) reinterpret_cast(p) forceinline unsigned int range_width(int min, int max) { @@ -63,30 +63,30 @@ namespace Gecode { namespace Int { forceinline IntVarImp::RangeList::RangeList(int min, int max, RangeList* p, RangeList* n) : _min(min), _max(max) { - _next = GECODE_INT_PD2RL(GECODE_INT_RL2PD(p)^GECODE_INT_RL2PD(n)); + _next = GECODE_INT_PI2RL(GECODE_INT_RL2PI(p)^GECODE_INT_RL2PI(n)); } forceinline IntVarImp::RangeList* IntVarImp::RangeList::next(const RangeList* p) const { - return GECODE_INT_PD2RL(GECODE_INT_RL2PD(_next)^GECODE_INT_RL2PD(p)); + return GECODE_INT_PI2RL(GECODE_INT_RL2PI(_next)^GECODE_INT_RL2PI(p)); } forceinline IntVarImp::RangeList* IntVarImp::RangeList::prev(const RangeList* n) const { - return GECODE_INT_PD2RL(GECODE_INT_RL2PD(_next)^GECODE_INT_RL2PD(n)); + return GECODE_INT_PI2RL(GECODE_INT_RL2PI(_next)^GECODE_INT_RL2PI(n)); } forceinline void IntVarImp::RangeList::prevnext(RangeList* p, RangeList* n) { - _next = GECODE_INT_PD2RL(GECODE_INT_RL2PD(p)^GECODE_INT_RL2PD(n)); + _next = GECODE_INT_PI2RL(GECODE_INT_RL2PI(p)^GECODE_INT_RL2PI(n)); } forceinline void IntVarImp::RangeList::next(RangeList* o, RangeList* n) { - _next = GECODE_INT_PD2RL(GECODE_INT_RL2PD(_next)^ - (GECODE_INT_RL2PD(o)^GECODE_INT_RL2PD(n))); + _next = GECODE_INT_PI2RL(GECODE_INT_RL2PI(_next)^ + (GECODE_INT_RL2PI(o)^GECODE_INT_RL2PI(n))); } forceinline void IntVarImp::RangeList::prev(RangeList* o, RangeList* n) { - _next = GECODE_INT_PD2RL(GECODE_INT_RL2PD(_next)^ - (GECODE_INT_RL2PD(o)^GECODE_INT_RL2PD(n))); + _next = GECODE_INT_PI2RL(GECODE_INT_RL2PI(_next)^ + (GECODE_INT_RL2PI(o)^GECODE_INT_RL2PI(n))); } forceinline void IntVarImp::RangeList::fix(RangeList* n) { @@ -159,8 +159,8 @@ namespace Gecode { namespace Int { home.fl_dispose(this,this); } -#undef GECODE_INT_RL2PD -#undef GECODE_INT_PD2RL +#undef GECODE_INT_RL2PI +#undef GECODE_INT_PI2RL /* * Mainitaining range lists for variable domain diff --git a/gecode/int/view-val-graph.hh b/gecode/int/view-val-graph.hh index 24d7c6f983..0851eae21d 100644 --- a/gecode/int/view-val-graph.hh +++ b/gecode/int/view-val-graph.hh @@ -54,7 +54,7 @@ namespace Gecode { namespace Int { namespace ViewValGraph { class CombPtrFlag { private: /// Store pointer and flag - ptrdiff_t cpf; + Support::uptrint_t cpf; public: /// Initialize with pointer \a p1 and \a p2 CombPtrFlag(T* p1, T* p2); diff --git a/gecode/int/view-val-graph/comb-ptr-flag.hpp b/gecode/int/view-val-graph/comb-ptr-flag.hpp index 022b944236..a2e3abaf0b 100644 --- a/gecode/int/view-val-graph/comb-ptr-flag.hpp +++ b/gecode/int/view-val-graph/comb-ptr-flag.hpp @@ -36,36 +36,40 @@ namespace Gecode { namespace Int { namespace ViewValGraph { template forceinline CombPtrFlag::CombPtrFlag(T* p1, T* p2) - : cpf(reinterpret_cast(p1) ^ reinterpret_cast(p2)) {} + : cpf(reinterpret_cast(p1) ^ + reinterpret_cast(p2)) {} template forceinline T* CombPtrFlag::ptr(T* p) const { - return reinterpret_cast((cpf&~1) ^ reinterpret_cast(p)); + return reinterpret_cast + ((cpf & ~static_cast(0b1)) ^ + reinterpret_cast(p)); } template forceinline int CombPtrFlag::is_set(void) const { - return static_cast(cpf&1); + return static_cast(cpf & 0b1); } template forceinline void CombPtrFlag::set(void) { - cpf |= 1; + cpf |= 0b1; } template forceinline void CombPtrFlag::unset(void) { - cpf &= ~1; + cpf &= ~static_cast(0b1); } template forceinline void CombPtrFlag::init(T* p1, T* p2) { - cpf = reinterpret_cast(p1) ^ reinterpret_cast(p2); + cpf = reinterpret_cast(p1) ^ + reinterpret_cast(p2); } }}} diff --git a/gecode/kernel/core.hpp b/gecode/kernel/core.hpp index ed257fdbb9..2f8b79326a 100755 --- a/gecode/kernel/core.hpp +++ b/gecode/kernel/core.hpp @@ -931,7 +931,7 @@ namespace Gecode { }; protected: /// Encoding a tagged pointer or a tagged group id - ptrdiff_t who; + Support::uptrint_t who; /// Record that propagator \a p is executing void propagator(Propagator& p); /// Record that brancher \a b is executing @@ -3485,15 +3485,15 @@ namespace Gecode { */ forceinline void ViewTraceInfo::propagator(Propagator& p) { - who = reinterpret_cast(&p) | PROPAGATOR; + who = reinterpret_cast(&p) | PROPAGATOR; } forceinline void ViewTraceInfo::brancher(Brancher& b) { - who = reinterpret_cast(&b) | BRANCHER; + who = reinterpret_cast(&b) | BRANCHER; } forceinline void ViewTraceInfo::post(PropagatorGroup g) { - who = (g.id() << 2) | POST; + who = (static_cast(g.id()) << 2) | POST; } forceinline void ViewTraceInfo::other(void) { @@ -3501,7 +3501,7 @@ namespace Gecode { } forceinline ViewTraceInfo::What ViewTraceInfo::what(void) const { - return static_cast(who & 3); + return static_cast(who & 0b11); } forceinline const Propagator& ViewTraceInfo::propagator(void) const { @@ -3512,7 +3512,8 @@ namespace Gecode { forceinline const Brancher& ViewTraceInfo::brancher(void) const { assert(what() == BRANCHER); - return *reinterpret_cast(who & ~3); + return *reinterpret_cast + (who & ~static_cast(0b11)); } forceinline PropagatorGroup ViewTraceInfo::post(void) const { @@ -3667,7 +3668,7 @@ namespace Gecode { forceinline bool Propagator::local_info(void) const { - return (reinterpret_cast(gpi_disabled) & 0b10) != 0; + return (reinterpret_cast(gpi_disabled) & 0b10) != 0; } forceinline bool diff --git a/gecode/support.hh b/gecode/support.hh index 79f4894608..63d1b46c54 100644 --- a/gecode/support.hh +++ b/gecode/support.hh @@ -99,8 +99,8 @@ #include #include #include -#include #include +#include /* * Common datastructures and algorithms diff --git a/gecode/support/int-type.hpp b/gecode/support/int-type.hpp index 2a089b3c27..f9c47c3822 100644 --- a/gecode/support/int-type.hpp +++ b/gecode/support/int-type.hpp @@ -32,9 +32,29 @@ */ #include +#include +#include +#include +#include namespace Gecode { namespace Support { + /** + * \brief Unsigned integer for pointer encodings + * + * Prefer uintptr_t for pointer round trips. If unavailable, use the + * unsigned counterpart of ptrdiff_t and require room for every pointer + * bit. Unsigned arithmetic keeps tag masks and shifts well-defined. + * Tagging assumes alignment leaves the low pointer bits unused. + */ +#ifdef UINTPTR_MAX + using uptrint_t = std::uintptr_t; +#else + using uptrint_t = std::make_unsigned_t; + static_assert(std::numeric_limits::digits >= sizeof(void*)*CHAR_BIT, + "Pointer encoding requires a pointer-wide integer"); +#endif + /// Description of integer types enum IntType { IT_CHAR = 0, ///< char integer type diff --git a/gecode/support/marked-pointer.hpp b/gecode/support/marked-pointer.hpp index 78e5265dfc..08b73cedde 100644 --- a/gecode/support/marked-pointer.hpp +++ b/gecode/support/marked-pointer.hpp @@ -32,18 +32,10 @@ */ #include -#include #include namespace Gecode { namespace Support { - /// Integer type for tagged pointers -#ifdef UINTPTR_MAX - using ptrint_t = std::uintptr_t; -#else - using ptrint_t = ptrdiff_t; -#endif - /// Check whether \a p is marked bool marked(void* p); /// Return marked pointer for unmarked pointer \a p @@ -62,39 +54,41 @@ namespace Gecode { namespace Support { forceinline bool marked(void* p) { - return (reinterpret_cast(p) & 1) != 0; + return (reinterpret_cast(p) & 0b1) != 0; } forceinline void* mark(void* p) { assert(!marked(p)); - return reinterpret_cast(reinterpret_cast(p)+1); + return reinterpret_cast(reinterpret_cast(p) | 0b1); } forceinline void* unmark(void* p) { assert(marked(p)); - return reinterpret_cast(reinterpret_cast(p)-1); + return funmark(p); } forceinline void* fmark(void* p) { - return reinterpret_cast(reinterpret_cast(p)|1); + return reinterpret_cast(reinterpret_cast(p) | 0b1); } template forceinline void* funmark(void* p) { - static_assert(bits < std::numeric_limits::digits, + static_assert(bits < std::numeric_limits::digits, "Too many pointer tag bits"); - return reinterpret_cast(reinterpret_cast(p)& - ~((static_cast(1) << bits)-1)); + return reinterpret_cast(reinterpret_cast(p)& + ~((static_cast(1) << bits)-1)); } forceinline void* ptrsplit(void* p, ptrdiff_t& m) { - ptrdiff_t mp = reinterpret_cast(p); - m = mp & 1; - return reinterpret_cast(mp & ~static_cast(1)); + uptrint_t mp = reinterpret_cast(p); + m = static_cast(mp & 0b1); + return funmark(p); } forceinline void* ptrjoin(void* p, ptrdiff_t m) { - return reinterpret_cast(reinterpret_cast(p) | m); + assert((reinterpret_cast(p) & static_cast(m)) == 0); + return reinterpret_cast(reinterpret_cast(p) | + static_cast(m)); } }}