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