aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/typecheck/iexport.go
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2021-03-12 12:57:39 -0800
committerKeith Randall <khr@golang.org>2021-04-10 14:58:18 +0000
commit1129a60f1c1e64147ca1133857c4571ce9b87a35 (patch)
tree6ea37d4fb479f7009ccf43aacc07a547db7e7287 /src/cmd/compile/internal/typecheck/iexport.go
parent11f159456b1dba3ec499da916852dd188d1e04a7 (diff)
downloadgo-1129a60f1c1e64147ca1133857c4571ce9b87a35.tar.gz
go-1129a60f1c1e64147ca1133857c4571ce9b87a35.zip
cmd/compile: include typecheck information in export/import
Include type information on exported function bodies, so that the importer does not have to re-typecheck the body. This involves including type information in the encoded output, as well as avoiding some of the opcode rewriting and other changes that the old exporter did assuming there would be a re-typechecking pass. This CL could be considered a cleanup, but is more important than that because it is an enabling change for generics. Without this CL, we'd have to upgrade the current typechecker to understand generics. With this CL, the current typechecker can mostly go away in favor of the types2 typechecker. For now, inlining of functions that contain closures is turned off. We will hopefully resolve this before freeze. Object files are only 0.07% bigger. Change-Id: I85c9da09f66bfdc910dc3e26abb2613a1831634d Reviewed-on: https://go-review.googlesource.com/c/go/+/301291 Trust: Keith Randall <khr@golang.org> Trust: Dan Scales <danscales@google.com> Reviewed-by: Dan Scales <danscales@google.com>
Diffstat (limited to 'src/cmd/compile/internal/typecheck/iexport.go')
-rw-r--r--src/cmd/compile/internal/typecheck/iexport.go276
1 files changed, 260 insertions, 16 deletions
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index 43cc4e4a25..8f8931e495 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -633,6 +633,79 @@ func (w *exportWriter) selector(s *types.Sym) {
w.string(s.Name)
}
+func (w *exportWriter) typ(t *types.Type) {
+ w.data.uint64(w.p.typOff(t))
+}
+
+// The "exotic" functions in this section encode a wider range of
+// items than the standard encoding functions above. These include
+// types that do not appear in declarations, only in code, such as
+// method types. These methods need to be separate from the standard
+// encoding functions because we don't want to modify the encoding
+// generated by the standard functions (because that exported
+// information is read by tools besides the compiler).
+
+// exoticType exports a type to the writer.
+func (w *exportWriter) exoticType(t *types.Type) {
+ switch {
+ case t == nil:
+ // Calls-as-statements have no type.
+ w.data.uint64(exoticTypeNil)
+ case t.IsStruct() && t.StructType().Funarg != types.FunargNone:
+ // These are weird structs for representing tuples of types returned
+ // by multi-return functions.
+ // They don't fit the standard struct type mold. For instance,
+ // they don't have any package info.
+ w.data.uint64(exoticTypeTuple)
+ w.uint64(uint64(t.StructType().Funarg))
+ w.uint64(uint64(t.NumFields()))
+ for _, f := range t.FieldSlice() {
+ w.pos(f.Pos)
+ s := f.Sym
+ if s == nil {
+ w.uint64(0)
+ } else if s.Pkg == nil {
+ w.uint64(exoticTypeSymNoPkg)
+ w.string(s.Name)
+ } else {
+ w.uint64(exoticTypeSymWithPkg)
+ w.pkg(s.Pkg)
+ w.string(s.Name)
+ }
+ w.typ(f.Type)
+ if f.Embedded != 0 || f.Note != "" {
+ panic("extra info in funarg struct field")
+ }
+ }
+ case t.Kind() == types.TFUNC && t.Recv() != nil:
+ w.data.uint64(exoticTypeRecv)
+ // interface method types have a fake receiver type.
+ isFakeRecv := t.Recv().Type == types.FakeRecvType()
+ w.bool(isFakeRecv)
+ if !isFakeRecv {
+ w.exoticParam(t.Recv())
+ }
+ w.exoticSignature(t)
+
+ default:
+ // A regular type.
+ w.data.uint64(exoticTypeRegular)
+ w.typ(t)
+ }
+}
+
+const (
+ exoticTypeNil = iota
+ exoticTypeTuple
+ exoticTypeRecv
+ exoticTypeRegular
+)
+const (
+ exoticTypeSymNil = iota
+ exoticTypeSymNoPkg
+ exoticTypeSymWithPkg
+)
+
// Export a selector, but one whose package may not match
// the package being compiled. This is a separate function
// because the standard selector() serialization format is fixed
@@ -653,8 +726,42 @@ func (w *exportWriter) exoticSelector(s *types.Sym) {
}
}
-func (w *exportWriter) typ(t *types.Type) {
- w.data.uint64(w.p.typOff(t))
+func (w *exportWriter) exoticSignature(t *types.Type) {
+ hasPkg := t.Pkg() != nil
+ w.bool(hasPkg)
+ if hasPkg {
+ w.pkg(t.Pkg())
+ }
+ w.exoticParamList(t.Params().FieldSlice())
+ w.exoticParamList(t.Results().FieldSlice())
+}
+
+func (w *exportWriter) exoticParamList(fs []*types.Field) {
+ w.uint64(uint64(len(fs)))
+ for _, f := range fs {
+ w.exoticParam(f)
+ }
+
+}
+func (w *exportWriter) exoticParam(f *types.Field) {
+ w.pos(f.Pos)
+ w.exoticSym(f.Sym)
+ w.uint64(uint64(f.Offset))
+ w.exoticType(f.Type)
+ w.bool(f.IsDDD())
+}
+func (w *exportWriter) exoticSym(s *types.Sym) {
+ if s == nil {
+ w.string("")
+ return
+ }
+ if s.Name == "" {
+ base.Fatalf("empty symbol name")
+ }
+ w.string(s.Name)
+ if !types.IsExported(s.Name) {
+ w.pkg(s.Pkg)
+ }
}
func (p *iexporter) newWriter() *exportWriter {
@@ -1133,6 +1240,7 @@ func (w *exportWriter) writeNames(dcl []*ir.Name) {
}
func (w *exportWriter) funcBody(fn *ir.Func) {
+ //fmt.Printf("Exporting %s\n", fn.Nname.Sym().Name)
w.writeNames(fn.Inl.Dcl)
w.stmtList(fn.Inl.Body)
@@ -1208,7 +1316,11 @@ func (w *exportWriter) stmt(n ir.Node) {
case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
n := n.(*ir.AssignListStmt)
- w.op(ir.OAS2)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.OAS2)
+ }
w.pos(n.Pos())
w.exprList(n.Lhs)
w.exprList(n.Rhs)
@@ -1220,7 +1332,7 @@ func (w *exportWriter) stmt(n ir.Node) {
w.exprList(n.Results)
// case ORETJMP:
- // unreachable - generated by compiler for trampolin routines
+ // unreachable - generated by compiler for trampoline routines
case ir.OGO, ir.ODEFER:
n := n.(*ir.GoDeferStmt)
@@ -1357,10 +1469,15 @@ func (w *exportWriter) expr(n ir.Node) {
if (n.Class == ir.PEXTERN || n.Class == ir.PFUNC) && !ir.IsBlank(n) {
w.op(ir.ONONAME)
w.qualifiedIdent(n)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
break
}
// Function scope name.
+ // We don't need a type here, as the type will be provided at the
+ // declaration of n.
w.op(ir.ONAME)
w.localName(n)
@@ -1418,9 +1535,16 @@ func (w *exportWriter) expr(n ir.Node) {
case ir.OPTRLIT:
n := n.(*ir.AddrExpr)
- w.op(ir.OADDR)
+ if go117ExportTypes {
+ w.op(ir.OPTRLIT)
+ } else {
+ w.op(ir.OADDR)
+ }
w.pos(n.Pos())
w.expr(n.X)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.OSTRUCTLIT:
n := n.(*ir.CompLitExpr)
@@ -1431,11 +1555,17 @@ func (w *exportWriter) expr(n ir.Node) {
case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
n := n.(*ir.CompLitExpr)
- w.op(ir.OCOMPLIT)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.OCOMPLIT)
+ }
w.pos(n.Pos())
w.typ(n.Type())
w.exprList(n.List)
-
+ if go117ExportTypes && n.Op() == ir.OSLICELIT {
+ w.uint64(uint64(n.Len))
+ }
case ir.OKEY:
n := n.(*ir.KeyExpr)
w.op(ir.OKEY)
@@ -1448,39 +1578,89 @@ func (w *exportWriter) expr(n ir.Node) {
case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR:
n := n.(*ir.SelectorExpr)
- w.op(ir.OXDOT)
+ if go117ExportTypes {
+ if n.Op() == ir.OXDOT {
+ base.Fatalf("shouldn't encounter XDOT in new exporter")
+ }
+ w.op(n.Op())
+ } else {
+ w.op(ir.OXDOT)
+ }
w.pos(n.Pos())
w.expr(n.X)
w.exoticSelector(n.Sel)
+ if go117ExportTypes {
+ w.exoticType(n.Type())
+ if n.Op() == ir.ODOT || n.Op() == ir.ODOTPTR || n.Op() == ir.ODOTINTER || n.Op() == ir.OMETHEXPR {
+ w.exoticParam(n.Selection)
+ if n.Op() == ir.OMETHEXPR {
+ name := ir.MethodExprName(n)
+ w.bool(name != nil)
+ if name != nil {
+ w.exoticType(name.Type())
+ }
+ }
+ }
+ // n.Selection is not required for ODOTMETH and OCALLPART. It will
+ // be reconstructed during import.
+ }
case ir.ODOTTYPE, ir.ODOTTYPE2:
n := n.(*ir.TypeAssertExpr)
- w.op(ir.ODOTTYPE)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.ODOTTYPE)
+ }
w.pos(n.Pos())
w.expr(n.X)
w.typ(n.Type())
case ir.OINDEX, ir.OINDEXMAP:
n := n.(*ir.IndexExpr)
- w.op(ir.OINDEX)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.OINDEX)
+ }
w.pos(n.Pos())
w.expr(n.X)
w.expr(n.Index)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ if n.Op() == ir.OINDEXMAP {
+ w.bool(n.Assigned)
+ }
+ }
case ir.OSLICE, ir.OSLICESTR, ir.OSLICEARR:
n := n.(*ir.SliceExpr)
- w.op(ir.OSLICE)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.OSLICE)
+ }
w.pos(n.Pos())
w.expr(n.X)
w.exprsOrNil(n.Low, n.High)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.OSLICE3, ir.OSLICE3ARR:
n := n.(*ir.SliceExpr)
- w.op(ir.OSLICE3)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.OSLICE3)
+ }
w.pos(n.Pos())
w.expr(n.X)
w.exprsOrNil(n.Low, n.High)
w.expr(n.Max)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.OCOPY, ir.OCOMPLEX:
// treated like other builtin calls (see e.g., OREAL)
@@ -1489,11 +1669,19 @@ func (w *exportWriter) expr(n ir.Node) {
w.pos(n.Pos())
w.expr(n.X)
w.expr(n.Y)
- w.op(ir.OEND)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ } else {
+ w.op(ir.OEND)
+ }
case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR:
n := n.(*ir.ConvExpr)
- w.op(ir.OCONV)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.OCONV)
+ }
w.pos(n.Pos())
w.typ(n.Type())
w.expr(n.X)
@@ -1503,7 +1691,13 @@ func (w *exportWriter) expr(n ir.Node) {
w.op(n.Op())
w.pos(n.Pos())
w.expr(n.X)
- w.op(ir.OEND)
+ if go117ExportTypes {
+ if n.Op() != ir.OPANIC {
+ w.typ(n.Type())
+ }
+ } else {
+ w.op(ir.OEND)
+ }
case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN:
n := n.(*ir.CallExpr)
@@ -1516,15 +1710,28 @@ func (w *exportWriter) expr(n ir.Node) {
} else if n.IsDDD {
base.Fatalf("exporter: unexpected '...' with %v call", n.Op())
}
+ if go117ExportTypes {
+ if n.Op() != ir.ODELETE && n.Op() != ir.OPRINT && n.Op() != ir.OPRINTN {
+ w.typ(n.Type())
+ }
+ }
case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OGETG:
n := n.(*ir.CallExpr)
- w.op(ir.OCALL)
+ if go117ExportTypes {
+ w.op(n.Op())
+ } else {
+ w.op(ir.OCALL)
+ }
w.pos(n.Pos())
w.stmtList(n.Init())
w.expr(n.X)
w.exprList(n.Args)
w.bool(n.IsDDD)
+ if go117ExportTypes {
+ w.exoticType(n.Type())
+ w.uint64(uint64(n.Use))
+ }
case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
n := n.(*ir.MakeExpr)
@@ -1540,6 +1747,12 @@ func (w *exportWriter) expr(n ir.Node) {
w.expr(n.Cap)
w.op(ir.OEND)
case n.Len != nil && (n.Op() == ir.OMAKESLICE || !n.Len.Type().IsUntyped()):
+ // Note: the extra conditional exists because make(T) for
+ // T a map or chan type, gets an untyped zero added as
+ // an argument. Don't serialize that argument here.
+ w.expr(n.Len)
+ w.op(ir.OEND)
+ case n.Len != nil && go117ExportTypes:
w.expr(n.Len)
w.op(ir.OEND)
}
@@ -1550,18 +1763,27 @@ func (w *exportWriter) expr(n ir.Node) {
w.op(n.Op())
w.pos(n.Pos())
w.expr(n.X)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.OADDR:
n := n.(*ir.AddrExpr)
w.op(n.Op())
w.pos(n.Pos())
w.expr(n.X)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.ODEREF:
n := n.(*ir.StarExpr)
w.op(n.Op())
w.pos(n.Pos())
w.expr(n.X)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.OSEND:
n := n.(*ir.SendStmt)
@@ -1578,6 +1800,9 @@ func (w *exportWriter) expr(n ir.Node) {
w.pos(n.Pos())
w.expr(n.X)
w.expr(n.Y)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.OANDAND, ir.OOROR:
n := n.(*ir.LogicalExpr)
@@ -1585,12 +1810,18 @@ func (w *exportWriter) expr(n ir.Node) {
w.pos(n.Pos())
w.expr(n.X)
w.expr(n.Y)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.OADDSTR:
n := n.(*ir.AddStringExpr)
w.op(ir.OADDSTR)
w.pos(n.Pos())
w.exprList(n.List)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
case ir.ODCLCONST:
// if exporting, DCLCONST should just be removed as its usage
@@ -1633,6 +1864,9 @@ func (w *exportWriter) fieldList(list ir.Nodes) {
w.pos(n.Pos())
w.selector(n.Field)
w.expr(n.Value)
+ if go117ExportTypes {
+ w.uint64(uint64(n.Offset))
+ }
}
}
@@ -1693,3 +1927,13 @@ func (w *intWriter) uint64(x uint64) {
n := binary.PutUvarint(buf[:], x)
w.Write(buf[:n])
}
+
+// If go117ExportTypes is true, then we write type information when
+// exporting function bodies, so those function bodies don't need to
+// be re-typechecked on import.
+// This flag adds some other info to the serialized stream as well
+// which was previously recomputed during typechecking, like
+// specializing opcodes (e.g. OXDOT to ODOTPTR) and ancillary
+// information (e.g. length field for OSLICELIT).
+const go117ExportTypes = true
+const Go117ExportTypes = go117ExportTypes