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