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
346
347
348 nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLE, hv2, ir.NewInt(base.Pos, utf8.RuneSelf-1))
349
350
351 nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))}
352
353
354
355 fn := typecheck.LookupRuntime("decoderune")
356 call := mkcall1(fn, fn.Type().ResultsTuple(), &nif.Else, ha, hv1)
357 a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{hv2, hv1}, []ir.Node{call})
358 nif.Else.Append(a)
359
360 body = append(body, nif)
361
362 if v1 != nil {
363 if v2 != nil {
364
365 body = append(body, rangeAssign2(nrange, hv1t, hv2))
366 } else {
367
368 body = append(body, rangeAssign(nrange, hv1t))
369 }
370 }
371 }
372
373 typecheck.Stmts(init)
374
375 nfor.PtrInit().Append(init...)
376
377 typecheck.Stmts(nfor.Cond.Init())
378
379 nfor.Cond = typecheck.Expr(nfor.Cond)
380 nfor.Cond = typecheck.DefaultLit(nfor.Cond, nil)
381 nfor.Post = typecheck.Stmt(nfor.Post)
382 typecheck.Stmts(body)
383 nfor.Body.Append(body...)
384 nfor.Body.Append(nrange.Body...)
385
386 var n ir.Node = nfor
387
388 n = walkStmt(n)
389
390 base.Pos = lno
391 return n
392 }
393
394
395 func rangeAssign(n *ir.RangeStmt, key ir.Node) ir.Node {
396 key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType)
397 return ir.NewAssignStmt(n.Pos(), n.Key, key)
398 }
399
400
401 func rangeAssign2(n *ir.RangeStmt, key, value ir.Node) ir.Node {
402
403
404 key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType)
405 value = rangeConvert(n, n.Value.Type(), value, n.ValueTypeWord, n.ValueSrcRType)
406 return ir.NewAssignListStmt(n.Pos(), ir.OAS2, []ir.Node{n.Key, n.Value}, []ir.Node{key, value})
407 }
408
409
410
411
412 func rangeConvert(nrange *ir.RangeStmt, dst *types.Type, src, typeWord, srcRType ir.Node) ir.Node {
413 src = typecheck.Expr(src)
414 if dst.Kind() == types.TBLANK || types.Identical(dst, src.Type()) {
415 return src
416 }
417
418 n := ir.NewConvExpr(nrange.Pos(), ir.OCONV, dst, src)
419 n.TypeWord = typeWord
420 n.SrcRType = srcRType
421 return typecheck.Expr(n)
422 }
423
424
425
426
427
428
429
430
431 func isMapClear(n *ir.RangeStmt) bool {
432 if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
433 return false
434 }
435
436 t := n.X.Type()
437 if n.Op() != ir.ORANGE || t.Kind() != types.TMAP || n.Key == nil || n.Value != nil {
438 return false
439 }
440
441 k := n.Key
442
443 if !ir.DeclaredBy(k, n) {
444 return false
445 }
446
447 if len(n.Body) != 1 {
448 return false
449 }
450
451 stmt := n.Body[0]
452 if stmt == nil || stmt.Op() != ir.ODELETE {
453 return false
454 }
455
456 m := n.X
457 if delete := stmt.(*ir.CallExpr); !ir.SameSafeExpr(delete.Args[0], m) || !ir.SameSafeExpr(delete.Args[1], k) {
458 return false
459 }
460
461
462 if !types.IsReflexive(t.Key()) {
463 return false
464 }
465
466 return true
467 }
468
469
470 func mapRangeClear(nrange *ir.RangeStmt) ir.Node {
471 m := nrange.X
472 origPos := ir.SetPos(m)
473 defer func() { base.Pos = origPos }()
474
475 return mapClear(m, reflectdata.RangeMapRType(base.Pos, nrange))
476 }
477
478
479 func mapClear(m, rtyp ir.Node) ir.Node {
480 t := m.Type()
481
482
483 fn := typecheck.LookupRuntime("mapclear", t.Key(), t.Elem())
484 n := mkcallstmt1(fn, rtyp, m)
485 return walkStmt(typecheck.Stmt(n))
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499 func arrayRangeClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node {
500 if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
501 return nil
502 }
503
504 if v1 == nil || v2 != nil {
505 return nil
506 }
507
508 if len(loop.Body) != 1 || loop.Body[0] == nil {
509 return nil
510 }
511
512 stmt1 := loop.Body[0]
513 if stmt1.Op() != ir.OAS {
514 return nil
515 }
516 stmt := stmt1.(*ir.AssignStmt)
517 if stmt.X.Op() != ir.OINDEX {
518 return nil
519 }
520 lhs := stmt.X.(*ir.IndexExpr)
521 x := lhs.X
522 if a.Type().IsPtr() && a.Type().Elem().IsArray() {
523 if s, ok := x.(*ir.StarExpr); ok && s.Op() == ir.ODEREF {
524 x = s.X
525 }
526 }
527
528 if !ir.SameSafeExpr(x, a) || !ir.SameSafeExpr(lhs.Index, v1) {
529 return nil
530 }
531
532 if !ir.IsZero(stmt.Y) {
533 return nil
534 }
535
536 return arrayClear(stmt.Pos(), a, loop)
537 }
538
539
540 func arrayClear(wbPos src.XPos, a ir.Node, nrange *ir.RangeStmt) ir.Node {
541 elemsize := typecheck.RangeExprType(a.Type()).Elem().Size()
542 if elemsize <= 0 {
543 return nil
544 }
545
546
547
548
549
550
551
552
553 n := ir.NewIfStmt(base.Pos, nil, nil, nil)
554 n.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(base.Pos, 0))
555
556
557 hp := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUNSAFEPTR])
558
559 ix := ir.NewIndexExpr(base.Pos, a, ir.NewInt(base.Pos, 0))
560 ix.SetBounded(true)
561 addr := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])
562 n.Body.Append(ir.NewAssignStmt(base.Pos, hp, addr))
563
564
565 hn := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUINTPTR])
566 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])
567 n.Body.Append(ir.NewAssignStmt(base.Pos, hn, mul))
568
569 var fn ir.Node
570 if a.Type().Elem().HasPointers() {
571
572 ir.CurFunc.SetWBPos(wbPos)
573 fn = mkcallstmt("memclrHasPointers", hp, hn)
574 } else {
575
576 fn = mkcallstmt("memclrNoHeapPointers", hp, hn)
577 }
578
579 n.Body.Append(fn)
580
581
582 if nrange != nil {
583 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)))
584 n.Body.Append(idx)
585 }
586
587 n.Cond = typecheck.Expr(n.Cond)
588 n.Cond = typecheck.DefaultLit(n.Cond, nil)
589 typecheck.Stmts(n.Body)
590 return walkStmt(n)
591 }
592
View as plain text