]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/ssa/writebarrier.go
5df65bfaa3cd5effcea67d33ea210eda9b21332c
[gostls13.git] / src / cmd / compile / internal / ssa / writebarrier.go
1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package ssa
6
7 import (
8         "cmd/compile/internal/reflectdata"
9         "cmd/compile/internal/types"
10         "cmd/internal/obj"
11         "cmd/internal/objabi"
12         "cmd/internal/src"
13         "fmt"
14         "internal/buildcfg"
15 )
16
17 // A ZeroRegion records parts of an object which are known to be zero.
18 // A ZeroRegion only applies to a single memory state.
19 // Each bit in mask is set if the corresponding pointer-sized word of
20 // the base object is known to be zero.
21 // In other words, if mask & (1<<i) != 0, then [base+i*ptrSize, base+(i+1)*ptrSize)
22 // is known to be zero.
23 type ZeroRegion struct {
24         base *Value
25         mask uint64
26 }
27
28 // mightBeHeapPointer reports whether v might point to the heap.
29 // v must have pointer type.
30 func mightBeHeapPointer(v *Value) bool {
31         if IsGlobalAddr(v) {
32                 return false
33         }
34         return true
35 }
36
37 // mightContainHeapPointer reports whether the data currently at addresses
38 // [ptr,ptr+size) might contain heap pointers. "currently" means at memory state mem.
39 // zeroes contains ZeroRegion data to help make that decision (see computeZeroMap).
40 func mightContainHeapPointer(ptr *Value, size int64, mem *Value, zeroes map[ID]ZeroRegion) bool {
41         if IsReadOnlyGlobalAddr(ptr) {
42                 // The read-only globals section cannot contain any heap pointers.
43                 return false
44         }
45
46         // See if we can prove that the queried memory is all zero.
47
48         // Find base pointer and offset. Hopefully, the base is the result of a new(T).
49         var off int64
50         for ptr.Op == OpOffPtr {
51                 off += ptr.AuxInt
52                 ptr = ptr.Args[0]
53         }
54
55         ptrSize := ptr.Block.Func.Config.PtrSize
56         if off%ptrSize != 0 || size%ptrSize != 0 {
57                 ptr.Fatalf("unaligned pointer write")
58         }
59         if off < 0 || off+size > 64*ptrSize {
60                 // memory range goes off end of tracked offsets
61                 return true
62         }
63         z := zeroes[mem.ID]
64         if ptr != z.base {
65                 // This isn't the object we know about at this memory state.
66                 return true
67         }
68         // Mask of bits we're asking about
69         m := (uint64(1)<<(size/ptrSize) - 1) << (off / ptrSize)
70
71         if z.mask&m == m {
72                 // All locations are known to be zero, so no heap pointers.
73                 return false
74         }
75         return true
76 }
77
78 // needwb reports whether we need write barrier for store op v.
79 // v must be Store/Move/Zero.
80 // zeroes provides known zero information (keyed by ID of memory-type values).
81 func needwb(v *Value, zeroes map[ID]ZeroRegion) bool {
82         t, ok := v.Aux.(*types.Type)
83         if !ok {
84                 v.Fatalf("store aux is not a type: %s", v.LongString())
85         }
86         if !t.HasPointers() {
87                 return false
88         }
89         dst := v.Args[0]
90         if IsStackAddr(dst) {
91                 return false // writes into the stack don't need write barrier
92         }
93         // If we're writing to a place that might have heap pointers, we need
94         // the write barrier.
95         if mightContainHeapPointer(dst, t.Size(), v.MemoryArg(), zeroes) {
96                 return true
97         }
98         // Lastly, check if the values we're writing might be heap pointers.
99         // If they aren't, we don't need a write barrier.
100         switch v.Op {
101         case OpStore:
102                 if !mightBeHeapPointer(v.Args[1]) {
103                         return false
104                 }
105         case OpZero:
106                 return false // nil is not a heap pointer
107         case OpMove:
108                 if !mightContainHeapPointer(v.Args[1], t.Size(), v.Args[2], zeroes) {
109                         return false
110                 }
111         default:
112                 v.Fatalf("store op unknown: %s", v.LongString())
113         }
114         return true
115 }
116
117 // needWBsrc reports whether GC needs to see v when it is the source of a store.
118 func needWBsrc(v *Value) bool {
119         return !IsGlobalAddr(v)
120 }
121
122 // needWBdst reports whether GC needs to see what used to be in *ptr when ptr is
123 // the target of a pointer store.
124 func needWBdst(ptr, mem *Value, zeroes map[ID]ZeroRegion) bool {
125         // Detect storing to zeroed memory.
126         var off int64
127         for ptr.Op == OpOffPtr {
128                 off += ptr.AuxInt
129                 ptr = ptr.Args[0]
130         }
131         ptrSize := ptr.Block.Func.Config.PtrSize
132         if off%ptrSize != 0 {
133                 ptr.Fatalf("unaligned pointer write")
134         }
135         if off < 0 || off >= 64*ptrSize {
136                 // write goes off end of tracked offsets
137                 return true
138         }
139         z := zeroes[mem.ID]
140         if ptr != z.base {
141                 return true
142         }
143         // If destination is known to be zeroed, we don't need the write barrier
144         // to record the old value in *ptr.
145         return z.mask>>uint(off/ptrSize)&1 == 0
146 }
147
148 // writebarrier pass inserts write barriers for store ops (Store, Move, Zero)
149 // when necessary (the condition above). It rewrites store ops to branches
150 // and runtime calls, like
151 //
152 //      if writeBarrier.enabled {
153 //              buf := gcWriteBarrier2()        // Not a regular Go call
154 //              buf[0] = val
155 //              buf[1] = *ptr
156 //      }
157 //      *ptr = val
158 //
159 // A sequence of WB stores for many pointer fields of a single type will
160 // be emitted together, with a single branch.
161 func writebarrier(f *Func) {
162         if !f.fe.UseWriteBarrier() {
163                 return
164         }
165
166         // Number of write buffer entries we can request at once.
167         // Must match runtime/mwbbuf.go:wbMaxEntriesPerCall.
168         // It must also match the number of instances of runtime.gcWriteBarrier{X}.
169         const maxEntries = 8
170
171         var sb, sp, wbaddr, const0 *Value
172         var cgoCheckPtrWrite, cgoCheckMemmove *obj.LSym
173         var wbZero, wbMove *obj.LSym
174         var stores, after []*Value
175         var sset, sset2 *sparseSet
176         var storeNumber []int32
177
178         // Compute map from a value to the SelectN [1] value that uses it.
179         select1 := f.Cache.allocValueSlice(f.NumValues())
180         defer func() { f.Cache.freeValueSlice(select1) }()
181         for _, b := range f.Blocks {
182                 for _, v := range b.Values {
183                         if v.Op != OpSelectN {
184                                 continue
185                         }
186                         if v.AuxInt != 1 {
187                                 continue
188                         }
189                         select1[v.Args[0].ID] = v
190                 }
191         }
192
193         zeroes := f.computeZeroMap(select1)
194         for _, b := range f.Blocks { // range loop is safe since the blocks we added contain no stores to expand
195                 // first, identify all the stores that need to insert a write barrier.
196                 // mark them with WB ops temporarily. record presence of WB ops.
197                 nWBops := 0 // count of temporarily created WB ops remaining to be rewritten in the current block
198                 for _, v := range b.Values {
199                         switch v.Op {
200                         case OpStore, OpMove, OpZero:
201                                 if needwb(v, zeroes) {
202                                         switch v.Op {
203                                         case OpStore:
204                                                 v.Op = OpStoreWB
205                                         case OpMove:
206                                                 v.Op = OpMoveWB
207                                         case OpZero:
208                                                 v.Op = OpZeroWB
209                                         }
210                                         nWBops++
211                                 }
212                         }
213                 }
214                 if nWBops == 0 {
215                         continue
216                 }
217
218                 if wbaddr == nil {
219                         // lazily initialize global values for write barrier test and calls
220                         // find SB and SP values in entry block
221                         initpos := f.Entry.Pos
222                         sp, sb = f.spSb()
223                         wbsym := f.fe.Syslook("writeBarrier")
224                         wbaddr = f.Entry.NewValue1A(initpos, OpAddr, f.Config.Types.UInt32Ptr, wbsym, sb)
225                         wbZero = f.fe.Syslook("wbZero")
226                         wbMove = f.fe.Syslook("wbMove")
227                         if buildcfg.Experiment.CgoCheck2 {
228                                 cgoCheckPtrWrite = f.fe.Syslook("cgoCheckPtrWrite")
229                                 cgoCheckMemmove = f.fe.Syslook("cgoCheckMemmove")
230                         }
231                         const0 = f.ConstInt32(f.Config.Types.UInt32, 0)
232
233                         // allocate auxiliary data structures for computing store order
234                         sset = f.newSparseSet(f.NumValues())
235                         defer f.retSparseSet(sset)
236                         sset2 = f.newSparseSet(f.NumValues())
237                         defer f.retSparseSet(sset2)
238                         storeNumber = f.Cache.allocInt32Slice(f.NumValues())
239                         defer f.Cache.freeInt32Slice(storeNumber)
240                 }
241
242                 // order values in store order
243                 b.Values = storeOrder(b.Values, sset, storeNumber)
244         again:
245                 // find the start and end of the last contiguous WB store sequence.
246                 // a branch will be inserted there. values after it will be moved
247                 // to a new block.
248                 var last *Value
249                 var start, end int
250                 values := b.Values
251         FindSeq:
252                 for i := len(values) - 1; i >= 0; i-- {
253                         w := values[i]
254                         switch w.Op {
255                         case OpStoreWB, OpMoveWB, OpZeroWB:
256                                 start = i
257                                 if last == nil {
258                                         last = w
259                                         end = i + 1
260                                 }
261                         case OpVarDef, OpVarLive:
262                                 continue
263                         default:
264                                 if last == nil {
265                                         continue
266                                 }
267                                 break FindSeq
268                         }
269                 }
270                 stores = append(stores[:0], b.Values[start:end]...) // copy to avoid aliasing
271                 after = append(after[:0], b.Values[end:]...)
272                 b.Values = b.Values[:start]
273
274                 // find the memory before the WB stores
275                 mem := stores[0].MemoryArg()
276                 pos := stores[0].Pos
277
278                 // If the source of a MoveWB is volatile (will be clobbered by a
279                 // function call), we need to copy it to a temporary location, as
280                 // marshaling the args of wbMove might clobber the value we're
281                 // trying to move.
282                 // Look for volatile source, copy it to temporary before we check
283                 // the write barrier flag.
284                 // It is unlikely to have more than one of them. Just do a linear
285                 // search instead of using a map.
286                 // See issue 15854.
287                 type volatileCopy struct {
288                         src *Value // address of original volatile value
289                         tmp *Value // address of temporary we've copied the volatile value into
290                 }
291                 var volatiles []volatileCopy
292
293                 if !(f.ABIDefault == f.ABI1 && len(f.Config.intParamRegs) >= 3) {
294                         // We don't need to do this if the calls we're going to do take
295                         // all their arguments in registers.
296                         // 3 is the magic number because it covers wbZero, wbMove, cgoCheckMemmove.
297                 copyLoop:
298                         for _, w := range stores {
299                                 if w.Op == OpMoveWB {
300                                         val := w.Args[1]
301                                         if isVolatile(val) {
302                                                 for _, c := range volatiles {
303                                                         if val == c.src {
304                                                                 continue copyLoop // already copied
305                                                         }
306                                                 }
307
308                                                 t := val.Type.Elem()
309                                                 tmp := f.fe.Auto(w.Pos, t)
310                                                 mem = b.NewValue1A(w.Pos, OpVarDef, types.TypeMem, tmp, mem)
311                                                 tmpaddr := b.NewValue2A(w.Pos, OpLocalAddr, t.PtrTo(), tmp, sp, mem)
312                                                 siz := t.Size()
313                                                 mem = b.NewValue3I(w.Pos, OpMove, types.TypeMem, siz, tmpaddr, val, mem)
314                                                 mem.Aux = t
315                                                 volatiles = append(volatiles, volatileCopy{val, tmpaddr})
316                                         }
317                                 }
318                         }
319                 }
320
321                 // Build branch point.
322                 bThen := f.NewBlock(BlockPlain)
323                 bEnd := f.NewBlock(b.Kind)
324                 bThen.Pos = pos
325                 bEnd.Pos = b.Pos
326                 b.Pos = pos
327
328                 // Set up control flow for end block.
329                 bEnd.CopyControls(b)
330                 bEnd.Likely = b.Likely
331                 for _, e := range b.Succs {
332                         bEnd.Succs = append(bEnd.Succs, e)
333                         e.b.Preds[e.i].b = bEnd
334                 }
335
336                 // set up control flow for write barrier test
337                 // load word, test word, avoiding partial register write from load byte.
338                 cfgtypes := &f.Config.Types
339                 flag := b.NewValue2(pos, OpLoad, cfgtypes.UInt32, wbaddr, mem)
340                 flag = b.NewValue2(pos, OpNeq32, cfgtypes.Bool, flag, const0)
341                 b.Kind = BlockIf
342                 b.SetControl(flag)
343                 b.Likely = BranchUnlikely
344                 b.Succs = b.Succs[:0]
345                 b.AddEdgeTo(bThen)
346                 b.AddEdgeTo(bEnd)
347                 bThen.AddEdgeTo(bEnd)
348
349                 // For each write barrier store, append write barrier code to bThen.
350                 memThen := mem
351                 var curCall *Value
352                 var curPtr *Value
353                 addEntry := func(v *Value) {
354                         if curCall == nil || curCall.AuxInt == maxEntries {
355                                 t := types.NewTuple(types.Types[types.TUINTPTR].PtrTo(), types.TypeMem)
356                                 curCall = bThen.NewValue1(pos, OpWB, t, memThen)
357                                 curPtr = bThen.NewValue1(pos, OpSelect0, types.Types[types.TUINTPTR].PtrTo(), curCall)
358                                 memThen = bThen.NewValue1(pos, OpSelect1, types.TypeMem, curCall)
359                         }
360                         // Store value in write buffer
361                         num := curCall.AuxInt
362                         curCall.AuxInt = num + 1
363                         wbuf := bThen.NewValue1I(pos, OpOffPtr, types.Types[types.TUINTPTR].PtrTo(), num*f.Config.PtrSize, curPtr)
364                         memThen = bThen.NewValue3A(pos, OpStore, types.TypeMem, types.Types[types.TUINTPTR], wbuf, v, memThen)
365                 }
366
367                 // Note: we can issue the write barrier code in any order. In particular,
368                 // it doesn't matter if they are in a different order *even if* they end
369                 // up referring to overlapping memory regions. For instance if an OpStore
370                 // stores to a location that is later read by an OpMove. In all cases
371                 // any pointers we must get into the write barrier buffer still make it,
372                 // possibly in a different order and possibly a different (but definitely
373                 // more than 0) number of times.
374                 // In light of that, we process all the OpStoreWBs first. This minimizes
375                 // the amount of spill/restore code we need around the Zero/Move calls.
376
377                 // srcs contains the value IDs of pointer values we've put in the write barrier buffer.
378                 srcs := sset
379                 srcs.clear()
380                 // dsts contains the value IDs of locations which we've read a pointer out of
381                 // and put the result in the write barrier buffer.
382                 dsts := sset2
383                 dsts.clear()
384
385                 for _, w := range stores {
386                         if w.Op != OpStoreWB {
387                                 continue
388                         }
389                         pos := w.Pos
390                         ptr := w.Args[0]
391                         val := w.Args[1]
392                         if !srcs.contains(val.ID) && needWBsrc(val) {
393                                 srcs.add(val.ID)
394                                 addEntry(val)
395                         }
396                         if !dsts.contains(ptr.ID) && needWBdst(ptr, w.Args[2], zeroes) {
397                                 dsts.add(ptr.ID)
398                                 // Load old value from store target.
399                                 // Note: This turns bad pointer writes into bad
400                                 // pointer reads, which could be confusing. We could avoid
401                                 // reading from obviously bad pointers, which would
402                                 // take care of the vast majority of these. We could
403                                 // patch this up in the signal handler, or use XCHG to
404                                 // combine the read and the write.
405                                 oldVal := bThen.NewValue2(pos, OpLoad, types.Types[types.TUINTPTR], ptr, memThen)
406                                 // Save old value to write buffer.
407                                 addEntry(oldVal)
408                         }
409                         f.fe.Func().SetWBPos(pos)
410                         nWBops--
411                 }
412
413                 for _, w := range stores {
414                         pos := w.Pos
415                         switch w.Op {
416                         case OpZeroWB:
417                                 dst := w.Args[0]
418                                 typ := reflectdata.TypeLinksym(w.Aux.(*types.Type))
419                                 // zeroWB(&typ, dst)
420                                 taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
421                                 memThen = wbcall(pos, bThen, wbZero, sp, memThen, taddr, dst)
422                                 f.fe.Func().SetWBPos(pos)
423                                 nWBops--
424                         case OpMoveWB:
425                                 dst := w.Args[0]
426                                 src := w.Args[1]
427                                 if isVolatile(src) {
428                                         for _, c := range volatiles {
429                                                 if src == c.src {
430                                                         src = c.tmp
431                                                         break
432                                                 }
433                                         }
434                                 }
435                                 typ := reflectdata.TypeLinksym(w.Aux.(*types.Type))
436                                 // moveWB(&typ, dst, src)
437                                 taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
438                                 memThen = wbcall(pos, bThen, wbMove, sp, memThen, taddr, dst, src)
439                                 f.fe.Func().SetWBPos(pos)
440                                 nWBops--
441                         }
442                 }
443
444                 // merge memory
445                 mem = bEnd.NewValue2(pos, OpPhi, types.TypeMem, mem, memThen)
446
447                 // Do raw stores after merge point.
448                 for _, w := range stores {
449                         switch w.Op {
450                         case OpStoreWB:
451                                 ptr := w.Args[0]
452                                 val := w.Args[1]
453                                 if buildcfg.Experiment.CgoCheck2 {
454                                         // Issue cgo checking code.
455                                         mem = wbcall(pos, bEnd, cgoCheckPtrWrite, sp, mem, ptr, val)
456                                 }
457                                 mem = bEnd.NewValue3A(pos, OpStore, types.TypeMem, w.Aux, ptr, val, mem)
458                         case OpZeroWB:
459                                 dst := w.Args[0]
460                                 mem = bEnd.NewValue2I(pos, OpZero, types.TypeMem, w.AuxInt, dst, mem)
461                                 mem.Aux = w.Aux
462                         case OpMoveWB:
463                                 dst := w.Args[0]
464                                 src := w.Args[1]
465                                 if isVolatile(src) {
466                                         for _, c := range volatiles {
467                                                 if src == c.src {
468                                                         src = c.tmp
469                                                         break
470                                                 }
471                                         }
472                                 }
473                                 if buildcfg.Experiment.CgoCheck2 {
474                                         // Issue cgo checking code.
475                                         typ := reflectdata.TypeLinksym(w.Aux.(*types.Type))
476                                         taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
477                                         mem = wbcall(pos, bEnd, cgoCheckMemmove, sp, mem, taddr, dst, src)
478                                 }
479                                 mem = bEnd.NewValue3I(pos, OpMove, types.TypeMem, w.AuxInt, dst, src, mem)
480                                 mem.Aux = w.Aux
481                         case OpVarDef, OpVarLive:
482                                 mem = bEnd.NewValue1A(pos, w.Op, types.TypeMem, w.Aux, mem)
483                         }
484                 }
485
486                 // The last store becomes the WBend marker. This marker is used by the liveness
487                 // pass to determine what parts of the code are preemption-unsafe.
488                 // All subsequent memory operations use this memory, so we have to sacrifice the
489                 // previous last memory op to become this new value.
490                 bEnd.Values = append(bEnd.Values, last)
491                 last.Block = bEnd
492                 last.reset(OpWBend)
493                 last.Pos = last.Pos.WithNotStmt()
494                 last.Type = types.TypeMem
495                 last.AddArg(mem)
496
497                 // Free all the old stores, except last which became the WBend marker.
498                 for _, w := range stores {
499                         if w != last {
500                                 w.resetArgs()
501                         }
502                 }
503                 for _, w := range stores {
504                         if w != last {
505                                 f.freeValue(w)
506                         }
507                 }
508
509                 // put values after the store sequence into the end block
510                 bEnd.Values = append(bEnd.Values, after...)
511                 for _, w := range after {
512                         w.Block = bEnd
513                 }
514
515                 // if we have more stores in this block, do this block again
516                 if nWBops > 0 {
517                         goto again
518                 }
519         }
520 }
521
522 // computeZeroMap returns a map from an ID of a memory value to
523 // a set of locations that are known to be zeroed at that memory value.
524 func (f *Func) computeZeroMap(select1 []*Value) map[ID]ZeroRegion {
525
526         ptrSize := f.Config.PtrSize
527         // Keep track of which parts of memory are known to be zero.
528         // This helps with removing write barriers for various initialization patterns.
529         // This analysis is conservative. We only keep track, for each memory state, of
530         // which of the first 64 words of a single object are known to be zero.
531         zeroes := map[ID]ZeroRegion{}
532         // Find new objects.
533         for _, b := range f.Blocks {
534                 for _, v := range b.Values {
535                         if mem, ok := IsNewObject(v, select1); ok {
536                                 // While compiling package runtime itself, we might see user
537                                 // calls to newobject, which will have result type
538                                 // unsafe.Pointer instead. We can't easily infer how large the
539                                 // allocated memory is, so just skip it.
540                                 if types.LocalPkg.Path == "runtime" && v.Type.IsUnsafePtr() {
541                                         continue
542                                 }
543
544                                 nptr := v.Type.Elem().Size() / ptrSize
545                                 if nptr > 64 {
546                                         nptr = 64
547                                 }
548                                 zeroes[mem.ID] = ZeroRegion{base: v, mask: 1<<uint(nptr) - 1}
549                         }
550                 }
551         }
552         // Find stores to those new objects.
553         for {
554                 changed := false
555                 for _, b := range f.Blocks {
556                         // Note: iterating forwards helps convergence, as values are
557                         // typically (but not always!) in store order.
558                         for _, v := range b.Values {
559                                 if v.Op != OpStore {
560                                         continue
561                                 }
562                                 z, ok := zeroes[v.MemoryArg().ID]
563                                 if !ok {
564                                         continue
565                                 }
566                                 ptr := v.Args[0]
567                                 var off int64
568                                 size := v.Aux.(*types.Type).Size()
569                                 for ptr.Op == OpOffPtr {
570                                         off += ptr.AuxInt
571                                         ptr = ptr.Args[0]
572                                 }
573                                 if ptr != z.base {
574                                         // Different base object - we don't know anything.
575                                         // We could even be writing to the base object we know
576                                         // about, but through an aliased but offset pointer.
577                                         // So we have to throw all the zero information we have away.
578                                         continue
579                                 }
580                                 // Round to cover any partially written pointer slots.
581                                 // Pointer writes should never be unaligned like this, but non-pointer
582                                 // writes to pointer-containing types will do this.
583                                 if d := off % ptrSize; d != 0 {
584                                         off -= d
585                                         size += d
586                                 }
587                                 if d := size % ptrSize; d != 0 {
588                                         size += ptrSize - d
589                                 }
590                                 // Clip to the 64 words that we track.
591                                 min := off
592                                 max := off + size
593                                 if min < 0 {
594                                         min = 0
595                                 }
596                                 if max > 64*ptrSize {
597                                         max = 64 * ptrSize
598                                 }
599                                 // Clear bits for parts that we are writing (and hence
600                                 // will no longer necessarily be zero).
601                                 for i := min; i < max; i += ptrSize {
602                                         bit := i / ptrSize
603                                         z.mask &^= 1 << uint(bit)
604                                 }
605                                 if z.mask == 0 {
606                                         // No more known zeros - don't bother keeping.
607                                         continue
608                                 }
609                                 // Save updated known zero contents for new store.
610                                 if zeroes[v.ID] != z {
611                                         zeroes[v.ID] = z
612                                         changed = true
613                                 }
614                         }
615                 }
616                 if !changed {
617                         break
618                 }
619         }
620         if f.pass.debug > 0 {
621                 fmt.Printf("func %s\n", f.Name)
622                 for mem, z := range zeroes {
623                         fmt.Printf("  memory=v%d ptr=%v zeromask=%b\n", mem, z.base, z.mask)
624                 }
625         }
626         return zeroes
627 }
628
629 // wbcall emits write barrier runtime call in b, returns memory.
630 func wbcall(pos src.XPos, b *Block, fn *obj.LSym, sp, mem *Value, args ...*Value) *Value {
631         config := b.Func.Config
632         typ := config.Types.Uintptr // type of all argument values
633         nargs := len(args)
634
635         // TODO (register args) this is a bit of a hack.
636         inRegs := b.Func.ABIDefault == b.Func.ABI1 && len(config.intParamRegs) >= 3
637
638         if !inRegs {
639                 // Store arguments to the appropriate stack slot.
640                 off := config.ctxt.Arch.FixedFrameSize
641                 for _, arg := range args {
642                         stkaddr := b.NewValue1I(pos, OpOffPtr, typ.PtrTo(), off, sp)
643                         mem = b.NewValue3A(pos, OpStore, types.TypeMem, typ, stkaddr, arg, mem)
644                         off += typ.Size()
645                 }
646                 args = args[:0]
647         }
648
649         args = append(args, mem)
650
651         // issue call
652         argTypes := make([]*types.Type, nargs, 3) // at most 3 args; allows stack allocation
653         for i := 0; i < nargs; i++ {
654                 argTypes[i] = typ
655         }
656         call := b.NewValue0A(pos, OpStaticCall, types.TypeResultMem, StaticAuxCall(fn, b.Func.ABIDefault.ABIAnalyzeTypes(nil, argTypes, nil)))
657         call.AddArgs(args...)
658         call.AuxInt = int64(nargs) * typ.Size()
659         return b.NewValue1I(pos, OpSelectN, types.TypeMem, 0, call)
660 }
661
662 // round to a multiple of r, r is a power of 2.
663 func round(o int64, r int64) int64 {
664         return (o + r - 1) &^ (r - 1)
665 }
666
667 // IsStackAddr reports whether v is known to be an address of a stack slot.
668 func IsStackAddr(v *Value) bool {
669         for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
670                 v = v.Args[0]
671         }
672         switch v.Op {
673         case OpSP, OpLocalAddr, OpSelectNAddr, OpGetCallerSP:
674                 return true
675         }
676         return false
677 }
678
679 // IsGlobalAddr reports whether v is known to be an address of a global (or nil).
680 func IsGlobalAddr(v *Value) bool {
681         for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
682                 v = v.Args[0]
683         }
684         if v.Op == OpAddr && v.Args[0].Op == OpSB {
685                 return true // address of a global
686         }
687         if v.Op == OpConstNil {
688                 return true
689         }
690         if v.Op == OpLoad && IsReadOnlyGlobalAddr(v.Args[0]) {
691                 return true // loading from a read-only global - the resulting address can't be a heap address.
692         }
693         return false
694 }
695
696 // IsReadOnlyGlobalAddr reports whether v is known to be an address of a read-only global.
697 func IsReadOnlyGlobalAddr(v *Value) bool {
698         if v.Op == OpConstNil {
699                 // Nil pointers are read only. See issue 33438.
700                 return true
701         }
702         if v.Op == OpAddr && v.Aux != nil && v.Aux.(*obj.LSym).Type == objabi.SRODATA {
703                 return true
704         }
705         return false
706 }
707
708 // IsNewObject reports whether v is a pointer to a freshly allocated & zeroed object,
709 // if so, also returns the memory state mem at which v is zero.
710 func IsNewObject(v *Value, select1 []*Value) (mem *Value, ok bool) {
711         f := v.Block.Func
712         c := f.Config
713         if f.ABIDefault == f.ABI1 && len(c.intParamRegs) >= 1 {
714                 if v.Op != OpSelectN || v.AuxInt != 0 {
715                         return nil, false
716                 }
717                 mem = select1[v.Args[0].ID]
718                 if mem == nil {
719                         return nil, false
720                 }
721         } else {
722                 if v.Op != OpLoad {
723                         return nil, false
724                 }
725                 mem = v.MemoryArg()
726                 if mem.Op != OpSelectN {
727                         return nil, false
728                 }
729                 if mem.Type != types.TypeMem {
730                         return nil, false
731                 } // assume it is the right selection if true
732         }
733         call := mem.Args[0]
734         if call.Op != OpStaticCall {
735                 return nil, false
736         }
737         if !isSameCall(call.Aux, "runtime.newobject") {
738                 return nil, false
739         }
740         if f.ABIDefault == f.ABI1 && len(c.intParamRegs) >= 1 {
741                 if v.Args[0] == call {
742                         return mem, true
743                 }
744                 return nil, false
745         }
746         if v.Args[0].Op != OpOffPtr {
747                 return nil, false
748         }
749         if v.Args[0].Args[0].Op != OpSP {
750                 return nil, false
751         }
752         if v.Args[0].AuxInt != c.ctxt.Arch.FixedFrameSize+c.RegSize { // offset of return value
753                 return nil, false
754         }
755         return mem, true
756 }
757
758 // IsSanitizerSafeAddr reports whether v is known to be an address
759 // that doesn't need instrumentation.
760 func IsSanitizerSafeAddr(v *Value) bool {
761         for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
762                 v = v.Args[0]
763         }
764         switch v.Op {
765         case OpSP, OpLocalAddr, OpSelectNAddr:
766                 // Stack addresses are always safe.
767                 return true
768         case OpITab, OpStringPtr, OpGetClosurePtr:
769                 // Itabs, string data, and closure fields are
770                 // read-only once initialized.
771                 return true
772         case OpAddr:
773                 vt := v.Aux.(*obj.LSym).Type
774                 return vt == objabi.SRODATA || vt == objabi.SLIBFUZZER_8BIT_COUNTER || vt == objabi.SCOVERAGE_COUNTER || vt == objabi.SCOVERAGE_AUXVAR
775         }
776         return false
777 }
778
779 // isVolatile reports whether v is a pointer to argument region on stack which
780 // will be clobbered by a function call.
781 func isVolatile(v *Value) bool {
782         for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy || v.Op == OpSelectNAddr {
783                 v = v.Args[0]
784         }
785         return v.Op == OpSP
786 }