@@ -72,10 +72,15 @@ type chunkHeader struct {
7272 _ uint32
7373}
7474
75- const chunkHeaderWords = uint32 (unsafe .Sizeof (chunkHeader {}) / wordSize )
75+ const (
76+ chunkHeaderSize = uint32 (unsafe .Sizeof (chunkHeader {}))
77+ chunkHeaderWords = chunkHeaderSize / uint32 (wordSize )
78+ )
7679
7780func newNodeStore () * nodeStore {
78- return & nodeStore {}
81+ // The first far slots are the absent node and list, which zero refs
82+ // refer to.
83+ return & nodeStore {far : make ([]Node , 1 ), farLists : make ([]NodeList , 1 )}
7984}
8085
8186// nodeBaseData is the header at the start of every node record.
@@ -84,21 +89,25 @@ type nodeBaseData struct {
8489 loc core.TextRange
8590 parent nodeRef
8691 flags NodeFlags
87- // chunk is the record's offset in words from the start of its chunk.
92+ // chunk is the record's offset in bytes from the start of its chunk.
8893 chunk uint32
8994 kind Kind
9095}
9196
9297// Every node has a header, so it must stay small.
9398var _ [32 - unsafe .Sizeof (nodeBaseData {})]struct {}
9499
95- // A nodeRef is a record field referring to a node. Zero is no node. With
96- // the low bit clear, the referenced record is in the same chunk, at the
97- // field's address plus twice the ref, read as a signed number of bytes;
98- // otherwise, the rest is an index into the store's far nodes.
100+ // A nodeRef is a record field referring to a node. With the low bit set,
101+ // the referenced record is in the same chunk, at the field's address plus
102+ // the rest of the ref, read as a signed number of bytes; otherwise, the
103+ // rest is an index into the store's far nodes, whose first is the absent
104+ // node, so that zero is no node.
105+ //
106+ // Records are word aligned and refs are four byte aligned, so the distance
107+ // from a ref to a record never has the low bit set.
99108type nodeRef uint32
100109
101- const refFar = 1
110+ const refNear = 1
102111
103112const wordSize = unsafe .Sizeof (uint64 (0 ))
104113
@@ -108,9 +117,9 @@ func (n Node) rec() unsafe.Pointer { return n.p }
108117func (n Node ) hdr () * nodeBaseData { return (* nodeBaseData )(n .p ) }
109118
110119// chunkOf returns the header of the chunk holding the record at p, which
111- // is chunk words from the chunk's start.
120+ // is chunk bytes from the chunk's start.
112121func chunkOf (p unsafe.Pointer , chunk uint32 ) * chunkHeader {
113- return (* chunkHeader )(unsafe .Add (p , - int (chunk )* int ( wordSize ) ))
122+ return (* chunkHeader )(unsafe .Add (p , - int (chunk )))
114123}
115124
116125func (n Node ) chunk () * chunkHeader { return chunkOf (n .p , n .hdr ().chunk ) }
@@ -120,14 +129,11 @@ func (n Node) store() *nodeStore { return n.chunk().store }
120129
121130// deref returns the node that the field f of n's record refers to.
122131func (n Node ) deref (f * nodeRef ) Node {
123- r := int32 (* f )
124- if r & refFar == 0 {
125- if r == 0 {
126- return Node {}
127- }
128- return Node {unsafe .Add (unsafe .Pointer (f ), int (r )* 2 )}
132+ r := * f
133+ if r & refNear != 0 {
134+ return Node {unsafe .Add (unsafe .Pointer (f ), int (int32 (r ))- refNear )}
129135 }
130- return n .store ().far [uint32 ( r ) >> 1 ]
136+ return n .store ().far [r >> 1 ]
131137}
132138
133139// refAt returns the node that the field at offset off of n's record refers
@@ -157,7 +163,7 @@ func setRef(c *chunkHeader, f *nodeRef, v Node) {
157163 return
158164 }
159165 s := c .store
160- if * f & refFar ! = 0 {
166+ if * f != 0 && * f & refNear = = 0 {
161167 // Slots are never shared, so the field's own slot can be reused.
162168 s .far [* f >> 1 ] = v
163169 return
@@ -169,14 +175,14 @@ func setRef(c *chunkHeader, f *nodeRef, v Node) {
169175// near returns the ref from the field at f to the record at p in the same
170176// chunk.
171177func near [F any ](f * F , p unsafe.Pointer ) uint32 {
172- return uint32 (int32 (( int64 ( uintptr (p )) - int64 ( uintptr (unsafe .Pointer (f )))) / 2 ))
178+ return uint32 (int32 (uintptr (p )- uintptr (unsafe .Pointer (f )))) | refNear
173179}
174180
175181func farSlot (i int ) uint32 {
176182 if i >= 1 << 31 {
177183 panic ("node store far ref overflow" )
178184 }
179- return uint32 (i )<< 1 | refFar
185+ return uint32 (i ) << 1
180186}
181187
182188// A nodeAnchor refers to a node of a store from outside its records: the
@@ -196,8 +202,8 @@ func (s *nodeStore) anchor(v Node) nodeAnchor {
196202 if v .p == nil {
197203 return 0
198204 }
199- if c := v .chunk (); c .store == s && c .index < maxAnchorChunks && v . hdr (). chunk < maxChunkWords {
200- return nodeAnchor ((c .index + 1 )<< anchorChunkShift | v . hdr (). chunk )
205+ if c , w := v .chunk (), v . hdr (). chunk / uint32 ( wordSize ) ; c .store == s && c .index < maxAnchorChunks && w < maxChunkWords {
206+ return nodeAnchor ((c .index + 1 )<< anchorChunkShift | w )
201207 }
202208 s .far = append (s .far , v )
203209 if len (s .far ) > anchorFar {
@@ -234,7 +240,7 @@ func newRecord[T any](s *nodeStore, layout nodeLayout) (*T, Node) {
234240}
235241
236242// alloc allocates size zeroed bytes for a record, returning its address and
237- // its word offset in its chunk.
243+ // its byte offset in its chunk.
238244func (s * nodeStore ) alloc (size uintptr ) (unsafe.Pointer , uint32 ) {
239245 if s .frozen {
240246 panic ("node store is frozen" )
@@ -245,7 +251,7 @@ func (s *nodeStore) alloc(size uintptr) (unsafe.Pointer, uint32) {
245251 }
246252 p := unsafe .Pointer (unsafe .SliceData (s .free ))
247253 s .free = s .free [words :]
248- return p , uint32 (( uintptr (p ) - uintptr (s .base )) / wordSize )
254+ return p , uint32 (uintptr (p ) - uintptr (s .base ))
249255}
250256
251257// grow allocates words in a new chunk. A record never spans two chunks.
@@ -258,12 +264,12 @@ func (s *nodeStore) grow(words int) (unsafe.Pointer, uint32) {
258264 // A record too large for a normal chunk gets one of its own, and
259265 // the current chunk stays in use.
260266 base , _ := s .addChunk (words )
261- return unsafe .Add (base , chunkHeaderWords * uint32 ( wordSize )), chunkHeaderWords
267+ return unsafe .Add (base , chunkHeaderSize ), chunkHeaderSize
262268 }
263269 base , all := s .addChunk (n )
264270 s .base = base
265271 s .free = all [words :]
266- return unsafe .Pointer (unsafe .SliceData (all )), chunkHeaderWords
272+ return unsafe .Pointer (unsafe .SliceData (all )), chunkHeaderSize
267273}
268274
269275// addChunk allocates a chunk with room for at least n words of records,
@@ -274,7 +280,7 @@ func (s *nodeStore) addChunk(n int) (unsafe.Pointer, []uint64) {
274280 * (* chunkHeader )(base ) = chunkHeader {store : s , index : uint32 (len (s .chunks ))}
275281 s .chunks = append (s .chunks , base )
276282 s .words += n
277- return base , unsafe .Slice ((* uint64 )(unsafe .Add (base , chunkHeaderWords * uint32 ( wordSize ) )), n )
283+ return base , unsafe .Slice ((* uint64 )(unsafe .Add (base , chunkHeaderSize )), n )
278284}
279285
280286// chunkClass rounds n up so that a chunk with n words of records is 2^k
0 commit comments