From: Matthew Dempsky Date: Tue, 21 Jun 2022 05:28:15 +0000 (-0700) Subject: [dev.unified] cmd/compile: avoid reflectType in ssagen X-Git-Tag: go1.20rc1~1807^2~56 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=5e0258c700cba33db1a41806ff328a9c3f42d4da;p=gostls13.git [dev.unified] cmd/compile: avoid reflectType in ssagen This CL adds alternate code paths for the frontend to plumb through rtypes to package ssagen, so the latter doesn't have to use reflectType (which in general will only have access to shape types). Note: This CL doesn't yet plumb through the rtypes for variables that escape to the heap. However, those rtypes are only used for calling runtime.newobject, and the status quo as of Go 1.18 is already to use shape rtypes for most runtime.newobject calls. (Longer term though, I would like to get rid of shape rtypes altogether.) Passes toolstash -cmp. Updates #53276. Change-Id: I76a281eca8300de2e701fbac89ead32f8568a5f2 Reviewed-on: https://go-review.googlesource.com/c/go/+/413357 TryBot-Result: Gopher Robot Run-TryBot: Matthew Dempsky Reviewed-by: David Chase --- diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index 8ac7e7f4f7..27dd390efc 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -246,6 +246,16 @@ func (n *ConstExpr) Val() constant.Value { return n.val } type ConvExpr struct { miniExpr X Node + + // For -d=checkptr instrumentation of conversions from + // unsafe.Pointer to *Elem or *[Len]Elem. + // + // TODO(mdempsky): We only ever need one of these, but currently we + // don't decide which one until walk. Longer term, it probably makes + // sense to have a dedicated IR op for `(*[Len]Elem)(ptr)[:n:m]` + // expressions. + ElemRType Node `mknode:"-"` + ElemElemRType Node `mknode:"-"` } func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr { @@ -650,6 +660,11 @@ type DynamicTypeAssertExpr struct { miniExpr X Node + // SrcRType is an expression that yields a *runtime._type value + // representing X's type. It's used in failed assertion panic + // messages. + SrcRType Node + // RType is an expression that yields a *runtime._type value // representing the asserted type. // diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index a7778d37fb..64a30d427f 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -664,7 +664,7 @@ func (s *state) paramsToHeap() { // newHeapaddr allocates heap memory for n and sets its heap address. func (s *state) newHeapaddr(n *ir.Name) { - s.setHeapaddr(n.Pos(), n, s.newObject(n.Type())) + s.setHeapaddr(n.Pos(), n, s.newObject(n.Type(), nil)) } // setHeapaddr allocates a new PAUTO variable to store ptr (which must be non-nil) @@ -692,23 +692,26 @@ func (s *state) setHeapaddr(pos src.XPos, n *ir.Name, ptr *ssa.Value) { } // newObject returns an SSA value denoting new(typ). -func (s *state) newObject(typ *types.Type) *ssa.Value { +func (s *state) newObject(typ *types.Type, rtype *ssa.Value) *ssa.Value { if typ.Size() == 0 { return s.newValue1A(ssa.OpAddr, types.NewPtr(typ), ir.Syms.Zerobase, s.sb) } - return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, s.reflectType(typ))[0] + if rtype == nil { + rtype = s.reflectType(typ) + } + return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, rtype)[0] } func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value) { if !n.Type().IsPtr() { s.Fatalf("expected pointer type: %v", n.Type()) } - elem := n.Type().Elem() + elem, rtypeExpr := n.Type().Elem(), n.ElemRType if count != nil { if !elem.IsArray() { s.Fatalf("expected array type: %v", elem) } - elem = elem.Elem() + elem, rtypeExpr = elem.Elem(), n.ElemElemRType } size := elem.Size() // Casting from larger type to smaller one is ok, so for smallest type, do nothing. @@ -721,12 +724,20 @@ func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value if count.Type.Size() != s.config.PtrSize { s.Fatalf("expected count fit to an uintptr size, have: %d, want: %d", count.Type.Size(), s.config.PtrSize) } - s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, s.reflectType(elem), count) + var rtype *ssa.Value + if rtypeExpr != nil { + rtype = s.expr(rtypeExpr) + } else { + rtype = s.reflectType(elem) + } + s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, rtype, count) } // reflectType returns an SSA value representing a pointer to typ's // reflection type descriptor. func (s *state) reflectType(typ *types.Type) *ssa.Value { + // TODO(mdempsky): Make this Fatalf under Unified IR; frontend needs + // to supply RType expressions. lsym := reflectdata.TypeLinksym(typ) return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(types.Types[types.TUINT8]), lsym, s.sb) } @@ -3290,7 +3301,11 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value { case ir.ONEW: n := n.(*ir.UnaryExpr) - return s.newObject(n.Type().Elem()) + var rtype *ssa.Value + if x, ok := n.X.(*ir.DynamicType); ok && x.Op() == ir.ODYNAMICTYPE { + rtype = s.expr(x.RType) + } + return s.newObject(n.Type().Elem(), rtype) case ir.OUNSAFEADD: n := n.(*ir.BinaryExpr) @@ -6222,12 +6237,15 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val if n.ITab != nil { targetItab = s.expr(n.ITab) } - return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, targetItab, commaok) + return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, nil, target, targetItab, commaok) } func (s *state) dynamicDottype(n *ir.DynamicTypeAssertExpr, commaok bool) (res, resok *ssa.Value) { iface := s.expr(n.X) - var target, targetItab *ssa.Value + var source, target, targetItab *ssa.Value + if n.SrcRType != nil { + source = s.expr(n.SrcRType) + } if !n.X.Type().IsEmptyInterface() && !n.Type().IsInterface() { byteptr := s.f.Config.Types.BytePtr targetItab = s.expr(n.ITab) @@ -6237,15 +6255,16 @@ func (s *state) dynamicDottype(n *ir.DynamicTypeAssertExpr, commaok bool) (res, } else { target = s.expr(n.RType) } - return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, targetItab, commaok) + return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, source, target, targetItab, commaok) } // dottype1 implements a x.(T) operation. iface is the argument (x), dst is the type we're asserting to (T) // and src is the type we're asserting from. +// source is the *runtime._type of src // target is the *runtime._type of dst. // If src is a nonempty interface and dst is not an interface, targetItab is an itab representing (dst, src). Otherwise it is nil. // commaok is true if the caller wants a boolean success value. Otherwise, the generated code panics if the conversion fails. -func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, target, targetItab *ssa.Value, commaok bool) (res, resok *ssa.Value) { +func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, target, targetItab *ssa.Value, commaok bool) (res, resok *ssa.Value) { byteptr := s.f.Config.Types.BytePtr if dst.IsInterface() { if dst.IsEmptyInterface() { @@ -6381,7 +6400,10 @@ func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, target, targ if !commaok { // on failure, panic by calling panicdottype s.startBlock(bFail) - taddr := s.reflectType(src) + taddr := source + if taddr == nil { + taddr = s.reflectType(src) + } if src.IsEmptyInterface() { s.rtcall(ir.Syms.PanicdottypeE, false, nil, itab, target, taddr) } else {