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