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

     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  	"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.
    22  func 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.
    41  func 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.
   113  func 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.
   205  func 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.
   211  func 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.
   228  func 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.
   242  func 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.
   252  func 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.
   295  func 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.
   308  func 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).
   322  func 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).
   354  func 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.
   398  func 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  
   414  func 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  

View as plain text