| |
| |
| |
|
|
| package walk |
|
|
| import ( |
| "encoding/binary" |
| "go/constant" |
|
|
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/reflectdata" |
| "cmd/compile/internal/ssagen" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "cmd/internal/sys" |
| ) |
|
|
| |
| func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| if n.Op() == ir.OCONVNOP && n.Type() == n.X.Type() { |
| return n.X |
| } |
| if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) { |
| if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { |
| return walkCheckPtrArithmetic(n, init) |
| } |
| } |
| param, result := rtconvfn(n.X.Type(), n.Type()) |
| if param == types.Txxx { |
| return n |
| } |
| fn := types.BasicTypeNames[param] + "to" + types.BasicTypeNames[result] |
| return typecheck.Conv(mkcall(fn, types.Types[result], init, typecheck.Conv(n.X, types.Types[param])), n.Type()) |
| } |
|
|
| |
| func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
|
|
| n.X = walkExpr(n.X, init) |
|
|
| fromType := n.X.Type() |
| toType := n.Type() |
| if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) { |
| |
| if fromType.HasShape() { |
| |
| |
| |
| |
| } else { |
| reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym) |
| } |
| } |
|
|
| if !fromType.IsInterface() { |
| typeWord := reflectdata.ConvIfaceTypeWord(base.Pos, n) |
| l := ir.NewBinaryExpr(base.Pos, ir.OMAKEFACE, typeWord, dataWord(n, init)) |
| l.SetType(toType) |
| l.SetTypecheck(n.Typecheck()) |
| return l |
| } |
| if fromType.IsEmptyInterface() { |
| base.Fatalf("OCONVIFACE can't operate on an empty interface") |
| } |
|
|
| |
| c := typecheck.TempAt(base.Pos, ir.CurFunc, fromType) |
| init.Append(ir.NewAssignStmt(base.Pos, c, n.X)) |
|
|
| if toType.IsEmptyInterface() { |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, c) |
| itab.SetType(types.Types[types.TUINTPTR].PtrTo()) |
| itab.SetTypecheck(1) |
| data := ir.NewUnaryExpr(n.Pos(), ir.OIDATA, c) |
| data.SetType(types.Types[types.TUINT8].PtrTo()) |
| data.SetTypecheck(1) |
|
|
| typeWord := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewPtr(types.Types[types.TUINT8])) |
| init.Append(ir.NewAssignStmt(base.Pos, typeWord, typecheck.Conv(typecheck.Conv(itab, types.Types[types.TUNSAFEPTR]), typeWord.Type()))) |
| nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, typeWord, typecheck.NodNil())), nil, nil) |
| nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, typeWord, itabType(typeWord))} |
| init.Append(nif) |
|
|
| |
| |
| e := ir.NewBinaryExpr(base.Pos, ir.OMAKEFACE, typeWord, data) |
| e.SetType(toType) |
| e.SetTypecheck(1) |
| return e |
| } |
|
|
| |
| |
| var rhs ir.Node |
| if n.TypeWord == nil || n.TypeWord.Op() == ir.OADDR && n.TypeWord.(*ir.AddrExpr).X.Op() == ir.OLINKSYMOFFSET { |
| |
| ta := ir.NewTypeAssertExpr(base.Pos, c, toType) |
| ta.SetOp(ir.ODOTTYPE2) |
| |
| ta.Descriptor = makeTypeAssertDescriptor(toType, true) |
| rhs = ta |
| } else { |
| ta := ir.NewDynamicTypeAssertExpr(base.Pos, ir.ODYNAMICDOTTYPE2, c, n.TypeWord) |
| rhs = ta |
| } |
| rhs.SetType(toType) |
| rhs.SetTypecheck(1) |
|
|
| res := typecheck.TempAt(base.Pos, ir.CurFunc, toType) |
| as := ir.NewAssignListStmt(base.Pos, ir.OAS2DOTTYPE, []ir.Node{res, ir.BlankNode}, []ir.Node{rhs}) |
| init.Append(as) |
| return res |
| } |
|
|
| |
| |
| func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| pos, n := conv.Pos(), conv.X |
| fromType := n.Type() |
|
|
| |
| if types.IsDirectIface(fromType) { |
| return n |
| } |
|
|
| isInteger := fromType.IsInteger() |
| isBool := fromType.IsBoolean() |
| if sc := fromType.SoleComponent(); sc != nil { |
| isInteger = sc.IsInteger() |
| isBool = sc.IsBoolean() |
| } |
|
|
| diagnose := func(msg string, n ir.Node) { |
| if base.Debug.EscapeDebug > 0 { |
| |
| |
| base.WarnfAt(n.Pos(), "convert: %s: %v", msg, n) |
| } |
| } |
|
|
| |
| var value ir.Node |
| switch { |
| case fromType.Size() == 0: |
| |
| diagnose("using global for zero-sized interface value", n) |
| cheapExpr(n, init) |
| value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR]) |
| case isBool || fromType.Size() == 1 && isInteger: |
| |
| |
| diagnose("using global for single-byte interface value", n) |
| n = cheapExpr(n, init) |
| n = soleComponent(init, n) |
| |
| index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(base.Pos, 3)) |
| if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian { |
| index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(base.Pos, 7)) |
| } |
| |
| |
| staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8)) |
| xe := ir.NewIndexExpr(base.Pos, staticuint64s, index) |
| xe.SetBounded(true) |
| value = xe |
| case n.Op() == ir.OLINKSYMOFFSET && n.(*ir.LinksymOffsetExpr).Linksym == ir.Syms.ZeroVal && n.(*ir.LinksymOffsetExpr).Offset_ == 0: |
| |
| |
| diagnose("using global for zero value interface value", conv) |
| value = n |
| case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly(): |
| |
| diagnose("using global for interface value", n) |
| value = n |
| case conv.Esc() == ir.EscNone && fromType.Size() <= 1024: |
| |
| diagnose("using stack temporary for interface value", n) |
| value = typecheck.TempAt(base.Pos, ir.CurFunc, fromType) |
| init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n))) |
| } |
| if value != nil { |
| |
| return typecheck.Expr(typecheck.NodAddr(value)) |
| } |
|
|
| |
| fnname, argType, needsaddr := dataWordFuncName(fromType) |
| var fn *ir.Name |
|
|
| var args []ir.Node |
| if needsaddr { |
| |
| |
| |
| |
| |
| |
| if !ir.IsAddressable(n) { |
| n = copyExpr(n, fromType, init) |
| } |
| fn = typecheck.LookupRuntime(fnname, fromType) |
| args = []ir.Node{reflectdata.ConvIfaceSrcRType(base.Pos, conv), typecheck.NodAddr(n)} |
| } else { |
| |
| |
| fn = typecheck.LookupRuntime(fnname) |
| var arg ir.Node |
| switch { |
| case fromType == argType: |
| |
| arg = n |
| case fromType.Kind() == argType.Kind(), |
| fromType.IsPtrShaped() && argType.IsPtrShaped(): |
| |
| |
| arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n) |
| case fromType.IsInteger() && argType.IsInteger(): |
| |
| arg = ir.NewConvExpr(pos, ir.OCONV, argType, n) |
| default: |
| |
| arg = copyExpr(n, fromType, init) |
| var addr ir.Node = typecheck.NodAddr(arg) |
| addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr) |
| arg = ir.NewStarExpr(pos, addr) |
| arg.SetType(argType) |
| } |
| args = []ir.Node{arg} |
| } |
| call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) |
| call.Args = args |
| return safeExpr(walkExpr(typecheck.Expr(call), init), init) |
| } |
|
|
| |
| func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| a := typecheck.NodNil() |
| if n.Esc() == ir.EscNone { |
| |
| a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8]) |
| } |
| if n.Op() == ir.ORUNES2STR { |
| |
| return mkcall("slicerunetostring", n.Type(), init, a, n.X) |
| } |
| |
| n.X = cheapExpr(n.X, init) |
| ptr, len := backingArrayPtrLen(n.X) |
| return mkcall("slicebytetostring", n.Type(), init, a, ptr, len) |
| } |
|
|
| |
| func walkBytesToStringTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| if !base.Flag.Cfg.Instrumenting { |
| |
| |
| return n |
| } |
| |
| n.X = cheapExpr(n.X, init) |
| ptr, len := backingArrayPtrLen(n.X) |
| return mkcall("slicebytetostringtmp", n.Type(), init, ptr, len) |
| } |
|
|
| |
| func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| a := typecheck.NodNil() |
| if n.Esc() == ir.EscNone { |
| a = stackBufAddr(4, types.Types[types.TUINT8]) |
| } |
| |
| return mkcall("intstring", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TINT64])) |
| } |
|
|
| |
| func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| s := n.X |
|
|
| if expr, ok := s.(*ir.AddStringExpr); ok { |
| return walkAddString(expr, init, n) |
| } |
|
|
| if ir.IsConst(s, constant.String) { |
| sc := ir.StringVal(s) |
|
|
| |
| t := types.NewArray(types.Types[types.TUINT8], int64(len(sc))) |
| var a ir.Node |
| if n.Esc() == ir.EscNone && len(sc) <= int(ir.MaxImplicitStackVarSize) { |
| a = stackBufAddr(t.NumElem(), t.Elem()) |
| } else { |
| types.CalcSize(t) |
| a = ir.NewUnaryExpr(base.Pos, ir.ONEW, nil) |
| a.SetType(types.NewPtr(t)) |
| a.SetTypecheck(1) |
| a.MarkNonNil() |
| } |
| p := typecheck.TempAt(base.Pos, ir.CurFunc, t.PtrTo()) |
| init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, p, a))) |
|
|
| |
| if len(sc) > 0 { |
| sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s) |
| sptr.SetBounded(true) |
| as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(sptr, t.PtrTo()))) |
| appendWalkStmt(init, as) |
| } |
|
|
| |
| slice := ir.NewSliceExpr(n.Pos(), ir.OSLICEARR, p, nil, nil, nil) |
| slice.SetType(n.Type()) |
| slice.SetTypecheck(1) |
| return walkExpr(slice, init) |
| } |
|
|
| a := typecheck.NodNil() |
| if n.Esc() == ir.EscNone { |
| |
| a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8]) |
| } |
| |
| return mkcall("stringtoslicebyte", n.Type(), init, a, typecheck.Conv(s, types.Types[types.TSTRING])) |
| } |
|
|
| |
| func walkStringToBytesTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| |
| |
| |
| |
| |
| |
| |
| n.X = walkExpr(n.X, init) |
| return n |
| } |
|
|
| |
| func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| a := typecheck.NodNil() |
| if n.Esc() == ir.EscNone { |
| |
| a = stackBufAddr(tmpstringbufsize, types.Types[types.TINT32]) |
| } |
| |
| return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING])) |
| } |
|
|
| |
| |
| |
| |
| func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) { |
| if from.IsInterface() { |
| base.Fatalf("can only handle non-interfaces") |
| } |
| switch { |
| case from.Size() == 2 && uint8(from.Alignment()) == 2: |
| return "convT16", types.Types[types.TUINT16], false |
| case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers(): |
| return "convT32", types.Types[types.TUINT32], false |
| case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers(): |
| return "convT64", types.Types[types.TUINT64], false |
| } |
| if sc := from.SoleComponent(); sc != nil { |
| switch { |
| case sc.IsString(): |
| return "convTstring", types.Types[types.TSTRING], false |
| case sc.IsSlice(): |
| return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false |
| } |
| } |
|
|
| if from.HasPointers() { |
| return "convT", types.Types[types.TUNSAFEPTR], true |
| } |
| return "convTnoptr", types.Types[types.TUNSAFEPTR], true |
| } |
|
|
| |
| |
| |
| |
| |
| func rtconvfn(src, dst *types.Type) (param, result types.Kind) { |
| if ssagen.Arch.SoftFloat { |
| return types.Txxx, types.Txxx |
| } |
|
|
| switch ssagen.Arch.LinkArch.Family { |
| case sys.ARM, sys.MIPS: |
| if src.IsFloat() { |
| switch dst.Kind() { |
| case types.TINT64, types.TUINT64: |
| return types.TFLOAT64, dst.Kind() |
| } |
| } |
| if dst.IsFloat() { |
| switch src.Kind() { |
| case types.TINT64, types.TUINT64: |
| return src.Kind(), dst.Kind() |
| } |
| } |
|
|
| case sys.I386: |
| if src.IsFloat() { |
| switch dst.Kind() { |
| case types.TINT64, types.TUINT64: |
| return types.TFLOAT64, dst.Kind() |
| case types.TUINT32, types.TUINT, types.TUINTPTR: |
| return types.TFLOAT64, types.TUINT32 |
| } |
| } |
| if dst.IsFloat() { |
| switch src.Kind() { |
| case types.TINT64, types.TUINT64: |
| return src.Kind(), dst.Kind() |
| case types.TUINT32, types.TUINT, types.TUINTPTR: |
| return types.TUINT32, types.TFLOAT64 |
| } |
| } |
| } |
| return types.Txxx, types.Txxx |
| } |
|
|
| func soleComponent(init *ir.Nodes, n ir.Node) ir.Node { |
| if n.Type().SoleComponent() == nil { |
| return n |
| } |
| |
| for { |
| switch { |
| case n.Type().IsStruct(): |
| if n.Type().Field(0).Sym.IsBlank() { |
| |
| n = typecheck.TempAt(base.Pos, ir.CurFunc, n.Type().Field(0).Type) |
| appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil)) |
| continue |
| } |
| n = typecheck.DotField(n.Pos(), n, 0) |
| case n.Type().IsArray(): |
| n = typecheck.Expr(ir.NewIndexExpr(n.Pos(), n, ir.NewInt(base.Pos, 0))) |
| default: |
| return n |
| } |
| } |
| } |
|
|
| |
| |
| |
| func byteindex(n ir.Node) ir.Node { |
| |
| |
| |
| |
| if !types.Identical(n.Type(), types.Types[types.TUINT8]) { |
| n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n) |
| n.SetType(types.Types[types.TUINT8]) |
| n.SetTypecheck(1) |
| } |
| n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n) |
| n.SetType(types.Types[types.TINT]) |
| n.SetTypecheck(1) |
| return n |
| } |
|
|
| func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| |
| |
| |
| if n.CheckPtr() { |
| return n |
| } |
| n.SetCheckPtr(true) |
| defer n.SetCheckPtr(false) |
|
|
| |
| |
| switch n.X.Op() { |
| case ir.OCALLMETH: |
| base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck") |
| case ir.OCALLFUNC, ir.OCALLINTER: |
| return n |
| } |
|
|
| if n.X.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(n.X) { |
| return n |
| } |
|
|
| |
| |
| |
| |
| |
| |
| var originals []ir.Node |
| var walk func(n ir.Node) |
| walk = func(n ir.Node) { |
| switch n.Op() { |
| case ir.OADD: |
| n := n.(*ir.BinaryExpr) |
| walk(n.X) |
| walk(n.Y) |
| case ir.OSUB, ir.OANDNOT: |
| n := n.(*ir.BinaryExpr) |
| walk(n.X) |
| case ir.OCONVNOP: |
| n := n.(*ir.ConvExpr) |
| if n.X.Type().IsUnsafePtr() { |
| n.X = cheapExpr(n.X, init) |
| originals = append(originals, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR])) |
| } |
| } |
| } |
| walk(n.X) |
|
|
| cheap := cheapExpr(n, init) |
|
|
| slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals) |
| slice.SetEsc(ir.EscNone) |
|
|
| init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice)) |
| |
| |
|
|
| return cheap |
| } |
|
|
| |
| func walkSliceToArray(n *ir.ConvExpr, init *ir.Nodes) ir.Node { |
| |
| conv := typecheck.Expr(ir.NewConvExpr(base.Pos, ir.OCONV, types.NewPtr(n.Type()), n.X)).(*ir.ConvExpr) |
| deref := typecheck.Expr(ir.NewStarExpr(base.Pos, conv)).(*ir.StarExpr) |
|
|
| |
| |
| |
| |
| |
| |
| |
| deref.SetBounded(true) |
|
|
| return walkExpr(deref, init) |
| } |
|
|