1
2
3
4
5 package walk
6
7 import (
8 "go/constant"
9 "unicode/utf8"
10
11 "cmd/compile/internal/base"
12 "cmd/compile/internal/ir"
13 "cmd/compile/internal/reflectdata"
14 "cmd/compile/internal/ssagen"
15 "cmd/compile/internal/typecheck"
16 "cmd/compile/internal/types"
17 "cmd/internal/src"
18 "cmd/internal/sys"
19 )
20
21 func cheapComputableIndex(width int64) bool {
22 switch ssagen.Arch.LinkArch.Family {
23
24
25
26 case sys.Loong64, sys.PPC64, sys.S390X:
27 return width == 1
28 case sys.AMD64, sys.I386, sys.ARM64, sys.ARM:
29 switch width {
30 case 1, 2, 4, 8:
31 return true
32 }
33 }
34 return false
35 }
36
37
38
39
40
41 func (w *walkState) walkRange(nrange *ir.RangeStmt) ir.Node {
42 base.Assert(!nrange.DistinctVars)
43 if isMapClear(nrange) {
44 return w.mapRangeClear(nrange)
45 }
46
47 nfor := ir.NewForStmt(nrange.Pos(), nil, nil, nil, nil, nrange.DistinctVars)
48 nfor.SetInit(nrange.Init())
49 nfor.Label = nrange.Label
50
51
52
53
54
55
56
57
58 a := nrange.X
59 t := a.Type()
60 lno := ir.SetPos(a)
61
62 v1, v2 := nrange.Key, nrange.Value
63
64 if ir.IsBlank(v2) {
65 v2 = nil
66 }
67
68 if ir.IsBlank(v1) && v2 == nil {
69 v1 = nil
70 }
71
72 if v1 == nil && v2 != nil {
73 base.Fatalf("walkRange: v2 != nil while v1 == nil")
74 }
75
76 var body []ir.Node
77 var init []ir.Node
78 switch k := t.Kind(); {
79 default:
80 base.Fatalf("walkRange")
81
82 case types.IsInt[k]:
83 if nn := w.arrayRangeClear(nrange, v1, v2, a); nn != nil {
84 base.Pos = lno
85 return nn
86 }
87 hv1 := typecheck.TempAt(base.Pos, w.curfunc, t)
88 hn := typecheck.TempAt(base.Pos, w.curfunc, t)
89
90 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
91 init = append(init, ir.NewAssignStmt(base.Pos, hn, a))
92
93 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, hn)
94 nfor.Post = ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))
95
96 if v1 != nil {
97 body = []ir.Node{rangeAssign(nrange, hv1)}
98 }
99
100 case k == types.TARRAY, k == types.TSLICE, k == types.TPTR:
101 if nn := w.arrayRangeClear(nrange, v1, v2, a); nn != nil {
102 base.Pos = lno
103 return nn
104 }
105
106
107 var elem *types.Type
108 switch t.Kind() {
109 case types.TSLICE, types.TARRAY:
110 elem = t.Elem()
111 case types.TPTR:
112 elem = t.Elem().Elem()
113 }
114
115
116 ha := a
117
118 hv1 := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TINT])
119 hn := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TINT])
120
121 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
122 init = append(init, ir.NewAssignStmt(base.Pos, hn, ir.NewUnaryExpr(base.Pos, ir.OLEN, ha)))
123
124 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, hn)
125 nfor.Post = ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))
126
127
128 if v1 == nil {
129 break
130 }
131
132
133 if v2 == nil {
134 body = []ir.Node{rangeAssign(nrange, hv1)}
135 break
136 }
137
138
139 if cheapComputableIndex(elem.Size()) {
140
141 tmp := ir.NewIndexExpr(base.Pos, ha, hv1)
142 tmp.SetBounded(true)
143 body = []ir.Node{rangeAssign2(nrange, hv1, tmp)}
144 break
145 }
146
147
148 var hs ir.Node
149 if t.IsSlice() {
150 hs = ha
151 } else {
152 var arr ir.Node
153 if t.IsPtr() {
154 arr = ha
155 } else {
156 arr = typecheck.NodAddr(ha)
157 arr.SetType(t.PtrTo())
158 arr.SetTypecheck(1)
159 }
160 hs = ir.NewSliceExpr(base.Pos, ir.OSLICEARR, arr, nil, nil, nil)
161
162 hs.SetType(types.NewSlice(elem))
163 hs.SetTypecheck(1)
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 ptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, hs)
215 ptr.SetBounded(true)
216 huVal := ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], ptr)
217 huVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUINTPTR], huVal)
218 hu := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TUINTPTR])
219 init = append(init, ir.NewAssignStmt(base.Pos, hu, huVal))
220
221
222 hpVal := ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], hu)
223 hpVal.SetCheckPtr(true)
224 hpVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, elem.PtrTo(), hpVal)
225 hp := typecheck.TempAt(base.Pos, w.curfunc, elem.PtrTo())
226 body = append(body, ir.NewAssignStmt(base.Pos, hp, hpVal))
227
228
229 e := ir.NewStarExpr(base.Pos, hp)
230 e.SetBounded(true)
231 a := rangeAssign2(nrange, hv1, e)
232 body = append(body, a)
233
234
235
236 huVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], hp)
237 huVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUINTPTR], huVal)
238 as := ir.NewAssignStmt(base.Pos, hu, ir.NewBinaryExpr(base.Pos, ir.OADD, huVal, ir.NewInt(base.Pos, elem.Size())))
239 nfor.Post = ir.NewBlockStmt(base.Pos, []ir.Node{nfor.Post, as})
240
241 case k == types.TMAP:
242
243
244 ha := a
245
246 hit := nrange.Prealloc
247 th := hit.Type()
248
249
250 keysym := th.Field(0).Sym
251 elemsym := th.Field(1).Sym
252 iterInit := "mapIterStart"
253 iterNext := "mapIterNext"
254
255 fn := typecheck.LookupRuntime(iterInit, t.Key(), t.Elem(), th)
256 init = append(init, w.mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), ha, typecheck.NodAddr(hit)))
257 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil())
258
259 fn = typecheck.LookupRuntime(iterNext, th)
260 nfor.Post = w.mkcallstmt1(fn, typecheck.NodAddr(hit))
261
262 key := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), types.NewPtr(t.Key())))
263 if v1 == nil {
264 body = nil
265 } else if v2 == nil {
266 body = []ir.Node{rangeAssign(nrange, key)}
267 } else {
268 elem := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym), types.NewPtr(t.Elem())))
269 body = []ir.Node{rangeAssign2(nrange, key, elem)}
270 }
271
272 case k == types.TCHAN:
273
274 ha := a
275
276 hv1 := typecheck.TempAt(base.Pos, w.curfunc, t.Elem())
277 hv1.SetTypecheck(1)
278 if t.Elem().HasPointers() {
279 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
280 }
281 hb := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TBOOL])
282
283 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, hb, ir.NewBool(base.Pos, false))
284 lhs := []ir.Node{hv1, hb}
285 rhs := []ir.Node{ir.NewUnaryExpr(base.Pos, ir.ORECV, ha)}
286 a := ir.NewAssignListStmt(base.Pos, ir.OAS2RECV, lhs, rhs)
287 a.SetTypecheck(1)
288 nfor.Cond = ir.InitExpr([]ir.Node{a}, nfor.Cond)
289 if v1 == nil {
290 body = nil
291 } else {
292 body = []ir.Node{rangeAssign(nrange, hv1)}
293 }
294
295
296
297 body = append(body, ir.NewAssignStmt(base.Pos, hv1, nil))
298
299 case k == types.TSTRING:
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316 ha := a
317
318 hv1 := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TINT])
319 hv1t := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TINT])
320 hv2 := typecheck.TempAt(base.Pos, w.curfunc, types.RuneType)
321
322
323 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
324
325
326 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, ir.NewUnaryExpr(base.Pos, ir.OLEN, ha))
327
328 if v1 != nil {
329
330 body = append(body, ir.NewAssignStmt(base.Pos, hv1t, hv1))
331 }
332
333
334 nind := ir.NewIndexExpr(base.Pos, ha, hv1)
335 nind.SetBounded(true)
336 body = append(body, ir.NewAssignStmt(base.Pos, hv2, typecheck.Conv(nind, types.RuneType)))
337
338
339 nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
340
341
342
343 nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLE, hv2, ir.NewInt(base.Pos, utf8.RuneSelf-1))
344
345
346 nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))}
347
348
349
350 fn := typecheck.LookupRuntime("decoderune")
351
352
353 call := w.mkcall1(fn, fn.Type().ResultsTuple(), &nif.Else, ha, hv1)
354 a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{hv2, hv1}, []ir.Node{call})
355 nif.Else.Append(a)
356
357 body = append(body, nif)
358
359 if v1 != nil {
360 if v2 != nil {
361
362 body = append(body, rangeAssign2(nrange, hv1t, hv2))
363 } else {
364
365 body = append(body, rangeAssign(nrange, hv1t))
366 }
367 }
368 }
369
370 typecheck.Stmts(init)
371
372 nfor.PtrInit().Append(init...)
373
374 typecheck.Stmts(nfor.Cond.Init())
375
376 nfor.Cond = typecheck.Expr(nfor.Cond)
377 nfor.Cond = typecheck.DefaultLit(nfor.Cond, nil)
378 nfor.Post = typecheck.Stmt(nfor.Post)
379 typecheck.Stmts(body)
380 nfor.Body.Append(body...)
381 nfor.Body.Append(nrange.Body...)
382
383 var n ir.Node = nfor
384
385 n = w.walkStmt(n)
386
387 base.Pos = lno
388 return n
389 }
390
391
392 func rangeAssign(n *ir.RangeStmt, key ir.Node) ir.Node {
393 key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType)
394 return ir.NewAssignStmt(n.Pos(), n.Key, key)
395 }
396
397
398 func rangeAssign2(n *ir.RangeStmt, key, value ir.Node) ir.Node {
399
400
401 key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType)
402 value = rangeConvert(n, n.Value.Type(), value, n.ValueTypeWord, n.ValueSrcRType)
403 return ir.NewAssignListStmt(n.Pos(), ir.OAS2, []ir.Node{n.Key, n.Value}, []ir.Node{key, value})
404 }
405
406
407
408
409 func rangeConvert(nrange *ir.RangeStmt, dst *types.Type, src, typeWord, srcRType ir.Node) ir.Node {
410 src = typecheck.Expr(src)
411 if dst.Kind() == types.TBLANK || types.Identical(dst, src.Type()) {
412 return src
413 }
414
415 n := ir.NewConvExpr(nrange.Pos(), ir.OCONV, dst, src)
416 n.TypeWord = typeWord
417 n.SrcRType = srcRType
418 return typecheck.Expr(n)
419 }
420
421
422
423
424
425
426
427
428 func isMapClear(n *ir.RangeStmt) bool {
429 if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
430 return false
431 }
432
433 t := n.X.Type()
434 if n.Op() != ir.ORANGE || t.Kind() != types.TMAP || n.Key == nil || n.Value != nil {
435 return false
436 }
437
438 k := n.Key
439
440 if !ir.DeclaredBy(k, n) {
441 return false
442 }
443
444 if len(n.Body) != 1 {
445 return false
446 }
447
448 stmt := n.Body[0]
449 if stmt == nil || stmt.Op() != ir.ODELETE {
450 return false
451 }
452
453 m := n.X
454 if delete := stmt.(*ir.CallExpr); !ir.SameSafeExpr(delete.Args[0], m) || !ir.SameSafeExpr(delete.Args[1], k) {
455 return false
456 }
457
458
459 if !types.IsReflexive(t.Key()) {
460 return false
461 }
462
463 return true
464 }
465
466
467 func (w *walkState) mapRangeClear(nrange *ir.RangeStmt) ir.Node {
468 m := nrange.X
469 origPos := ir.SetPos(m)
470 defer func() { base.Pos = origPos }()
471
472 return w.mapClear(m, reflectdata.RangeMapRType(base.Pos, nrange))
473 }
474
475
476 func (w *walkState) mapClear(m, rtyp ir.Node) ir.Node {
477 t := m.Type()
478
479
480 fn := typecheck.LookupRuntime("mapclear", t.Key(), t.Elem())
481 n := w.mkcallstmt1(fn, rtyp, m)
482 return typecheck.Stmt(n)
483 }
484
485
486
487 func arrayRangeClearTargetSafe(n ir.Node) bool {
488 if !n.Type().IsArray() {
489 return !ir.Any(n, readsMemory)
490 }
491
492 return arrayRangeClearAddressSafe(n)
493 }
494
495
496
497
498 func arrayRangeClearAddressSafe(n ir.Node) bool {
499 switch n.Op() {
500 case ir.ONAME:
501 return true
502 case ir.ODOT:
503 return arrayRangeClearAddressSafe(n.(*ir.SelectorExpr).X)
504 case ir.ODOTPTR:
505 return !ir.Any(n.(*ir.SelectorExpr).X, readsMemory)
506 case ir.ODEREF:
507 return !ir.Any(n.(*ir.StarExpr).X, readsMemory)
508 case ir.OINDEX:
509 n := n.(*ir.IndexExpr)
510 var baseSafe bool
511 if n.X.Type().IsArray() {
512 baseSafe = arrayRangeClearAddressSafe(n.X)
513 } else {
514 baseSafe = !ir.Any(n.X, readsMemory)
515 }
516 return baseSafe && !ir.Any(n.Index, readsMemory)
517 case ir.OCONVNOP:
518 return arrayRangeClearAddressSafe(n.(*ir.ConvExpr).X)
519 }
520 return false
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534 func (w *walkState) arrayRangeClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node {
535 if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
536 return nil
537 }
538
539 if v1 == nil || v2 != nil {
540 return nil
541 }
542
543 if len(loop.Body) != 1 || loop.Body[0] == nil {
544 return nil
545 }
546
547 stmt1 := loop.Body[0]
548 if stmt1.Op() != ir.OAS {
549 return nil
550 }
551 stmt := stmt1.(*ir.AssignStmt)
552 if !ir.IsZero(stmt.Y) {
553 return nil
554 }
555 if stmt.X.Op() != ir.OINDEX {
556 return nil
557 }
558 lhs := stmt.X.(*ir.IndexExpr)
559 x := lhs.X
560 if !ir.SameSafeExpr(lhs.Index, v1) {
561 return nil
562 }
563
564
565 if !ir.SameSafeExpr(x, x) || !arrayRangeClearTargetSafe(x) {
566 return nil
567 }
568
569
570 name, ok := v1.(*ir.Name)
571 if !ok || !name.OnStack() || name.Addrtaken() || ir.Any(x, func(n ir.Node) bool { return ir.Uses(n, name) }) {
572 return nil
573 }
574
575
576 n := int64(-1)
577 if ir.IsConst(a, constant.Int) {
578 n = ir.Int64Val(a)
579 } else if a.Type().IsArray() {
580 n = a.Type().NumElem()
581 } else if a.Type().IsPtr() && a.Type().Elem().IsArray() {
582 n = a.Type().Elem().NumElem()
583 }
584
585 if n >= 0 {
586
587 if !x.Type().IsArray() {
588 return nil
589 }
590 if x.Type().NumElem() != n {
591 return nil
592 }
593 } else {
594
595 if !ir.SameSafeExpr(x, a) {
596 return nil
597 }
598 }
599
600 return w.arrayClear(stmt.Pos(), x, loop)
601 }
602
603
604 func (w *walkState) arrayClear(wbPos src.XPos, a ir.Node, nrange *ir.RangeStmt) ir.Node {
605 elemsize := typecheck.RangeExprType(a.Type()).Elem().Size()
606 if elemsize <= 0 {
607 return nil
608 }
609
610
611
612
613
614
615
616
617 n := ir.NewIfStmt(base.Pos, nil, nil, nil)
618 ln := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TINT])
619 as := ir.NewAssignStmt(base.Pos, ln, ir.NewUnaryExpr(base.Pos, ir.OLEN, a))
620 n.PtrInit().Append(typecheck.Stmt(as))
621 n.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ln, ir.NewInt(base.Pos, 0))
622
623
624 hp := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TUNSAFEPTR])
625
626 ix := ir.NewIndexExpr(base.Pos, a, ir.NewInt(base.Pos, 0))
627 ix.SetBounded(true)
628 addr := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])
629 n.Body.Append(ir.NewAssignStmt(base.Pos, hp, addr))
630
631
632 hn := typecheck.TempAt(base.Pos, w.curfunc, types.Types[types.TUINTPTR])
633 mul := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, ln, ir.NewInt(base.Pos, elemsize)), types.Types[types.TUINTPTR])
634 n.Body.Append(ir.NewAssignStmt(base.Pos, hn, mul))
635
636 var fn ir.Node
637 if a.Type().Elem().HasPointers() {
638
639 w.curfunc.SetWBPos(wbPos)
640 fn = w.mkcallstmt("memclrHasPointers", hp, hn)
641 } else {
642
643 fn = w.mkcallstmt("memclrNoHeapPointers", hp, hn)
644 }
645
646 n.Body.Append(fn)
647
648
649 if nrange != nil {
650 idx := ir.NewAssignStmt(base.Pos, nrange.Key, typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OSUB, ln, ir.NewInt(base.Pos, 1)), nrange.Key.Type()))
651 n.Body.Append(idx)
652 }
653
654 n.Cond = typecheck.Expr(n.Cond)
655 n.Cond = typecheck.DefaultLit(n.Cond, nil)
656 typecheck.Stmts(n.Body)
657 return w.walkStmt(n)
658 }
659
View as plain text