1
2
3
4
5 package walk
6
7 import (
8 "internal/buildcfg"
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.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 walkRange(nrange *ir.RangeStmt) ir.Node {
42 base.Assert(!nrange.DistinctVars)
43 if isMapClear(nrange) {
44 return 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 hv1 := typecheck.TempAt(base.Pos, ir.CurFunc, t)
84 hn := typecheck.TempAt(base.Pos, ir.CurFunc, t)
85
86 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
87 init = append(init, ir.NewAssignStmt(base.Pos, hn, a))
88
89 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, hn)
90 nfor.Post = ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))
91
92 if v1 != nil {
93 body = []ir.Node{rangeAssign(nrange, hv1)}
94 }
95
96 case k == types.TARRAY, k == types.TSLICE, k == types.TPTR:
97 if nn := arrayRangeClear(nrange, v1, v2, a); nn != nil {
98 base.Pos = lno
99 return nn
100 }
101
102
103 var elem *types.Type
104 switch t.Kind() {
105 case types.TSLICE, types.TARRAY:
106 elem = t.Elem()
107 case types.TPTR:
108 elem = t.Elem().Elem()
109 }
110
111
112 ha := a
113
114 hv1 := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
115 hn := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
116
117 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
118 init = append(init, ir.NewAssignStmt(base.Pos, hn, ir.NewUnaryExpr(base.Pos, ir.OLEN, ha)))
119
120 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, hn)
121 nfor.Post = ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))
122
123
124 if v1 == nil {
125 break
126 }
127
128
129 if v2 == nil {
130 body = []ir.Node{rangeAssign(nrange, hv1)}
131 break
132 }
133
134
135 if cheapComputableIndex(elem.Size()) {
136
137 tmp := ir.NewIndexExpr(base.Pos, ha, hv1)
138 tmp.SetBounded(true)
139 body = []ir.Node{rangeAssign2(nrange, hv1, tmp)}
140 break
141 }
142
143
144 var hs ir.Node
145 if t.IsSlice() {
146 hs = ha
147 } else {
148 var arr ir.Node
149 if t.IsPtr() {
150 arr = ha
151 } else {
152 arr = typecheck.NodAddr(ha)
153 arr.SetType(t.PtrTo())
154 arr.SetTypecheck(1)
155 }
156 hs = ir.NewSliceExpr(base.Pos, ir.OSLICEARR, arr, nil, nil, nil)
157
158 hs.SetType(types.NewSlice(elem))
159 hs.SetTypecheck(1)
160 }
161
162
163
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 ptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, hs)
211 ptr.SetBounded(true)
212 huVal := ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], ptr)
213 huVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUINTPTR], huVal)
214 hu := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUINTPTR])
215 init = append(init, ir.NewAssignStmt(base.Pos, hu, huVal))
216
217
218 hpVal := ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], hu)
219 hpVal.SetCheckPtr(true)
220 hpVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, elem.PtrTo(), hpVal)
221 hp := typecheck.TempAt(base.Pos, ir.CurFunc, elem.PtrTo())
222 body = append(body, ir.NewAssignStmt(base.Pos, hp, hpVal))
223
224
225 e := ir.NewStarExpr(base.Pos, hp)
226 e.SetBounded(true)
227 a := rangeAssign2(nrange, hv1, e)
228 body = append(body, a)
229
230
231
232 huVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], hp)
233 huVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUINTPTR], huVal)
234 as := ir.NewAssignStmt(base.Pos, hu, ir.NewBinaryExpr(base.Pos, ir.OADD, huVal, ir.NewInt(base.Pos, elem.Size())))
235 nfor.Post = ir.NewBlockStmt(base.Pos, []ir.Node{nfor.Post, as})
236
237 case k == types.TMAP:
238
239
240 ha := a
241
242 hit := nrange.Prealloc
243 th := hit.Type()
244
245
246 var keysym, elemsym *types.Sym
247 var iterInit, iterNext string
248 if buildcfg.Experiment.SwissMap {
249 keysym = th.Field(0).Sym
250 elemsym = th.Field(1).Sym
251 iterInit = "mapIterStart"
252 iterNext = "mapIterNext"
253 } else {
254 keysym = th.Field(0).Sym
255 elemsym = th.Field(1).Sym
256 iterInit = "mapiterinit"
257 iterNext = "mapiternext"
258 }
259
260 fn := typecheck.LookupRuntime(iterInit, t.Key(), t.Elem(), th)
261 init = append(init, mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), ha, typecheck.NodAddr(hit)))
262 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil())
263
264 fn = typecheck.LookupRuntime(iterNext, th)
265 nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit))
266
267 key := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), types.NewPtr(t.Key())))
268 if v1 == nil {
269 body = nil
270 } else if v2 == nil {
271 body = []ir.Node{rangeAssign(nrange, key)}
272 } else {
273 elem := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym), types.NewPtr(t.Elem())))
274 body = []ir.Node{rangeAssign2(nrange, key, elem)}
275 }
276
277 case k == types.TCHAN:
278
279 ha := a
280
281 hv1 := typecheck.TempAt(base.Pos, ir.CurFunc, t.Elem())
282 hv1.SetTypecheck(1)
283 if t.Elem().HasPointers() {
284 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
285 }
286 hb := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
287
288 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, hb, ir.NewBool(base.Pos, false))
289 lhs := []ir.Node{hv1, hb}
290 rhs := []ir.Node{ir.NewUnaryExpr(base.Pos, ir.ORECV, ha)}
291 a := ir.NewAssignListStmt(base.Pos, ir.OAS2RECV, lhs, rhs)
292 a.SetTypecheck(1)
293 nfor.Cond = ir.InitExpr([]ir.Node{a}, nfor.Cond)
294 if v1 == nil {
295 body = nil
296 } else {
297 body = []ir.Node{rangeAssign(nrange, hv1)}
298 }
299
300
301
302 body = append(body, ir.NewAssignStmt(base.Pos, hv1, nil))
303
304 case k == types.TSTRING:
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321 ha := a
322
323 hv1 := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
324 hv1t := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
325 hv2 := typecheck.TempAt(base.Pos, ir.CurFunc, types.RuneType)
326
327
328 init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
329
330
331 nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, ir.NewUnaryExpr(base.Pos, ir.OLEN, ha))
332
333 if v1 != nil {
334
335 body = append(body, ir.NewAssignStmt(base.Pos, hv1t, hv1))
336 }
337
338
339 nind := ir.NewIndexExpr(base.Pos, ha, hv1)
340 nind.SetBounded(true)
341 body = append(body, ir.NewAssignStmt(base.Pos, hv2, typecheck.Conv(nind, types.RuneType)))
342
343
344 nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
345 nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv2, ir.NewInt(base.Pos, utf8.RuneSelf))
346
347
348 nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))}
349
350
351
352 fn := typecheck.LookupRuntime("decoderune")
353 call := 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 = 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 mapRangeClear(nrange *ir.RangeStmt) ir.Node {
468 m := nrange.X
469 origPos := ir.SetPos(m)
470 defer func() { base.Pos = origPos }()
471
472 return mapClear(m, reflectdata.RangeMapRType(base.Pos, nrange))
473 }
474
475
476 func mapClear(m, rtyp ir.Node) ir.Node {
477 t := m.Type()
478
479
480 fn := typecheck.LookupRuntime("mapclear", t.Key(), t.Elem())
481 n := mkcallstmt1(fn, rtyp, m)
482 return walkStmt(typecheck.Stmt(n))
483 }
484
485
486
487
488
489
490
491
492
493
494
495
496 func arrayRangeClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node {
497 if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
498 return nil
499 }
500
501 if v1 == nil || v2 != nil {
502 return nil
503 }
504
505 if len(loop.Body) != 1 || loop.Body[0] == nil {
506 return nil
507 }
508
509 stmt1 := loop.Body[0]
510 if stmt1.Op() != ir.OAS {
511 return nil
512 }
513 stmt := stmt1.(*ir.AssignStmt)
514 if stmt.X.Op() != ir.OINDEX {
515 return nil
516 }
517 lhs := stmt.X.(*ir.IndexExpr)
518 x := lhs.X
519 if a.Type().IsPtr() && a.Type().Elem().IsArray() {
520 if s, ok := x.(*ir.StarExpr); ok && s.Op() == ir.ODEREF {
521 x = s.X
522 }
523 }
524
525 if !ir.SameSafeExpr(x, a) || !ir.SameSafeExpr(lhs.Index, v1) {
526 return nil
527 }
528
529 if !ir.IsZero(stmt.Y) {
530 return nil
531 }
532
533 return arrayClear(stmt.Pos(), a, loop)
534 }
535
536
537 func arrayClear(wbPos src.XPos, a ir.Node, nrange *ir.RangeStmt) ir.Node {
538 elemsize := typecheck.RangeExprType(a.Type()).Elem().Size()
539 if elemsize <= 0 {
540 return nil
541 }
542
543
544
545
546
547
548
549
550 n := ir.NewIfStmt(base.Pos, nil, nil, nil)
551 n.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(base.Pos, 0))
552
553
554 hp := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUNSAFEPTR])
555
556 ix := ir.NewIndexExpr(base.Pos, a, ir.NewInt(base.Pos, 0))
557 ix.SetBounded(true)
558 addr := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])
559 n.Body.Append(ir.NewAssignStmt(base.Pos, hp, addr))
560
561
562 hn := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUINTPTR])
563 mul := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(base.Pos, elemsize)), types.Types[types.TUINTPTR])
564 n.Body.Append(ir.NewAssignStmt(base.Pos, hn, mul))
565
566 var fn ir.Node
567 if a.Type().Elem().HasPointers() {
568
569 ir.CurFunc.SetWBPos(wbPos)
570 fn = mkcallstmt("memclrHasPointers", hp, hn)
571 } else {
572
573 fn = mkcallstmt("memclrNoHeapPointers", hp, hn)
574 }
575
576 n.Body.Append(fn)
577
578
579 if nrange != nil {
580 idx := ir.NewAssignStmt(base.Pos, nrange.Key, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(base.Pos, 1)))
581 n.Body.Append(idx)
582 }
583
584 n.Cond = typecheck.Expr(n.Cond)
585 n.Cond = typecheck.DefaultLit(n.Cond, nil)
586 typecheck.Stmts(n.Body)
587 return walkStmt(n)
588 }
589
View as plain text