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	"encoding/binary"
9	"go/constant"
10
11	"cmd/compile/internal/base"
12	"cmd/compile/internal/ir"
13	"cmd/compile/internal/reflectdata"
14	"cmd/compile/internal/ssagen"
15	"cmd/compile/internal/typecheck"
16	"cmd/compile/internal/types"
17	"cmd/internal/src"
18	"cmd/internal/sys"
19)
20
21// walkConv walks an OCONV or OCONVNOP (but not OCONVIFACE) node.
22func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
23	n.X = walkExpr(n.X, init)
24	if n.Op() == ir.OCONVNOP && n.Type() == n.X.Type() {
25		return n.X
26	}
27	if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) {
28		if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { // uintptr to unsafe.Pointer
29			return walkCheckPtrArithmetic(n, init)
30		}
31	}
32	param, result := rtconvfn(n.X.Type(), n.Type())
33	if param == types.Txxx {
34		return n
35	}
36	fn := types.BasicTypeNames[param] + "to" + types.BasicTypeNames[result]
37	return typecheck.Conv(mkcall(fn, types.Types[result], init, typecheck.Conv(n.X, types.Types[param])), n.Type())
38}
39
40// walkConvInterface walks an OCONVIFACE node.
41func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
42
43	n.X = walkExpr(n.X, init)
44
45	fromType := n.X.Type()
46	toType := n.Type()
47	if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) {
48		// skip unnamed functions (func _())
49		reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym)
50	}
51
52	if !fromType.IsInterface() {
53		var typeWord ir.Node
54		if toType.IsEmptyInterface() {
55			typeWord = reflectdata.TypePtr(fromType)
56		} else {
57			typeWord = reflectdata.ITabAddr(fromType, toType)
58		}
59		l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone))
60		l.SetType(toType)
61		l.SetTypecheck(n.Typecheck())
62		return l
63	}
64	if fromType.IsEmptyInterface() {
65		base.Fatalf("OCONVIFACE can't operate on an empty interface")
66	}
67
68	// Evaluate the input interface.
69	c := typecheck.Temp(fromType)
70	init.Append(ir.NewAssignStmt(base.Pos, c, n.X))
71
72	// Grab its parts.
73	itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, c)
74	itab.SetType(types.Types[types.TUINTPTR].PtrTo())
75	itab.SetTypecheck(1)
76	data := ir.NewUnaryExpr(n.Pos(), ir.OIDATA, c)
77	data.SetType(types.Types[types.TUINT8].PtrTo()) // Type is generic pointer - we're just passing it through.
78	data.SetTypecheck(1)
79
80	var typeWord ir.Node
81	if toType.IsEmptyInterface() {
82		// Implement interface to empty interface conversion.
83		// res = itab
84		// if res != nil {
85		//    res = res.type
86		// }
87		typeWord = typecheck.Temp(types.NewPtr(types.Types[types.TUINT8]))
88		init.Append(ir.NewAssignStmt(base.Pos, typeWord, itab))
89		nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, typeWord, typecheck.NodNil())), nil, nil)
90		nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, typeWord, itabType(typeWord))}
91		init.Append(nif)
92	} else {
93		// Must be converting I2I (more specific to less specific interface).
94		// res = convI2I(toType, itab)
95		fn := typecheck.LookupRuntime("convI2I")
96		types.CalcSize(fn.Type())
97		call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
98		call.Args = []ir.Node{reflectdata.TypePtr(toType), itab}
99		typeWord = walkExpr(typecheck.Expr(call), init)
100	}
101
102	// Build the result.
103	// e = iface{typeWord, data}
104	e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, data)
105	e.SetType(toType) // assign type manually, typecheck doesn't understand OEFACE.
106	e.SetTypecheck(1)
107	return e
108}
109
110// Returns the data word (the second word) used to represent n in an interface.
111// n must not be of interface type.
112// esc describes whether the result escapes.
113func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node {
114	fromType := n.Type()
115
116	// If it's a pointer, it is its own representation.
117	if types.IsDirectIface(fromType) {
118		return n
119	}
120
121	// Try a bunch of cases to avoid an allocation.
122	var value ir.Node
123	switch {
124	case fromType.Size() == 0:
125		// n is zero-sized. Use zerobase.
126		cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246.
127		value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
128	case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()):
129		// n is a bool/byte. Use staticuint64s[n * 8] on little-endian
130		// and staticuint64s[n * 8 + 7] on big-endian.
131		n = cheapExpr(n, init)
132		// byteindex widens n so that the multiplication doesn't overflow.
133		index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(3))
134		if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
135			index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(7))
136		}
137		// The actual type is [256]uint64, but we use [256*8]uint8 so we can address
138		// individual bytes.
139		staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8))
140		xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
141		xe.SetBounded(true)
142		value = xe
143	case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
144		// n is a readonly global; use it directly.
145		value = n
146	case !escapes && fromType.Size() <= 1024:
147		// n does not escape. Use a stack temporary initialized to n.
148		value = typecheck.Temp(fromType)
149		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
150	}
151	if value != nil {
152		// The interface data word is &value.
153		return typecheck.Expr(typecheck.NodAddr(value))
154	}
155
156	// Time to do an allocation. We'll call into the runtime for that.
157	fnname, argType, needsaddr := dataWordFuncName(fromType)
158	fn := typecheck.LookupRuntime(fnname)
159
160	var args []ir.Node
161	if needsaddr {
162		// Types of large or unknown size are passed by reference.
163		// Orderexpr arranged for n to be a temporary for all
164		// the conversions it could see. Comparison of an interface
165		// with a non-interface, especially in a switch on interface value
166		// with non-interface cases, is not visible to order.stmt, so we
167		// have to fall back on allocating a temp here.
168		if !ir.IsAddressable(n) {
169			n = copyExpr(n, fromType, init)
170		}
171		fn = typecheck.SubstArgTypes(fn, fromType)
172		args = []ir.Node{reflectdata.TypePtr(fromType), typecheck.NodAddr(n)}
173	} else {
174		// Use a specialized conversion routine that takes the type being
175		// converted by value, not by pointer.
176		var arg ir.Node
177		switch {
178		case fromType == argType:
179			// already in the right type, nothing to do
180			arg = n
181		case fromType.Kind() == argType.Kind(),
182			fromType.IsPtrShaped() && argType.IsPtrShaped():
183			// can directly convert (e.g. named type to underlying type, or one pointer to another)
184			// TODO: never happens because pointers are directIface?
185			arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n)
186		case fromType.IsInteger() && argType.IsInteger():
187			// can directly convert (e.g. int32 to uint32)
188			arg = ir.NewConvExpr(pos, ir.OCONV, argType, n)
189		default:
190			// unsafe cast through memory
191			arg = copyExpr(n, fromType, init)
192			var addr ir.Node = typecheck.NodAddr(arg)
193			addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr)
194			arg = ir.NewStarExpr(pos, addr)
195			arg.SetType(argType)
196		}
197		args = []ir.Node{arg}
198	}
199	call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
200	call.Args = args
201	return safeExpr(walkExpr(typecheck.Expr(call), init), init)
202}
203
204// walkConvIData walks an OCONVIDATA node.
205func walkConvIData(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
206	n.X = walkExpr(n.X, init)
207	return dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone)
208}
209
210// walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
211func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
212	a := typecheck.NodNil()
213	if n.Esc() == ir.EscNone {
214		// Create temporary buffer for string on stack.
215		a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
216	}
217	if n.Op() == ir.ORUNES2STR {
218		// slicerunetostring(*[32]byte, []rune) string
219		return mkcall("slicerunetostring", n.Type(), init, a, n.X)
220	}
221	// slicebytetostring(*[32]byte, ptr *byte, n int) string
222	n.X = cheapExpr(n.X, init)
223	ptr, len := backingArrayPtrLen(n.X)
224	return mkcall("slicebytetostring", n.Type(), init, a, ptr, len)
225}
226
227// walkBytesToStringTemp walks an OBYTES2STRTMP node.
228func walkBytesToStringTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
229	n.X = walkExpr(n.X, init)
230	if !base.Flag.Cfg.Instrumenting {
231		// Let the backend handle OBYTES2STRTMP directly
232		// to avoid a function call to slicebytetostringtmp.
233		return n
234	}
235	// slicebytetostringtmp(ptr *byte, n int) string
236	n.X = cheapExpr(n.X, init)
237	ptr, len := backingArrayPtrLen(n.X)
238	return mkcall("slicebytetostringtmp", n.Type(), init, ptr, len)
239}
240
241// walkRuneToString walks an ORUNESTR node.
242func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
243	a := typecheck.NodNil()
244	if n.Esc() == ir.EscNone {
245		a = stackBufAddr(4, types.Types[types.TUINT8])
246	}
247	// intstring(*[4]byte, rune)
248	return mkcall("intstring", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TINT64]))
249}
250
251// walkStringToBytes walks an OSTR2BYTES node.
252func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
253	s := n.X
254	if ir.IsConst(s, constant.String) {
255		sc := ir.StringVal(s)
256
257		// Allocate a [n]byte of the right size.
258		t := types.NewArray(types.Types[types.TUINT8], int64(len(sc)))
259		var a ir.Node
260		if n.Esc() == ir.EscNone && len(sc) <= int(ir.MaxImplicitStackVarSize) {
261			a = stackBufAddr(t.NumElem(), t.Elem())
262		} else {
263			types.CalcSize(t)
264			a = ir.NewUnaryExpr(base.Pos, ir.ONEW, nil)
265			a.SetType(types.NewPtr(t))
266			a.SetTypecheck(1)
267			a.MarkNonNil()
268		}
269		p := typecheck.Temp(t.PtrTo()) // *[n]byte
270		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, p, a)))
271
272		// Copy from the static string data to the [n]byte.
273		if len(sc) > 0 {
274			as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), t.PtrTo())))
275			appendWalkStmt(init, as)
276		}
277
278		// Slice the [n]byte to a []byte.
279		slice := ir.NewSliceExpr(n.Pos(), ir.OSLICEARR, p, nil, nil, nil)
280		slice.SetType(n.Type())
281		slice.SetTypecheck(1)
282		return walkExpr(slice, init)
283	}
284
285	a := typecheck.NodNil()
286	if n.Esc() == ir.EscNone {
287		// Create temporary buffer for slice on stack.
288		a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
289	}
290	// stringtoslicebyte(*32[byte], string) []byte
291	return mkcall("stringtoslicebyte", n.Type(), init, a, typecheck.Conv(s, types.Types[types.TSTRING]))
292}
293
294// walkStringToBytesTemp walks an OSTR2BYTESTMP node.
295func walkStringToBytesTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
296	// []byte(string) conversion that creates a slice
297	// referring to the actual string bytes.
298	// This conversion is handled later by the backend and
299	// is only for use by internal compiler optimizations
300	// that know that the slice won't be mutated.
301	// The only such case today is:
302	// for i, c := range []byte(string)
303	n.X = walkExpr(n.X, init)
304	return n
305}
306
307// walkStringToRunes walks an OSTR2RUNES node.
308func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
309	a := typecheck.NodNil()
310	if n.Esc() == ir.EscNone {
311		// Create temporary buffer for slice on stack.
312		a = stackBufAddr(tmpstringbufsize, types.Types[types.TINT32])
313	}
314	// stringtoslicerune(*[32]rune, string) []rune
315	return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING]))
316}
317
318// dataWordFuncName returns the name of the function used to convert a value of type "from"
319// to the data word of an interface.
320// argType is the type the argument needs to be coerced to.
321// needsaddr reports whether the value should be passed (needaddr==false) or its address (needsaddr==true).
322func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) {
323	if from.IsInterface() {
324		base.Fatalf("can only handle non-interfaces")
325	}
326	switch {
327	case from.Size() == 2 && uint8(from.Alignment()) == 2:
328		return "convT16", types.Types[types.TUINT16], false
329	case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers():
330		return "convT32", types.Types[types.TUINT32], false
331	case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers():
332		return "convT64", types.Types[types.TUINT64], false
333	}
334	if sc := from.SoleComponent(); sc != nil {
335		switch {
336		case sc.IsString():
337			return "convTstring", types.Types[types.TSTRING], false
338		case sc.IsSlice():
339			return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // the element type doesn't matter
340		}
341	}
342
343	if from.HasPointers() {
344		return "convT", types.Types[types.TUNSAFEPTR], true
345	}
346	return "convTnoptr", types.Types[types.TUNSAFEPTR], true
347}
348
349// rtconvfn returns the parameter and result types that will be used by a
350// runtime function to convert from type src to type dst. The runtime function
351// name can be derived from the names of the returned types.
352//
353// If no such function is necessary, it returns (Txxx, Txxx).
354func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
355	if ssagen.Arch.SoftFloat {
356		return types.Txxx, types.Txxx
357	}
358
359	switch ssagen.Arch.LinkArch.Family {
360	case sys.ARM, sys.MIPS:
361		if src.IsFloat() {
362			switch dst.Kind() {
363			case types.TINT64, types.TUINT64:
364				return types.TFLOAT64, dst.Kind()
365			}
366		}
367		if dst.IsFloat() {
368			switch src.Kind() {
369			case types.TINT64, types.TUINT64:
370				return src.Kind(), dst.Kind()
371			}
372		}
373
374	case sys.I386:
375		if src.IsFloat() {
376			switch dst.Kind() {
377			case types.TINT64, types.TUINT64:
378				return types.TFLOAT64, dst.Kind()
379			case types.TUINT32, types.TUINT, types.TUINTPTR:
380				return types.TFLOAT64, types.TUINT32
381			}
382		}
383		if dst.IsFloat() {
384			switch src.Kind() {
385			case types.TINT64, types.TUINT64:
386				return src.Kind(), dst.Kind()
387			case types.TUINT32, types.TUINT, types.TUINTPTR:
388				return types.TUINT32, types.TFLOAT64
389			}
390		}
391	}
392	return types.Txxx, types.Txxx
393}
394
395// byteindex converts n, which is byte-sized, to an int used to index into an array.
396// We cannot use conv, because we allow converting bool to int here,
397// which is forbidden in user code.
398func byteindex(n ir.Node) ir.Node {
399	// We cannot convert from bool to int directly.
400	// While converting from int8 to int is possible, it would yield
401	// the wrong result for negative values.
402	// Reinterpreting the value as an unsigned byte solves both cases.
403	if !types.Identical(n.Type(), types.Types[types.TUINT8]) {
404		n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
405		n.SetType(types.Types[types.TUINT8])
406		n.SetTypecheck(1)
407	}
408	n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
409	n.SetType(types.Types[types.TINT])
410	n.SetTypecheck(1)
411	return n
412}
413
414func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
415	// Calling cheapExpr(n, init) below leads to a recursive call to
416	// walkExpr, which leads us back here again. Use n.Checkptr to
417	// prevent infinite loops.
418	if n.CheckPtr() {
419		return n
420	}
421	n.SetCheckPtr(true)
422	defer n.SetCheckPtr(false)
423
424	// TODO(mdempsky): Make stricter. We only need to exempt
425	// reflect.Value.Pointer and reflect.Value.UnsafeAddr.
426	switch n.X.Op() {
427	case ir.OCALLMETH:
428		base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
429	case ir.OCALLFUNC, ir.OCALLINTER:
430		return n
431	}
432
433	if n.X.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(n.X) {
434		return n
435	}
436
437	// Find original unsafe.Pointer operands involved in this
438	// arithmetic expression.
439	//
440	// "It is valid both to add and to subtract offsets from a
441	// pointer in this way. It is also valid to use &^ to round
442	// pointers, usually for alignment."
443	var originals []ir.Node
444	var walk func(n ir.Node)
445	walk = func(n ir.Node) {
446		switch n.Op() {
447		case ir.OADD:
448			n := n.(*ir.BinaryExpr)
449			walk(n.X)
450			walk(n.Y)
451		case ir.OSUB, ir.OANDNOT:
452			n := n.(*ir.BinaryExpr)
453			walk(n.X)
454		case ir.OCONVNOP:
455			n := n.(*ir.ConvExpr)
456			if n.X.Type().IsUnsafePtr() {
457				n.X = cheapExpr(n.X, init)
458				originals = append(originals, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]))
459			}
460		}
461	}
462	walk(n.X)
463
464	cheap := cheapExpr(n, init)
465
466	slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
467	slice.SetEsc(ir.EscNone)
468
469	init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice))
470	// TODO(khr): Mark backing store of slice as dead. This will allow us to reuse
471	// the backing store for multiple calls to checkptrArithmetic.
472
473	return cheap
474}
475