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 5package walk 6 7import ( 8 "go/constant" 9 "internal/abi" 10 11 "cmd/compile/internal/base" 12 "cmd/compile/internal/ir" 13 "cmd/compile/internal/reflectdata" 14 "cmd/compile/internal/typecheck" 15 "cmd/compile/internal/types" 16 "cmd/internal/src" 17) 18 19// walkAssign walks an OAS (AssignExpr) or OASOP (AssignOpExpr) node. 20func walkAssign(init *ir.Nodes, n ir.Node) ir.Node { 21 init.Append(ir.TakeInit(n)...) 22 23 var left, right ir.Node 24 switch n.Op() { 25 case ir.OAS: 26 n := n.(*ir.AssignStmt) 27 left, right = n.X, n.Y 28 case ir.OASOP: 29 n := n.(*ir.AssignOpStmt) 30 left, right = n.X, n.Y 31 } 32 33 // Recognize m[k] = append(m[k], ...) so we can reuse 34 // the mapassign call. 35 var mapAppend *ir.CallExpr 36 if left.Op() == ir.OINDEXMAP && right.Op() == ir.OAPPEND { 37 left := left.(*ir.IndexExpr) 38 mapAppend = right.(*ir.CallExpr) 39 if !ir.SameSafeExpr(left, mapAppend.Args[0]) { 40 base.Fatalf("not same expressions: %v != %v", left, mapAppend.Args[0]) 41 } 42 } 43 44 left = walkExpr(left, init) 45 left = safeExpr(left, init) 46 if mapAppend != nil { 47 mapAppend.Args[0] = left 48 } 49 50 if n.Op() == ir.OASOP { 51 // Rewrite x op= y into x = x op y. 52 n = ir.NewAssignStmt(base.Pos, left, typecheck.Expr(ir.NewBinaryExpr(base.Pos, n.(*ir.AssignOpStmt).AsOp, left, right))) 53 } else { 54 n.(*ir.AssignStmt).X = left 55 } 56 as := n.(*ir.AssignStmt) 57 58 if oaslit(as, init) { 59 return ir.NewBlockStmt(as.Pos(), nil) 60 } 61 62 if as.Y == nil { 63 // TODO(austin): Check all "implicit zeroing" 64 return as 65 } 66 67 if !base.Flag.Cfg.Instrumenting && ir.IsZero(as.Y) { 68 return as 69 } 70 71 switch as.Y.Op() { 72 default: 73 as.Y = walkExpr(as.Y, init) 74 75 case ir.ORECV: 76 // x = <-c; as.Left is x, as.Right.Left is c. 77 // order.stmt made sure x is addressable. 78 recv := as.Y.(*ir.UnaryExpr) 79 recv.X = walkExpr(recv.X, init) 80 81 n1 := typecheck.NodAddr(as.X) 82 r := recv.X // the channel 83 return mkcall1(chanfn("chanrecv1", 2, r.Type()), nil, init, r, n1) 84 85 case ir.OAPPEND: 86 // x = append(...) 87 call := as.Y.(*ir.CallExpr) 88 if call.Type().Elem().NotInHeap() { 89 base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", call.Type().Elem()) 90 } 91 var r ir.Node 92 switch { 93 case isAppendOfMake(call): 94 // x = append(y, make([]T, y)...) 95 r = extendSlice(call, init) 96 case call.IsDDD: 97 r = appendSlice(call, init) // also works for append(slice, string). 98 default: 99 r = walkAppend(call, init, as) 100 } 101 as.Y = r 102 if r.Op() == ir.OAPPEND { 103 r := r.(*ir.CallExpr) 104 // Left in place for back end. 105 // Do not add a new write barrier. 106 // Set up address of type for back end. 107 r.Fun = reflectdata.AppendElemRType(base.Pos, r) 108 return as 109 } 110 // Otherwise, lowered for race detector. 111 // Treat as ordinary assignment. 112 } 113 114 if as.X != nil && as.Y != nil { 115 return convas(as, init) 116 } 117 return as 118} 119 120// walkAssignDotType walks an OAS2DOTTYPE node. 121func walkAssignDotType(n *ir.AssignListStmt, init *ir.Nodes) ir.Node { 122 walkExprListSafe(n.Lhs, init) 123 n.Rhs[0] = walkExpr(n.Rhs[0], init) 124 return n 125} 126 127// walkAssignFunc walks an OAS2FUNC node. 128func walkAssignFunc(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { 129 init.Append(ir.TakeInit(n)...) 130 131 r := n.Rhs[0] 132 walkExprListSafe(n.Lhs, init) 133 r = walkExpr(r, init) 134 135 if ir.IsIntrinsicCall(r.(*ir.CallExpr)) { 136 n.Rhs = []ir.Node{r} 137 return n 138 } 139 init.Append(r) 140 141 ll := ascompatet(n.Lhs, r.Type()) 142 return ir.NewBlockStmt(src.NoXPos, ll) 143} 144 145// walkAssignList walks an OAS2 node. 146func walkAssignList(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { 147 init.Append(ir.TakeInit(n)...) 148 return ir.NewBlockStmt(src.NoXPos, ascompatee(ir.OAS, n.Lhs, n.Rhs)) 149} 150 151// walkAssignMapRead walks an OAS2MAPR node. 152func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { 153 init.Append(ir.TakeInit(n)...) 154 155 r := n.Rhs[0].(*ir.IndexExpr) 156 walkExprListSafe(n.Lhs, init) 157 r.X = walkExpr(r.X, init) 158 r.Index = walkExpr(r.Index, init) 159 t := r.X.Type() 160 161 fast := mapfast(t) 162 key := mapKeyArg(fast, r, r.Index, false) 163 164 // from: 165 // a,b = m[i] 166 // to: 167 // var,b = mapaccess2*(t, m, i) 168 // a = *var 169 a := n.Lhs[0] 170 171 var call *ir.CallExpr 172 if w := t.Elem().Size(); w <= abi.ZeroValSize { 173 fn := mapfn(mapaccess2[fast], t, false) 174 call = mkcall1(fn, fn.Type().ResultsTuple(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key) 175 } else { 176 fn := mapfn("mapaccess2_fat", t, true) 177 z := reflectdata.ZeroAddr(w) 178 call = mkcall1(fn, fn.Type().ResultsTuple(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key, z) 179 } 180 181 // mapaccess2* returns a typed bool, but due to spec changes, 182 // the boolean result of i.(T) is now untyped so we make it the 183 // same type as the variable on the lhs. 184 if ok := n.Lhs[1]; !ir.IsBlank(ok) && ok.Type().IsBoolean() { 185 call.Type().Field(1).Type = ok.Type() 186 } 187 n.Rhs = []ir.Node{call} 188 n.SetOp(ir.OAS2FUNC) 189 190 // don't generate a = *var if a is _ 191 if ir.IsBlank(a) { 192 return walkExpr(typecheck.Stmt(n), init) 193 } 194 195 var_ := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewPtr(t.Elem())) 196 var_.SetTypecheck(1) 197 var_.MarkNonNil() // mapaccess always returns a non-nil pointer 198 199 n.Lhs[0] = var_ 200 init.Append(walkExpr(n, init)) 201 202 as := ir.NewAssignStmt(base.Pos, a, ir.NewStarExpr(base.Pos, var_)) 203 return walkExpr(typecheck.Stmt(as), init) 204} 205 206// walkAssignRecv walks an OAS2RECV node. 207func walkAssignRecv(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { 208 init.Append(ir.TakeInit(n)...) 209 210 r := n.Rhs[0].(*ir.UnaryExpr) // recv 211 walkExprListSafe(n.Lhs, init) 212 r.X = walkExpr(r.X, init) 213 var n1 ir.Node 214 if ir.IsBlank(n.Lhs[0]) { 215 n1 = typecheck.NodNil() 216 } else { 217 n1 = typecheck.NodAddr(n.Lhs[0]) 218 } 219 fn := chanfn("chanrecv2", 2, r.X.Type()) 220 ok := n.Lhs[1] 221 call := mkcall1(fn, types.Types[types.TBOOL], init, r.X, n1) 222 return typecheck.Stmt(ir.NewAssignStmt(base.Pos, ok, call)) 223} 224 225// walkReturn walks an ORETURN node. 226func walkReturn(n *ir.ReturnStmt) ir.Node { 227 fn := ir.CurFunc 228 229 fn.NumReturns++ 230 if len(n.Results) == 0 { 231 return n 232 } 233 234 results := fn.Type().Results() 235 dsts := make([]ir.Node, len(results)) 236 for i, v := range results { 237 // TODO(mdempsky): typecheck should have already checked the result variables. 238 dsts[i] = typecheck.AssignExpr(v.Nname.(*ir.Name)) 239 } 240 241 n.Results = ascompatee(n.Op(), dsts, n.Results) 242 return n 243} 244 245// check assign type list to 246// an expression list. called in 247// 248// expr-list = func() 249func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node { 250 if len(nl) != nr.NumFields() { 251 base.Fatalf("ascompatet: assignment count mismatch: %d = %d", len(nl), nr.NumFields()) 252 } 253 254 var nn ir.Nodes 255 for i, l := range nl { 256 if ir.IsBlank(l) { 257 continue 258 } 259 r := nr.Field(i) 260 261 // Order should have created autotemps of the appropriate type for 262 // us to store results into. 263 if tmp, ok := l.(*ir.Name); !ok || !tmp.AutoTemp() || !types.Identical(tmp.Type(), r.Type) { 264 base.FatalfAt(l.Pos(), "assigning %v to %+v", r.Type, l) 265 } 266 267 res := ir.NewResultExpr(base.Pos, nil, types.BADWIDTH) 268 res.Index = int64(i) 269 res.SetType(r.Type) 270 res.SetTypecheck(1) 271 272 nn.Append(ir.NewAssignStmt(base.Pos, l, res)) 273 } 274 return nn 275} 276 277// check assign expression list to 278// an expression list. called in 279// 280// expr-list = expr-list 281func ascompatee(op ir.Op, nl, nr []ir.Node) []ir.Node { 282 // cannot happen: should have been rejected during type checking 283 if len(nl) != len(nr) { 284 base.Fatalf("assignment operands mismatch: %+v / %+v", ir.Nodes(nl), ir.Nodes(nr)) 285 } 286 287 var assigned ir.NameSet 288 var memWrite, deferResultWrite bool 289 290 // affected reports whether expression n could be affected by 291 // the assignments applied so far. 292 affected := func(n ir.Node) bool { 293 if deferResultWrite { 294 return true 295 } 296 return ir.Any(n, func(n ir.Node) bool { 297 if n.Op() == ir.ONAME && assigned.Has(n.(*ir.Name)) { 298 return true 299 } 300 if memWrite && readsMemory(n) { 301 return true 302 } 303 return false 304 }) 305 } 306 307 // If a needed expression may be affected by an 308 // earlier assignment, make an early copy of that 309 // expression and use the copy instead. 310 var early ir.Nodes 311 save := func(np *ir.Node) { 312 if n := *np; affected(n) { 313 *np = copyExpr(n, n.Type(), &early) 314 } 315 } 316 317 var late ir.Nodes 318 for i, lorig := range nl { 319 l, r := lorig, nr[i] 320 321 // Do not generate 'x = x' during return. See issue 4014. 322 if op == ir.ORETURN && ir.SameSafeExpr(l, r) { 323 continue 324 } 325 326 // Save subexpressions needed on left side. 327 // Drill through non-dereferences. 328 for { 329 // If an expression has init statements, they must be evaluated 330 // before any of its saved sub-operands (#45706). 331 // TODO(mdempsky): Disallow init statements on lvalues. 332 init := ir.TakeInit(l) 333 walkStmtList(init) 334 early.Append(init...) 335 336 switch ll := l.(type) { 337 case *ir.IndexExpr: 338 if ll.X.Type().IsArray() { 339 save(&ll.Index) 340 l = ll.X 341 continue 342 } 343 case *ir.ParenExpr: 344 l = ll.X 345 continue 346 case *ir.SelectorExpr: 347 if ll.Op() == ir.ODOT { 348 l = ll.X 349 continue 350 } 351 } 352 break 353 } 354 355 var name *ir.Name 356 switch l.Op() { 357 default: 358 base.Fatalf("unexpected lvalue %v", l.Op()) 359 case ir.ONAME: 360 name = l.(*ir.Name) 361 case ir.OINDEX, ir.OINDEXMAP: 362 l := l.(*ir.IndexExpr) 363 save(&l.X) 364 save(&l.Index) 365 case ir.ODEREF: 366 l := l.(*ir.StarExpr) 367 save(&l.X) 368 case ir.ODOTPTR: 369 l := l.(*ir.SelectorExpr) 370 save(&l.X) 371 } 372 373 // Save expression on right side. 374 save(&r) 375 376 appendWalkStmt(&late, convas(ir.NewAssignStmt(base.Pos, lorig, r), &late)) 377 378 // Check for reasons why we may need to compute later expressions 379 // before this assignment happens. 380 381 if name == nil { 382 // Not a direct assignment to a declared variable. 383 // Conservatively assume any memory access might alias. 384 memWrite = true 385 continue 386 } 387 388 if name.Class == ir.PPARAMOUT && ir.CurFunc.HasDefer() { 389 // Assignments to a result parameter in a function with defers 390 // becomes visible early if evaluation of any later expression 391 // panics (#43835). 392 deferResultWrite = true 393 continue 394 } 395 396 if ir.IsBlank(name) { 397 // We can ignore assignments to blank or anonymous result parameters. 398 // These can't appear in expressions anyway. 399 continue 400 } 401 402 if name.Addrtaken() || !name.OnStack() { 403 // Global variable, heap escaped, or just addrtaken. 404 // Conservatively assume any memory access might alias. 405 memWrite = true 406 continue 407 } 408 409 // Local, non-addrtaken variable. 410 // Assignments can only alias with direct uses of this variable. 411 assigned.Add(name) 412 } 413 414 early.Append(late.Take()...) 415 return early 416} 417 418// readsMemory reports whether the evaluation n directly reads from 419// memory that might be written to indirectly. 420func readsMemory(n ir.Node) bool { 421 switch n.Op() { 422 case ir.ONAME: 423 n := n.(*ir.Name) 424 if n.Class == ir.PFUNC { 425 return false 426 } 427 return n.Addrtaken() || !n.OnStack() 428 429 case ir.OADD, 430 ir.OAND, 431 ir.OANDAND, 432 ir.OANDNOT, 433 ir.OBITNOT, 434 ir.OCONV, 435 ir.OCONVIFACE, 436 ir.OCONVNOP, 437 ir.ODIV, 438 ir.ODOT, 439 ir.ODOTTYPE, 440 ir.OLITERAL, 441 ir.OLSH, 442 ir.OMOD, 443 ir.OMUL, 444 ir.ONEG, 445 ir.ONIL, 446 ir.OOR, 447 ir.OOROR, 448 ir.OPAREN, 449 ir.OPLUS, 450 ir.ORSH, 451 ir.OSUB, 452 ir.OXOR: 453 return false 454 } 455 456 // Be conservative. 457 return true 458} 459 460// expand append(l1, l2...) to 461// 462// init { 463// s := l1 464// newLen := s.len + l2.len 465// // Compare as uint so growslice can panic on overflow. 466// if uint(newLen) <= uint(s.cap) { 467// s = s[:newLen] 468// } else { 469// s = growslice(s.ptr, s.len, s.cap, l2.len, T) 470// } 471// memmove(&s[s.len-l2.len], &l2[0], l2.len*sizeof(T)) 472// } 473// s 474// 475// l2 is allowed to be a string. 476func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { 477 walkAppendArgs(n, init) 478 479 l1 := n.Args[0] 480 l2 := n.Args[1] 481 l2 = cheapExpr(l2, init) 482 n.Args[1] = l2 483 484 var nodes ir.Nodes 485 486 // var s []T 487 s := typecheck.TempAt(base.Pos, ir.CurFunc, l1.Type()) 488 nodes.Append(ir.NewAssignStmt(base.Pos, s, l1)) // s = l1 489 490 elemtype := s.Type().Elem() 491 492 // Decompose slice. 493 oldPtr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s) 494 oldLen := ir.NewUnaryExpr(base.Pos, ir.OLEN, s) 495 oldCap := ir.NewUnaryExpr(base.Pos, ir.OCAP, s) 496 497 // Number of elements we are adding 498 num := ir.NewUnaryExpr(base.Pos, ir.OLEN, l2) 499 500 // newLen := oldLen + num 501 newLen := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT]) 502 nodes.Append(ir.NewAssignStmt(base.Pos, newLen, ir.NewBinaryExpr(base.Pos, ir.OADD, oldLen, num))) 503 504 // if uint(newLen) <= uint(oldCap) 505 nif := ir.NewIfStmt(base.Pos, nil, nil, nil) 506 nuint := typecheck.Conv(newLen, types.Types[types.TUINT]) 507 scapuint := typecheck.Conv(oldCap, types.Types[types.TUINT]) 508 nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLE, nuint, scapuint) 509 nif.Likely = true 510 511 // then { s = s[:newLen] } 512 slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, newLen, nil) 513 slice.SetBounded(true) 514 nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, slice)} 515 516 // else { s = growslice(oldPtr, newLen, oldCap, num, T) } 517 call := walkGrowslice(s, nif.PtrInit(), oldPtr, newLen, oldCap, num) 518 nif.Else = []ir.Node{ir.NewAssignStmt(base.Pos, s, call)} 519 520 nodes.Append(nif) 521 522 // Index to start copying into s. 523 // idx = newLen - len(l2) 524 // We use this expression instead of oldLen because it avoids 525 // a spill/restore of oldLen. 526 // Note: this doesn't work optimally currently because 527 // the compiler optimizer undoes this arithmetic. 528 idx := ir.NewBinaryExpr(base.Pos, ir.OSUB, newLen, ir.NewUnaryExpr(base.Pos, ir.OLEN, l2)) 529 530 var ncopy ir.Node 531 if elemtype.HasPointers() { 532 // copy(s[idx:], l2) 533 slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, idx, nil, nil) 534 slice.SetType(s.Type()) 535 slice.SetBounded(true) 536 537 ir.CurFunc.SetWBPos(n.Pos()) 538 539 // instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int 540 fn := typecheck.LookupRuntime("typedslicecopy", l1.Type().Elem(), l2.Type().Elem()) 541 ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes)) 542 ptr2, len2 := backingArrayPtrLen(l2) 543 ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.AppendElemRType(base.Pos, n), ptr1, len1, ptr2, len2) 544 } else if base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime { 545 // rely on runtime to instrument: 546 // copy(s[idx:], l2) 547 // l2 can be a slice or string. 548 slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, idx, nil, nil) 549 slice.SetType(s.Type()) 550 slice.SetBounded(true) 551 552 ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes)) 553 ptr2, len2 := backingArrayPtrLen(l2) 554 555 fn := typecheck.LookupRuntime("slicecopy", ptr1.Type().Elem(), ptr2.Type().Elem()) 556 ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, ptr1, len1, ptr2, len2, ir.NewInt(base.Pos, elemtype.Size())) 557 } else { 558 // memmove(&s[idx], &l2[0], len(l2)*sizeof(T)) 559 ix := ir.NewIndexExpr(base.Pos, s, idx) 560 ix.SetBounded(true) 561 addr := typecheck.NodAddr(ix) 562 563 sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l2) 564 565 nwid := cheapExpr(typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, l2), types.Types[types.TUINTPTR]), &nodes) 566 nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(base.Pos, elemtype.Size())) 567 568 // instantiate func memmove(to *any, frm *any, length uintptr) 569 fn := typecheck.LookupRuntime("memmove", elemtype, elemtype) 570 ncopy = mkcall1(fn, nil, &nodes, addr, sptr, nwid) 571 } 572 ln := append(nodes, ncopy) 573 574 typecheck.Stmts(ln) 575 walkStmtList(ln) 576 init.Append(ln...) 577 return s 578} 579 580// isAppendOfMake reports whether n is of the form append(x, make([]T, y)...). 581// isAppendOfMake assumes n has already been typechecked. 582func isAppendOfMake(n ir.Node) bool { 583 if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting { 584 return false 585 } 586 587 if n.Typecheck() == 0 { 588 base.Fatalf("missing typecheck: %+v", n) 589 } 590 591 if n.Op() != ir.OAPPEND { 592 return false 593 } 594 call := n.(*ir.CallExpr) 595 if !call.IsDDD || len(call.Args) != 2 || call.Args[1].Op() != ir.OMAKESLICE { 596 return false 597 } 598 599 mk := call.Args[1].(*ir.MakeExpr) 600 if mk.Cap != nil { 601 return false 602 } 603 604 // y must be either an integer constant or the largest possible positive value 605 // of variable y needs to fit into a uint. 606 607 // typecheck made sure that constant arguments to make are not negative and fit into an int. 608 609 // The care of overflow of the len argument to make will be handled by an explicit check of int(len) < 0 during runtime. 610 y := mk.Len 611 if !ir.IsConst(y, constant.Int) && y.Type().Size() > types.Types[types.TUINT].Size() { 612 return false 613 } 614 615 return true 616} 617 618// extendSlice rewrites append(l1, make([]T, l2)...) to 619// 620// init { 621// if l2 >= 0 { // Empty if block here for more meaningful node.SetLikely(true) 622// } else { 623// panicmakeslicelen() 624// } 625// s := l1 626// if l2 != 0 { 627// n := len(s) + l2 628// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2. 629// // cap is a positive int and n can become negative when len(s) + l2 630// // overflows int. Interpreting n when negative as uint makes it larger 631// // than cap(s). growslice will check the int n arg and panic if n is 632// // negative. This prevents the overflow from being undetected. 633// if uint(n) <= uint(cap(s)) { 634// s = s[:n] 635// } else { 636// s = growslice(T, s.ptr, n, s.cap, l2, T) 637// } 638// // clear the new portion of the underlying array. 639// hp := &s[len(s)-l2] 640// hn := l2 * sizeof(T) 641// memclr(hp, hn) 642// } 643// } 644// s 645// 646// if T has pointers, the final memclr can go inside the "then" branch, as 647// growslice will have done the clearing for us. 648 649func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { 650 // isAppendOfMake made sure all possible positive values of l2 fit into a uint. 651 // The case of l2 overflow when converting from e.g. uint to int is handled by an explicit 652 // check of l2 < 0 at runtime which is generated below. 653 l2 := typecheck.Conv(n.Args[1].(*ir.MakeExpr).Len, types.Types[types.TINT]) 654 l2 = typecheck.Expr(l2) 655 n.Args[1] = l2 // walkAppendArgs expects l2 in n.List.Second(). 656 657 walkAppendArgs(n, init) 658 659 l1 := n.Args[0] 660 l2 = n.Args[1] // re-read l2, as it may have been updated by walkAppendArgs 661 662 var nodes []ir.Node 663 664 // if l2 >= 0 (likely happens), do nothing 665 nifneg := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGE, l2, ir.NewInt(base.Pos, 0)), nil, nil) 666 nifneg.Likely = true 667 668 // else panicmakeslicelen() 669 nifneg.Else = []ir.Node{mkcall("panicmakeslicelen", nil, init)} 670 nodes = append(nodes, nifneg) 671 672 // s := l1 673 s := typecheck.TempAt(base.Pos, ir.CurFunc, l1.Type()) 674 nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, l1)) 675 676 // if l2 != 0 { 677 // Avoid work if we're not appending anything. But more importantly, 678 // avoid allowing hp to be a past-the-end pointer when clearing. See issue 67255. 679 nifnz := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.ONE, l2, ir.NewInt(base.Pos, 0)), nil, nil) 680 nifnz.Likely = true 681 nodes = append(nodes, nifnz) 682 683 elemtype := s.Type().Elem() 684 685 // n := s.len + l2 686 nn := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT]) 687 nifnz.Body = append(nifnz.Body, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2))) 688 689 // if uint(n) <= uint(s.cap) 690 nuint := typecheck.Conv(nn, types.Types[types.TUINT]) 691 capuint := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT]) 692 nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLE, nuint, capuint), nil, nil) 693 nif.Likely = true 694 695 // then { s = s[:n] } 696 nt := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, nn, nil) 697 nt.SetBounded(true) 698 nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, nt)} 699 700 // else { s = growslice(s.ptr, n, s.cap, l2, T) } 701 nif.Else = []ir.Node{ 702 ir.NewAssignStmt(base.Pos, s, walkGrowslice(s, nif.PtrInit(), 703 ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), 704 nn, 705 ir.NewUnaryExpr(base.Pos, ir.OCAP, s), 706 l2)), 707 } 708 709 nifnz.Body = append(nifnz.Body, nif) 710 711 // hp := &s[s.len - l2] 712 // TODO: &s[s.len] - hn? 713 ix := ir.NewIndexExpr(base.Pos, s, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2)) 714 ix.SetBounded(true) 715 hp := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR]) 716 717 // hn := l2 * sizeof(elem(s)) 718 hn := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, l2, ir.NewInt(base.Pos, elemtype.Size())), types.Types[types.TUINTPTR]) 719 720 clrname := "memclrNoHeapPointers" 721 hasPointers := elemtype.HasPointers() 722 if hasPointers { 723 clrname = "memclrHasPointers" 724 ir.CurFunc.SetWBPos(n.Pos()) 725 } 726 727 var clr ir.Nodes 728 clrfn := mkcall(clrname, nil, &clr, hp, hn) 729 clr.Append(clrfn) 730 if hasPointers { 731 // growslice will have cleared the new entries, so only 732 // if growslice isn't called do we need to do the zeroing ourselves. 733 nif.Body = append(nif.Body, clr...) 734 } else { 735 nifnz.Body = append(nifnz.Body, clr...) 736 } 737 738 typecheck.Stmts(nodes) 739 walkStmtList(nodes) 740 init.Append(nodes...) 741 return s 742} 743