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 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 if nn := arrayRangeClear(nrange, v1, v2, a); nn != nil {
84 base.Pos = lno
85 return nn
86 }
87 hv1 := typecheck.TempAt(base.Pos, ir.CurFunc, t)
88 hn := typecheck.TempAt(base.Pos, ir.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 := 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, ir.CurFunc, types.Types[types.TINT])
119 hn := typecheck.TempAt(base.Pos, ir.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, ir.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, ir.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, 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 = 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
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 := 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 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
520
521 n := int64(-1)
522 if ir.IsConst(a, constant.Int) {
523 n = ir.Int64Val(a)
524 } else if a.Type().IsArray() {
525 n = a.Type().NumElem()
526 } else if a.Type().IsPtr() && a.Type().Elem().IsArray() {
527 n = a.Type().Elem().NumElem()
528 }
529
530 if n >= 0 {
531
532 if !x.Type().IsArray() {
533 return nil
534 }
535 if x.Type().NumElem() != n {
536 return nil
537 }
538 } else {
539
540 if !ir.SameSafeExpr(x, a) {
541 return nil
542 }
543 }
544
545 if !ir.SameSafeExpr(lhs.Index, v1) {
546 return nil
547 }
548
549 if !ir.IsZero(stmt.Y) {
550 return nil
551 }
552
553 return arrayClear(stmt.Pos(), x, loop)
554 }
555
556
557 func arrayClear(wbPos src.XPos, a ir.Node, nrange *ir.RangeStmt) ir.Node {
558 elemsize := typecheck.RangeExprType(a.Type()).Elem().Size()
559 if elemsize <= 0 {
560 return nil
561 }
562
563
564
565
566
567
568
569
570 n := ir.NewIfStmt(base.Pos, nil, nil, nil)
571 n.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(base.Pos, 0))
572
573
574 hp := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUNSAFEPTR])
575
576 ix := ir.NewIndexExpr(base.Pos, a, ir.NewInt(base.Pos, 0))
577 ix.SetBounded(true)
578 addr := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])
579 n.Body.Append(ir.NewAssignStmt(base.Pos, hp, addr))
580
581
582 hn := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUINTPTR])
583 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])
584 n.Body.Append(ir.NewAssignStmt(base.Pos, hn, mul))
585
586 var fn ir.Node
587 if a.Type().Elem().HasPointers() {
588
589 ir.CurFunc.SetWBPos(wbPos)
590 fn = mkcallstmt("memclrHasPointers", hp, hn)
591 } else {
592
593 fn = mkcallstmt("memclrNoHeapPointers", hp, hn)
594 }
595
596 n.Body.Append(fn)
597
598
599 if nrange != nil {
600 idx := ir.NewAssignStmt(base.Pos, nrange.Key, typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(base.Pos, 1)), nrange.Key.Type()))
601 n.Body.Append(idx)
602 }
603
604 n.Cond = typecheck.Expr(n.Cond)
605 n.Cond = typecheck.DefaultLit(n.Cond, nil)
606 typecheck.Stmts(n.Body)
607 return walkStmt(n)
608 }
609
View as plain text