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