| 1 | // A revision of <collections/list.hfa>.
|
|---|
| 2 | // Supports headless (and headed) lists.
|
|---|
| 3 | // It's basically all good, and should become the official libcfa one, except:
|
|---|
| 4 | // - perf tests compare this one ("thesis's work") to libcfa ("old, before supporting headless")
|
|---|
| 5 | // - in libcfa, some uses of list.hfa make assumptions about list's private representation, which need fixing
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 | //
|
|---|
| 11 | // Cforall Version 1.0.0 Copyright (C) 2020 University of Waterloo
|
|---|
| 12 | //
|
|---|
| 13 | // The contents of this file are covered under the licence agreement in the
|
|---|
| 14 | // file "LICENCE" distributed with Cforall.
|
|---|
| 15 | //
|
|---|
| 16 | // list -- lets a user-defined stuct form intrusive linked lists
|
|---|
| 17 | //
|
|---|
| 18 | // Author : Michael Brooks
|
|---|
| 19 | // Created On : Wed Apr 22 18:00:00 2020
|
|---|
| 20 | // Last Modified By : Peter A. Buhr
|
|---|
| 21 | // Last Modified On : Thu Feb 2 11:32:26 2023
|
|---|
| 22 | // Update Count : 2
|
|---|
| 23 | //
|
|---|
| 24 |
|
|---|
| 25 | #pragma once
|
|---|
| 26 |
|
|---|
| 27 | #include <assert.h>
|
|---|
| 28 |
|
|---|
| 29 | forall( Decorator &, T & )
|
|---|
| 30 | struct tytagref {
|
|---|
| 31 | inline T &;
|
|---|
| 32 | };
|
|---|
| 33 |
|
|---|
| 34 | forall( tOuter &, tMid &, tInner & )
|
|---|
| 35 | trait embedded {
|
|---|
| 36 | tytagref( tMid, tInner ) ?`inner( tOuter & );
|
|---|
| 37 | };
|
|---|
| 38 |
|
|---|
| 39 | // embedded is reflexive, with no info (void) as the type tag
|
|---|
| 40 | forall (T &)
|
|---|
| 41 | static inline tytagref(void, T) ?`inner ( T & this ) { tytagref( void, T ) ret = {this}; return ret; }
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 | //
|
|---|
| 45 | // P9_EMBEDDED: Use on every case of plan-9 inheritance, to make "implements embedded" be a closure of plan-9 inheritance.
|
|---|
| 46 | //
|
|---|
| 47 | // struct foo {
|
|---|
| 48 | // int a, b, c;
|
|---|
| 49 | // inline (bar);
|
|---|
| 50 | // };
|
|---|
| 51 | // P9_EMBEDDED( foo, bar )
|
|---|
| 52 | //
|
|---|
| 53 |
|
|---|
| 54 | // usual version, for structs that are top-level declarations
|
|---|
| 55 | #define P9_EMBEDDED( derived, immedBase ) P9_EMBEDDED_DECL_( derived, immedBase, static ) P9_EMBEDDED_BDY_( immedBase )
|
|---|
| 56 |
|
|---|
| 57 | // special version, for structs that are declared in functions
|
|---|
| 58 | #define P9_EMBEDDED_INFUNC( derived, immedBase ) P9_EMBEDDED_DECL_( derived, immedBase, ) P9_EMBEDDED_BDY_( immedBase )
|
|---|
| 59 |
|
|---|
| 60 | // forward declarations of both the above; generally not needed
|
|---|
| 61 | // may help you control where the P9_EMBEEDED cruft goes, in case "right after the stuct" isn't where you want it
|
|---|
| 62 | #define P9_EMBEDDED_FWD( derived, immedBase ) P9_EMBEDDED_DECL_( derived, immedBase, static ) ;
|
|---|
| 63 | #define P9_EMBEDDED_FWD_INFUNC( derived, immedBase ) auto P9_EMBEDDED_DECL_( derived, immedBase, ) ;
|
|---|
| 64 |
|
|---|
| 65 | // private helpers
|
|---|
| 66 | #define P9_EMBEDDED_DECL_( derived, immedBase, STORAGE ) \
|
|---|
| 67 | forall( Tbase &, TdiscardPath & | { tytagref( TdiscardPath, Tbase ) ?`inner( immedBase & ); } ) \
|
|---|
| 68 | STORAGE inline tytagref(immedBase, Tbase) ?`inner( derived & this )
|
|---|
| 69 |
|
|---|
| 70 | #define P9_EMBEDDED_BDY_( immedBase ) { \
|
|---|
| 71 | immedBase & ib = this; \
|
|---|
| 72 | Tbase & b = ib`inner; \
|
|---|
| 73 | tytagref(immedBase, Tbase) result = { b }; \
|
|---|
| 74 | return result; \
|
|---|
| 75 | }
|
|---|
| 76 |
|
|---|
| 77 | #define EMBEDDED_VIA( OUTER, MID, INNER ) \
|
|---|
| 78 | (struct { tytagref(MID, INNER) ( * ?`inner ) ( OUTER & ); }){ ?`inner }
|
|---|
| 79 |
|
|---|
| 80 | #define DLINK_VIA( TE, TLINK ) \
|
|---|
| 81 | EMBEDDED_VIA( TE, TLINK, dlink(TE) )
|
|---|
| 82 |
|
|---|
| 83 | // The origin is the position encountered at the start of iteration,
|
|---|
| 84 | // signifying, "need to advance to the first element," and at the end
|
|---|
| 85 | // of iteration, signifying, "no more elements." Normal comsumption of
|
|---|
| 86 | // an iterator runs advance as the first step, and uses the return
|
|---|
| 87 | // of advance as a guard, before dereferencing the iterator. So
|
|---|
| 88 | // normal consumption of an iterator does not dereference an iterator
|
|---|
| 89 | // in origin position. The value of a pointer (underlying a refence)
|
|---|
| 90 | // that is exposed publicly as an iteraor, and also a pointer stored
|
|---|
| 91 | // internally in a link field, is tagged, to indicate "is the origin"
|
|---|
| 92 | // (internally, is the list-head sentinel node), or untagged, to indicate
|
|---|
| 93 | // "is a regular node." Intent is to help a user who dereferences an
|
|---|
| 94 | // iterator in origin position (which would be an API-use error on their
|
|---|
| 95 | // part), by failing fast.
|
|---|
| 96 |
|
|---|
| 97 | #ifdef __EXPERIMENTAL_DISABLE_OTAG__ // Perf experimention alt mode
|
|---|
| 98 |
|
|---|
| 99 | // With origin tagging disabled, iteration never reports "no more elements."
|
|---|
| 100 | // In this mode, the list API is buggy.
|
|---|
| 101 | // This mode is used to quantify the cost of the normal tagging scheme.
|
|---|
| 102 |
|
|---|
| 103 | #define ORIGIN_TAG_ENABL(p) (p)
|
|---|
| 104 | #define ORIGIN_TAG_CLEAR(p) (p)
|
|---|
| 105 | #define ORIGIN_TAG_QUERY(p) 0
|
|---|
| 106 | #define ORIGIN_TAG_ASGN(p, v) (p)
|
|---|
| 107 | #define ORIGIN_TAG_EITHER(p, v) (p)
|
|---|
| 108 | #define ORIGIN_TAG_NEQ(v1, v2) 0
|
|---|
| 109 |
|
|---|
| 110 | #define TAGSONLY(...)
|
|---|
| 111 | #define NOTAGS(...) __VA_ARGS__
|
|---|
| 112 |
|
|---|
| 113 | #else // Normal
|
|---|
| 114 |
|
|---|
| 115 | #if defined( __x86_64 )
|
|---|
| 116 | // Preferred case: tag in the most-significant bit. Dereference
|
|---|
| 117 | // has been shown to segfault consistently. Maintenance should
|
|---|
| 118 | // list more architectures as "ok" here, to let them use the
|
|---|
| 119 | // preferred case, when valid.
|
|---|
| 120 | #define ORIGIN_TAG_BITNO ( 8 * sizeof( size_t ) - 1 )
|
|---|
| 121 | #else
|
|---|
| 122 | // Fallback case: tag in the least-significant bit. Dereference
|
|---|
| 123 | // will often give an alignment error, but may not, e.g. if
|
|---|
| 124 | // accessing a char-typed member. 32-bit x86 uses the most-
|
|---|
| 125 | // significant bit for real room on the heap.
|
|---|
| 126 | #define ORIGIN_TAG_BITNO 0
|
|---|
| 127 | #endif
|
|---|
| 128 |
|
|---|
| 129 | #define ORIGIN_TAG_MASK (((size_t)1) << ORIGIN_TAG_BITNO)
|
|---|
| 130 |
|
|---|
| 131 | #define ORIGIN_TAG_ENABL(p) ((p) | ORIGIN_TAG_MASK)
|
|---|
| 132 | #define ORIGIN_TAG_CLEAR(p) ((p) & ~ORIGIN_TAG_MASK)
|
|---|
| 133 | #define ORIGIN_TAG_QUERY(p) ((p) & ORIGIN_TAG_MASK)
|
|---|
| 134 |
|
|---|
| 135 | #define ORIGIN_TAG_ASGN(p, v) ( \
|
|---|
| 136 | verify( ! ORIGIN_TAG_QUERY(p) && "p had no tagbit" ), \
|
|---|
| 137 | ORIGIN_TAG_EITHER((p), (v)) \
|
|---|
| 138 | )
|
|---|
| 139 |
|
|---|
| 140 | #define ORIGIN_TAG_EITHER(p, v) ( \
|
|---|
| 141 | verify( ! ORIGIN_TAG_CLEAR(v) && "v is a pure tagbit" ), \
|
|---|
| 142 | ( (p) | (v) ) \
|
|---|
| 143 | )
|
|---|
| 144 |
|
|---|
| 145 | #define ORIGIN_TAG_NEQ(v1, v2) ( \
|
|---|
| 146 | verify( ! ORIGIN_TAG_CLEAR(v1) && "v1 is a pure tagbit" ), \
|
|---|
| 147 | verify( ! ORIGIN_TAG_CLEAR(v2) && "v2 is a pure tagbit" ), \
|
|---|
| 148 | ( (v1) ^ (v2) ) \
|
|---|
| 149 | )
|
|---|
| 150 |
|
|---|
| 151 | #define TAGSONLY(...) __VA_ARGS__
|
|---|
| 152 | #define NOTAGS(...)
|
|---|
| 153 |
|
|---|
| 154 | #endif
|
|---|
| 155 |
|
|---|
| 156 |
|
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
|
|---|
| 160 |
|
|---|
| 161 | #ifdef __EXPERIMENTAL_LOOSE_SINGLES__ // Perf experimention alt mode
|
|---|
| 162 |
|
|---|
| 163 | // In loose-singles mode, the ability to answer an "is listed" query is disabled, as is "to insert an element,
|
|---|
| 164 | // it must not be listed already" checking. The user must know separately whether an element is listed.
|
|---|
| 165 | // Other than inserting it, any list-api action on an unlisted element is undefined. Notably, list iteration
|
|---|
| 166 | // starting from an unlisted element is not defined to respond "no more elements," and may instead continue
|
|---|
| 167 | // iterating from a formerly occupied list position. This mode matches LQ usage.
|
|---|
| 168 |
|
|---|
| 169 | #define NOLOOSE(...)
|
|---|
| 170 | #define LOOSEONLY(...) __VA_ARGS__
|
|---|
| 171 |
|
|---|
| 172 | #else // Normal
|
|---|
| 173 |
|
|---|
| 174 | #define NOLOOSE(...) __VA_ARGS__
|
|---|
| 175 | #define LOOSEONLY(...)
|
|---|
| 176 |
|
|---|
| 177 | #endif
|
|---|
| 178 |
|
|---|
| 179 |
|
|---|
| 180 |
|
|---|
| 181 |
|
|---|
| 182 |
|
|---|
| 183 |
|
|---|
| 184 |
|
|---|
| 185 |
|
|---|
| 186 | // struct workaround0_t {};
|
|---|
| 187 |
|
|---|
| 188 | forall( tE & ) {
|
|---|
| 189 |
|
|---|
| 190 | struct dlink;
|
|---|
| 191 |
|
|---|
| 192 | // do not use; presence of the field declaration unblocks ability to define dlink (#304)
|
|---|
| 193 | struct __dlink_selfref_workaround_t {
|
|---|
| 194 | dlink(tE) *ref_notSelfRef;
|
|---|
| 195 | };
|
|---|
| 196 |
|
|---|
| 197 | struct dlink {
|
|---|
| 198 | dlink(tE) *next; // TODO: rename with $
|
|---|
| 199 | dlink(tE) *prev;
|
|---|
| 200 | };
|
|---|
| 201 |
|
|---|
| 202 | static inline void ?{}( dlink(tE) & this ) {
|
|---|
| 203 | NOLOOSE(
|
|---|
| 204 | dlink(tE) * toSelf = & this;
|
|---|
| 205 | size_t toSelfNum = (size_t) toSelf;
|
|---|
| 206 | size_t toSelfNumTagged = ORIGIN_TAG_ENABL( toSelfNum );
|
|---|
| 207 | dlink(tE) * toSelfPtrTagged = (dlink(tE) *) toSelfNumTagged;
|
|---|
| 208 | toSelf = toSelfPtrTagged;
|
|---|
| 209 | (this.next){ toSelf };
|
|---|
| 210 | (this.prev){ toSelf };
|
|---|
| 211 | )
|
|---|
| 212 | }
|
|---|
| 213 |
|
|---|
| 214 | // You can "copy" a dlink. But the result won't be linked.
|
|---|
| 215 | // Lets you copy what you inline the dlink into.
|
|---|
| 216 | static inline void ?{}( dlink(tE) & this, dlink(tE) ) {
|
|---|
| 217 | this{};
|
|---|
| 218 | }
|
|---|
| 219 |
|
|---|
| 220 | forall( tLinks & = dlink(tE) | embedded(tE, tLinks, dlink(tE)) ) {
|
|---|
| 221 | struct dlist {
|
|---|
| 222 | inline dlink(tE);
|
|---|
| 223 | };
|
|---|
| 224 |
|
|---|
| 225 | static inline tE * $get_list_origin_addr( dlist(tE, tLinks) & lst ) {
|
|---|
| 226 | dlink(tE) & link_from_null = ( * (tE *) 0p )`inner;
|
|---|
| 227 | ptrdiff_t link_offset = (ptrdiff_t) & link_from_null;
|
|---|
| 228 | size_t origin_addr = ((size_t) & lst) - link_offset;
|
|---|
| 229 | size_t preResult = ORIGIN_TAG_ENABL( origin_addr );
|
|---|
| 230 | return (tE *)preResult;
|
|---|
| 231 | }
|
|---|
| 232 |
|
|---|
| 233 | static inline void ?{}( dlist(tE, tLinks) & this ) {
|
|---|
| 234 | NOLOOSE(
|
|---|
| 235 | ( (dlink(tE) &) this ){};
|
|---|
| 236 | )
|
|---|
| 237 | LOOSEONLY(
|
|---|
| 238 | dlink(tE) * listOrigin = (dlink(tE) *) $get_list_origin_addr( this );
|
|---|
| 239 | dlink( tE ) & thisl = this;
|
|---|
| 240 | (thisl.prev) = listOrigin;
|
|---|
| 241 | (thisl.next) = listOrigin;
|
|---|
| 242 | )
|
|---|
| 243 | }
|
|---|
| 244 | }
|
|---|
| 245 | }
|
|---|
| 246 |
|
|---|
| 247 | forall( tE & ) {
|
|---|
| 248 | #ifdef __EXPERIMENTAL_DISABLE_OTAG__ // Perf experimention alt mode
|
|---|
| 249 | static inline size_t origin_tag_query_arith$( tE & raw ) {
|
|---|
| 250 | return 0;
|
|---|
| 251 | }
|
|---|
| 252 | static inline tE & nullif$( tE & val, size_t arith_ctrl ) {
|
|---|
| 253 | verify( arith_ctrl == 0 ); (void) arith_ctrl;
|
|---|
| 254 | return val;
|
|---|
| 255 | }
|
|---|
| 256 | #else // Normal
|
|---|
| 257 | // like ORIGIN_TAG_QUERY, but return is arithmetic number 0 or 1 (rather than 0 or non-0)
|
|---|
| 258 | static inline size_t origin_tag_query_arith$( tE & raw ) {
|
|---|
| 259 | size_t ret = (((size_t) & raw) >> ORIGIN_TAG_BITNO) & 1;
|
|---|
| 260 | verify( ORIGIN_TAG_QUERY( (size_t) & raw ) ? ret == 1 : ret == 0 );
|
|---|
| 261 | return ret;
|
|---|
| 262 | }
|
|---|
| 263 | // Requires arith_ctrl being 0 or 1.
|
|---|
| 264 | // When 0, passes val through; when 1, returns null reference.
|
|---|
| 265 | // Importantly, implemented without jumps or tests.
|
|---|
| 266 | static inline tE & nullif$( tE & val, size_t arith_ctrl ) {
|
|---|
| 267 | verify( ! ORIGIN_TAG_QUERY( (size_t) & val ) );
|
|---|
| 268 | verify( arith_ctrl == 0 || arith_ctrl == 1 );
|
|---|
| 269 | size_t mask_ctrl = ~ - arith_ctrl;
|
|---|
| 270 | verify( arith_ctrl == 0 && mask_ctrl == -1 || arith_ctrl == 1 && mask_ctrl ==0 );
|
|---|
| 271 | tE & ret = * (tE*) ( ((size_t) & val) & mask_ctrl);
|
|---|
| 272 | verify( arith_ctrl == 0 && &ret == &val || arith_ctrl == 1 && &ret == 0p );
|
|---|
| 273 | return ret;
|
|---|
| 274 | }
|
|---|
| 275 | #endif
|
|---|
| 276 | }
|
|---|
| 277 |
|
|---|
| 278 | // Compile-time memory (cmem) barrier
|
|---|
| 279 | // Prevents the optimizer from reordering instructions across it
|
|---|
| 280 | // Originally included for correctness, though a broken state is not known to be reproducible.
|
|---|
| 281 | // Found to have a critical impact on performance:
|
|---|
| 282 | // - in the positions given by default: generally optimal
|
|---|
| 283 | // - absent: sometimes much slower, depending on the test harness
|
|---|
| 284 | // - in positions (that my be influenced by a principle but) that are arbitrary wrt microarchitecture: typically, much slower
|
|---|
| 285 | #ifdef __EXPERIMENTAL_DISABLE_CMEM_BARRIER__
|
|---|
| 286 | // upon request, disable cmem barriers
|
|---|
| 287 | #define MAYBE_CMEM_BARRIER
|
|---|
| 288 | #else
|
|---|
| 289 | // by default, enable cmem barriers
|
|---|
| 290 | #define MAYBE_CMEM_BARRIER asm( "" : : : "memory" )
|
|---|
| 291 | #endif
|
|---|
| 292 |
|
|---|
| 293 | // Insert read (location)
|
|---|
| 294 | // One of the read operations that occurs during an insert operation was found to be performace-critical under certain harnesses.
|
|---|
| 295 | // Arguably, the position should not matter if cmem barriers are off. Treating the factors as independent allows for measuring this idea.
|
|---|
| 296 | #ifdef __EXPERIMENTAL_DELAY_INSERT_READ__
|
|---|
| 297 | // upon request: do the read late (between the cmem barriers); this location is where the read was originally found when this insert read first became a performance-perterbing hypothesis
|
|---|
| 298 | #define MAYBE_INSERT_READ_EARLY(...)
|
|---|
| 299 | #define MAYBE_INSERT_READ_LATE(...) __VA_ARGS__
|
|---|
| 300 | #else
|
|---|
| 301 | // by default: do the read early (before the first cmem barrier); better performance has been seen here
|
|---|
| 302 | #define MAYBE_INSERT_READ_EARLY(...) __VA_ARGS__
|
|---|
| 303 | #define MAYBE_INSERT_READ_LATE(...)
|
|---|
| 304 | #endif
|
|---|
| 305 |
|
|---|
| 306 | forall( tE &, tLinks & | embedded( tE, tLinks, dlink(tE) ) ) {
|
|---|
| 307 |
|
|---|
| 308 | static inline void insert_after(tE & list_pos, tE &to_insert) {
|
|---|
| 309 | size_t list_pos_tag = ORIGIN_TAG_QUERY((size_t) & list_pos); // a request to insert after the origin is fine
|
|---|
| 310 | tE & list_pos_real = * (tE *) ORIGIN_TAG_CLEAR((size_t) & list_pos);
|
|---|
| 311 | verify (&list_pos_real != 0p);
|
|---|
| 312 |
|
|---|
| 313 | verify (&to_insert != 0p);
|
|---|
| 314 | NOLOOSE(
|
|---|
| 315 | verify(! ORIGIN_TAG_QUERY((size_t) & to_insert));
|
|---|
| 316 | )
|
|---|
| 317 | dlink(tE) & linkToInsert = to_insert`inner;
|
|---|
| 318 | NOLOOSE(
|
|---|
| 319 | TAGSONLY(
|
|---|
| 320 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.prev));
|
|---|
| 321 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.next));
|
|---|
| 322 | )
|
|---|
| 323 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.prev) == (size_t)&linkToInsert);
|
|---|
| 324 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.next) == (size_t)&linkToInsert);
|
|---|
| 325 | )
|
|---|
| 326 | dlink(tE) & list_pos_links = list_pos_real`inner;
|
|---|
| 327 | MAYBE_INSERT_READ_EARLY(
|
|---|
| 328 | dlink(tE) & afterLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.next );
|
|---|
| 329 | )
|
|---|
| 330 | MAYBE_CMEM_BARRIER;
|
|---|
| 331 | size_t list_pos_links_num = (size_t)(& list_pos_links);
|
|---|
| 332 | size_t to_insert_prev_num = ORIGIN_TAG_ASGN(list_pos_links_num, list_pos_tag);
|
|---|
| 333 | dlink(tE) * to_insert_prev = (dlink(tE)*)to_insert_prev_num;
|
|---|
| 334 | linkToInsert.prev = to_insert_prev;
|
|---|
| 335 | linkToInsert.next = list_pos_links.next;
|
|---|
| 336 | MAYBE_INSERT_READ_LATE(
|
|---|
| 337 | dlink(tE) & afterLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.next );
|
|---|
| 338 | )
|
|---|
| 339 | size_t afterLinks_prev_tag = ORIGIN_TAG_QUERY((size_t)afterLinks.prev);
|
|---|
| 340 | size_t linkToInsert_num = (size_t)(& linkToInsert);
|
|---|
| 341 | size_t afterLinks_prev_num = ORIGIN_TAG_ASGN(linkToInsert_num, ORIGIN_TAG_NEQ(list_pos_tag, afterLinks_prev_tag));
|
|---|
| 342 | afterLinks.prev = (dlink(tE)*)(afterLinks_prev_num);
|
|---|
| 343 | list_pos_links.next = &linkToInsert;
|
|---|
| 344 | MAYBE_CMEM_BARRIER;
|
|---|
| 345 | }
|
|---|
| 346 |
|
|---|
| 347 | static inline void insert_before(tE & list_pos, tE &to_insert) {
|
|---|
| 348 | size_t list_pos_tag = ORIGIN_TAG_QUERY((size_t) & list_pos); // a request to insert before the origin is fine
|
|---|
| 349 | tE & list_pos_real = * (tE *) ORIGIN_TAG_CLEAR((size_t) & list_pos);
|
|---|
| 350 | verify (&list_pos_real != 0p);
|
|---|
| 351 |
|
|---|
| 352 | verify (&to_insert != 0p);
|
|---|
| 353 | NOLOOSE(
|
|---|
| 354 | verify(! ORIGIN_TAG_QUERY((size_t) & to_insert));
|
|---|
| 355 | )
|
|---|
| 356 | dlink(tE) & linkToInsert = to_insert`inner;
|
|---|
| 357 | NOLOOSE(
|
|---|
| 358 | TAGSONLY(
|
|---|
| 359 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.prev));
|
|---|
| 360 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.next));
|
|---|
| 361 | )
|
|---|
| 362 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.prev) == (size_t)&linkToInsert);
|
|---|
| 363 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.next) == (size_t)&linkToInsert);
|
|---|
| 364 | )
|
|---|
| 365 | dlink(tE) & list_pos_links = list_pos_real`inner;
|
|---|
| 366 | MAYBE_INSERT_READ_EARLY(
|
|---|
| 367 | dlink(tE) & beforeLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.prev );
|
|---|
| 368 | )
|
|---|
| 369 | MAYBE_CMEM_BARRIER;
|
|---|
| 370 | size_t list_pos_links_num = (size_t)(& list_pos_links);
|
|---|
| 371 | size_t to_insert_next_num = ORIGIN_TAG_ASGN(list_pos_links_num, list_pos_tag);
|
|---|
| 372 | dlink(tE) * to_insert_next = (dlink(tE)*)to_insert_next_num;
|
|---|
| 373 | linkToInsert.next = to_insert_next;
|
|---|
| 374 | linkToInsert.prev = list_pos_links.prev;
|
|---|
| 375 | MAYBE_INSERT_READ_LATE(
|
|---|
| 376 | dlink(tE) & beforeLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.prev );
|
|---|
| 377 | )
|
|---|
| 378 | size_t beforeLinks_next_tag = ORIGIN_TAG_QUERY((size_t)beforeLinks.next);
|
|---|
| 379 | size_t linkToInsert_num = (size_t)(& linkToInsert);
|
|---|
| 380 | size_t beforeLinks_next_num = ORIGIN_TAG_ASGN(linkToInsert_num, ORIGIN_TAG_NEQ(list_pos_tag, beforeLinks_next_tag));
|
|---|
| 381 | beforeLinks.next = (dlink(tE)*)(beforeLinks_next_num);
|
|---|
| 382 | list_pos_links.prev = &linkToInsert;
|
|---|
| 383 | MAYBE_CMEM_BARRIER;
|
|---|
| 384 | }
|
|---|
| 385 |
|
|---|
| 386 | static inline tE & remove(tE & list_pos) {
|
|---|
| 387 | verify( ! ORIGIN_TAG_QUERY((size_t) & list_pos) );
|
|---|
| 388 | verify (&list_pos != 0p);
|
|---|
| 389 |
|
|---|
| 390 | dlink(tE) & list_pos_links = list_pos`inner;
|
|---|
| 391 | dlink(tE) & before_raw = * list_pos_links.prev;
|
|---|
| 392 | dlink(tE) & before_links = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) & before_raw );
|
|---|
| 393 | size_t before_links_next_tag = ORIGIN_TAG_QUERY( (size_t) (before_links.next) );
|
|---|
| 394 |
|
|---|
| 395 | dlink(tE) & after_raw = * list_pos_links.next;
|
|---|
| 396 | dlink(tE) & after_links = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) & after_raw );
|
|---|
| 397 | size_t after_links_prev_tag = ORIGIN_TAG_QUERY( (size_t) (after_links.prev) );
|
|---|
| 398 |
|
|---|
| 399 | size_t before_links_next_rslt = ORIGIN_TAG_EITHER( ((size_t) & after_raw), before_links_next_tag );
|
|---|
| 400 | size_t after_links_prev_rslt = ORIGIN_TAG_EITHER( ((size_t) & before_raw), after_links_prev_tag );
|
|---|
| 401 | before_links.next = (dlink(tE) *) before_links_next_rslt;
|
|---|
| 402 | after_links.prev = (dlink(tE) *) after_links_prev_rslt;
|
|---|
| 403 |
|
|---|
| 404 | NOLOOSE(
|
|---|
| 405 | MAYBE_CMEM_BARRIER;
|
|---|
| 406 | size_t list_pos_links_num = (size_t) &list_pos_links;
|
|---|
| 407 | size_t list_pos_links_tagged_num = ORIGIN_TAG_ENABL( list_pos_links_num );
|
|---|
| 408 | list_pos_links.next = list_pos_links.prev = (dlink(tE)*) list_pos_links_tagged_num;
|
|---|
| 409 | MAYBE_CMEM_BARRIER;
|
|---|
| 410 | )
|
|---|
| 411 | return list_pos;
|
|---|
| 412 | }
|
|---|
| 413 |
|
|---|
| 414 | static inline tE & downcast$( tytagref( tLinks, dlink(tE) ) ref ) {
|
|---|
| 415 | dlink(tE) & lnk = ref;
|
|---|
| 416 | dlink(tE) & link_from_null = ( * (tE *) 0p )`inner;
|
|---|
| 417 | ptrdiff_t link_offset = (ptrdiff_t) & link_from_null;
|
|---|
| 418 | size_t elm_addr = ((size_t) & lnk) - link_offset;
|
|---|
| 419 | return * (tE*) elm_addr;
|
|---|
| 420 | }
|
|---|
| 421 |
|
|---|
| 422 | static inline tE & first( dlist(tE, tLinks) & lst ) {
|
|---|
| 423 | verify (&lst != 0p);
|
|---|
| 424 | dlink(tE) * firstLnk = lst.next;
|
|---|
| 425 | if (ORIGIN_TAG_QUERY((size_t)firstLnk)) return * 0p;
|
|---|
| 426 | tytagref( tLinks, dlink(tE) ) firstLnkTagged = {*firstLnk};
|
|---|
| 427 | return downcast$( firstLnkTagged );
|
|---|
| 428 | }
|
|---|
| 429 | static inline tE & last ( dlist(tE, tLinks) & lst ) {
|
|---|
| 430 | verify (&lst != 0p);
|
|---|
| 431 | dlink(tE) * lastLnk = lst.prev;
|
|---|
| 432 | if (ORIGIN_TAG_QUERY((size_t)lastLnk)) return * 0p;
|
|---|
| 433 | tytagref( tLinks, dlink(tE) ) lastLnkTagged = {*lastLnk};
|
|---|
| 434 | return downcast$( lastLnkTagged );
|
|---|
| 435 | }
|
|---|
| 436 |
|
|---|
| 437 | static inline bool isEmpty( dlist(tE, tLinks) & lst ) {
|
|---|
| 438 | verify (&lst != 0p);
|
|---|
| 439 | if ( & first(lst) == 0p || & last(lst) == 0p ) {
|
|---|
| 440 | verify( & last(lst) == 0p && & last(lst) == 0p );
|
|---|
| 441 | return true;
|
|---|
| 442 | }
|
|---|
| 443 | return false;
|
|---|
| 444 | }
|
|---|
| 445 |
|
|---|
| 446 | static inline bool isListed( tE & e ) {
|
|---|
| 447 | NOLOOSE(
|
|---|
| 448 | verify (&e != 0p);
|
|---|
| 449 | verify(! ORIGIN_TAG_QUERY( (size_t) & e ));
|
|---|
| 450 | dlink(tE) & e_links = e`inner;
|
|---|
| 451 | dlink(tE) * lprev = (dlink(tE)*) ORIGIN_TAG_CLEAR((size_t) e_links.prev);
|
|---|
| 452 | dlink(tE) * lnext = (dlink(tE)*) ORIGIN_TAG_CLEAR((size_t) e_links.next);
|
|---|
| 453 | return ( lprev != &e_links ) || ( lnext != &e_links );
|
|---|
| 454 | )
|
|---|
| 455 | LOOSEONLY(
|
|---|
| 456 | verify(false && "isListed is undefined");
|
|---|
| 457 | return true;
|
|---|
| 458 | )
|
|---|
| 459 | }
|
|---|
| 460 |
|
|---|
| 461 | static inline tE & iter( dlist(tE, tLinks) & lst ) {
|
|---|
| 462 | tE * origin = $get_list_origin_addr( lst );
|
|---|
| 463 | return *origin;
|
|---|
| 464 | }
|
|---|
| 465 |
|
|---|
| 466 |
|
|---|
| 467 | // todo: resolve the pun:
|
|---|
| 468 | // tag as in proxy (tytagref)
|
|---|
| 469 | // tag as in bit manipulation on a funny pointer
|
|---|
| 470 |
|
|---|
| 471 | static inline bool advance( tE && refx ) {
|
|---|
| 472 | tE && ref_inner = refx;
|
|---|
| 473 | tE & oldReferent = * (tE*) ORIGIN_TAG_CLEAR( (size_t) & ref_inner );
|
|---|
| 474 | verify (& oldReferent != 0p);
|
|---|
| 475 | dlink(tE) * tgt = oldReferent`inner.next;
|
|---|
| 476 | size_t tgt_tags = ORIGIN_TAG_QUERY( (size_t)tgt);
|
|---|
| 477 | dlink(tE) * nextl = (dlink(tE) *)ORIGIN_TAG_CLEAR((size_t)tgt);
|
|---|
| 478 | tytagref( tLinks, dlink(tE) ) nextLnkTagged = { * nextl };
|
|---|
| 479 | tE & nexte = downcast$( nextLnkTagged );
|
|---|
| 480 | size_t next_te_num = (size_t) & nexte;
|
|---|
| 481 | size_t new_ref_inner_num = ORIGIN_TAG_ASGN(next_te_num, tgt_tags);
|
|---|
| 482 | tE * new_ref_inner = (tE *) new_ref_inner_num;
|
|---|
| 483 | &ref_inner = new_ref_inner;
|
|---|
| 484 | return ! tgt_tags;
|
|---|
| 485 | }
|
|---|
| 486 |
|
|---|
| 487 | static inline bool recede( tE && refx ) {
|
|---|
| 488 | tE && ref_inner = refx;
|
|---|
| 489 | tE & oldReferent = * (tE*) ORIGIN_TAG_CLEAR( (size_t) & ref_inner );
|
|---|
| 490 | verify (& oldReferent != 0p);
|
|---|
| 491 | dlink(tE) * tgt = oldReferent`inner.prev;
|
|---|
| 492 | size_t tgt_tags = ORIGIN_TAG_QUERY( (size_t)tgt);
|
|---|
| 493 | dlink(tE) * prevl = (dlink(tE) *)ORIGIN_TAG_CLEAR((size_t)tgt);
|
|---|
| 494 | tytagref( tLinks, dlink(tE) ) prevLnkTagged = { * prevl };
|
|---|
| 495 | tE & preve = downcast$( prevLnkTagged );
|
|---|
| 496 | size_t prev_te_num = (size_t) & preve;
|
|---|
| 497 | size_t new_ref_inner_num = ORIGIN_TAG_ASGN(prev_te_num, tgt_tags);
|
|---|
| 498 | tE * new_ref_inner = (tE *) new_ref_inner_num;
|
|---|
| 499 | &ref_inner = new_ref_inner;
|
|---|
| 500 | return ! tgt_tags;
|
|---|
| 501 | }
|
|---|
| 502 |
|
|---|
| 503 | bool isFirst( tE & node ) {
|
|---|
| 504 | // Probable bug copied from master
|
|---|
| 505 | // should be `! recede(node)`
|
|---|
| 506 | // correct: "is first iff cannot recede"
|
|---|
| 507 | // used backward in test suite too, probably victim of a grep rename
|
|---|
| 508 | return recede( node );
|
|---|
| 509 | }
|
|---|
| 510 |
|
|---|
| 511 | bool isLast( tE & node ) {
|
|---|
| 512 | // ditto, vice versa
|
|---|
| 513 | return advance( node );
|
|---|
| 514 | }
|
|---|
| 515 |
|
|---|
| 516 | static inline tE & next( tE & e ) {
|
|---|
| 517 | if( advance(e) ) return e;
|
|---|
| 518 | return * 0p;
|
|---|
| 519 | }
|
|---|
| 520 |
|
|---|
| 521 | static inline tE & prev( tE & e ) {
|
|---|
| 522 | if( recede(e) ) return e;
|
|---|
| 523 | return * 0p;
|
|---|
| 524 | }
|
|---|
| 525 |
|
|---|
| 526 | // Next 4 headed operations:
|
|---|
| 527 | // Manual inline of the equivalent headless operation, manually simplified.
|
|---|
| 528 | // Applies knowledge of tag pattern around head (unknown to optimizer) to reduce runtime tag operations.
|
|---|
| 529 |
|
|---|
| 530 | static inline void insert_first( dlist(tE, tLinks) &lst, tE & e ) {
|
|---|
| 531 | dlink(tE) & linkToInsert = e`inner;
|
|---|
| 532 | NOLOOSE(
|
|---|
| 533 | TAGSONLY(
|
|---|
| 534 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.prev));
|
|---|
| 535 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.next));
|
|---|
| 536 | )
|
|---|
| 537 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.prev) == (size_t)&linkToInsert);
|
|---|
| 538 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.next) == (size_t)&linkToInsert);
|
|---|
| 539 | )
|
|---|
| 540 | dlink(tE) & list_pos_links = lst;
|
|---|
| 541 | MAYBE_INSERT_READ_EARLY(
|
|---|
| 542 | dlink(tE) & afterLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.next );
|
|---|
| 543 | )
|
|---|
| 544 | MAYBE_CMEM_BARRIER;
|
|---|
| 545 | size_t list_pos_links_num = (size_t)(& list_pos_links);
|
|---|
| 546 | size_t to_insert_prev_num = ORIGIN_TAG_ENABL(list_pos_links_num);
|
|---|
| 547 | dlink(tE) * to_insert_prev = (dlink(tE)*)to_insert_prev_num;
|
|---|
| 548 | linkToInsert.prev = to_insert_prev;
|
|---|
| 549 | linkToInsert.next = list_pos_links.next;
|
|---|
| 550 | MAYBE_INSERT_READ_LATE(
|
|---|
| 551 | dlink(tE) & afterLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.next );
|
|---|
| 552 | )
|
|---|
| 553 | size_t linkToInsert_num = (size_t)(& linkToInsert);
|
|---|
| 554 | size_t afterLinks_prev_num = linkToInsert_num;
|
|---|
| 555 | afterLinks.prev = (dlink(tE)*)(afterLinks_prev_num);
|
|---|
| 556 | list_pos_links.next = &linkToInsert;
|
|---|
| 557 | MAYBE_CMEM_BARRIER;
|
|---|
| 558 | }
|
|---|
| 559 |
|
|---|
| 560 | static inline void insert_last( dlist(tE, tLinks) &lst, tE & e ) {
|
|---|
| 561 | dlink(tE) & linkToInsert = e`inner;
|
|---|
| 562 | NOLOOSE(
|
|---|
| 563 | TAGSONLY(
|
|---|
| 564 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.next));
|
|---|
| 565 | verify(ORIGIN_TAG_QUERY((size_t)linkToInsert.prev));
|
|---|
| 566 | )
|
|---|
| 567 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.next) == (size_t)&linkToInsert);
|
|---|
| 568 | verify(ORIGIN_TAG_CLEAR((size_t)linkToInsert.prev) == (size_t)&linkToInsert);
|
|---|
| 569 | )
|
|---|
| 570 | dlink(tE) & list_pos_links = lst;
|
|---|
| 571 | MAYBE_INSERT_READ_EARLY(
|
|---|
| 572 | dlink(tE) & beforeLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.prev );
|
|---|
| 573 | )
|
|---|
| 574 | MAYBE_CMEM_BARRIER;
|
|---|
| 575 | size_t list_pos_links_num = (size_t)(& list_pos_links);
|
|---|
| 576 | size_t to_insert_next_num = ORIGIN_TAG_ENABL(list_pos_links_num);
|
|---|
| 577 | dlink(tE) * to_insert_next = (dlink(tE)*)to_insert_next_num;
|
|---|
| 578 | linkToInsert.next = to_insert_next;
|
|---|
| 579 | linkToInsert.prev = list_pos_links.prev;
|
|---|
| 580 | MAYBE_INSERT_READ_LATE(
|
|---|
| 581 | dlink(tE) & beforeLinks = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) list_pos_links.prev );
|
|---|
| 582 | )
|
|---|
| 583 | size_t linkToInsert_num = (size_t)(& linkToInsert);
|
|---|
| 584 | size_t beforeLinks_next_num = linkToInsert_num;
|
|---|
| 585 | beforeLinks.next = (dlink(tE)*)(beforeLinks_next_num);
|
|---|
| 586 | list_pos_links.prev = &linkToInsert;
|
|---|
| 587 | MAYBE_CMEM_BARRIER;
|
|---|
| 588 | }
|
|---|
| 589 |
|
|---|
| 590 | static inline tE & remove_first( dlist(tE, tLinks) &lst ) {
|
|---|
| 591 | verify (&lst != 0p);
|
|---|
| 592 | dlink(tE) & list_links = lst;
|
|---|
| 593 | // call is valid on empty list; when so, list_links.next and after_links.prev have otags set
|
|---|
| 594 |
|
|---|
| 595 | dlink(tE) & fst_raw = * list_links.next;
|
|---|
| 596 | dlink(tE) & fst_links = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) & fst_raw );
|
|---|
| 597 | size_t fst_tagval = origin_tag_query_arith$( fst_raw );
|
|---|
| 598 |
|
|---|
| 599 | dlink(tE) & after_raw = * fst_links.next;
|
|---|
| 600 | dlink(tE) & after_links = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) & after_raw );
|
|---|
| 601 |
|
|---|
| 602 | size_t before_links_next_rslt = ((size_t) & after_raw);
|
|---|
| 603 | size_t after_links_prev_rslt = ORIGIN_TAG_ENABL( (size_t) & list_links );
|
|---|
| 604 | list_links.next = (dlink(tE) *) before_links_next_rslt;
|
|---|
| 605 | after_links.prev = (dlink(tE) *) after_links_prev_rslt;
|
|---|
| 606 |
|
|---|
| 607 | MAYBE_CMEM_BARRIER;
|
|---|
| 608 | size_t list_pos_links_num = (size_t) &fst_links;
|
|---|
| 609 | size_t list_pos_links_tagged_num = ORIGIN_TAG_ENABL( list_pos_links_num );
|
|---|
| 610 | fst_links.next = fst_links.prev = (dlink(tE)*) list_pos_links_tagged_num;
|
|---|
| 611 | MAYBE_CMEM_BARRIER;
|
|---|
| 612 |
|
|---|
| 613 | tytagref( tLinks, dlink(tE) ) retExt = { fst_links };
|
|---|
| 614 | return nullif$( downcast$( retExt ), fst_tagval );
|
|---|
| 615 | }
|
|---|
| 616 |
|
|---|
| 617 | static inline tE & remove_last( dlist(tE, tLinks) &lst ) {
|
|---|
| 618 | verify (&lst != 0p);
|
|---|
| 619 | dlink(tE) & list_links = lst;
|
|---|
| 620 | // call is valid on empty list; when so, list_links.prev and before_links.next have otags set
|
|---|
| 621 |
|
|---|
| 622 | dlink(tE) & last_raw = * list_links.prev;
|
|---|
| 623 | dlink(tE) & last_links = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) & last_raw );
|
|---|
| 624 | size_t last_tagval = origin_tag_query_arith$( last_raw );
|
|---|
| 625 |
|
|---|
| 626 | dlink(tE) & before_raw = * last_links.prev;
|
|---|
| 627 | dlink(tE) & before_links = * (dlink(tE) *) ORIGIN_TAG_CLEAR( (size_t) & before_raw );
|
|---|
| 628 |
|
|---|
| 629 | size_t after_links_prev_rslt = ((size_t) & before_raw);
|
|---|
| 630 | size_t before_links_next_rslt = ORIGIN_TAG_ENABL( (size_t) & list_links );
|
|---|
| 631 | list_links.prev = (dlink(tE) *) after_links_prev_rslt;
|
|---|
| 632 | before_links.next = (dlink(tE) *) before_links_next_rslt;
|
|---|
| 633 |
|
|---|
| 634 | MAYBE_CMEM_BARRIER;
|
|---|
| 635 | size_t list_pos_links_num = (size_t) &last_links;
|
|---|
| 636 | size_t list_pos_links_tagged_num = ORIGIN_TAG_ENABL( list_pos_links_num );
|
|---|
| 637 | last_links.prev = last_links.next = (dlink(tE)*) list_pos_links_tagged_num;
|
|---|
| 638 | MAYBE_CMEM_BARRIER;
|
|---|
| 639 |
|
|---|
| 640 | tytagref( tLinks, dlink(tE) ) lpLnkTagged = { last_links };
|
|---|
| 641 | return nullif$( downcast$( lpLnkTagged ), last_tagval );
|
|---|
| 642 | }
|
|---|
| 643 |
|
|---|
| 644 | static inline tE & try_pop_first( dlist(tE, tLinks) &lst ) {
|
|---|
| 645 | tE & first_inlist = first(lst);
|
|---|
| 646 | tE & first_item = first_inlist;
|
|---|
| 647 | if (&first_item) remove(first_inlist); // TODO: should it use pop_front?
|
|---|
| 648 | return first_item;
|
|---|
| 649 | }
|
|---|
| 650 |
|
|---|
| 651 | static inline tE & try_pop_last( dlist(tE, tLinks) &lst ) {
|
|---|
| 652 | tE & last_inlist = last(lst);
|
|---|
| 653 | tE & last_item = last_inlist;
|
|---|
| 654 | if (&last_item) remove(last_inlist); // TODO: should it use pop_back?
|
|---|
| 655 | return last_item;
|
|---|
| 656 | }
|
|---|
| 657 |
|
|---|
| 658 |
|
|---|
| 659 | #if !defined(NDEBUG) && (defined(__CFA_DEBUG__) || defined(__CFA_VERIFY__))
|
|---|
| 660 | static bool $validate_fwd( dlist(tE, tLinks) & this ) {
|
|---|
| 661 | tE & lagElem = *0p;
|
|---|
| 662 |
|
|---|
| 663 | while ( tE & it = iter(this); advance(it) ) {
|
|---|
| 664 | if (& lagElem == 0p && &it != & first(this) ) return false;
|
|---|
| 665 | & lagElem = & it;
|
|---|
| 666 | }
|
|---|
| 667 |
|
|---|
| 668 | if (& lagElem != & last(this)) return false;
|
|---|
| 669 |
|
|---|
| 670 | // TODO: verify that it is back at iter(this);
|
|---|
| 671 | return true;
|
|---|
| 672 | }
|
|---|
| 673 | static bool $validate_rev( dlist(tE, tLinks) & this ) {
|
|---|
| 674 | tE & lagElem = *0p;
|
|---|
| 675 |
|
|---|
| 676 | while ( tE & it = iter(this); recede(it) ) {
|
|---|
| 677 | if (& lagElem == 0p && &it != & last(this) ) return false;
|
|---|
| 678 | & lagElem = & it;
|
|---|
| 679 | }
|
|---|
| 680 |
|
|---|
| 681 | if (& lagElem != & first(this)) return false;
|
|---|
| 682 |
|
|---|
| 683 | // TODO: verify that it is back at iter(this);
|
|---|
| 684 | return true;
|
|---|
| 685 | }
|
|---|
| 686 | static inline bool validate( dlist(tE, tLinks) & this ) {
|
|---|
| 687 | bool reportsHavingFirst = ((& first(this)) == 0p);
|
|---|
| 688 | bool reportsHavingLast = ((& last(this)) == 0p);
|
|---|
| 689 | if ( reportsHavingFirst != reportsHavingLast ) return false;
|
|---|
| 690 |
|
|---|
| 691 | return $validate_fwd(this) && $validate_rev(this);
|
|---|
| 692 | }
|
|---|
| 693 | #endif
|
|---|
| 694 |
|
|---|
| 695 | }
|
|---|