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	"fmt"
9	"go/constant"
10	"go/token"
11	"internal/abi"
12	"strings"
13
14	"cmd/compile/internal/base"
15	"cmd/compile/internal/escape"
16	"cmd/compile/internal/ir"
17	"cmd/compile/internal/reflectdata"
18	"cmd/compile/internal/typecheck"
19	"cmd/compile/internal/types"
20)
21
22// Rewrite append(src, x, y, z) so that any side effects in
23// x, y, z (including runtime panics) are evaluated in
24// initialization statements before the append.
25// For normal code generation, stop there and leave the
26// rest to ssagen.
27//
28// For race detector, expand append(src, a [, b]* ) to
29//
30//	init {
31//	  s := src
32//	  const argc = len(args) - 1
33//	  newLen := s.len + argc
34//	  if uint(newLen) <= uint(s.cap) {
35//	    s = s[:newLen]
36//	  } else {
37//	    s = growslice(s.ptr, newLen, s.cap, argc, elemType)
38//	  }
39//	  s[s.len - argc] = a
40//	  s[s.len - argc + 1] = b
41//	  ...
42//	}
43//	s
44func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node {
45	if !ir.SameSafeExpr(dst, n.Args[0]) {
46		n.Args[0] = safeExpr(n.Args[0], init)
47		n.Args[0] = walkExpr(n.Args[0], init)
48	}
49	walkExprListSafe(n.Args[1:], init)
50
51	nsrc := n.Args[0]
52
53	// walkExprListSafe will leave OINDEX (s[n]) alone if both s
54	// and n are name or literal, but those may index the slice we're
55	// modifying here. Fix explicitly.
56	// Using cheapExpr also makes sure that the evaluation
57	// of all arguments (and especially any panics) happen
58	// before we begin to modify the slice in a visible way.
59	ls := n.Args[1:]
60	for i, n := range ls {
61		n = cheapExpr(n, init)
62		if !types.Identical(n.Type(), nsrc.Type().Elem()) {
63			n = typecheck.AssignConv(n, nsrc.Type().Elem(), "append")
64			n = walkExpr(n, init)
65		}
66		ls[i] = n
67	}
68
69	argc := len(n.Args) - 1
70	if argc < 1 {
71		return nsrc
72	}
73
74	// General case, with no function calls left as arguments.
75	// Leave for ssagen, except that instrumentation requires the old form.
76	if !base.Flag.Cfg.Instrumenting || base.Flag.CompilingRuntime {
77		return n
78	}
79
80	var l []ir.Node
81
82	// s = slice to append to
83	s := typecheck.TempAt(base.Pos, ir.CurFunc, nsrc.Type())
84	l = append(l, ir.NewAssignStmt(base.Pos, s, nsrc))
85
86	// num = number of things to append
87	num := ir.NewInt(base.Pos, int64(argc))
88
89	// newLen := s.len + num
90	newLen := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
91	l = append(l, ir.NewAssignStmt(base.Pos, newLen, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), num)))
92
93	// if uint(newLen) <= uint(s.cap)
94	nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
95	nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLE, typecheck.Conv(newLen, types.Types[types.TUINT]), typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT]))
96	nif.Likely = true
97
98	// then { s = s[:n] }
99	slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, newLen, nil)
100	slice.SetBounded(true)
101	nif.Body = []ir.Node{
102		ir.NewAssignStmt(base.Pos, s, slice),
103	}
104
105	// else { s = growslice(s.ptr, n, s.cap, a, T) }
106	nif.Else = []ir.Node{
107		ir.NewAssignStmt(base.Pos, s, walkGrowslice(s, nif.PtrInit(),
108			ir.NewUnaryExpr(base.Pos, ir.OSPTR, s),
109			newLen,
110			ir.NewUnaryExpr(base.Pos, ir.OCAP, s),
111			num)),
112	}
113
114	l = append(l, nif)
115
116	ls = n.Args[1:]
117	for i, n := range ls {
118		// s[s.len-argc+i] = arg
119		ix := ir.NewIndexExpr(base.Pos, s, ir.NewBinaryExpr(base.Pos, ir.OSUB, newLen, ir.NewInt(base.Pos, int64(argc-i))))
120		ix.SetBounded(true)
121		l = append(l, ir.NewAssignStmt(base.Pos, ix, n))
122	}
123
124	typecheck.Stmts(l)
125	walkStmtList(l)
126	init.Append(l...)
127	return s
128}
129
130// growslice(ptr *T, newLen, oldCap, num int, <type>) (ret []T)
131func walkGrowslice(slice *ir.Name, init *ir.Nodes, oldPtr, newLen, oldCap, num ir.Node) *ir.CallExpr {
132	elemtype := slice.Type().Elem()
133	fn := typecheck.LookupRuntime("growslice", elemtype, elemtype)
134	elemtypeptr := reflectdata.TypePtrAt(base.Pos, elemtype)
135	return mkcall1(fn, slice.Type(), init, oldPtr, newLen, oldCap, num, elemtypeptr)
136}
137
138// walkClear walks an OCLEAR node.
139func walkClear(n *ir.UnaryExpr) ir.Node {
140	typ := n.X.Type()
141	switch {
142	case typ.IsSlice():
143		if n := arrayClear(n.X.Pos(), n.X, nil); n != nil {
144			return n
145		}
146		// If n == nil, we are clearing an array which takes zero memory, do nothing.
147		return ir.NewBlockStmt(n.Pos(), nil)
148	case typ.IsMap():
149		return mapClear(n.X, reflectdata.TypePtrAt(n.X.Pos(), n.X.Type()))
150	}
151	panic("unreachable")
152}
153
154// walkClose walks an OCLOSE node.
155func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
156	return mkcall1(chanfn("closechan", 1, n.X.Type()), nil, init, n.X)
157}
158
159// Lower copy(a, b) to a memmove call or a runtime call.
160//
161//	init {
162//	  n := len(a)
163//	  if n > len(b) { n = len(b) }
164//	  if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) }
165//	}
166//	n;
167//
168// Also works if b is a string.
169func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node {
170	if n.X.Type().Elem().HasPointers() {
171		ir.CurFunc.SetWBPos(n.Pos())
172		fn := writebarrierfn("typedslicecopy", n.X.Type().Elem(), n.Y.Type().Elem())
173		n.X = cheapExpr(n.X, init)
174		ptrL, lenL := backingArrayPtrLen(n.X)
175		n.Y = cheapExpr(n.Y, init)
176		ptrR, lenR := backingArrayPtrLen(n.Y)
177		return mkcall1(fn, n.Type(), init, reflectdata.CopyElemRType(base.Pos, n), ptrL, lenL, ptrR, lenR)
178	}
179
180	if runtimecall {
181		// rely on runtime to instrument:
182		//  copy(n.Left, n.Right)
183		// n.Right can be a slice or string.
184
185		n.X = cheapExpr(n.X, init)
186		ptrL, lenL := backingArrayPtrLen(n.X)
187		n.Y = cheapExpr(n.Y, init)
188		ptrR, lenR := backingArrayPtrLen(n.Y)
189
190		fn := typecheck.LookupRuntime("slicecopy", ptrL.Type().Elem(), ptrR.Type().Elem())
191
192		return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(base.Pos, n.X.Type().Elem().Size()))
193	}
194
195	n.X = walkExpr(n.X, init)
196	n.Y = walkExpr(n.Y, init)
197	nl := typecheck.TempAt(base.Pos, ir.CurFunc, n.X.Type())
198	nr := typecheck.TempAt(base.Pos, ir.CurFunc, n.Y.Type())
199	var l []ir.Node
200	l = append(l, ir.NewAssignStmt(base.Pos, nl, n.X))
201	l = append(l, ir.NewAssignStmt(base.Pos, nr, n.Y))
202
203	nfrm := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nr)
204	nto := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nl)
205
206	nlen := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
207
208	// n = len(to)
209	l = append(l, ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nl)))
210
211	// if n > len(frm) { n = len(frm) }
212	nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
213
214	nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr))
215	nif.Body.Append(ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr)))
216	l = append(l, nif)
217
218	// if to.ptr != frm.ptr { memmove( ... ) }
219	ne := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.ONE, nto, nfrm), nil, nil)
220	ne.Likely = true
221	l = append(l, ne)
222
223	fn := typecheck.LookupRuntime("memmove", nl.Type().Elem(), nl.Type().Elem())
224	nwid := ir.Node(typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUINTPTR]))
225	setwid := ir.NewAssignStmt(base.Pos, nwid, typecheck.Conv(nlen, types.Types[types.TUINTPTR]))
226	ne.Body.Append(setwid)
227	nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(base.Pos, nl.Type().Elem().Size()))
228	call := mkcall1(fn, nil, init, nto, nfrm, nwid)
229	ne.Body.Append(call)
230
231	typecheck.Stmts(l)
232	walkStmtList(l)
233	init.Append(l...)
234	return nlen
235}
236
237// walkDelete walks an ODELETE node.
238func walkDelete(init *ir.Nodes, n *ir.CallExpr) ir.Node {
239	init.Append(ir.TakeInit(n)...)
240	map_ := n.Args[0]
241	key := n.Args[1]
242	map_ = walkExpr(map_, init)
243	key = walkExpr(key, init)
244
245	t := map_.Type()
246	fast := mapfast(t)
247	key = mapKeyArg(fast, n, key, false)
248	return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.DeleteMapRType(base.Pos, n), map_, key)
249}
250
251// walkLenCap walks an OLEN or OCAP node.
252func walkLenCap(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
253	if isRuneCount(n) {
254		// Replace len([]rune(string)) with runtime.countrunes(string).
255		return mkcall("countrunes", n.Type(), init, typecheck.Conv(n.X.(*ir.ConvExpr).X, types.Types[types.TSTRING]))
256	}
257	if isByteCount(n) {
258		conv := n.X.(*ir.ConvExpr)
259		walkStmtList(conv.Init())
260		init.Append(ir.TakeInit(conv)...)
261		_, len := backingArrayPtrLen(cheapExpr(conv.X, init))
262		return len
263	}
264	if isChanLenCap(n) {
265		name := "chanlen"
266		if n.Op() == ir.OCAP {
267			name = "chancap"
268		}
269		// cannot use chanfn - closechan takes any, not chan any,
270		// because it accepts both send-only and recv-only channels.
271		fn := typecheck.LookupRuntime(name, n.X.Type())
272		return mkcall1(fn, n.Type(), init, n.X)
273	}
274
275	n.X = walkExpr(n.X, init)
276
277	// replace len(*[10]int) with 10.
278	// delayed until now to preserve side effects.
279	t := n.X.Type()
280
281	if t.IsPtr() {
282		t = t.Elem()
283	}
284	if t.IsArray() {
285		safeExpr(n.X, init)
286		con := ir.NewConstExpr(constant.MakeInt64(t.NumElem()), n)
287		con.SetTypecheck(1)
288		return con
289	}
290	return n
291}
292
293// walkMakeChan walks an OMAKECHAN node.
294func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
295	// When size fits into int, use makechan instead of
296	// makechan64, which is faster and shorter on 32 bit platforms.
297	size := n.Len
298	fnname := "makechan64"
299	argtype := types.Types[types.TINT64]
300
301	// Type checking guarantees that TIDEAL size is positive and fits in an int.
302	// The case of size overflow when converting TUINT or TUINTPTR to TINT
303	// will be handled by the negative range checks in makechan during runtime.
304	if size.Type().IsKind(types.TIDEAL) || size.Type().Size() <= types.Types[types.TUINT].Size() {
305		fnname = "makechan"
306		argtype = types.Types[types.TINT]
307	}
308
309	return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.MakeChanRType(base.Pos, n), typecheck.Conv(size, argtype))
310}
311
312// walkMakeMap walks an OMAKEMAP node.
313func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
314	t := n.Type()
315	hmapType := reflectdata.MapType()
316	hint := n.Len
317
318	// var h *hmap
319	var h ir.Node
320	if n.Esc() == ir.EscNone {
321		// Allocate hmap on stack.
322
323		// var hv hmap
324		// h = &hv
325		h = stackTempAddr(init, hmapType)
326
327		// Allocate one bucket pointed to by hmap.buckets on stack if hint
328		// is not larger than BUCKETSIZE. In case hint is larger than
329		// BUCKETSIZE runtime.makemap will allocate the buckets on the heap.
330		// Maximum key and elem size is 128 bytes, larger objects
331		// are stored with an indirection. So max bucket size is 2048+eps.
332		if !ir.IsConst(hint, constant.Int) ||
333			constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(abi.MapBucketCount)) {
334
335			// In case hint is larger than BUCKETSIZE runtime.makemap
336			// will allocate the buckets on the heap, see #20184
337			//
338			// if hint <= BUCKETSIZE {
339			//     var bv bmap
340			//     b = &bv
341			//     h.buckets = b
342			// }
343
344			nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLE, hint, ir.NewInt(base.Pos, abi.MapBucketCount)), nil, nil)
345			nif.Likely = true
346
347			// var bv bmap
348			// b = &bv
349			b := stackTempAddr(&nif.Body, reflectdata.MapBucketType(t))
350
351			// h.buckets = b
352			bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap
353			na := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, bsym), typecheck.ConvNop(b, types.Types[types.TUNSAFEPTR]))
354			nif.Body.Append(na)
355			appendWalkStmt(init, nif)
356		}
357	}
358
359	if ir.IsConst(hint, constant.Int) && constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(abi.MapBucketCount)) {
360		// Handling make(map[any]any) and
361		// make(map[any]any, hint) where hint <= BUCKETSIZE
362		// special allows for faster map initialization and
363		// improves binary size by using calls with fewer arguments.
364		// For hint <= BUCKETSIZE overLoadFactor(hint, 0) is false
365		// and no buckets will be allocated by makemap. Therefore,
366		// no buckets need to be allocated in this code path.
367		if n.Esc() == ir.EscNone {
368			// Only need to initialize h.hash0 since
369			// hmap h has been allocated on the stack already.
370			// h.hash0 = rand32()
371			rand := mkcall("rand32", types.Types[types.TUINT32], init)
372			hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap
373			appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, hashsym), rand))
374			return typecheck.ConvNop(h, t)
375		}
376		// Call runtime.makehmap to allocate an
377		// hmap on the heap and initialize hmap's hash0 field.
378		fn := typecheck.LookupRuntime("makemap_small", t.Key(), t.Elem())
379		return mkcall1(fn, n.Type(), init)
380	}
381
382	if n.Esc() != ir.EscNone {
383		h = typecheck.NodNil()
384	}
385	// Map initialization with a variable or large hint is
386	// more complicated. We therefore generate a call to
387	// runtime.makemap to initialize hmap and allocate the
388	// map buckets.
389
390	// When hint fits into int, use makemap instead of
391	// makemap64, which is faster and shorter on 32 bit platforms.
392	fnname := "makemap64"
393	argtype := types.Types[types.TINT64]
394
395	// Type checking guarantees that TIDEAL hint is positive and fits in an int.
396	// See checkmake call in TMAP case of OMAKE case in OpSwitch in typecheck1 function.
397	// The case of hint overflow when converting TUINT or TUINTPTR to TINT
398	// will be handled by the negative range checks in makemap during runtime.
399	if hint.Type().IsKind(types.TIDEAL) || hint.Type().Size() <= types.Types[types.TUINT].Size() {
400		fnname = "makemap"
401		argtype = types.Types[types.TINT]
402	}
403
404	fn := typecheck.LookupRuntime(fnname, hmapType, t.Key(), t.Elem())
405	return mkcall1(fn, n.Type(), init, reflectdata.MakeMapRType(base.Pos, n), typecheck.Conv(hint, argtype), h)
406}
407
408// walkMakeSlice walks an OMAKESLICE node.
409func walkMakeSlice(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
410	l := n.Len
411	r := n.Cap
412	if r == nil {
413		r = safeExpr(l, init)
414		l = r
415	}
416	t := n.Type()
417	if t.Elem().NotInHeap() {
418		base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
419	}
420	if n.Esc() == ir.EscNone {
421		if why := escape.HeapAllocReason(n); why != "" {
422			base.Fatalf("%v has EscNone, but %v", n, why)
423		}
424		// var arr [r]T
425		// n = arr[:l]
426		i := typecheck.IndexConst(r)
427		if i < 0 {
428			base.Fatalf("walkExpr: invalid index %v", r)
429		}
430
431		// cap is constrained to [0,2^31) or [0,2^63) depending on whether
432		// we're in 32-bit or 64-bit systems. So it's safe to do:
433		//
434		// if uint64(len) > cap {
435		//     if len < 0 { panicmakeslicelen() }
436		//     panicmakeslicecap()
437		// }
438		nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(l, types.Types[types.TUINT64]), ir.NewInt(base.Pos, i)), nil, nil)
439		niflen := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLT, l, ir.NewInt(base.Pos, 0)), nil, nil)
440		niflen.Body = []ir.Node{mkcall("panicmakeslicelen", nil, init)}
441		nif.Body.Append(niflen, mkcall("panicmakeslicecap", nil, init))
442		init.Append(typecheck.Stmt(nif))
443
444		t = types.NewArray(t.Elem(), i) // [r]T
445		var_ := typecheck.TempAt(base.Pos, ir.CurFunc, t)
446		appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, nil))  // zero temp
447		r := ir.NewSliceExpr(base.Pos, ir.OSLICE, var_, nil, l, nil) // arr[:l]
448		// The conv is necessary in case n.Type is named.
449		return walkExpr(typecheck.Expr(typecheck.Conv(r, n.Type())), init)
450	}
451
452	// n escapes; set up a call to makeslice.
453	// When len and cap can fit into int, use makeslice instead of
454	// makeslice64, which is faster and shorter on 32 bit platforms.
455
456	len, cap := l, r
457
458	fnname := "makeslice64"
459	argtype := types.Types[types.TINT64]
460
461	// Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
462	// The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
463	// will be handled by the negative range checks in makeslice during runtime.
464	if (len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size()) &&
465		(cap.Type().IsKind(types.TIDEAL) || cap.Type().Size() <= types.Types[types.TUINT].Size()) {
466		fnname = "makeslice"
467		argtype = types.Types[types.TINT]
468	}
469	fn := typecheck.LookupRuntime(fnname)
470	ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype))
471	ptr.MarkNonNil()
472	len = typecheck.Conv(len, types.Types[types.TINT])
473	cap = typecheck.Conv(cap, types.Types[types.TINT])
474	sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, len, cap)
475	return walkExpr(typecheck.Expr(sh), init)
476}
477
478// walkMakeSliceCopy walks an OMAKESLICECOPY node.
479func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
480	if n.Esc() == ir.EscNone {
481		base.Fatalf("OMAKESLICECOPY with EscNone: %v", n)
482	}
483
484	t := n.Type()
485	if t.Elem().NotInHeap() {
486		base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
487	}
488
489	length := typecheck.Conv(n.Len, types.Types[types.TINT])
490	copylen := ir.NewUnaryExpr(base.Pos, ir.OLEN, n.Cap)
491	copyptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, n.Cap)
492
493	if !t.Elem().HasPointers() && n.Bounded() {
494		// When len(to)==len(from) and elements have no pointers:
495		// replace make+copy with runtime.mallocgc+runtime.memmove.
496
497		// We do not check for overflow of len(to)*elem.Width here
498		// since len(from) is an existing checked slice capacity
499		// with same elem.Width for the from slice.
500		size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(base.Pos, t.Elem().Size()), types.Types[types.TUINTPTR]))
501
502		// instantiate mallocgc(size uintptr, typ *byte, needszero bool) unsafe.Pointer
503		fn := typecheck.LookupRuntime("mallocgc")
504		ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, size, typecheck.NodNil(), ir.NewBool(base.Pos, false))
505		ptr.MarkNonNil()
506		sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length)
507
508		s := typecheck.TempAt(base.Pos, ir.CurFunc, t)
509		r := typecheck.Stmt(ir.NewAssignStmt(base.Pos, s, sh))
510		r = walkExpr(r, init)
511		init.Append(r)
512
513		// instantiate memmove(to *any, frm *any, size uintptr)
514		fn = typecheck.LookupRuntime("memmove", t.Elem(), t.Elem())
515		ncopy := mkcall1(fn, nil, init, ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), copyptr, size)
516		init.Append(walkExpr(typecheck.Stmt(ncopy), init))
517
518		return s
519	}
520	// Replace make+copy with runtime.makeslicecopy.
521	// instantiate makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
522	fn := typecheck.LookupRuntime("makeslicecopy")
523	ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR]))
524	ptr.MarkNonNil()
525	sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length)
526	return walkExpr(typecheck.Expr(sh), init)
527}
528
529// walkNew walks an ONEW node.
530func walkNew(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
531	t := n.Type().Elem()
532	if t.NotInHeap() {
533		base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type().Elem())
534	}
535	if n.Esc() == ir.EscNone {
536		if t.Size() > ir.MaxImplicitStackVarSize {
537			base.Fatalf("large ONEW with EscNone: %v", n)
538		}
539		return stackTempAddr(init, t)
540	}
541	types.CalcSize(t)
542	n.MarkNonNil()
543	return n
544}
545
546func walkMinMax(n *ir.CallExpr, init *ir.Nodes) ir.Node {
547	init.Append(ir.TakeInit(n)...)
548	walkExprList(n.Args, init)
549	return n
550}
551
552// generate code for print.
553func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
554	// Hoist all the argument evaluation up before the lock.
555	walkExprListCheap(nn.Args, init)
556
557	// For println, add " " between elements and "\n" at the end.
558	if nn.Op() == ir.OPRINTLN {
559		s := nn.Args
560		t := make([]ir.Node, 0, len(s)*2)
561		for i, n := range s {
562			if i != 0 {
563				t = append(t, ir.NewString(base.Pos, " "))
564			}
565			t = append(t, n)
566		}
567		t = append(t, ir.NewString(base.Pos, "\n"))
568		nn.Args = t
569	}
570
571	// Collapse runs of constant strings.
572	s := nn.Args
573	t := make([]ir.Node, 0, len(s))
574	for i := 0; i < len(s); {
575		var strs []string
576		for i < len(s) && ir.IsConst(s[i], constant.String) {
577			strs = append(strs, ir.StringVal(s[i]))
578			i++
579		}
580		if len(strs) > 0 {
581			t = append(t, ir.NewString(base.Pos, strings.Join(strs, "")))
582		}
583		if i < len(s) {
584			t = append(t, s[i])
585			i++
586		}
587	}
588	nn.Args = t
589
590	calls := []ir.Node{mkcall("printlock", nil, init)}
591	for i, n := range nn.Args {
592		if n.Op() == ir.OLITERAL {
593			if n.Type() == types.UntypedRune {
594				n = typecheck.DefaultLit(n, types.RuneType)
595			}
596
597			switch n.Val().Kind() {
598			case constant.Int:
599				n = typecheck.DefaultLit(n, types.Types[types.TINT64])
600
601			case constant.Float:
602				n = typecheck.DefaultLit(n, types.Types[types.TFLOAT64])
603			}
604		}
605
606		if n.Op() != ir.OLITERAL && n.Type() != nil && n.Type().Kind() == types.TIDEAL {
607			n = typecheck.DefaultLit(n, types.Types[types.TINT64])
608		}
609		n = typecheck.DefaultLit(n, nil)
610		nn.Args[i] = n
611		if n.Type() == nil || n.Type().Kind() == types.TFORW {
612			continue
613		}
614
615		var on *ir.Name
616		switch n.Type().Kind() {
617		case types.TINTER:
618			if n.Type().IsEmptyInterface() {
619				on = typecheck.LookupRuntime("printeface", n.Type())
620			} else {
621				on = typecheck.LookupRuntime("printiface", n.Type())
622			}
623		case types.TPTR:
624			if n.Type().Elem().NotInHeap() {
625				on = typecheck.LookupRuntime("printuintptr")
626				n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
627				n.SetType(types.Types[types.TUNSAFEPTR])
628				n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
629				n.SetType(types.Types[types.TUINTPTR])
630				break
631			}
632			fallthrough
633		case types.TCHAN, types.TMAP, types.TFUNC, types.TUNSAFEPTR:
634			on = typecheck.LookupRuntime("printpointer", n.Type())
635		case types.TSLICE:
636			on = typecheck.LookupRuntime("printslice", n.Type())
637		case types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, types.TUINTPTR:
638			if types.RuntimeSymName(n.Type().Sym()) == "hex" {
639				on = typecheck.LookupRuntime("printhex")
640			} else {
641				on = typecheck.LookupRuntime("printuint")
642			}
643		case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64:
644			on = typecheck.LookupRuntime("printint")
645		case types.TFLOAT32, types.TFLOAT64:
646			on = typecheck.LookupRuntime("printfloat")
647		case types.TCOMPLEX64, types.TCOMPLEX128:
648			on = typecheck.LookupRuntime("printcomplex")
649		case types.TBOOL:
650			on = typecheck.LookupRuntime("printbool")
651		case types.TSTRING:
652			cs := ""
653			if ir.IsConst(n, constant.String) {
654				cs = ir.StringVal(n)
655			}
656			switch cs {
657			case " ":
658				on = typecheck.LookupRuntime("printsp")
659			case "\n":
660				on = typecheck.LookupRuntime("printnl")
661			default:
662				on = typecheck.LookupRuntime("printstring")
663			}
664		default:
665			badtype(ir.OPRINT, n.Type(), nil)
666			continue
667		}
668
669		r := ir.NewCallExpr(base.Pos, ir.OCALL, on, nil)
670		if params := on.Type().Params(); len(params) > 0 {
671			t := params[0].Type
672			n = typecheck.Conv(n, t)
673			r.Args.Append(n)
674		}
675		calls = append(calls, r)
676	}
677
678	calls = append(calls, mkcall("printunlock", nil, init))
679
680	typecheck.Stmts(calls)
681	walkExprList(calls, init)
682
683	r := ir.NewBlockStmt(base.Pos, nil)
684	r.List = calls
685	return walkStmt(typecheck.Stmt(r))
686}
687
688// walkRecoverFP walks an ORECOVERFP node.
689func walkRecoverFP(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
690	return mkcall("gorecover", nn.Type(), init, walkExpr(nn.Args[0], init))
691}
692
693// walkUnsafeData walks an OUNSAFESLICEDATA or OUNSAFESTRINGDATA expression.
694func walkUnsafeData(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
695	slice := walkExpr(n.X, init)
696	res := typecheck.Expr(ir.NewUnaryExpr(n.Pos(), ir.OSPTR, slice))
697	res.SetType(n.Type())
698	return walkExpr(res, init)
699}
700
701func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
702	ptr := safeExpr(n.X, init)
703	len := safeExpr(n.Y, init)
704	sliceType := n.Type()
705
706	lenType := types.Types[types.TINT64]
707	unsafePtr := typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR])
708
709	// If checkptr enabled, call runtime.unsafeslicecheckptr to check ptr and len.
710	// for simplicity, unsafeslicecheckptr always uses int64.
711	// Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
712	// The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
713	// will be handled by the negative range checks in unsafeslice during runtime.
714	if ir.ShouldCheckPtr(ir.CurFunc, 1) {
715		fnname := "unsafeslicecheckptr"
716		fn := typecheck.LookupRuntime(fnname)
717		init.Append(mkcall1(fn, nil, init, reflectdata.UnsafeSliceElemRType(base.Pos, n), unsafePtr, typecheck.Conv(len, lenType)))
718	} else {
719		// Otherwise, open code unsafe.Slice to prevent runtime call overhead.
720		// Keep this code in sync with runtime.unsafeslice{,64}
721		if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
722			lenType = types.Types[types.TINT]
723		} else {
724			// len64 := int64(len)
725			// if int64(int(len64)) != len64 {
726			//     panicunsafeslicelen()
727			// }
728			len64 := typecheck.Conv(len, lenType)
729			nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
730			nif.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, typecheck.Conv(typecheck.Conv(len64, types.Types[types.TINT]), lenType), len64)
731			nif.Body.Append(mkcall("panicunsafeslicelen", nil, &nif.Body))
732			appendWalkStmt(init, nif)
733		}
734
735		// if len < 0 { panicunsafeslicelen() }
736		nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
737		nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, typecheck.Conv(len, lenType), ir.NewInt(base.Pos, 0))
738		nif.Body.Append(mkcall("panicunsafeslicelen", nil, &nif.Body))
739		appendWalkStmt(init, nif)
740
741		if sliceType.Elem().Size() == 0 {
742			// if ptr == nil && len > 0  {
743			//      panicunsafesliceptrnil()
744			// }
745			nifPtr := ir.NewIfStmt(base.Pos, nil, nil, nil)
746			isNil := ir.NewBinaryExpr(base.Pos, ir.OEQ, unsafePtr, typecheck.NodNil())
747			gtZero := ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(len, lenType), ir.NewInt(base.Pos, 0))
748			nifPtr.Cond =
749				ir.NewLogicalExpr(base.Pos, ir.OANDAND, isNil, gtZero)
750			nifPtr.Body.Append(mkcall("panicunsafeslicenilptr", nil, &nifPtr.Body))
751			appendWalkStmt(init, nifPtr)
752
753			h := ir.NewSliceHeaderExpr(n.Pos(), sliceType,
754				typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
755				typecheck.Conv(len, types.Types[types.TINT]),
756				typecheck.Conv(len, types.Types[types.TINT]))
757			return walkExpr(typecheck.Expr(h), init)
758		}
759
760		// mem, overflow := math.mulUintptr(et.size, len)
761		mem := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUINTPTR])
762		overflow := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
763
764		decl := types.NewSignature(nil,
765			[]*types.Field{
766				types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
767				types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
768			},
769			[]*types.Field{
770				types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
771				types.NewField(base.Pos, nil, types.Types[types.TBOOL]),
772			})
773
774		fn := ir.NewFunc(n.Pos(), n.Pos(), math_MulUintptr, decl)
775
776		call := mkcall1(fn.Nname, fn.Type().ResultsTuple(), init, ir.NewInt(base.Pos, sliceType.Elem().Size()), typecheck.Conv(typecheck.Conv(len, lenType), types.Types[types.TUINTPTR]))
777		appendWalkStmt(init, ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{mem, overflow}, []ir.Node{call}))
778
779		// if overflow || mem > -uintptr(ptr) {
780		//     if ptr == nil {
781		//         panicunsafesliceptrnil()
782		//     }
783		//     panicunsafeslicelen()
784		// }
785		nif = ir.NewIfStmt(base.Pos, nil, nil, nil)
786		memCond := ir.NewBinaryExpr(base.Pos, ir.OGT, mem, ir.NewUnaryExpr(base.Pos, ir.ONEG, typecheck.Conv(unsafePtr, types.Types[types.TUINTPTR])))
787		nif.Cond = ir.NewLogicalExpr(base.Pos, ir.OOROR, overflow, memCond)
788		nifPtr := ir.NewIfStmt(base.Pos, nil, nil, nil)
789		nifPtr.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, unsafePtr, typecheck.NodNil())
790		nifPtr.Body.Append(mkcall("panicunsafeslicenilptr", nil, &nifPtr.Body))
791		nif.Body.Append(nifPtr, mkcall("panicunsafeslicelen", nil, &nif.Body))
792		appendWalkStmt(init, nif)
793	}
794
795	h := ir.NewSliceHeaderExpr(n.Pos(), sliceType,
796		typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
797		typecheck.Conv(len, types.Types[types.TINT]),
798		typecheck.Conv(len, types.Types[types.TINT]))
799	return walkExpr(typecheck.Expr(h), init)
800}
801
802var math_MulUintptr = &types.Sym{Pkg: types.NewPkg("runtime/internal/math", "math"), Name: "MulUintptr"}
803
804func walkUnsafeString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
805	ptr := safeExpr(n.X, init)
806	len := safeExpr(n.Y, init)
807
808	lenType := types.Types[types.TINT64]
809	unsafePtr := typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR])
810
811	// If checkptr enabled, call runtime.unsafestringcheckptr to check ptr and len.
812	// for simplicity, unsafestringcheckptr always uses int64.
813	// Type checking guarantees that TIDEAL len are positive and fit in an int.
814	if ir.ShouldCheckPtr(ir.CurFunc, 1) {
815		fnname := "unsafestringcheckptr"
816		fn := typecheck.LookupRuntime(fnname)
817		init.Append(mkcall1(fn, nil, init, unsafePtr, typecheck.Conv(len, lenType)))
818	} else {
819		// Otherwise, open code unsafe.String to prevent runtime call overhead.
820		// Keep this code in sync with runtime.unsafestring{,64}
821		if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
822			lenType = types.Types[types.TINT]
823		} else {
824			// len64 := int64(len)
825			// if int64(int(len64)) != len64 {
826			//     panicunsafestringlen()
827			// }
828			len64 := typecheck.Conv(len, lenType)
829			nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
830			nif.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, typecheck.Conv(typecheck.Conv(len64, types.Types[types.TINT]), lenType), len64)
831			nif.Body.Append(mkcall("panicunsafestringlen", nil, &nif.Body))
832			appendWalkStmt(init, nif)
833		}
834
835		// if len < 0 { panicunsafestringlen() }
836		nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
837		nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, typecheck.Conv(len, lenType), ir.NewInt(base.Pos, 0))
838		nif.Body.Append(mkcall("panicunsafestringlen", nil, &nif.Body))
839		appendWalkStmt(init, nif)
840
841		// if uintpr(len) > -uintptr(ptr) {
842		//    if ptr == nil {
843		//       panicunsafestringnilptr()
844		//    }
845		//    panicunsafeslicelen()
846		// }
847		nifLen := ir.NewIfStmt(base.Pos, nil, nil, nil)
848		nifLen.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(len, types.Types[types.TUINTPTR]), ir.NewUnaryExpr(base.Pos, ir.ONEG, typecheck.Conv(unsafePtr, types.Types[types.TUINTPTR])))
849		nifPtr := ir.NewIfStmt(base.Pos, nil, nil, nil)
850		nifPtr.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, unsafePtr, typecheck.NodNil())
851		nifPtr.Body.Append(mkcall("panicunsafestringnilptr", nil, &nifPtr.Body))
852		nifLen.Body.Append(nifPtr, mkcall("panicunsafestringlen", nil, &nifLen.Body))
853		appendWalkStmt(init, nifLen)
854	}
855	h := ir.NewStringHeaderExpr(n.Pos(),
856		typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
857		typecheck.Conv(len, types.Types[types.TINT]),
858	)
859	return walkExpr(typecheck.Expr(h), init)
860}
861
862func badtype(op ir.Op, tl, tr *types.Type) {
863	var s string
864	if tl != nil {
865		s += fmt.Sprintf("\n\t%v", tl)
866	}
867	if tr != nil {
868		s += fmt.Sprintf("\n\t%v", tr)
869	}
870
871	// common mistake: *struct and *interface.
872	if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() {
873		if tl.Elem().IsStruct() && tr.Elem().IsInterface() {
874			s += "\n\t(*struct vs *interface)"
875		} else if tl.Elem().IsInterface() && tr.Elem().IsStruct() {
876			s += "\n\t(*interface vs *struct)"
877		}
878	}
879
880	base.Errorf("illegal types for operand: %v%s", op, s)
881}
882
883func writebarrierfn(name string, l *types.Type, r *types.Type) ir.Node {
884	return typecheck.LookupRuntime(name, l, r)
885}
886
887// isRuneCount reports whether n is of the form len([]rune(string)).
888// These are optimized into a call to runtime.countrunes.
889func isRuneCount(n ir.Node) bool {
890	return base.Flag.N == 0 && !base.Flag.Cfg.Instrumenting && n.Op() == ir.OLEN && n.(*ir.UnaryExpr).X.Op() == ir.OSTR2RUNES
891}
892
893// isByteCount reports whether n is of the form len(string([]byte)).
894func isByteCount(n ir.Node) bool {
895	return base.Flag.N == 0 && !base.Flag.Cfg.Instrumenting && n.Op() == ir.OLEN &&
896		(n.(*ir.UnaryExpr).X.Op() == ir.OBYTES2STR || n.(*ir.UnaryExpr).X.Op() == ir.OBYTES2STRTMP)
897}
898
899// isChanLenCap reports whether n is of the form len(c) or cap(c) for a channel c.
900// Note that this does not check for -n or instrumenting because this
901// is a correctness rewrite, not an optimization.
902func isChanLenCap(n ir.Node) bool {
903	return (n.Op() == ir.OLEN || n.Op() == ir.OCAP) && n.(*ir.UnaryExpr).X.Type().IsChan()
904}
905