...

Source file src/cmd/compile/internal/walk/assign.go

Documentation: cmd/compile/internal/walk

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

View as plain text