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