1
2
3
4
5 package ssacompile
6
7 import (
8 "internal/buildcfg"
9
10 "cmd/compile/internal/reflectdata"
11 "cmd/compile/internal/ssa"
12 "cmd/compile/internal/ssa/block"
13 "cmd/compile/internal/ssa/ssaop"
14 "cmd/compile/internal/types"
15 "cmd/internal/obj"
16 "cmd/internal/objabi"
17 "cmd/internal/src"
18 )
19
20
21
22 func mightBeHeapPointer(v *ssa.Value) bool {
23 if IsGlobalAddr(v) {
24 return false
25 }
26 return true
27 }
28
29
30
31
32 func mightContainHeapPointer(ptr *ssa.Value, size int64, mem *ssa.Value, zeroes map[ssa.ID]ssa.ZeroRegion) bool {
33 if IsReadOnlyGlobalAddr(ptr) {
34
35 return false
36 }
37
38
39
40
41 var off int64
42 for ptr.Op == ssaop.OpOffPtr {
43 off += ptr.AuxInt
44 ptr = ptr.Args[0]
45 }
46
47 ptrSize := ptr.Block.Func.Config.PtrSize
48 if off%ptrSize != 0 {
49 return true
50 }
51 if size%ptrSize != 0 {
52 ptr.Fatalf("unaligned pointer write")
53 }
54 if off < 0 || off+size > 64*ptrSize {
55
56 return true
57 }
58 z := zeroes[mem.ID]
59 if ptr != z.Base {
60
61 return true
62 }
63
64 m := (uint64(1)<<(size/ptrSize) - 1) << (off / ptrSize)
65
66 if z.Mask&m == m {
67
68 return false
69 }
70 return true
71 }
72
73
74
75
76 func needwb(v *ssa.Value, zeroes map[ssa.ID]ssa.ZeroRegion) bool {
77 t, ok := v.Aux.(*types.Type)
78 if !ok {
79 v.Fatalf("store aux is not a type: %s", v.LongString())
80 }
81 if !t.HasPointers() {
82 return false
83 }
84 dst := v.Args[0]
85 if ssa.IsStackAddr(dst) {
86 return false
87 }
88
89
90 if mightContainHeapPointer(dst, t.Size(), v.MemoryArg(), zeroes) {
91 return true
92 }
93
94
95 switch v.Op {
96 case ssaop.OpStore:
97 if !mightBeHeapPointer(v.Args[1]) {
98 return false
99 }
100 case ssaop.OpZero:
101 return false
102 case ssaop.OpMove:
103 if !mightContainHeapPointer(v.Args[1], t.Size(), v.Args[2], zeroes) {
104 return false
105 }
106 default:
107 v.Fatalf("store op unknown: %s", v.LongString())
108 }
109 return true
110 }
111
112
113 func needWBsrc(v *ssa.Value) bool {
114 return !IsGlobalAddr(v)
115 }
116
117
118
119 func needWBdst(ptr, mem *ssa.Value, zeroes map[ssa.ID]ssa.ZeroRegion) bool {
120
121 var off int64
122 for ptr.Op == ssaop.OpOffPtr {
123 off += ptr.AuxInt
124 ptr = ptr.Args[0]
125 }
126 ptrSize := ptr.Block.Func.Config.PtrSize
127 if off%ptrSize != 0 {
128 return true
129 }
130 if off < 0 || off >= 64*ptrSize {
131
132 return true
133 }
134 z := zeroes[mem.ID]
135 if ptr != z.Base {
136 return true
137 }
138
139
140 return z.Mask>>uint(off/ptrSize)&1 == 0
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 func writebarrier(f *ssa.Func) {
157 if !f.Fe.UseWriteBarrier() {
158 return
159 }
160
161
162
163
164 const maxEntries = 8
165
166 var sb, sp, wbaddr, const0 *ssa.Value
167 var cgoCheckPtrWrite, cgoCheckMemmove *obj.LSym
168 var wbZero, wbMove *obj.LSym
169 var stores, after []*ssa.Value
170 var sset, sset2 *ssa.SparseSet
171 var storeNumber []int32
172
173
174 select1 := f.Cache.AllocValueSlice(f.NumValues())
175 defer func() { f.Cache.FreeValueSlice(select1) }()
176 for _, b := range f.Blocks {
177 for _, v := range b.Values {
178 if v.Op != ssaop.OpSelectN {
179 continue
180 }
181 if v.AuxInt != 1 {
182 continue
183 }
184 select1[v.Args[0].ID] = v
185 }
186 }
187
188 zeroes := f.ComputeZeroMap(select1)
189 for _, b := range f.Blocks {
190
191
192 nWBops := 0
193 for _, v := range b.Values {
194 switch v.Op {
195 case ssaop.OpStore, ssaop.OpMove, ssaop.OpZero:
196 if needwb(v, zeroes) {
197 switch v.Op {
198 case ssaop.OpStore:
199 v.Op = ssaop.OpStoreWB
200 case ssaop.OpMove:
201 v.Op = ssaop.OpMoveWB
202 case ssaop.OpZero:
203 v.Op = ssaop.OpZeroWB
204 }
205 nWBops++
206 }
207 }
208 }
209 if nWBops == 0 {
210 continue
211 }
212
213 if wbaddr == nil {
214
215
216 initpos := f.Entry.Pos
217 sp, sb = f.SpSb()
218 wbsym := f.Fe.Syslook("writeBarrier")
219 wbaddr = f.Entry.NewValue1A(initpos, ssaop.OpAddr, f.Config.Types.UInt32Ptr, wbsym, sb)
220 wbZero = f.Fe.Syslook("wbZero")
221 wbMove = f.Fe.Syslook("wbMove")
222 if buildcfg.Experiment.CgoCheck2 {
223 cgoCheckPtrWrite = f.Fe.Syslook("cgoCheckPtrWrite")
224 cgoCheckMemmove = f.Fe.Syslook("cgoCheckMemmove")
225 }
226 const0 = f.ConstInt32(f.Config.Types.UInt32, 0)
227
228
229 sset = f.NewSparseSet(f.NumValues())
230 defer f.RetSparseSet(sset)
231 sset2 = f.NewSparseSet(f.NumValues())
232 defer f.RetSparseSet(sset2)
233 storeNumber = f.Cache.AllocInt32Slice(f.NumValues())
234 defer f.Cache.FreeInt32Slice(storeNumber)
235 }
236
237
238 b.Values = storeOrder(b.Values, sset, storeNumber)
239 again:
240
241
242
243 var last *ssa.Value
244 var start, end int
245 var nonPtrStores int
246 values := b.Values
247 hasMove := false
248 FindSeq:
249 for i := len(values) - 1; i >= 0; i-- {
250 w := values[i]
251 switch w.Op {
252 case ssaop.OpStoreWB, ssaop.OpMoveWB, ssaop.OpZeroWB:
253 start = i
254 if last == nil {
255 last = w
256 end = i + 1
257 }
258 nonPtrStores = 0
259 if w.Op == ssaop.OpMoveWB {
260 hasMove = true
261 }
262 case ssaop.OpVarDef, ssaop.OpVarLive:
263 continue
264 case ssaop.OpStore:
265 if last == nil {
266 continue
267 }
268 nonPtrStores++
269 if nonPtrStores > 2 {
270 break FindSeq
271 }
272 if hasMove {
273
274
275
276
277
278
279
280
281 break FindSeq
282 }
283 default:
284 if last == nil {
285 continue
286 }
287 break FindSeq
288 }
289 }
290 stores = append(stores[:0], b.Values[start:end]...)
291 after = append(after[:0], b.Values[end:]...)
292 b.Values = b.Values[:start]
293
294
295 mem := stores[0].MemoryArg()
296 pos := stores[0].Pos
297
298
299
300
301
302 var nilcheck, nilcheckThen, nilcheckEnd *ssa.Value
303 if a := stores[0].Args[0]; a.Op == ssaop.OpNilCheck && a.Args[1] == mem {
304 nilcheck = a
305 }
306
307
308
309
310
311
312
313
314
315
316 type volatileCopy struct {
317 src *ssa.Value
318 tmp *ssa.Value
319 }
320 var volatiles []volatileCopy
321 copyLoop:
322 for _, w := range stores {
323 if w.Op == ssaop.OpMoveWB {
324 val := w.Args[1]
325 if ssa.IsVolatile(val) {
326 for _, c := range volatiles {
327 if val == c.src {
328 continue copyLoop
329 }
330 }
331
332 t := val.Type.Elem()
333 tmp := f.NewLocal(w.Pos, t)
334 mem = b.NewValue1A(w.Pos, ssaop.OpVarDef, types.TypeMem, tmp, mem)
335 tmpaddr := b.NewValue2A(w.Pos, ssaop.OpLocalAddr, t.PtrTo(), tmp, sp, mem)
336 siz := t.Size()
337 mem = b.NewValue3I(w.Pos, ssaop.OpMove, types.TypeMem, siz, tmpaddr, val, mem)
338 mem.Aux = t
339 volatiles = append(volatiles, volatileCopy{val, tmpaddr})
340 }
341 }
342 }
343
344
345 bThen := f.NewBlock(block.BlockPlain)
346 bEnd := f.NewBlock(b.Kind)
347 bThen.Pos = pos
348 bEnd.Pos = b.Pos
349 b.Pos = pos
350
351
352 bEnd.CopyControls(b)
353 bEnd.Likely = b.Likely
354 for _, e := range b.Succs {
355 bEnd.Succs = append(bEnd.Succs, e)
356 e.B.Preds[e.I].B = bEnd
357 }
358
359
360
361 cfgtypes := &f.Config.Types
362 flag := b.NewValue2(pos, ssaop.OpLoad, cfgtypes.UInt32, wbaddr, mem)
363 flag = b.NewValue2(pos, ssaop.OpNeq32, cfgtypes.Bool, flag, const0)
364 b.Kind = block.BlockIf
365 b.SetControl(flag)
366 b.Likely = ssa.BranchUnlikely
367 b.Succs = b.Succs[:0]
368 b.AddEdgeTo(bThen)
369 b.AddEdgeTo(bEnd)
370 bThen.AddEdgeTo(bEnd)
371
372
373 memThen := mem
374
375 if nilcheck != nil {
376 nilcheckThen = bThen.NewValue2(nilcheck.Pos, ssaop.OpNilCheck, nilcheck.Type, nilcheck.Args[0], memThen)
377 }
378
379
380
381
382
383
384
385
386
387
388
389
390 srcs := sset
391 srcs.Clear()
392
393
394 dsts := sset2
395 dsts.Clear()
396
397
398 type write struct {
399 ptr *ssa.Value
400 pos src.XPos
401 }
402 var writeStore [maxEntries]write
403 writes := writeStore[:0]
404
405 flush := func() {
406 if len(writes) == 0 {
407 return
408 }
409
410 t := types.NewTuple(types.Types[types.TUINTPTR].PtrTo(), types.TypeMem)
411 call := bThen.NewValue1I(pos, ssaop.OpWB, t, int64(len(writes)), memThen)
412 curPtr := bThen.NewValue1(pos, ssaop.OpSelect0, types.Types[types.TUINTPTR].PtrTo(), call)
413 memThen = bThen.NewValue1(pos, ssaop.OpSelect1, types.TypeMem, call)
414
415 for i, write := range writes {
416 wbuf := bThen.NewValue1I(write.pos, ssaop.OpOffPtr, types.Types[types.TUINTPTR].PtrTo(), int64(i)*f.Config.PtrSize, curPtr)
417 memThen = bThen.NewValue3A(write.pos, ssaop.OpStore, types.TypeMem, types.Types[types.TUINTPTR], wbuf, write.ptr, memThen)
418 }
419 writes = writes[:0]
420 }
421 addEntry := func(pos src.XPos, ptr *ssa.Value) {
422 writes = append(writes, write{ptr: ptr, pos: pos})
423 if len(writes) == maxEntries {
424 flush()
425 }
426 }
427
428
429 for _, w := range stores {
430 if w.Op != ssaop.OpStoreWB {
431 continue
432 }
433 pos := w.Pos
434 ptr := w.Args[0]
435 val := w.Args[1]
436 if !srcs.Contains(val.ID) && needWBsrc(val) {
437 srcs.Add(val.ID)
438 addEntry(pos, val)
439 }
440 if !dsts.Contains(ptr.ID) && needWBdst(ptr, w.Args[2], zeroes) {
441 dsts.Add(ptr.ID)
442
443
444
445
446
447
448
449 if ptr == nilcheck {
450 ptr = nilcheckThen
451 }
452 oldVal := bThen.NewValue2(pos, ssaop.OpLoad, types.Types[types.TUINTPTR], ptr, memThen)
453
454 addEntry(pos, oldVal)
455 }
456 f.Fe.Func().SetWBPos(pos)
457 nWBops--
458 }
459 flush()
460
461
462 for _, w := range stores {
463 pos := w.Pos
464 dst := w.Args[0]
465 if dst == nilcheck {
466 dst = nilcheckThen
467 }
468 switch w.Op {
469 case ssaop.OpZeroWB:
470 typ := reflectdata.TypeLinksym(w.Aux.(*types.Type))
471
472 taddr := b.NewValue1A(pos, ssaop.OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
473 memThen = wbcall(pos, bThen, wbZero, sp, memThen, taddr, dst)
474 f.Fe.Func().SetWBPos(pos)
475 nWBops--
476 case ssaop.OpMoveWB:
477 src := w.Args[1]
478 if ssa.IsVolatile(src) {
479 for _, c := range volatiles {
480 if src == c.src {
481 src = c.tmp
482 break
483 }
484 }
485 }
486 typ := reflectdata.TypeLinksym(w.Aux.(*types.Type))
487
488 taddr := b.NewValue1A(pos, ssaop.OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
489 memThen = wbcall(pos, bThen, wbMove, sp, memThen, taddr, dst, src)
490 f.Fe.Func().SetWBPos(pos)
491 nWBops--
492 }
493 }
494
495
496 mem = bEnd.NewValue2(pos, ssaop.OpPhi, types.TypeMem, mem, memThen)
497
498 if nilcheck != nil {
499 nilcheckEnd = bEnd.NewValue2(nilcheck.Pos, ssaop.OpNilCheck, nilcheck.Type, nilcheck.Args[0], mem)
500 }
501
502
503 for _, w := range stores {
504 pos := w.Pos
505 dst := w.Args[0]
506 if dst == nilcheck {
507 dst = nilcheckEnd
508 }
509 switch w.Op {
510 case ssaop.OpStoreWB:
511 val := w.Args[1]
512 if buildcfg.Experiment.CgoCheck2 {
513
514 mem = wbcall(pos, bEnd, cgoCheckPtrWrite, sp, mem, dst, val)
515 }
516 mem = bEnd.NewValue3A(pos, ssaop.OpStore, types.TypeMem, w.Aux, dst, val, mem)
517 case ssaop.OpZeroWB:
518 mem = bEnd.NewValue2I(pos, ssaop.OpZero, types.TypeMem, w.AuxInt, dst, mem)
519 mem.Aux = w.Aux
520 case ssaop.OpMoveWB:
521 src := w.Args[1]
522 if ssa.IsVolatile(src) {
523 for _, c := range volatiles {
524 if src == c.src {
525 src = c.tmp
526 break
527 }
528 }
529 }
530 if buildcfg.Experiment.CgoCheck2 {
531
532 typ := reflectdata.TypeLinksym(w.Aux.(*types.Type))
533 taddr := b.NewValue1A(pos, ssaop.OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
534 mem = wbcall(pos, bEnd, cgoCheckMemmove, sp, mem, taddr, dst, src)
535 }
536 mem = bEnd.NewValue3I(pos, ssaop.OpMove, types.TypeMem, w.AuxInt, dst, src, mem)
537 mem.Aux = w.Aux
538 case ssaop.OpVarDef, ssaop.OpVarLive:
539 mem = bEnd.NewValue1A(pos, w.Op, types.TypeMem, w.Aux, mem)
540 case ssaop.OpStore:
541 val := w.Args[1]
542 mem = bEnd.NewValue3A(pos, ssaop.OpStore, types.TypeMem, w.Aux, dst, val, mem)
543 }
544 }
545
546
547
548
549
550 bEnd.Values = append(bEnd.Values, last)
551 last.Block = bEnd
552 last.Reset(ssaop.OpWBend)
553 last.Pos = last.Pos.WithNotStmt()
554 last.Type = types.TypeMem
555 last.AddArg(mem)
556
557
558 for _, w := range stores {
559 if w != last {
560 w.ResetArgs()
561 }
562 }
563 for _, w := range stores {
564 if w != last {
565 f.FreeValue(w)
566 }
567 }
568 if nilcheck != nil && nilcheck.Uses == 0 {
569 nilcheck.Reset(ssaop.OpInvalid)
570 }
571
572
573 bEnd.Values = append(bEnd.Values, after...)
574 for _, w := range after {
575 w.Block = bEnd
576 }
577
578
579 if nWBops > 0 {
580 goto again
581 }
582 }
583 }
584
585
586 func wbcall(pos src.XPos, b *ssa.Block, fn *obj.LSym, sp, mem *ssa.Value, args ...*ssa.Value) *ssa.Value {
587 config := b.Func.Config
588 typ := config.Types.Uintptr
589 nargs := len(args)
590
591
592 inRegs := b.Func.ABIDefault == b.Func.ABI1 && len(config.IntParamRegs) >= 3
593
594 if !inRegs {
595
596 off := config.Ctxt.Arch.FixedFrameSize
597 for _, arg := range args {
598 stkaddr := b.NewValue1I(pos, ssaop.OpOffPtr, typ.PtrTo(), off, sp)
599 mem = b.NewValue3A(pos, ssaop.OpStore, types.TypeMem, typ, stkaddr, arg, mem)
600 off += typ.Size()
601 }
602 args = args[:0]
603 }
604
605 args = append(args, mem)
606
607
608 argTypes := make([]*types.Type, nargs, 3)
609 for i := 0; i < nargs; i++ {
610 argTypes[i] = typ
611 }
612 call := b.NewValue0A(pos, ssaop.OpStaticCall, types.TypeResultMem, ssa.StaticAuxCall(fn, b.Func.ABIDefault.ABIAnalyzeTypes(argTypes, nil)))
613 call.AddArgs(args...)
614 call.AuxInt = int64(nargs) * typ.Size()
615 return b.NewValue1I(pos, ssaop.OpSelectN, types.TypeMem, 0, call)
616 }
617
618
619 func IsGlobalAddr(v *ssa.Value) bool {
620 for v.Op == ssaop.OpOffPtr || v.Op == ssaop.OpAddPtr || v.Op == ssaop.OpPtrIndex || v.Op == ssaop.OpCopy {
621 v = v.Args[0]
622 }
623 if v.Op == ssaop.OpAddr && v.Args[0].Op == ssaop.OpSB {
624 return true
625 }
626 if v.Op == ssaop.OpConstNil {
627 return true
628 }
629 if v.Op == ssaop.OpLoad && IsReadOnlyGlobalAddr(v.Args[0]) {
630 return true
631 }
632 return false
633 }
634
635
636 func IsReadOnlyGlobalAddr(v *ssa.Value) bool {
637 if v.Op == ssaop.OpConstNil {
638
639 return true
640 }
641 if v.Op == ssaop.OpAddr && v.Aux != nil && v.Aux.(*obj.LSym).Type == objabi.SRODATA {
642 return true
643 }
644 return false
645 }
646
View as plain text