github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/cmd/compile/internal/gc/range.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gc 6 7 import ( 8 "cmd/compile/internal/types" 9 "cmd/internal/objabi" 10 "cmd/internal/sys" 11 "unicode/utf8" 12 ) 13 14 // range 15 func typecheckrange(n *Node) { 16 // Typechecking order is important here: 17 // 0. first typecheck range expression (slice/map/chan), 18 // it is evaluated only once and so logically it is not part of the loop. 19 // 1. typcheck produced values, 20 // this part can declare new vars and so it must be typechecked before body, 21 // because body can contain a closure that captures the vars. 22 // 2. decldepth++ to denote loop body. 23 // 3. typecheck body. 24 // 4. decldepth--. 25 typecheckrangeExpr(n) 26 27 // second half of dance, the first half being typecheckrangeExpr 28 n.SetTypecheck(1) 29 ls := n.List.Slice() 30 for i1, n1 := range ls { 31 if n1.Typecheck() == 0 { 32 ls[i1] = typecheck(ls[i1], Erv|Easgn) 33 } 34 } 35 36 decldepth++ 37 typecheckslice(n.Nbody.Slice(), Etop) 38 decldepth-- 39 } 40 41 func typecheckrangeExpr(n *Node) { 42 n.Right = typecheck(n.Right, Erv) 43 44 t := n.Right.Type 45 if t == nil { 46 return 47 } 48 // delicate little dance. see typecheckas2 49 ls := n.List.Slice() 50 for i1, n1 := range ls { 51 if n1.Name == nil || n1.Name.Defn != n { 52 ls[i1] = typecheck(ls[i1], Erv|Easgn) 53 } 54 } 55 56 if t.IsPtr() && t.Elem().IsArray() { 57 t = t.Elem() 58 } 59 n.Type = t 60 61 var t1, t2 *types.Type 62 toomany := false 63 switch t.Etype { 64 default: 65 yyerrorl(n.Pos, "cannot range over %L", n.Right) 66 return 67 68 case TARRAY, TSLICE: 69 t1 = types.Types[TINT] 70 t2 = t.Elem() 71 72 case TMAP: 73 t1 = t.Key() 74 t2 = t.Val() 75 76 case TCHAN: 77 if !t.ChanDir().CanRecv() { 78 yyerrorl(n.Pos, "invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type) 79 return 80 } 81 82 t1 = t.Elem() 83 t2 = nil 84 if n.List.Len() == 2 { 85 toomany = true 86 } 87 88 case TSTRING: 89 t1 = types.Types[TINT] 90 t2 = types.Runetype 91 } 92 93 if n.List.Len() > 2 || toomany { 94 yyerrorl(n.Pos, "too many variables in range") 95 } 96 97 var v1, v2 *Node 98 if n.List.Len() != 0 { 99 v1 = n.List.First() 100 } 101 if n.List.Len() > 1 { 102 v2 = n.List.Second() 103 } 104 105 // this is not only a optimization but also a requirement in the spec. 106 // "if the second iteration variable is the blank identifier, the range 107 // clause is equivalent to the same clause with only the first variable 108 // present." 109 if isblank(v2) { 110 if v1 != nil { 111 n.List.Set1(v1) 112 } 113 v2 = nil 114 } 115 116 var why string 117 if v1 != nil { 118 if v1.Name != nil && v1.Name.Defn == n { 119 v1.Type = t1 120 } else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 { 121 yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t1, v1, why) 122 } 123 checkassign(n, v1) 124 } 125 126 if v2 != nil { 127 if v2.Name != nil && v2.Name.Defn == n { 128 v2.Type = t2 129 } else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 { 130 yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t2, v2, why) 131 } 132 checkassign(n, v2) 133 } 134 } 135 136 func cheapComputableIndex(width int64) bool { 137 switch thearch.LinkArch.Family { 138 // MIPS does not have R+R addressing 139 // Arm64 may lack ability to generate this code in our assembler, 140 // but the architecture supports it. 141 case sys.PPC64, sys.S390X: 142 return width == 1 143 case sys.AMD64, sys.I386, sys.ARM64, sys.ARM: 144 switch width { 145 case 1, 2, 4, 8: 146 return true 147 } 148 } 149 return false 150 } 151 152 // walkrange transforms various forms of ORANGE into 153 // simpler forms. The result must be assigned back to n. 154 // Node n may also be modified in place, and may also be 155 // the returned node. 156 func walkrange(n *Node) *Node { 157 // variable name conventions: 158 // ohv1, hv1, hv2: hidden (old) val 1, 2 159 // ha, hit: hidden aggregate, iterator 160 // hn, hp: hidden len, pointer 161 // hb: hidden bool 162 // a, v1, v2: not hidden aggregate, val 1, 2 163 164 t := n.Type 165 166 a := n.Right 167 lno := setlineno(a) 168 n.Right = nil 169 170 var v1, v2 *Node 171 l := n.List.Len() 172 if l > 0 { 173 v1 = n.List.First() 174 } 175 176 if l > 1 { 177 v2 = n.List.Second() 178 } 179 180 if isblank(v2) { 181 v2 = nil 182 } 183 184 if isblank(v1) && v2 == nil { 185 v1 = nil 186 } 187 188 if v1 == nil && v2 != nil { 189 Fatalf("walkrange: v2 != nil while v1 == nil") 190 } 191 192 // n.List has no meaning anymore, clear it 193 // to avoid erroneous processing by racewalk. 194 n.List.Set(nil) 195 196 var ifGuard *Node 197 198 translatedLoopOp := OFOR 199 200 var body []*Node 201 var init []*Node 202 switch t.Etype { 203 default: 204 Fatalf("walkrange") 205 206 case TARRAY, TSLICE: 207 if memclrrange(n, v1, v2, a) { 208 lineno = lno 209 return n 210 } 211 212 // orderstmt arranged for a copy of the array/slice variable if needed. 213 ha := a 214 215 hv1 := temp(types.Types[TINT]) 216 hn := temp(types.Types[TINT]) 217 218 init = append(init, nod(OAS, hv1, nil)) 219 init = append(init, nod(OAS, hn, nod(OLEN, ha, nil))) 220 221 n.Left = nod(OLT, hv1, hn) 222 n.Right = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1))) 223 224 // for range ha { body } 225 if v1 == nil { 226 break 227 } 228 229 // for v1 := range ha { body } 230 if v2 == nil { 231 body = []*Node{nod(OAS, v1, hv1)} 232 break 233 } 234 235 // for v1, v2 := range ha { body } 236 if cheapComputableIndex(n.Type.Elem().Width) { 237 // v1, v2 = hv1, ha[hv1] 238 tmp := nod(OINDEX, ha, hv1) 239 tmp.SetBounded(true) 240 // Use OAS2 to correctly handle assignments 241 // of the form "v1, a[v1] := range". 242 a := nod(OAS2, nil, nil) 243 a.List.Set2(v1, v2) 244 a.Rlist.Set2(hv1, tmp) 245 body = []*Node{a} 246 break 247 } 248 249 if objabi.Preemptibleloops_enabled != 0 { 250 // Doing this transformation makes a bounds check removal less trivial; see #20711 251 // TODO enhance the preemption check insertion so that this transformation is not necessary. 252 ifGuard = nod(OIF, nil, nil) 253 ifGuard.Left = nod(OLT, hv1, hn) 254 translatedLoopOp = OFORUNTIL 255 } 256 257 hp := temp(types.NewPtr(n.Type.Elem())) 258 tmp := nod(OINDEX, ha, nodintconst(0)) 259 tmp.SetBounded(true) 260 init = append(init, nod(OAS, hp, nod(OADDR, tmp, nil))) 261 262 // Use OAS2 to correctly handle assignments 263 // of the form "v1, a[v1] := range". 264 a := nod(OAS2, nil, nil) 265 a.List.Set2(v1, v2) 266 a.Rlist.Set2(hv1, nod(OIND, hp, nil)) 267 body = append(body, a) 268 269 // Advance pointer as part of increment. 270 // We used to advance the pointer before executing the loop body, 271 // but doing so would make the pointer point past the end of the 272 // array during the final iteration, possibly causing another unrelated 273 // piece of memory not to be garbage collected until the loop finished. 274 // Advancing during the increment ensures that the pointer p only points 275 // pass the end of the array during the final "p++; i++; if(i >= len(x)) break;", 276 // after which p is dead, so it cannot confuse the collector. 277 tmp = nod(OADD, hp, nodintconst(t.Elem().Width)) 278 279 tmp.Type = hp.Type 280 tmp.SetTypecheck(1) 281 tmp.Right.Type = types.Types[types.Tptr] 282 tmp.Right.SetTypecheck(1) 283 a = nod(OAS, hp, tmp) 284 a = typecheck(a, Etop) 285 n.Right.Ninit.Set1(a) 286 287 case TMAP: 288 // orderstmt allocated the iterator for us. 289 // we only use a once, so no copy needed. 290 ha := a 291 292 hit := prealloc[n] 293 th := hit.Type 294 n.Left = nil 295 keysym := th.Field(0).Sym // depends on layout of iterator struct. See reflect.go:hiter 296 valsym := th.Field(1).Sym // ditto 297 298 fn := syslook("mapiterinit") 299 300 fn = substArgTypes(fn, t.Key(), t.Val(), th) 301 init = append(init, mkcall1(fn, nil, nil, typename(t), ha, nod(OADDR, hit, nil))) 302 n.Left = nod(ONE, nodSym(ODOT, hit, keysym), nodnil()) 303 304 fn = syslook("mapiternext") 305 fn = substArgTypes(fn, th) 306 n.Right = mkcall1(fn, nil, nil, nod(OADDR, hit, nil)) 307 308 key := nodSym(ODOT, hit, keysym) 309 key = nod(OIND, key, nil) 310 if v1 == nil { 311 body = nil 312 } else if v2 == nil { 313 body = []*Node{nod(OAS, v1, key)} 314 } else { 315 val := nodSym(ODOT, hit, valsym) 316 val = nod(OIND, val, nil) 317 a := nod(OAS2, nil, nil) 318 a.List.Set2(v1, v2) 319 a.Rlist.Set2(key, val) 320 body = []*Node{a} 321 } 322 323 case TCHAN: 324 // orderstmt arranged for a copy of the channel variable. 325 ha := a 326 327 n.Left = nil 328 329 hv1 := temp(t.Elem()) 330 hv1.SetTypecheck(1) 331 if types.Haspointers(t.Elem()) { 332 init = append(init, nod(OAS, hv1, nil)) 333 } 334 hb := temp(types.Types[TBOOL]) 335 336 n.Left = nod(ONE, hb, nodbool(false)) 337 a := nod(OAS2RECV, nil, nil) 338 a.SetTypecheck(1) 339 a.List.Set2(hv1, hb) 340 a.Rlist.Set1(nod(ORECV, ha, nil)) 341 n.Left.Ninit.Set1(a) 342 if v1 == nil { 343 body = nil 344 } else { 345 body = []*Node{nod(OAS, v1, hv1)} 346 } 347 // Zero hv1. This prevents hv1 from being the sole, inaccessible 348 // reference to an otherwise GC-able value during the next channel receive. 349 // See issue 15281. 350 body = append(body, nod(OAS, hv1, nil)) 351 352 case TSTRING: 353 // Transform string range statements like "for v1, v2 = range a" into 354 // 355 // ha := a 356 // for hv1 := 0; hv1 < len(ha); { 357 // hv1t := hv1 358 // hv2 := rune(ha[hv1]) 359 // if hv2 < utf8.RuneSelf { 360 // hv1++ 361 // } else { 362 // hv2, hv1 = decoderune(ha, hv1) 363 // } 364 // v1, v2 = hv1t, hv2 365 // // original body 366 // } 367 368 // orderstmt arranged for a copy of the string variable. 369 ha := a 370 371 hv1 := temp(types.Types[TINT]) 372 hv1t := temp(types.Types[TINT]) 373 hv2 := temp(types.Runetype) 374 375 // hv1 := 0 376 init = append(init, nod(OAS, hv1, nil)) 377 378 // hv1 < len(ha) 379 n.Left = nod(OLT, hv1, nod(OLEN, ha, nil)) 380 381 if v1 != nil { 382 // hv1t = hv1 383 body = append(body, nod(OAS, hv1t, hv1)) 384 } 385 386 // hv2 := rune(ha[hv1]) 387 nind := nod(OINDEX, ha, hv1) 388 nind.SetBounded(true) 389 body = append(body, nod(OAS, hv2, conv(nind, types.Runetype))) 390 391 // if hv2 < utf8.RuneSelf 392 nif := nod(OIF, nil, nil) 393 nif.Left = nod(OLT, hv2, nodintconst(utf8.RuneSelf)) 394 395 // hv1++ 396 nif.Nbody.Set1(nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)))) 397 398 // } else { 399 eif := nod(OAS2, nil, nil) 400 nif.Rlist.Set1(eif) 401 402 // hv2, hv1 = decoderune(ha, hv1) 403 eif.List.Set2(hv2, hv1) 404 fn := syslook("decoderune") 405 eif.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1)) 406 407 body = append(body, nif) 408 409 if v1 != nil { 410 if v2 != nil { 411 // v1, v2 = hv1t, hv2 412 a := nod(OAS2, nil, nil) 413 a.List.Set2(v1, v2) 414 a.Rlist.Set2(hv1t, hv2) 415 body = append(body, a) 416 } else { 417 // v1 = hv1t 418 body = append(body, nod(OAS, v1, hv1t)) 419 } 420 } 421 } 422 423 n.Op = translatedLoopOp 424 typecheckslice(init, Etop) 425 426 if ifGuard != nil { 427 ifGuard.Ninit.Append(init...) 428 typecheckslice(ifGuard.Left.Ninit.Slice(), Etop) 429 ifGuard.Left = typecheck(ifGuard.Left, Erv) 430 } else { 431 n.Ninit.Append(init...) 432 } 433 434 typecheckslice(n.Left.Ninit.Slice(), Etop) 435 436 n.Left = typecheck(n.Left, Erv) 437 n.Right = typecheck(n.Right, Etop) 438 typecheckslice(body, Etop) 439 n.Nbody.Prepend(body...) 440 441 if ifGuard != nil { 442 ifGuard.Nbody.Set1(n) 443 n = ifGuard 444 } 445 446 n = walkstmt(n) 447 448 lineno = lno 449 return n 450 } 451 452 // Lower n into runtime·memclr if possible, for 453 // fast zeroing of slices and arrays (issue 5373). 454 // Look for instances of 455 // 456 // for i := range a { 457 // a[i] = zero 458 // } 459 // 460 // in which the evaluation of a is side-effect-free. 461 // 462 // Parameters are as in walkrange: "for v1, v2 = range a". 463 func memclrrange(n, v1, v2, a *Node) bool { 464 if Debug['N'] != 0 || instrumenting { 465 return false 466 } 467 if v1 == nil || v2 != nil { 468 return false 469 } 470 if n.Nbody.Len() == 0 || n.Nbody.First() == nil || n.Nbody.Len() > 1 { 471 return false 472 } 473 stmt := n.Nbody.First() // only stmt in body 474 if stmt.Op != OAS || stmt.Left.Op != OINDEX { 475 return false 476 } 477 if !samesafeexpr(stmt.Left.Left, a) || !samesafeexpr(stmt.Left.Right, v1) { 478 return false 479 } 480 elemsize := n.Type.Elem().Width 481 if elemsize <= 0 || !iszero(stmt.Right) { 482 return false 483 } 484 485 // Convert to 486 // if len(a) != 0 { 487 // hp = &a[0] 488 // hn = len(a)*sizeof(elem(a)) 489 // memclr{NoHeap,Has}Pointers(hp, hn) 490 // i = len(a) - 1 491 // } 492 n.Op = OIF 493 494 n.Nbody.Set(nil) 495 n.Left = nod(ONE, nod(OLEN, a, nil), nodintconst(0)) 496 497 // hp = &a[0] 498 hp := temp(types.Types[TUNSAFEPTR]) 499 500 tmp := nod(OINDEX, a, nodintconst(0)) 501 tmp.SetBounded(true) 502 tmp = nod(OADDR, tmp, nil) 503 tmp = nod(OCONVNOP, tmp, nil) 504 tmp.Type = types.Types[TUNSAFEPTR] 505 n.Nbody.Append(nod(OAS, hp, tmp)) 506 507 // hn = len(a) * sizeof(elem(a)) 508 hn := temp(types.Types[TUINTPTR]) 509 510 tmp = nod(OLEN, a, nil) 511 tmp = nod(OMUL, tmp, nodintconst(elemsize)) 512 tmp = conv(tmp, types.Types[TUINTPTR]) 513 n.Nbody.Append(nod(OAS, hn, tmp)) 514 515 var fn *Node 516 if types.Haspointers(a.Type.Elem()) { 517 // memclrHasPointers(hp, hn) 518 fn = mkcall("memclrHasPointers", nil, nil, hp, hn) 519 } else { 520 // memclrNoHeapPointers(hp, hn) 521 fn = mkcall("memclrNoHeapPointers", nil, nil, hp, hn) 522 } 523 524 n.Nbody.Append(fn) 525 526 // i = len(a) - 1 527 v1 = nod(OAS, v1, nod(OSUB, nod(OLEN, a, nil), nodintconst(1))) 528 529 n.Nbody.Append(v1) 530 531 n.Left = typecheck(n.Left, Erv) 532 typecheckslice(n.Nbody.Slice(), Etop) 533 n = walkstmt(n) 534 return true 535 }