aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder/reader.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/noder/reader.go')
-rw-r--r--src/cmd/compile/internal/noder/reader.go804
1 files changed, 671 insertions, 133 deletions
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 296cdd7d54..d02d05bc5d 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -1,5 +1,3 @@
-// UNREVIEWED
-
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -19,6 +17,7 @@ import (
"cmd/compile/internal/dwarfgen"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
@@ -26,15 +25,25 @@ import (
"cmd/internal/src"
)
+// This file implements cmd/compile backend's reader for the Unified
+// IR export data.
+
+// A pkgReader reads Unified IR export data.
type pkgReader struct {
pkgbits.PkgDecoder
+ // Indices for encoded things; lazily populated as needed.
+ //
+ // Note: Objects (i.e., ir.Names) are lazily instantiated by
+ // populating their types.Sym.Def; see objReader below.
+
posBases []*src.PosBase
pkgs []*types.Pkg
typs []*types.Type
- // offset for rewriting the given index into the output,
- // but bitwise inverted so we can detect if we're missing the entry or not.
+ // offset for rewriting the given (absolute!) index into the output,
+ // but bitwise inverted so we can detect if we're missing the entry
+ // or not.
newindex []pkgbits.Index
}
@@ -50,15 +59,19 @@ func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
}
}
+// A pkgReaderIndex compactly identifies an index (and its
+// corresponding dictionary) within a package's export data.
type pkgReaderIndex struct {
- pr *pkgReader
- idx pkgbits.Index
- dict *readerDict
+ pr *pkgReader
+ idx pkgbits.Index
+ dict *readerDict
+ shapedFn *ir.Func
}
func (pri pkgReaderIndex) asReader(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *reader {
r := pri.pr.newReader(k, pri.idx, marker)
r.dict = pri.dict
+ r.shapedFn = pri.shapedFn
return r
}
@@ -69,6 +82,7 @@ func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pk
}
}
+// A writer provides APIs for reading an individual element.
type reader struct {
pkgbits.Decoder
@@ -87,6 +101,12 @@ type reader struct {
funarghack bool
+ // shapedFn is the shape-typed version of curfn, if any.
+ shapedFn *ir.Func
+
+ // dictParam is the .dict param, if any.
+ dictParam *ir.Name
+
// scopeVars is a stack tracking the number of variables declared in
// the current function at the moment each open scope was opened.
scopeVars []int
@@ -108,7 +128,13 @@ type reader struct {
// Label to return to.
retlabel *types.Sym
- inlvars, retvars ir.Nodes
+ // inlvars is the list of variables that the inlinee's arguments are
+ // assigned to, one for each receiver and normal parameter, in order.
+ inlvars ir.Nodes
+
+ // retvars is the list of variables that the inlinee's results are
+ // assigned to, one for each result parameter, in order.
+ retvars ir.Nodes
}
type readerDict struct {
@@ -143,6 +169,8 @@ type readerDict struct {
funcsObj []ir.Node
itabs []itabInfo2
+
+ methodExprs []ir.Node
}
type itabInfo2 struct {
@@ -162,6 +190,7 @@ func setValue(name *ir.Name, val constant.Value) {
// @@@ Positions
+// pos reads a position from the bitstream.
func (r *reader) pos() src.XPos {
return base.Ctxt.PosTable.XPos(r.pos0())
}
@@ -178,10 +207,13 @@ func (r *reader) pos0() src.Pos {
return src.MakePos(posBase, line, col)
}
+// posBase reads a position base from the bitstream.
func (r *reader) posBase() *src.PosBase {
return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)))
}
+// posBaseIdx returns the specified position base, reading it first if
+// needed.
func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase {
if b := pr.posBases[idx]; b != nil {
return b
@@ -222,6 +254,7 @@ func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase {
return b
}
+// TODO(mdempsky): Document this.
func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
if r.inlCall == nil {
return oldBase
@@ -236,36 +269,23 @@ func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
return newBase
}
+// TODO(mdempsky): Document this.
func (r *reader) updatePos(xpos src.XPos) src.XPos {
pos := base.Ctxt.PosTable.Pos(xpos)
pos.SetBase(r.inlPosBase(pos.Base()))
return base.Ctxt.PosTable.XPos(pos)
}
-func (r *reader) origPos(xpos src.XPos) src.XPos {
- if r.inlCall == nil {
- return xpos
- }
-
- pos := base.Ctxt.PosTable.Pos(xpos)
- for old, new := range r.inlPosBases {
- if pos.Base() == new {
- pos.SetBase(old)
- return base.Ctxt.PosTable.XPos(pos)
- }
- }
-
- base.FatalfAt(xpos, "pos base missing from inlPosBases")
- panic("unreachable")
-}
-
// @@@ Packages
+// pkg reads a package reference from the bitstream.
func (r *reader) pkg() *types.Pkg {
r.Sync(pkgbits.SyncPkg)
return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
}
+// pkgIdx returns the specified package from the export data, reading
+// it first if needed.
func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg {
if pkg := pr.pkgs[idx]; pkg != nil {
return pkg
@@ -276,6 +296,7 @@ func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg {
return pkg
}
+// doPkg reads a package definition from the bitstream.
func (r *reader) doPkg() *types.Pkg {
path := r.String()
switch path {
@@ -288,7 +309,6 @@ func (r *reader) doPkg() *types.Pkg {
}
name := r.String()
- height := r.Len()
pkg := types.NewPkg(path, "")
@@ -298,12 +318,6 @@ func (r *reader) doPkg() *types.Pkg {
base.Assertf(pkg.Name == name, "package %q has name %q, but want %q", pkg.Path, pkg.Name, name)
}
- if pkg.Height == 0 {
- pkg.Height = height
- } else {
- base.Assertf(pkg.Height == height, "package %q has height %v, but want %v", pkg.Path, pkg.Height, height)
- }
-
return pkg
}
@@ -536,8 +550,12 @@ func (r *reader) param() (*types.Pkg, *types.Field) {
// @@@ Objects
+// objReader maps qualified identifiers (represented as *types.Sym) to
+// a pkgReader and corresponding index that can be used for reading
+// that object's definition.
var objReader = map[*types.Sym]pkgReaderIndex{}
+// obj reads an instantiated object reference from the bitstream.
func (r *reader) obj() ir.Node {
r.Sync(pkgbits.SyncObject)
@@ -573,6 +591,8 @@ func (r *reader) obj() ir.Node {
return r.p.objIdx(idx, implicits, explicits)
}
+// objIdx returns the specified object from the bitstream,
+// instantiated with the given type arguments, if any.
func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Type) ir.Node {
rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
_, sym := rname.qualifiedIdent()
@@ -605,6 +625,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
do := func(op ir.Op, hasTParams bool) *ir.Name {
pos := r.pos()
+ setBasePos(pos)
if hasTParams {
r.typeParamNames()
}
@@ -712,6 +733,7 @@ func (r *reader) mangle(sym *types.Sym) *types.Sym {
return sym.Pkg.Lookup(buf.String())
}
+// objDictIdx reads and returns the specified object dictionary.
func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, explicits []*types.Type) *readerDict {
r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
@@ -730,12 +752,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, ex
// For stenciling, we can just skip over the type parameters.
for range dict.targs[dict.implicits:] {
// Skip past bounds without actually evaluating them.
- r.Sync(pkgbits.SyncType)
- if r.Bool() {
- r.Len()
- } else {
- r.Reloc(pkgbits.RelocType)
- }
+ r.typInfo()
}
dict.derived = make([]derivedInfo, r.Len())
@@ -771,6 +788,14 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, ex
dict.itabs[i] = itabInfo2{typ: typ, lsym: lsym}
}
+ dict.methodExprs = make([]ir.Node, r.Len())
+ for i := range dict.methodExprs {
+ recv := pr.typIdx(typeInfo{idx: pkgbits.Index(r.Len()), derived: true}, &dict, true)
+ _, sym := r.selector()
+
+ dict.methodExprs[i] = typecheck.Expr(ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, ir.TypeNode(recv), sym))
+ }
+
return &dict
}
@@ -791,9 +816,7 @@ func (r *reader) method(rext *reader) *types.Field {
_, recv := r.param()
typ := r.signature(pkg, recv)
- fnsym := sym
- fnsym = ir.MethodSym(recv.Type, fnsym)
- name := ir.NewNameAt(pos, fnsym)
+ name := ir.NewNameAt(pos, ir.MethodSym(recv.Type, sym))
setType(name, typ)
name.Func = ir.NewFunc(r.pos())
@@ -882,6 +905,8 @@ func (r *reader) funcExt(name *ir.Name) {
typecheck.Func(fn)
if r.Bool() {
+ assert(name.Defn == nil)
+
fn.ABI = obj.ABI(r.Uint64())
// Escape analysis.
@@ -896,7 +921,6 @@ func (r *reader) funcExt(name *ir.Name) {
Cost: int32(r.Len()),
CanDelayResults: r.Bool(),
}
- r.addBody(name.Func)
}
} else {
r.addBody(name.Func)
@@ -952,24 +976,71 @@ func (r *reader) pragmaFlag() ir.PragmaFlag {
// @@@ Function bodies
-// bodyReader tracks where the serialized IR for a function's body can
-// be found.
+// bodyReader tracks where the serialized IR for a local or imported,
+// generic function's body can be found.
var bodyReader = map[*ir.Func]pkgReaderIndex{}
+// importBodyReader tracks where the serialized IR for an imported,
+// static (i.e., non-generic) function body can be read.
+var importBodyReader = map[*types.Sym]pkgReaderIndex{}
+
+// bodyReaderFor returns the pkgReaderIndex for reading fn's
+// serialized IR, and whether one was found.
+func bodyReaderFor(fn *ir.Func) (pri pkgReaderIndex, ok bool) {
+ if fn.Nname.Defn != nil {
+ pri, ok = bodyReader[fn]
+ base.AssertfAt(ok, base.Pos, "must have bodyReader for %v", fn) // must always be available
+ } else {
+ pri, ok = importBodyReader[fn.Sym()]
+ }
+ return
+}
+
// todoBodies holds the list of function bodies that still need to be
// constructed.
var todoBodies []*ir.Func
+// addBody reads a function body reference from the element bitstream,
+// and associates it with fn.
func (r *reader) addBody(fn *ir.Func) {
- pri := pkgReaderIndex{r.p, r.Reloc(pkgbits.RelocBody), r.dict}
- bodyReader[fn] = pri
+ // addBody should only be called for local functions or imported
+ // generic functions; see comment in funcExt.
+ assert(fn.Nname.Defn != nil)
- if fn.Nname.Defn == nil {
- // Don't read in function body for imported functions.
- // See comment in funcExt.
- return
+ idx := r.Reloc(pkgbits.RelocBody)
+
+ var shapedFn *ir.Func
+ if r.hasTypeParams() && fn.OClosure == nil {
+ name := fn.Nname
+ sym := name.Sym()
+
+ shapedSym := sym.Pkg.Lookup(sym.Name + "-shaped")
+
+ // TODO(mdempsky): Once we actually start shaping functions, we'll
+ // need to deduplicate them.
+ shaped := ir.NewDeclNameAt(name.Pos(), ir.ONAME, shapedSym)
+ setType(shaped, shapeSig(fn, r.dict)) // TODO(mdempsky): Use shape types.
+
+ shapedFn = ir.NewFunc(fn.Pos())
+ shaped.Func = shapedFn
+ shapedFn.Nname = shaped
+ shapedFn.SetDupok(true)
+
+ shaped.Class = 0 // so MarkFunc doesn't complain
+ ir.MarkFunc(shaped)
+
+ shaped.Defn = shapedFn
+
+ shapedFn.Pragma = fn.Pragma // TODO(mdempsky): How does stencil.go handle pragmas?
+ typecheck.Func(shapedFn)
+
+ bodyReader[shapedFn] = pkgReaderIndex{r.p, idx, r.dict, nil}
+ todoBodies = append(todoBodies, shapedFn)
}
+ pri := pkgReaderIndex{r.p, idx, r.dict, shapedFn}
+ bodyReader[fn] = pri
+
if r.curfn == nil {
todoBodies = append(todoBodies, fn)
return
@@ -983,9 +1054,14 @@ func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
r.funcBody(fn)
}
+// funcBody reads a function body definition from the element
+// bitstream, and populates fn with it.
func (r *reader) funcBody(fn *ir.Func) {
r.curfn = fn
r.closureVars = fn.ClosureVars
+ if len(r.closureVars) != 0 && r.hasTypeParams() {
+ r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
+ }
ir.WithFunc(fn, func() {
r.funcargs(fn)
@@ -994,6 +1070,11 @@ func (r *reader) funcBody(fn *ir.Func) {
return
}
+ if r.shapedFn != nil {
+ r.callShaped(fn.Pos())
+ return
+ }
+
body := r.stmts()
if body == nil {
body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(src.NoXPos, nil))}
@@ -1005,6 +1086,139 @@ func (r *reader) funcBody(fn *ir.Func) {
r.marker.WriteTo(fn)
}
+// callShaped emits a tail call to r.shapedFn, passing along the
+// arguments to the current function.
+func (r *reader) callShaped(pos src.XPos) {
+ sig := r.curfn.Nname.Type()
+
+ var args ir.Nodes
+
+ // First argument is a pointer to the -dict global variable.
+ args.Append(r.dictPtr())
+
+ // Collect the arguments to the current function, so we can pass
+ // them along to the shaped function. (This is unfortunately quite
+ // hairy.)
+ for _, params := range &types.RecvsParams {
+ for _, param := range params(sig).FieldSlice() {
+ var arg ir.Node
+ if param.Nname != nil {
+ name := param.Nname.(*ir.Name)
+ if !ir.IsBlank(name) {
+ if r.inlCall != nil {
+ // During inlining, we want the respective inlvar where we
+ // assigned the callee's arguments.
+ arg = r.inlvars[len(args)-1]
+ } else {
+ // Otherwise, we can use the parameter itself directly.
+ base.AssertfAt(name.Curfn == r.curfn, name.Pos(), "%v has curfn %v, but want %v", name, name.Curfn, r.curfn)
+ arg = name
+ }
+ }
+ }
+
+ // For anonymous and blank parameters, we don't have an *ir.Name
+ // to use as the argument. However, since we know the shaped
+ // function won't use the value either, we can just pass the
+ // zero value. (Also unfortunately, we don't have an easy
+ // zero-value IR node; so we use a default-initialized temporary
+ // variable.)
+ if arg == nil {
+ tmp := typecheck.TempAt(pos, r.curfn, param.Type)
+ r.curfn.Body.Append(
+ typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)),
+ typecheck.Stmt(ir.NewAssignStmt(pos, tmp, nil)),
+ )
+ arg = tmp
+ }
+
+ args.Append(arg)
+ }
+ }
+
+ // Mark the function as a wrapper so it doesn't show up in stack
+ // traces.
+ r.curfn.SetWrapper(true)
+
+ call := typecheck.Call(pos, r.shapedFn.Nname, args, sig.IsVariadic()).(*ir.CallExpr)
+
+ var stmt ir.Node
+ if sig.NumResults() != 0 {
+ stmt = typecheck.Stmt(ir.NewReturnStmt(pos, []ir.Node{call}))
+ } else {
+ stmt = call
+ }
+ r.curfn.Body.Append(stmt)
+}
+
+// dictPtr returns a pointer to the runtime dictionary variable needed
+// for the current function to call its shaped variant.
+func (r *reader) dictPtr() ir.Node {
+ var fn *ir.Func
+ if r.inlCall != nil {
+ // During inlining, r.curfn is named after the caller (not the
+ // callee), because it's relevant to closure naming, sigh.
+ fn = r.inlFunc
+ } else {
+ fn = r.curfn
+ }
+
+ var baseSym *types.Sym
+ if recv := fn.Nname.Type().Recv(); recv != nil {
+ // All methods of a given instantiated receiver type share the
+ // same dictionary.
+ baseSym = deref(recv.Type).Sym()
+ } else {
+ baseSym = fn.Nname.Sym()
+ }
+
+ sym := baseSym.Pkg.Lookup(baseSym.Name + "-dict")
+
+ if sym.Def == nil {
+ dict := ir.NewNameAt(r.curfn.Pos(), sym)
+ dict.Class = ir.PEXTERN
+
+ lsym := dict.Linksym()
+ ot := 0
+
+ for idx, info := range r.dict.derived {
+ if info.needed {
+ typ := r.p.typIdx(typeInfo{idx: pkgbits.Index(idx), derived: true}, r.dict, false)
+ rtype := reflectdata.TypeLinksym(typ)
+ ot = objw.SymPtr(lsym, ot, rtype, 0)
+ } else {
+ // TODO(mdempsky): Compact unused runtime dictionary space.
+ ot = objw.Uintptr(lsym, ot, 0)
+ }
+ }
+
+ // TODO(mdempsky): Write out more dictionary information.
+
+ objw.Global(lsym, int32(ot), obj.DUPOK|obj.RODATA)
+
+ dict.SetType(r.dict.varType())
+ dict.SetTypecheck(1)
+
+ sym.Def = dict
+ }
+
+ return typecheck.Expr(ir.NewAddrExpr(r.curfn.Pos(), sym.Def.(*ir.Name)))
+}
+
+// numWords returns the number of words that dict's runtime dictionary
+// variable requires.
+func (dict *readerDict) numWords() int64 {
+ var num int
+ num += len(dict.derivedTypes)
+ // TODO(mdempsky): Add space for more dictionary information.
+ return int64(num)
+}
+
+// varType returns the type of dict's runtime dictionary variable.
+func (dict *readerDict) varType() *types.Type {
+ return types.NewArray(types.Types[types.TUINTPTR], dict.numWords())
+}
+
func (r *reader) funcargs(fn *ir.Func) {
sig := fn.Nname.Type()
@@ -1062,16 +1276,20 @@ func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) {
func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) {
assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
- r.Sync(pkgbits.SyncAddLocal)
- if pkgbits.EnableSync {
- want := r.Int()
- if have := len(r.locals); have != want {
- base.FatalfAt(name.Pos(), "locals table has desynced")
+ if name.Sym().Name == dictParamName {
+ r.dictParam = name
+ } else {
+ r.Sync(pkgbits.SyncAddLocal)
+ if r.p.SyncMarkers() {
+ want := r.Int()
+ if have := len(r.locals); have != want {
+ base.FatalfAt(name.Pos(), "locals table has desynced")
+ }
}
+ r.locals = append(r.locals, name)
}
name.SetUsed(true)
- r.locals = append(r.locals, name)
// TODO(mdempsky): Move earlier.
if ir.IsBlank(name) {
@@ -1229,11 +1447,8 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
case stmtAssign:
pos := r.pos()
-
- // TODO(mdempsky): After quirks mode is gone, swap these
- // statements so we visit LHS before RHS again.
- rhs := r.exprList()
names, lhs := r.assignList()
+ rhs := r.multiExpr()
if len(rhs) == 0 {
for _, name := range names {
@@ -1301,7 +1516,7 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
case stmtReturn:
pos := r.pos()
- results := r.exprList()
+ results := r.multiExpr()
return ir.NewReturnStmt(pos, results)
case stmtSelect:
@@ -1323,25 +1538,42 @@ func (r *reader) assignList() ([]*ir.Name, []ir.Node) {
var names []*ir.Name
for i := range lhs {
- if r.Bool() {
- pos := r.pos()
- _, sym := r.localIdent()
- typ := r.typ()
-
- name := ir.NewNameAt(pos, sym)
- lhs[i] = name
- names = append(names, name)
- setType(name, typ)
- r.addLocal(name, ir.PAUTO)
- continue
+ expr, def := r.assign()
+ lhs[i] = expr
+ if def {
+ names = append(names, expr.(*ir.Name))
}
-
- lhs[i] = r.expr()
}
return names, lhs
}
+// assign returns an assignee expression. It also reports whether the
+// returned expression is a newly declared variable.
+func (r *reader) assign() (ir.Node, bool) {
+ switch tag := codeAssign(r.Code(pkgbits.SyncAssign)); tag {
+ default:
+ panic("unhandled assignee expression")
+
+ case assignBlank:
+ return typecheck.AssignExpr(ir.BlankNode), false
+
+ case assignDef:
+ pos := r.pos()
+ setBasePos(pos)
+ _, sym := r.localIdent()
+ typ := r.typ()
+
+ name := ir.NewNameAt(pos, sym)
+ setType(name, typ)
+ r.addLocal(name, ir.PAUTO)
+ return name, true
+
+ case assignExpr:
+ return r.expr(), false
+ }
+}
+
func (r *reader) blockStmt() []ir.Node {
r.Sync(pkgbits.SyncBlockStmt)
r.openScope()
@@ -1357,16 +1589,10 @@ func (r *reader) forStmt(label *types.Sym) ir.Node {
if r.Bool() {
pos := r.pos()
+ rang := ir.NewRangeStmt(pos, nil, nil, nil, nil)
+ rang.Label = label
- // TODO(mdempsky): After quirks mode is gone, swap these
- // statements so we read LHS before X again.
- x := r.expr()
names, lhs := r.assignList()
-
- body := r.blockStmt()
- r.closeAnotherScope()
-
- rang := ir.NewRangeStmt(pos, nil, nil, x, body)
if len(lhs) >= 1 {
rang.Key = lhs[0]
if len(lhs) >= 2 {
@@ -1374,13 +1600,27 @@ func (r *reader) forStmt(label *types.Sym) ir.Node {
}
}
rang.Def = r.initDefn(rang, names)
- rang.Label = label
+
+ rang.X = r.expr()
+ if rang.X.Type().IsMap() {
+ rang.RType = r.rtype(pos)
+ }
+ if rang.Key != nil && !ir.IsBlank(rang.Key) {
+ rang.KeyTypeWord, rang.KeySrcRType = r.convRTTI(pos)
+ }
+ if rang.Value != nil && !ir.IsBlank(rang.Value) {
+ rang.ValueTypeWord, rang.ValueSrcRType = r.convRTTI(pos)
+ }
+
+ rang.Body = r.blockStmt()
+ r.closeAnotherScope()
+
return rang
}
pos := r.pos()
init := r.stmt()
- cond := r.expr()
+ cond := r.optExpr()
post := r.stmt()
body := r.blockStmt()
r.closeAnotherScope()
@@ -1419,6 +1659,44 @@ func (r *reader) selectStmt(label *types.Sym) ir.Node {
comm := r.stmt()
body := r.stmts()
+ // "case i = <-c: ..." may require an implicit conversion (e.g.,
+ // see fixedbugs/bug312.go). Currently, typecheck throws away the
+ // implicit conversion and relies on it being reinserted later,
+ // but that would lose any explicit RTTI operands too. To preserve
+ // RTTI, we rewrite this as "case tmp := <-c: i = tmp; ...".
+ if as, ok := comm.(*ir.AssignStmt); ok && as.Op() == ir.OAS && !as.Def {
+ if conv, ok := as.Y.(*ir.ConvExpr); ok && conv.Op() == ir.OCONVIFACE {
+ base.AssertfAt(conv.Implicit(), conv.Pos(), "expected implicit conversion: %v", conv)
+
+ recv := conv.X
+ base.AssertfAt(recv.Op() == ir.ORECV, recv.Pos(), "expected receive expression: %v", recv)
+
+ tmp := r.temp(pos, recv.Type())
+
+ // Replace comm with `tmp := <-c`.
+ tmpAs := ir.NewAssignStmt(pos, tmp, recv)
+ tmpAs.Def = true
+ tmpAs.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
+ comm = tmpAs
+
+ // Change original assignment to `i = tmp`, and prepend to body.
+ conv.X = tmp
+ body = append([]ir.Node{as}, body...)
+ }
+ }
+
+ // multiExpr will have desugared a comma-ok receive expression
+ // into a separate statement. However, the rest of the compiler
+ // expects comm to be the OAS2RECV statement itself, so we need to
+ // shuffle things around to fit that pattern.
+ if as2, ok := comm.(*ir.AssignListStmt); ok && as2.Op() == ir.OAS2 {
+ init := ir.TakeInit(as2.Rhs[0])
+ base.AssertfAt(len(init) == 1 && init[0].Op() == ir.OAS2RECV, as2.Pos(), "unexpected assignment: %+v", as2)
+
+ comm = init[0]
+ body = append([]ir.Node{as2}, body...)
+ }
+
clauses[i] = ir.NewCommStmt(pos, comm, body)
}
if len(clauses) > 0 {
@@ -1450,7 +1728,7 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node {
iface = x.Type()
tag = ir.NewTypeSwitchGuard(pos, ident, x)
} else {
- tag = r.expr()
+ tag = r.optExpr()
}
clauses := make([]*ir.CaseClause, r.Len())
@@ -1461,20 +1739,43 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node {
r.openScope()
pos := r.pos()
- var cases []ir.Node
+ var cases, rtypes []ir.Node
if iface != nil {
cases = make([]ir.Node, r.Len())
if len(cases) == 0 {
cases = nil // TODO(mdempsky): Unclear if this matters.
}
for i := range cases {
- cases[i] = r.exprType(true)
+ if r.Bool() { // case nil
+ cases[i] = typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr))
+ } else {
+ cases[i] = r.exprType()
+ }
}
} else {
cases = r.exprList()
+
+ // For `switch { case any(true): }` (e.g., issue 3980 in
+ // test/switch.go), the backend still creates a mixed bool/any
+ // comparison, and we need to explicitly supply the RTTI for the
+ // comparison.
+ //
+ // TODO(mdempsky): Change writer.go to desugar "switch {" into
+ // "switch true {", which we already handle correctly.
+ if tag == nil {
+ for i, cas := range cases {
+ if cas.Type().IsEmptyInterface() {
+ for len(rtypes) < i {
+ rtypes = append(rtypes, nil)
+ }
+ rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), types.Types[types.TBOOL]))
+ }
+ }
+ }
}
clause := ir.NewCaseStmt(pos, cases, nil)
+ clause.RTypes = rtypes
if ident != nil {
pos := r.pos()
@@ -1551,25 +1852,14 @@ func (r *reader) expr() (res ir.Node) {
default:
panic("unhandled expression")
- case exprNone:
- return nil
-
- case exprBlank:
- // blank only allowed in LHS of assignments
- // TODO(mdempsky): Handle directly in assignList instead?
- return typecheck.AssignExpr(ir.BlankNode)
-
case exprLocal:
return typecheck.Expr(r.useLocal())
- case exprName:
+ case exprGlobal:
// Callee instead of Expr allows builtins
// TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
return typecheck.Callee(r.obj())
- case exprType:
- return r.exprType(false)
-
case exprConst:
pos := r.pos()
typ := r.typ()
@@ -1578,6 +1868,11 @@ func (r *reader) expr() (res ir.Node) {
orig := r.String()
return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
+ case exprNil:
+ pos := r.pos()
+ typ := r.typ()
+ return Nil(pos, typ)
+
case exprCompLit:
return r.compLit()
@@ -1585,17 +1880,20 @@ func (r *reader) expr() (res ir.Node) {
return r.funcLit()
case exprSelector:
- x := r.expr()
- pos := r.pos()
- _, sym := r.selector()
+ var x ir.Node
+ if r.Bool() { // MethodExpr
+ if r.Bool() {
+ return r.dict.methodExprs[r.Len()]
+ }
- // Method expression with derived receiver type.
- if x.Op() == ir.ODYNAMICTYPE {
- // TODO(mdempsky): Handle with runtime dictionary lookup.
- n := ir.TypeNode(x.Type())
+ n := ir.TypeNode(r.typ())
n.SetTypecheck(1)
x = n
+ } else { // FieldVal, MethodVal
+ x = r.expr()
}
+ pos := r.pos()
+ _, sym := r.selector()
n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
if n.Op() == ir.OMETHVALUE {
@@ -1615,14 +1913,20 @@ func (r *reader) expr() (res ir.Node) {
x := r.expr()
pos := r.pos()
index := r.expr()
- return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
+ n := typecheck.Expr(ir.NewIndexExpr(pos, x, index))
+ switch n.Op() {
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ n.RType = r.rtype(pos)
+ }
+ return n
case exprSlice:
x := r.expr()
pos := r.pos()
var index [3]ir.Node
for i := range index {
- index[i] = r.expr()
+ index[i] = r.optExpr()
}
op := ir.OSLICE
if index[2] != nil {
@@ -1633,10 +1937,13 @@ func (r *reader) expr() (res ir.Node) {
case exprAssert:
x := r.expr()
pos := r.pos()
- typ := r.exprType(false)
+ typ := r.exprType()
+ srcRType := r.rtype(pos)
+ // TODO(mdempsky): Always emit ODYNAMICDOTTYPE for uniformity?
if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE {
assert := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.RType)
+ assert.SrcRType = srcRType
assert.ITab = typ.ITab
return typed(typ.Type(), assert)
}
@@ -1675,13 +1982,43 @@ func (r *reader) expr() (res ir.Node) {
fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym))
}
pos := r.pos()
- args := r.exprs()
+ args := r.multiExpr()
dots := r.Bool()
- return typecheck.Call(pos, fun, args, dots)
+ n := typecheck.Call(pos, fun, args, dots)
+ switch n.Op() {
+ case ir.OAPPEND:
+ n := n.(*ir.CallExpr)
+ n.RType = r.rtype(pos)
+ case ir.OCOPY:
+ n := n.(*ir.BinaryExpr)
+ n.RType = r.rtype(pos)
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ n.RType = r.rtype(pos)
+ case ir.OUNSAFESLICE:
+ n := n.(*ir.BinaryExpr)
+ n.RType = r.rtype(pos)
+ }
+ return n
+
+ case exprMake:
+ pos := r.pos()
+ typ := r.exprType()
+ extra := r.exprs()
+ n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr)
+ n.RType = r.rtype(pos)
+ return n
+
+ case exprNew:
+ pos := r.pos()
+ typ := r.exprType()
+ return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
case exprConvert:
+ implicit := r.Bool()
typ := r.typ()
pos := r.pos()
+ typeWord, srcRType := r.convRTTI(pos)
x := r.expr()
// TODO(mdempsky): Stop constructing expressions of untyped type.
@@ -1697,8 +2034,72 @@ func (r *reader) expr() (res ir.Node) {
base.ErrorExit() // harsh, but prevents constructing invalid IR
}
- return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x))
+ n := ir.NewConvExpr(pos, ir.OCONV, typ, x)
+ n.TypeWord, n.SrcRType = typeWord, srcRType
+ if implicit {
+ n.SetImplicit(true)
+ }
+ return typecheck.Expr(n)
+ }
+}
+
+func (r *reader) optExpr() ir.Node {
+ if r.Bool() {
+ return r.expr()
}
+ return nil
+}
+
+func (r *reader) multiExpr() []ir.Node {
+ r.Sync(pkgbits.SyncMultiExpr)
+
+ if r.Bool() { // N:1
+ pos := r.pos()
+ expr := r.expr()
+
+ results := make([]ir.Node, r.Len())
+ as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr})
+ as.Def = true
+ for i := range results {
+ tmp := r.temp(pos, r.typ())
+ as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
+ as.Lhs.Append(tmp)
+
+ res := ir.Node(tmp)
+ if r.Bool() {
+ n := ir.NewConvExpr(pos, ir.OCONV, r.typ(), res)
+ n.TypeWord, n.SrcRType = r.convRTTI(pos)
+ n.SetImplicit(true)
+ res = typecheck.Expr(n)
+ }
+ results[i] = res
+ }
+
+ // TODO(mdempsky): Could use ir.InlinedCallExpr instead?
+ results[0] = ir.InitExpr([]ir.Node{typecheck.Stmt(as)}, results[0])
+ return results
+ }
+
+ // N:N
+ exprs := make([]ir.Node, r.Len())
+ if len(exprs) == 0 {
+ return nil
+ }
+ for i := range exprs {
+ exprs[i] = r.expr()
+ }
+ return exprs
+}
+
+// temp returns a new autotemp of the specified type.
+func (r *reader) temp(pos src.XPos, typ *types.Type) *ir.Name {
+ // See typecheck.typecheckargs.
+ curfn := r.curfn
+ if curfn == nil {
+ curfn = typecheck.InitTodoFunc
+ }
+
+ return typecheck.TempAt(pos, curfn, typ)
}
func (r *reader) compLit() ir.Node {
@@ -1713,6 +2114,10 @@ func (r *reader) compLit() ir.Node {
if typ.Kind() == types.TFORW {
base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
}
+ var rtype ir.Node
+ if typ.IsMap() {
+ rtype = r.rtype(pos)
+ }
isStruct := typ.Kind() == types.TSTRUCT
elems := make([]ir.Node, r.Len())
@@ -1731,6 +2136,10 @@ func (r *reader) compLit() ir.Node {
}
lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems))
+ if rtype != nil {
+ lit := lit.(*ir.CompLitExpr)
+ lit.RType = rtype
+ }
if typ0.IsPtr() {
lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
lit.SetType(typ0)
@@ -1775,6 +2184,11 @@ func (r *reader) funcLit() ir.Node {
for len(fn.ClosureVars) < cap(fn.ClosureVars) {
ir.NewClosureVar(r.pos(), fn, r.useLocal())
}
+ if param := r.dictParam; param != nil {
+ // If we have a dictionary parameter, capture it too. For
+ // simplicity, we capture it last and unconditionally.
+ ir.NewClosureVar(param.Pos(), fn, param)
+ }
r.addBody(fn)
@@ -1799,17 +2213,73 @@ func (r *reader) exprs() []ir.Node {
return nodes
}
-func (r *reader) exprType(nilOK bool) ir.Node {
- r.Sync(pkgbits.SyncExprType)
+// dictWord returns an expression to return the specified
+// uintptr-typed word from the dictionary parameter.
+func (r *reader) dictWord(pos src.XPos, idx int64) ir.Node {
+ base.AssertfAt(r.dictParam != nil, pos, "expected dictParam in %v", r.curfn)
+ return typecheck.Expr(ir.NewIndexExpr(pos, r.dictParam, ir.NewBasicLit(pos, constant.MakeInt64(idx))))
+}
- if nilOK && r.Bool() {
- return typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr))
+// rtype reads a type reference from the element bitstream, and
+// returns an expression of type *runtime._type representing that
+// type.
+func (r *reader) rtype(pos src.XPos) ir.Node {
+ r.Sync(pkgbits.SyncRType)
+ return r.rtypeInfo(pos, r.typInfo())
+}
+
+// rtypeInfo returns an expression of type *runtime._type representing
+// the given decoded type reference.
+func (r *reader) rtypeInfo(pos src.XPos, info typeInfo) ir.Node {
+ if !info.derived {
+ typ := r.p.typIdx(info, r.dict, true)
+ return reflectdata.TypePtrAt(pos, typ)
+ }
+ return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, types.NewPtr(types.Types[types.TUINT8]), r.dictWord(pos, int64(info.idx))))
+}
+
+// convRTTI returns expressions appropriate for populating an
+// ir.ConvExpr's TypeWord and SrcRType fields, respectively.
+func (r *reader) convRTTI(pos src.XPos) (typeWord, srcRType ir.Node) {
+ r.Sync(pkgbits.SyncConvRTTI)
+ srcInfo := r.typInfo()
+ dstInfo := r.typInfo()
+
+ dst := r.p.typIdx(dstInfo, r.dict, true)
+ if !dst.IsInterface() {
+ return
}
+ src := r.p.typIdx(srcInfo, r.dict, true)
+
+ // See reflectdata.ConvIfaceTypeWord.
+ switch {
+ case dst.IsEmptyInterface():
+ if !src.IsInterface() {
+ typeWord = r.rtypeInfo(pos, srcInfo) // direct eface construction
+ }
+ case !src.IsInterface():
+ typeWord = reflectdata.ITabAddrAt(pos, src, dst) // direct iface construction
+ default:
+ typeWord = r.rtypeInfo(pos, dstInfo) // convI2I
+ }
+
+ // See reflectdata.ConvIfaceSrcRType.
+ if !src.IsInterface() {
+ srcRType = r.rtypeInfo(pos, srcInfo)
+ }
+
+ return
+}
+
+func (r *reader) exprType() ir.Node {
+ r.Sync(pkgbits.SyncExprType)
+
pos := r.pos()
+ setBasePos(pos)
lsymPtr := func(lsym *obj.LSym) ir.Node {
- return typecheck.Expr(typecheck.NodAddr(ir.NewLinksymExpr(pos, lsym, types.Types[types.TUINT8])))
+ return typecheck.Expr(typecheck.NodAddrAt(pos, ir.NewLinksymExpr(pos, lsym, types.Types[types.TUINT8])))
}
var typ *types.Type
@@ -1836,7 +2306,7 @@ func (r *reader) exprType(nilOK bool) ir.Node {
return n
}
- rtype = lsymPtr(reflectdata.TypeLinksym(typ))
+ rtype = r.rtypeInfo(pos, info)
}
dt := ir.NewDynamicType(pos, rtype)
@@ -1974,13 +2444,20 @@ func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
var inlgen = 0
+// InlineCall implements inline.NewInline by re-reading the function
+// body from its Unified IR export data.
func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
// TODO(mdempsky): Turn callerfn into an explicit parameter.
callerfn := ir.CurFunc
- pri, ok := bodyReader[fn]
+ pri, ok := bodyReaderFor(fn)
if !ok {
- base.FatalfAt(call.Pos(), "missing function body for call to %v", fn)
+ // TODO(mdempsky): Reconsider this diagnostic's wording, if it's
+ // to be included in Go 1.20.
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(call.Pos(), "cannot inline call to %v: missing inline body", fn)
+ }
+ return nil
}
if fn.Inl.Body == nil {
@@ -2008,6 +2485,9 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
for i, cv := range r.inlFunc.ClosureVars {
r.closureVars[i] = cv.Outer
}
+ if len(r.closureVars) != 0 && r.hasTypeParams() {
+ r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
+ }
r.funcargs(fn)
@@ -2070,8 +2550,12 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
nparams := len(r.curfn.Dcl)
ir.WithFunc(r.curfn, func() {
- r.curfn.Body = r.stmts()
- r.curfn.Endlineno = r.pos()
+ if r.shapedFn != nil {
+ r.callShaped(call.Pos())
+ } else {
+ r.curfn.Body = r.stmts()
+ r.curfn.Endlineno = r.pos()
+ }
// TODO(mdempsky): This shouldn't be necessary. Inlining might
// read in new function/method declarations, which could
@@ -2260,18 +2744,21 @@ func (r *reader) needWrapper(typ *types.Type) {
}
}
+// importedDef reports whether r is reading from an imported and
+// non-generic element.
+//
+// If a type was found in an imported package, then we can assume that
+// package (or one of its transitive dependencies) already generated
+// method wrappers for it.
+//
+// Exception: If we're instantiating an imported generic type or
+// function, we might be instantiating it with type arguments not
+// previously seen before.
+//
+// TODO(mdempsky): Distinguish when a generic function or type was
+// instantiated in an imported package so that we can add types to
+// haveWrapperTypes instead.
func (r *reader) importedDef() bool {
- // If a type was found in an imported package, then we can assume
- // that package (or one of its transitive dependencies) already
- // generated method wrappers for it.
- //
- // Exception: If we're instantiating an imported generic type or
- // function, we might be instantiating it with type arguments not
- // previously seen before.
- //
- // TODO(mdempsky): Distinguish when a generic function or type was
- // instantiated in an imported package so that we can add types to
- // haveWrapperTypes instead.
return r.p != localPkgReader && !r.hasTypeParams()
}
@@ -2459,6 +2946,15 @@ func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
// so we're responsible for applying inlining ourselves here.
inline.InlineCalls(fn)
+ // The body of wrapper function after inlining may reveal new ir.OMETHVALUE node,
+ // we don't know whether wrapper function has been generated for it or not, so
+ // generate one immediately here.
+ ir.VisitList(fn.Body, func(n ir.Node) {
+ if n, ok := n.(*ir.SelectorExpr); ok && n.Op() == ir.OMETHVALUE {
+ wrapMethodValue(n.X.Type(), n.Selection, target, true)
+ }
+ })
+
target.Decls = append(target.Decls, fn)
}
@@ -2516,3 +3012,45 @@ func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
ret.Results = []ir.Node{call}
fn.Body.Append(ret)
}
+
+func setBasePos(pos src.XPos) {
+ // Set the position for any error messages we might print (e.g. too large types).
+ base.Pos = pos
+}
+
+// dictParamName is the name of the synthetic dictionary parameter
+// added to shaped functions.
+const dictParamName = ".dict"
+
+// shapeSig returns a copy of fn's signature, except adding a
+// dictionary parameter and promoting the receiver parameter (if any)
+// to a normal parameter.
+//
+// The parameter types.Fields are all copied too, so their Nname
+// fields can be initialized for use by the shape function.
+func shapeSig(fn *ir.Func, dict *readerDict) *types.Type {
+ sig := fn.Nname.Type()
+ recv := sig.Recv()
+ nrecvs := 0
+ if recv != nil {
+ nrecvs++
+ }
+
+ params := make([]*types.Field, 1+nrecvs+sig.Params().Fields().Len())
+ params[0] = types.NewField(fn.Pos(), fn.Sym().Pkg.Lookup(dictParamName), types.NewPtr(dict.varType()))
+ if recv != nil {
+ params[1] = types.NewField(recv.Pos, recv.Sym, recv.Type)
+ }
+ for i, param := range sig.Params().Fields().Slice() {
+ d := types.NewField(param.Pos, param.Sym, param.Type)
+ d.SetIsDDD(param.IsDDD())
+ params[1+nrecvs+i] = d
+ }
+
+ results := make([]*types.Field, sig.Results().Fields().Len())
+ for i, result := range sig.Results().Fields().Slice() {
+ results[i] = types.NewField(result.Pos, result.Sym, result.Type)
+ }
+
+ return types.NewSignature(types.LocalPkg, nil, nil, params, results)
+}