From 6c33f1d52efd4f70fb8cdb1c3b4ea1c8c579af2a Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 3 Jun 2022 12:38:39 -0700 Subject: [dev.unified] cmd/compile/internal/noder: rename exprName to exprGlobal More descriptive. Change-Id: I70a07adbe1d395da797fe15b54d2a1106f5f36a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/410098 Reviewed-by: Cuong Manh Le TryBot-Result: Gopher Robot Reviewed-by: David Chase Run-TryBot: Matthew Dempsky --- src/cmd/compile/internal/noder/codes.go | 6 +++--- src/cmd/compile/internal/noder/reader.go | 2 +- src/cmd/compile/internal/noder/writer.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go index 8f54a07ca4..28991e7b9c 100644 --- a/src/cmd/compile/internal/noder/codes.go +++ b/src/cmd/compile/internal/noder/codes.go @@ -40,9 +40,9 @@ func (c codeExpr) Value() int { return int(c) } const ( exprNone codeExpr = iota exprConst - exprType // type expression - exprLocal // local variable - exprName // global variable or function + exprType // type expression + exprLocal // local variable + exprGlobal // global variable or function exprBlank exprCompLit exprFuncLit diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 296cdd7d54..a231fe5d50 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1562,7 +1562,7 @@ func (r *reader) expr() (res ir.Node) { 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()) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index b440ad3a1e..3bf6717117 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1236,7 +1236,7 @@ func (w *writer) expr(expr syntax.Expr) { if obj != nil { if isGlobal(obj) { - w.Code(exprName) + w.Code(exprGlobal) w.obj(obj, targs) return } -- cgit v1.3-6-g1900 From 55fc07e16416bd3677c81bb6379ac8f9e881e5cf Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 3 Jun 2022 12:49:11 -0700 Subject: [dev.unified] cmd/compile/internal/noder: add optExpr for optional expressions Previously, {writer,reader}.expr would allow for nil expressions (i.e., no expression at all, not a "nil" identifier). But only a few contexts allow this, and it simplifies some logic if we can assume the expression is non-nil. So this CL introduces optExpr as a wrapper method for handling nil expressions specially. Change-Id: I438bae7a3191126f7790ec0bf5b77320fe855514 Reviewed-on: https://go-review.googlesource.com/c/go/+/410099 TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/noder/codes.go | 9 ++++----- src/cmd/compile/internal/noder/reader.go | 16 ++++++++++------ src/cmd/compile/internal/noder/writer.go | 17 +++++++++++------ 3 files changed, 25 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go index 28991e7b9c..7fe6e39c15 100644 --- a/src/cmd/compile/internal/noder/codes.go +++ b/src/cmd/compile/internal/noder/codes.go @@ -38,11 +38,10 @@ func (c codeExpr) Value() int { return int(c) } // TODO(mdempsky): Split expr into addr, for lvalues. const ( - exprNone codeExpr = iota - exprConst - exprType // type expression - exprLocal // local variable - exprGlobal // global variable or function + exprConst codeExpr = iota + exprType // type expression + exprLocal // local variable + exprGlobal // global variable or function exprBlank exprCompLit exprFuncLit diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index a231fe5d50..d4ab6a975f 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1380,7 +1380,7 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { pos := r.pos() init := r.stmt() - cond := r.expr() + cond := r.optExpr() post := r.stmt() body := r.blockStmt() r.closeAnotherScope() @@ -1450,7 +1450,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()) @@ -1551,9 +1551,6 @@ 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? @@ -1622,7 +1619,7 @@ func (r *reader) expr() (res ir.Node) { 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 { @@ -1701,6 +1698,13 @@ func (r *reader) expr() (res ir.Node) { } } +func (r *reader) optExpr() ir.Node { + if r.Bool() { + return r.expr() + } + return nil +} + func (r *reader) compLit() ir.Node { r.Sync(pkgbits.SyncCompLit) pos := r.pos() diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 3bf6717117..ac1ec97285 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1077,7 +1077,7 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { } else { w.pos(stmt) w.stmt(stmt.Init) - w.expr(stmt.Cond) + w.optExpr(stmt.Cond) w.stmt(stmt.Post) } @@ -1136,7 +1136,7 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { } w.expr(guard.X) } else { - w.expr(stmt.Tag) + w.optExpr(stmt.Tag) } w.Len(len(stmt.Body)) @@ -1201,6 +1201,8 @@ func (w *writer) optLabel(label *syntax.Name) { // @@@ Expressions func (w *writer) expr(expr syntax.Expr) { + base.Assertf(expr != nil, "missing expression") + expr = unparen(expr) // skip parens; unneeded after typecheck obj, inst := lookupObj(w.p.info, expr) @@ -1254,9 +1256,6 @@ func (w *writer) expr(expr syntax.Expr) { default: w.p.unexpected("expression", expr) - case nil: // absent slice index, for condition, or switch tag - w.Code(exprNone) - case *syntax.Name: assert(expr.Value == "_") w.Code(exprBlank) @@ -1292,7 +1291,7 @@ func (w *writer) expr(expr syntax.Expr) { w.expr(expr.X) w.pos(expr) for _, n := range &expr.Index { - w.expr(n) + w.optExpr(n) } case *syntax.AssertExpr: @@ -1356,6 +1355,12 @@ func (w *writer) expr(expr syntax.Expr) { } } +func (w *writer) optExpr(expr syntax.Expr) { + if w.Bool(expr != nil) { + w.expr(expr) + } +} + func (w *writer) compLit(lit *syntax.CompositeLit) { tv, ok := w.p.info.Types[lit] assert(ok) -- cgit v1.3-6-g1900 From b39ac808714add90df425298c1f1bdc9d47cfd45 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 3 Jun 2022 13:21:09 -0700 Subject: [dev.unified] cmd/compile/internal/noder: push exprBlank up into assignment handling Blanks can only appear on the LHS of an assignment. Instead of handling them as an arbitrary expression, handle them as part of assignee expression lists. Change-Id: Iaeb0a5c471ffa1abd2bbbd9c95f7876533e5a607 Reviewed-on: https://go-review.googlesource.com/c/go/+/410100 Reviewed-by: Cherry Mui Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/noder/codes.go | 12 +++++- src/cmd/compile/internal/noder/reader.go | 47 +++++++++++++++--------- src/cmd/compile/internal/noder/writer.go | 47 ++++++++++++++---------- src/internal/pkgbits/sync.go | 1 + src/internal/pkgbits/syncmarker_string.go | 61 ++++++++++++++++--------------- 5 files changed, 99 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go index 7fe6e39c15..581eb8344f 100644 --- a/src/cmd/compile/internal/noder/codes.go +++ b/src/cmd/compile/internal/noder/codes.go @@ -42,7 +42,6 @@ const ( exprType // type expression exprLocal // local variable exprGlobal // global variable or function - exprBlank exprCompLit exprFuncLit exprSelector @@ -55,6 +54,17 @@ const ( exprConvert ) +type codeAssign int + +func (c codeAssign) Marker() pkgbits.SyncMarker { return pkgbits.SyncAssign } +func (c codeAssign) Value() int { return int(c) } + +const ( + assignBlank codeAssign = iota + assignDef + assignExpr +) + type codeDecl int func (c codeDecl) Marker() pkgbits.SyncMarker { return pkgbits.SyncDecl } diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index d4ab6a975f..6614d1693f 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1323,25 +1323,41 @@ 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() + _, 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() @@ -1551,11 +1567,6 @@ func (r *reader) expr() (res ir.Node) { default: panic("unhandled expression") - 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()) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index ac1ec97285..2d1a7ee457 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1023,25 +1023,36 @@ func (w *writer) assignList(expr syntax.Expr) { w.Len(len(exprs)) for _, expr := range exprs { - if name, ok := expr.(*syntax.Name); ok && name.Value != "_" { - if obj, ok := w.p.info.Defs[name]; ok { - obj := obj.(*types2.Var) - - w.Bool(true) - w.pos(obj) - w.localIdent(obj) - w.typ(obj.Type()) - - // TODO(mdempsky): Minimize locals index size by deferring - // this until the variables actually come into scope. - w.addLocal(obj) - continue - } + w.assign(expr) + } +} + +func (w *writer) assign(expr syntax.Expr) { + expr = unparen(expr) + + if name, ok := expr.(*syntax.Name); ok { + if name.Value == "_" { + w.Code(assignBlank) + return } - w.Bool(false) - w.expr(expr) + if obj, ok := w.p.info.Defs[name]; ok { + obj := obj.(*types2.Var) + + w.Code(assignDef) + w.pos(obj) + w.localIdent(obj) + w.typ(obj.Type()) + + // TODO(mdempsky): Minimize locals index size by deferring + // this until the variables actually come into scope. + w.addLocal(obj) + return + } } + + w.Code(assignExpr) + w.expr(expr) } func (w *writer) declStmt(decl syntax.Decl) { @@ -1256,10 +1267,6 @@ func (w *writer) expr(expr syntax.Expr) { default: w.p.unexpected("expression", expr) - case *syntax.Name: - assert(expr.Value == "_") - w.Code(exprBlank) - case *syntax.CompositeLit: w.Code(exprCompLit) w.compLit(expr) diff --git a/src/internal/pkgbits/sync.go b/src/internal/pkgbits/sync.go index 4b9ea4863f..77178af6ce 100644 --- a/src/internal/pkgbits/sync.go +++ b/src/internal/pkgbits/sync.go @@ -90,6 +90,7 @@ const ( SyncExprs SyncExpr SyncExprType + SyncAssign SyncOp SyncFuncLit SyncCompLit diff --git a/src/internal/pkgbits/syncmarker_string.go b/src/internal/pkgbits/syncmarker_string.go index 39db9eddad..4a5b0ca5f2 100644 --- a/src/internal/pkgbits/syncmarker_string.go +++ b/src/internal/pkgbits/syncmarker_string.go @@ -45,39 +45,40 @@ func _() { _ = x[SyncExprs-35] _ = x[SyncExpr-36] _ = x[SyncExprType-37] - _ = x[SyncOp-38] - _ = x[SyncFuncLit-39] - _ = x[SyncCompLit-40] - _ = x[SyncDecl-41] - _ = x[SyncFuncBody-42] - _ = x[SyncOpenScope-43] - _ = x[SyncCloseScope-44] - _ = x[SyncCloseAnotherScope-45] - _ = x[SyncDeclNames-46] - _ = x[SyncDeclName-47] - _ = x[SyncStmts-48] - _ = x[SyncBlockStmt-49] - _ = x[SyncIfStmt-50] - _ = x[SyncForStmt-51] - _ = x[SyncSwitchStmt-52] - _ = x[SyncRangeStmt-53] - _ = x[SyncCaseClause-54] - _ = x[SyncCommClause-55] - _ = x[SyncSelectStmt-56] - _ = x[SyncDecls-57] - _ = x[SyncLabeledStmt-58] - _ = x[SyncUseObjLocal-59] - _ = x[SyncAddLocal-60] - _ = x[SyncLinkname-61] - _ = x[SyncStmt1-62] - _ = x[SyncStmtsEnd-63] - _ = x[SyncLabel-64] - _ = x[SyncOptLabel-65] + _ = x[SyncAssign-38] + _ = x[SyncOp-39] + _ = x[SyncFuncLit-40] + _ = x[SyncCompLit-41] + _ = x[SyncDecl-42] + _ = x[SyncFuncBody-43] + _ = x[SyncOpenScope-44] + _ = x[SyncCloseScope-45] + _ = x[SyncCloseAnotherScope-46] + _ = x[SyncDeclNames-47] + _ = x[SyncDeclName-48] + _ = x[SyncStmts-49] + _ = x[SyncBlockStmt-50] + _ = x[SyncIfStmt-51] + _ = x[SyncForStmt-52] + _ = x[SyncSwitchStmt-53] + _ = x[SyncRangeStmt-54] + _ = x[SyncCaseClause-55] + _ = x[SyncCommClause-56] + _ = x[SyncSelectStmt-57] + _ = x[SyncDecls-58] + _ = x[SyncLabeledStmt-59] + _ = x[SyncUseObjLocal-60] + _ = x[SyncAddLocal-61] + _ = x[SyncLinkname-62] + _ = x[SyncStmt1-63] + _ = x[SyncStmtsEnd-64] + _ = x[SyncLabel-65] + _ = x[SyncOptLabel-66] } -const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprAssertTypeOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel" +const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel" -var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 228, 230, 237, 244, 248, 256, 265, 275, 292, 301, 309, 314, 323, 329, 336, 346, 355, 365, 375, 385, 390, 401, 412, 420, 428, 433, 441, 446, 454} +var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458} func (i SyncMarker) String() string { i -= 1 -- cgit v1.3-6-g1900 From df7cb59de427dcfee9af0713ecf8b7033948303b Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 7 Jun 2022 00:07:26 +0700 Subject: [dev.unified] cmd/compile: only sort symbols by name and package path Since CL 393715, the path of package being compiled is now always known, so symbols can be sorted by package path instead of package height. Updates #51734 Change-Id: Ie543e2fdef4b93f3f0b97c6bcec0a4dcff788f2c Reviewed-on: https://go-review.googlesource.com/c/go/+/410654 TryBot-Result: Gopher Robot Reviewed-by: Matthew Dempsky Auto-Submit: Cuong Manh Le Run-TryBot: Cuong Manh Le Reviewed-by: Cherry Mui --- src/cmd/compile/internal/types/sym.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/types/sym.go b/src/cmd/compile/internal/types/sym.go index 927ebc453a..9d8707befa 100644 --- a/src/cmd/compile/internal/types/sym.go +++ b/src/cmd/compile/internal/types/sym.go @@ -97,14 +97,7 @@ func (sym *Sym) LinksymABI(abi obj.ABI) *obj.LSym { // Less reports whether symbol a is ordered before symbol b. // // Symbols are ordered exported before non-exported, then by name, and -// finally (for non-exported symbols) by package height and path. -// -// Ordering by package height is necessary to establish a consistent -// ordering for non-exported names with the same spelling but from -// different packages. We don't necessarily know the path for the -// package being compiled, but by definition it will have a height -// greater than any other packages seen within the compilation unit. -// For more background, see issue #24693. +// finally (for non-exported symbols) by package path. func (a *Sym) Less(b *Sym) bool { if a == b { return false @@ -131,9 +124,6 @@ func (a *Sym) Less(b *Sym) bool { return a.Name < b.Name } if !ea { - if a.Pkg.Height != b.Pkg.Height { - return a.Pkg.Height < b.Pkg.Height - } return a.Pkg.Path < b.Pkg.Path } return false -- cgit v1.3-6-g1900 From 3a1f1e15757e4c2fd310e3659eefff577d87717b Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 17 May 2022 16:11:36 +0700 Subject: [dev.unified] cmd/compile: remove package height After CL 410654, symbols are now sorted by package path, package height is not necessary anymore. Updates #51734 Change-Id: I976edd2e574dda68eb5c76cf95645b9dce051393 Reviewed-on: https://go-review.googlesource.com/c/go/+/410342 Run-TryBot: Cuong Manh Le TryBot-Result: Gopher Robot Reviewed-by: Matthew Dempsky Reviewed-by: Cherry Mui --- src/cmd/compile/internal/gc/main.go | 5 ----- src/cmd/compile/internal/noder/irgen.go | 1 - src/cmd/compile/internal/noder/reader.go | 8 +------- src/cmd/compile/internal/noder/unified.go | 1 - src/cmd/compile/internal/noder/writer.go | 2 +- src/cmd/compile/internal/typecheck/iexport.go | 2 +- src/cmd/compile/internal/typecheck/iimport.go | 6 +----- src/cmd/compile/internal/types/pkg.go | 9 --------- 8 files changed, 4 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 70f1a2f847..a5a2d56c46 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -75,11 +75,6 @@ func Main(archInit func(*ssagen.ArchInfo)) { types.LocalPkg = types.NewPkg(base.Ctxt.Pkgpath, "") - // We won't know localpkg's height until after import - // processing. In the mean time, set to MaxPkgHeight to ensure - // height comparisons at least work until then. - types.LocalPkg.Height = types.MaxPkgHeight - // pseudo-package, for scoping types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin? types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index 628c0f54fc..74e7401024 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -219,7 +219,6 @@ type typeDelayInfo struct { func (g *irgen) generate(noders []*noder) { types.LocalPkg.Name = g.self.Name() - types.LocalPkg.Height = g.self.Height() typecheck.TypecheckAllowed = true // Prevent size calculations until we set the underlying type diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 6614d1693f..0440d324cc 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -288,7 +288,7 @@ func (r *reader) doPkg() *types.Pkg { } name := r.String() - height := r.Len() + _ = r.Len() // was package height, but not necessary anymore. pkg := types.NewPkg(path, "") @@ -298,12 +298,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 } diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index 46acdab79e..f7cf7f90b2 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -82,7 +82,6 @@ func unified(noders []*noder) { base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version) types.ParseLangFlag() - types.LocalPkg.Height = 0 // reset so pkgReader.pkgIdx doesn't complain target := typecheck.Target typecheck.TypecheckAllowed = true diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 2d1a7ee457..2fb1583437 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -241,7 +241,7 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path) w.String(path) w.String(pkg.Name()) - w.Len(pkg.Height()) + w.Len(0) // was package height, but not necessary anymore. w.Len(len(pkg.Imports())) for _, imp := range pkg.Imports() { diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index d5c4b8e1e8..fa0e292ed2 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -405,7 +405,7 @@ func (w *exportWriter) writeIndex(index map[*types.Sym]uint64, mainIndex bool) { w.string(exportPath(pkg)) if mainIndex { w.string(pkg.Name) - w.uint64(uint64(pkg.Height)) + w.uint64(0) // was package height, but not necessary anymore. } // Sort symbols within a package by name. diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index 3a51f781f0..1968af7f1c 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -175,10 +175,9 @@ func ReadImports(pkg *types.Pkg, data string) { for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- { pkg := p.pkgAt(ird.uint64()) pkgName := p.stringAt(ird.uint64()) - pkgHeight := int(ird.uint64()) + _ = int(ird.uint64()) // was package height, but not necessary anymore. if pkg.Name == "" { pkg.Name = pkgName - pkg.Height = pkgHeight types.NumImport[pkgName]++ // TODO(mdempsky): This belongs somewhere else. @@ -187,9 +186,6 @@ func ReadImports(pkg *types.Pkg, data string) { if pkg.Name != pkgName { base.Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path) } - if pkg.Height != pkgHeight { - base.Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path) - } } for nSyms := ird.uint64(); nSyms > 0; nSyms-- { diff --git a/src/cmd/compile/internal/types/pkg.go b/src/cmd/compile/internal/types/pkg.go index 4bf39a5e9d..9a21494017 100644 --- a/src/cmd/compile/internal/types/pkg.go +++ b/src/cmd/compile/internal/types/pkg.go @@ -16,9 +16,6 @@ import ( // pkgMap maps a package path to a package. var pkgMap = make(map[string]*Pkg) -// MaxPkgHeight is a height greater than any likely package height. -const MaxPkgHeight = 1e9 - type Pkg struct { Path string // string literal used in import statement, e.g. "runtime/internal/sys" Name string // package name, e.g. "sys" @@ -26,12 +23,6 @@ type Pkg struct { Syms map[string]*Sym Pathsym *obj.LSym - // Height is the package's height in the import graph. Leaf - // packages (i.e., packages with no imports) have height 0, - // and all other packages have height 1 plus the maximum - // height of their imported packages. - Height int - Direct bool // imported directly } -- cgit v1.3-6-g1900 From a8780f94c3eb19dda8aaa15ad83468b2d54a0e5a Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 24 May 2022 00:41:13 +0700 Subject: [dev.unified] cmd/compile: fix missing method value wrapper in unified IR Unified IR uses to generate wrappers after the global inlining pass, so it needs to apply inlining for the wrappers itself. However, inlining may reveal new method value nodes which have not been seen yet, thus unified IR never generates wrappers for them. To fix it, just visiting the wrapper function body once more time after inlining, and generate wrappers for any new method value nodes. Fixes #52128 Change-Id: I78631c4faa0b00357d4f84704d3525fd38a52cd7 Reviewed-on: https://go-review.googlesource.com/c/go/+/410344 Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui --- src/cmd/compile/internal/noder/reader.go | 9 +++++++++ test/fixedbugs/issue52128.dir/a.go | 21 +++++++++++++++++++++ test/fixedbugs/issue52128.dir/b.go | 17 +++++++++++++++++ test/fixedbugs/issue52128.dir/p.go | 14 ++++++++++++++ test/fixedbugs/issue52128.go | 7 +++++++ 5 files changed, 68 insertions(+) create mode 100644 test/fixedbugs/issue52128.dir/a.go create mode 100644 test/fixedbugs/issue52128.dir/b.go create mode 100644 test/fixedbugs/issue52128.dir/p.go create mode 100644 test/fixedbugs/issue52128.go (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 0440d324cc..635f02630f 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -2468,6 +2468,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) } diff --git a/test/fixedbugs/issue52128.dir/a.go b/test/fixedbugs/issue52128.dir/a.go new file mode 100644 index 0000000000..0abf831c6f --- /dev/null +++ b/test/fixedbugs/issue52128.dir/a.go @@ -0,0 +1,21 @@ +// Copyright 2022 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. + +package a + +type I interface{} + +type F func() + +type s struct { + f F +} + +func NewWithF(f F) *s { + return &s{f: f} +} + +func NewWithFuncI(func() I) *s { + return &s{} +} diff --git a/test/fixedbugs/issue52128.dir/b.go b/test/fixedbugs/issue52128.dir/b.go new file mode 100644 index 0000000000..86f6ed7e05 --- /dev/null +++ b/test/fixedbugs/issue52128.dir/b.go @@ -0,0 +1,17 @@ +// Copyright 2022 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. + +package b + +import ( + "./a" +) + +type S struct{} + +func (s *S) M1() a.I { + return a.NewWithF(s.M2) +} + +func (s *S) M2() {} diff --git a/test/fixedbugs/issue52128.dir/p.go b/test/fixedbugs/issue52128.dir/p.go new file mode 100644 index 0000000000..d3f3dbbfb9 --- /dev/null +++ b/test/fixedbugs/issue52128.dir/p.go @@ -0,0 +1,14 @@ +// Copyright 2022 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. + +package p + +import ( + "./a" + "./b" +) + +func f() { + a.NewWithFuncI((&b.S{}).M1) +} diff --git a/test/fixedbugs/issue52128.go b/test/fixedbugs/issue52128.go new file mode 100644 index 0000000000..8bb5c3e213 --- /dev/null +++ b/test/fixedbugs/issue52128.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2022 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. + +package ignored -- cgit v1.3-6-g1900 From 46ddf0873e48de0062fbc67d058ddb13147cb9fe Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 6 Jun 2022 22:23:48 +0700 Subject: [dev.unified] cmd/compile: export/import implicit attribute for conversion exprs So they can be formatted more presicely, and make it easier in the transition to Unified IR. Updates #53058 Change-Id: I8b5a46db05a2e2822289458995b8653f0a3ffbbe Reviewed-on: https://go-review.googlesource.com/c/go/+/410594 TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui Reviewed-by: Matthew Dempsky Run-TryBot: Cuong Manh Le --- src/cmd/compile/internal/typecheck/iexport.go | 1 + src/cmd/compile/internal/typecheck/iimport.go | 4 +++- test/fixedbugs/issue42284.dir/b.go | 2 +- test/run.go | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index fa0e292ed2..43ec7b80a0 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -1978,6 +1978,7 @@ func (w *exportWriter) expr(n ir.Node) { w.pos(n.Pos()) w.typ(n.Type()) w.expr(n.X) + w.bool(n.Implicit()) case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC: n := n.(*ir.UnaryExpr) diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index 1968af7f1c..96aaac6362 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -1489,7 +1489,9 @@ func (r *importReader) node() ir.Node { return n case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR: - return ir.NewConvExpr(r.pos(), op, r.typ(), r.expr()) + n := ir.NewConvExpr(r.pos(), op, r.typ(), r.expr()) + n.SetImplicit(r.bool()) + return n case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN, ir.OUNSAFEADD, ir.OUNSAFESLICE: pos := r.pos() diff --git a/test/fixedbugs/issue42284.dir/b.go b/test/fixedbugs/issue42284.dir/b.go index 652aa32122..8cd93b8db4 100644 --- a/test/fixedbugs/issue42284.dir/b.go +++ b/test/fixedbugs/issue42284.dir/b.go @@ -7,7 +7,7 @@ package b import "./a" func g() { - h := a.E() // ERROR "inlining call to a.E" "a.I\(a.T\(0\)\) does not escape" + h := a.E() // ERROR "inlining call to a.E" "T\(0\) does not escape" h.M() // ERROR "devirtualizing h.M to a.T" // BAD: T(0) could be stack allocated. diff --git a/test/run.go b/test/run.go index cb1622ccc9..8ef11020da 100644 --- a/test/run.go +++ b/test/run.go @@ -1998,7 +1998,6 @@ var unifiedFailures = setOf( "inline.go", // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures "linkname3.go", // unified IR is missing some linkname errors - "fixedbugs/issue42284.go", // prints "T(0) does not escape", but test expects "a.I(a.T(0)) does not escape" "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap" "typeparam/issue47631.go", // unified IR can handle local type declarations "fixedbugs/issue42058a.go", // unified IR doesn't report channel element too large -- cgit v1.3-6-g1900 From 9e5c96802164c17df6667047932142eb6894e6d3 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sun, 22 May 2022 02:14:46 +0700 Subject: [dev.unified] cmd/compile: visit LHS before RHS/X in assign/for statement Unified IR used to visit RHS/X before LHS in assign/for statements for satisfying toolstash in quirksmode. After CL 385998, unified IR quirks mode was gone, the constraint to visit RHS/X first is no longer necessary. Change-Id: I1c3825168b67fb094928f5aa21748a3c81b118ce Reviewed-on: https://go-review.googlesource.com/c/go/+/410343 Run-TryBot: Cuong Manh Le Reviewed-by: Cherry Mui Reviewed-by: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/reader.go | 8 ++------ src/cmd/compile/internal/noder/writer.go | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 635f02630f..7c35172f12 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1224,10 +1224,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.exprList() if len(rhs) == 0 { for _, name := range names { @@ -1368,10 +1366,8 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { if r.Bool() { pos := r.pos() - // TODO(mdempsky): After quirks mode is gone, swap these - // statements so we read LHS before X again. - x := r.expr() names, lhs := r.assignList() + x := r.expr() body := r.blockStmt() r.closeAnotherScope() diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 2fb1583437..c3955c2cb6 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -954,8 +954,8 @@ func (w *writer) stmt1(stmt syntax.Stmt) { default: w.Code(stmtAssign) w.pos(stmt) - w.exprList(stmt.Rhs) w.assignList(stmt.Lhs) + w.exprList(stmt.Rhs) } case *syntax.BlockStmt: @@ -1065,8 +1065,8 @@ func (w *writer) declStmt(decl syntax.Decl) { case *syntax.VarDecl: w.Code(stmtAssign) w.pos(decl) - w.exprList(decl.Values) w.assignList(namesAsExpr(decl.NameList)) + w.exprList(decl.Values) } } @@ -1083,8 +1083,8 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) { w.pos(rang) - w.expr(rang.X) w.assignList(rang.Lhs) + w.expr(rang.X) } else { w.pos(stmt) w.stmt(stmt.Init) -- cgit v1.3-6-g1900 From e7ef58542c7f12842cdefdb1cd5e1b794996dc96 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 27 May 2022 19:06:30 +0700 Subject: [dev.unified] cmd/compile: restore Unified IR linkname pragma diagnostic CL 333109 restore the diagnostic for irgen, now it's safe to restore for Unified IR, too. Updates #53058 Change-Id: I467902c0e9fa451aaa78cf0813231f14d9d7a3a0 Reviewed-on: https://go-review.googlesource.com/c/go/+/410346 Reviewed-by: Cherry Mui TryBot-Result: Gopher Robot Run-TryBot: Cuong Manh Le Reviewed-by: Keith Randall Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/writer.go | 5 +---- test/run.go | 7 +++---- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index c3955c2cb6..2b22046de1 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1661,10 +1661,7 @@ func (pw *pkgWriter) collectDecls(noders []*noder) { } default: - // TODO(mdempsky): Enable after #42938 is fixed. - if false { - pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") - } + pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") } } } diff --git a/test/run.go b/test/run.go index 8ef11020da..a4ec19c73c 100644 --- a/test/run.go +++ b/test/run.go @@ -1993,10 +1993,9 @@ var _ = setOf( ) var unifiedFailures = setOf( - "closure3.go", // unified IR numbers closures differently than -d=inlfuncswithclosures - "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this - "inline.go", // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures - "linkname3.go", // unified IR is missing some linkname errors + "closure3.go", // unified IR numbers closures differently than -d=inlfuncswithclosures + "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this + "inline.go", // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap" "typeparam/issue47631.go", // unified IR can handle local type declarations -- cgit v1.3-6-g1900 From d6df08693cd1639a7d9f0df292b4aa469b1cd748 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 25 May 2022 23:27:45 +0700 Subject: [dev.unified] cmd/compile: fix unified IR don't report type size too large error For error reported during type size calculation, base.Pos needs to be set, otherwise, the compiler will treat them as the same error and only report once. Old typechecker and irgen all set base.Pos before processing types, this CL do the same thing for unified IR. Updates #53058 Change-Id: I686984ffe4aca3e8b14d2103018c8d3c7d71fb02 Reviewed-on: https://go-review.googlesource.com/c/go/+/410345 TryBot-Result: Gopher Robot Run-TryBot: Cuong Manh Le Reviewed-by: Cherry Mui Reviewed-by: Keith Randall Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/reader.go | 7 +++++++ test/run.go | 9 +++------ 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 7c35172f12..d050275f9e 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -599,6 +599,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() } @@ -1812,6 +1813,7 @@ func (r *reader) exprType(nilOK bool) ir.Node { } 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]))) @@ -2530,3 +2532,8 @@ 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 +} diff --git a/test/run.go b/test/run.go index a4ec19c73c..8763660ea8 100644 --- a/test/run.go +++ b/test/run.go @@ -1997,12 +1997,9 @@ var unifiedFailures = setOf( "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this "inline.go", // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures - "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap" - "typeparam/issue47631.go", // unified IR can handle local type declarations - "fixedbugs/issue42058a.go", // unified IR doesn't report channel element too large - "fixedbugs/issue42058b.go", // unified IR doesn't report channel element too large - "fixedbugs/issue49767.go", // unified IR doesn't report channel element too large - "fixedbugs/issue49814.go", // unified IR doesn't report array type too large + "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap" + "typeparam/issue47631.go", // unified IR can handle local type declarations + "fixedbugs/issue49767.go", // unified IR reports channel element too large on different line than irgen ) func setOf(keys ...string) map[string]bool { -- cgit v1.3-6-g1900 From c50c6bbc030ea170320f438c3f328fa475e87e2b Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 7 Jun 2022 09:22:32 +0700 Subject: [dev.unified] cmd/compile: set base.Pos when process assignDef in Unified IR CL 410343 changes Unified IR to visit LHS before RHS/X in assign/for statement. Thus, it needs to set base.Pos before processing assignee expression, so invalid type can be reported with correct position. Updates #53058 Change-Id: Ic9f60cbf35c8bd71cb391e806396572c37811af7 Reviewed-on: https://go-review.googlesource.com/c/go/+/410794 Reviewed-by: Cherry Mui Run-TryBot: Cuong Manh Le Reviewed-by: Keith Randall Reviewed-by: Keith Randall TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/reader.go | 1 + test/run.go | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index d050275f9e..3cd6ec5668 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1338,6 +1338,7 @@ func (r *reader) assign() (ir.Node, bool) { case assignDef: pos := r.pos() + setBasePos(pos) _, sym := r.localIdent() typ := r.typ() diff --git a/test/run.go b/test/run.go index 8763660ea8..866654629e 100644 --- a/test/run.go +++ b/test/run.go @@ -1999,7 +1999,6 @@ var unifiedFailures = setOf( "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap" "typeparam/issue47631.go", // unified IR can handle local type declarations - "fixedbugs/issue49767.go", // unified IR reports channel element too large on different line than irgen ) func setOf(keys ...string) map[string]bool { -- cgit v1.3-6-g1900 From 1a6c96bb9b0e1674048758d4c92e33fb03c4833e Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 7 Jun 2022 14:57:11 +0700 Subject: [dev.unified] test: relax issue7921.go diagnostic message For constants literal, iimport/iexport read/write them as basic literal nodes. So they are printed in diagnostic message as Go syntax. So "foo" will be reported as string("foo"). Unified IR read/write the raw expression as string value, and when printed in diagnostic, the string value is written out exactly as-is, so "foo" will be written as "foo". Thus, this CL relax the test in issue7921.go to match the string value only. Updates #53058 Change-Id: I6fcf4fdcfc4b3be91cb53b081c48bd57186d8f35 Reviewed-on: https://go-review.googlesource.com/c/go/+/410795 Reviewed-by: Cherry Mui Run-TryBot: Cuong Manh Le Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/helpers.go | 4 ---- test/fixedbugs/issue7921.go | 2 +- test/run.go | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go index 33acd6051a..8efcef26cf 100644 --- a/src/cmd/compile/internal/noder/helpers.go +++ b/src/cmd/compile/internal/noder/helpers.go @@ -39,10 +39,6 @@ func typed(typ *types.Type, n ir.Node) ir.Node { // Values -func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node { - return typed(typ, ir.NewBasicLit(pos, val)) -} - func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node { orig := ir.NewRawOrigExpr(pos, op, raw) return ir.NewConstExpr(val, typed(typ, orig)) diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go index 65be4b5bbe..f9efb7f55d 100644 --- a/test/fixedbugs/issue7921.go +++ b/test/fixedbugs/issue7921.go @@ -41,7 +41,7 @@ func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$" func bufferNoEscape4() []byte { var b bytes.Buffer - b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m\]$" "inlining call to bytes.\(\*Buffer\).Grow$" "string\(.*\) escapes to heap" + b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m\]$" "inlining call to bytes.\(\*Buffer\).Grow$" `".+" escapes to heap` useBuffer(&b) return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$" } diff --git a/test/run.go b/test/run.go index 866654629e..ff3cc1aabb 100644 --- a/test/run.go +++ b/test/run.go @@ -1997,7 +1997,6 @@ var unifiedFailures = setOf( "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this "inline.go", // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures - "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap" "typeparam/issue47631.go", // unified IR can handle local type declarations ) -- cgit v1.3-6-g1900 From 8ef8b60e1816e0624fc894df90c853772d5059bb Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 3 Jun 2022 14:30:04 -0700 Subject: [dev.unified] cmd/compile/internal/noder: stop handling type expressions as expressions There are two places currently where we rely on type expressions as generic expressions: the first argument to "make" and "new", and the selectable operand within a method expression. This CL makes that code responsible for handling the type expressions directly. Longer term, this will be relevant to appropriately handling derived types, because it will provide additional context about how the derived type is to be used. Change-Id: I9d7dcf9d32dada032ff411cd103b9df413c298a5 Reviewed-on: https://go-review.googlesource.com/c/go/+/410101 Reviewed-by: Cherry Mui Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/codes.go | 3 ++- src/cmd/compile/internal/noder/reader.go | 37 ++++++++++++++++++++---------- src/cmd/compile/internal/noder/writer.go | 39 +++++++++++++++++++++++++------- 3 files changed, 58 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go index 581eb8344f..59c8ec8121 100644 --- a/src/cmd/compile/internal/noder/codes.go +++ b/src/cmd/compile/internal/noder/codes.go @@ -39,7 +39,6 @@ func (c codeExpr) Value() int { return int(c) } // TODO(mdempsky): Split expr into addr, for lvalues. const ( exprConst codeExpr = iota - exprType // type expression exprLocal // local variable exprGlobal // global variable or function exprCompLit @@ -52,6 +51,8 @@ const ( exprBinaryOp exprCall exprConvert + exprNew + exprMake ) type codeAssign int diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 3cd6ec5668..e8401c5775 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1567,9 +1567,6 @@ func (r *reader) expr() (res ir.Node) { // 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() @@ -1585,18 +1582,23 @@ func (r *reader) expr() (res ir.Node) { return r.funcLit() case exprSelector: - x := r.expr() + var x ir.Node + if r.Bool() { // MethodExpr + x = r.exprType(false) + + // Method expression with derived receiver type. + if x.Op() == ir.ODYNAMICTYPE { + // TODO(mdempsky): Handle with runtime dictionary lookup. + n := ir.TypeNode(x.Type()) + n.SetTypecheck(1) + x = n + } + } else { // FieldVal, MethodVal + x = r.expr() + } pos := r.pos() _, sym := r.selector() - // Method expression with derived receiver type. - if x.Op() == ir.ODYNAMICTYPE { - // TODO(mdempsky): Handle with runtime dictionary lookup. - n := ir.TypeNode(x.Type()) - n.SetTypecheck(1) - x = n - } - n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr) if n.Op() == ir.OMETHVALUE { wrapper := methodValueWrapper{ @@ -1679,6 +1681,17 @@ func (r *reader) expr() (res ir.Node) { dots := r.Bool() return typecheck.Call(pos, fun, args, dots) + case exprMake: + pos := r.pos() + typ := r.exprType(false) + extra := r.exprs() + return typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))) + + case exprNew: + pos := r.pos() + typ := r.exprType(false) + return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ)) + case exprConvert: typ := r.typ() pos := r.pos() diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 2b22046de1..4d133e033e 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -619,6 +619,8 @@ func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj { } // typExpr writes the type represented by the given expression. +// +// TODO(mdempsky): Document how this differs from exprType. func (w *writer) typExpr(expr syntax.Expr) { tv, ok := w.p.info.Types[expr] assert(ok) @@ -1228,9 +1230,7 @@ func (w *writer) expr(expr syntax.Expr) { } if tv.IsType() { - w.Code(exprType) - w.exprType(nil, expr, false) - return + w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr)) } if tv.Value != nil { @@ -1280,7 +1280,11 @@ func (w *writer) expr(expr syntax.Expr) { assert(ok) w.Code(exprSelector) - w.expr(expr.X) + if w.Bool(sel.Kind() == types2.MethodExpr) { + w.exprType(nil, expr.X, false) + } else { + w.expr(expr.X) + } w.pos(expr) w.selector(sel.Obj()) @@ -1339,6 +1343,29 @@ func (w *writer) expr(expr syntax.Expr) { break } + if name, ok := unparen(expr.Fun).(*syntax.Name); ok && tv.IsBuiltin() { + switch name.Value { + case "make": + assert(len(expr.ArgList) >= 1) + assert(!expr.HasDots) + + w.Code(exprMake) + w.pos(expr) + w.exprType(nil, expr.ArgList[0], false) + w.exprs(expr.ArgList[1:]) + return + + case "new": + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + + w.Code(exprNew) + w.pos(expr) + w.exprType(nil, expr.ArgList[0], false) + return + } + } + writeFunExpr := func() { if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok { if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal { @@ -1438,10 +1465,6 @@ func (w *writer) exprList(expr syntax.Expr) { } func (w *writer) exprs(exprs []syntax.Expr) { - if len(exprs) == 0 { - assert(exprs == nil) - } - w.Sync(pkgbits.SyncExprs) w.Len(len(exprs)) for _, expr := range exprs { -- cgit v1.3-6-g1900 From 394ea70cc925ab59b6c1a3d41accbc5613c71e3b Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 24 May 2022 10:24:05 -0700 Subject: [dev.unified] cmd/compile: more Unified IR docs and review This adds more documentation throughout the core Unified IR logic and removes their UNREVIEWED notices. Updates #48194. Change-Id: Iddd30edaee1c6ea8a05a5a7e013480e02be00d29 Reviewed-on: https://go-review.googlesource.com/c/go/+/411917 Auto-Submit: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/codes.go | 5 +- src/cmd/compile/internal/noder/linker.go | 24 +++- src/cmd/compile/internal/noder/quirks.go | 10 +- src/cmd/compile/internal/noder/reader.go | 84 ++++++++------ src/cmd/compile/internal/noder/unified.go | 6 +- src/cmd/compile/internal/noder/writer.go | 181 +++++++++++++++++++++++++----- 6 files changed, 241 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go index 59c8ec8121..f7ad2503c2 100644 --- a/src/cmd/compile/internal/noder/codes.go +++ b/src/cmd/compile/internal/noder/codes.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. @@ -8,6 +6,7 @@ package noder import "internal/pkgbits" +// A codeStmt distinguishes among statement encodings. type codeStmt int func (c codeStmt) Marker() pkgbits.SyncMarker { return pkgbits.SyncStmt1 } @@ -31,6 +30,7 @@ const ( stmtSelect ) +// A codeExpr distinguishes among expression encodings. type codeExpr int func (c codeExpr) Marker() pkgbits.SyncMarker { return pkgbits.SyncExpr } @@ -66,6 +66,7 @@ const ( assignExpr ) +// A codeDecl distinguishes among declaration encodings. type codeDecl int func (c codeDecl) Marker() pkgbits.SyncMarker { return pkgbits.SyncDecl } diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go index a58b9b930c..1626c04090 100644 --- a/src/cmd/compile/internal/noder/linker.go +++ b/src/cmd/compile/internal/noder/linker.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. @@ -34,6 +32,9 @@ import ( // low-level linking details can be moved there, but the logic for // handling extension data needs to stay in the compiler. +// A linker combines a package's stub export data with any referenced +// elements from imported packages into a single, self-contained +// export data file. type linker struct { pw pkgbits.PkgEncoder @@ -41,6 +42,9 @@ type linker struct { decls map[*types.Sym]pkgbits.Index } +// relocAll ensures that all elements specified by pr and relocs are +// copied into the output export data file, and returns the +// corresponding indices in the output. func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt { res := make([]pkgbits.RelocEnt, len(relocs)) for i, rent := range relocs { @@ -50,6 +54,8 @@ func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.Re return res } +// relocIdx ensures a single element is copied into the output export +// data file, and returns the corresponding index in the output. func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index { assert(pr != nil) @@ -85,10 +91,19 @@ func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) return newidx } +// relocString copies the specified string from pr into the output +// export data file, deduplicating it against other strings. func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { return l.pw.StringIdx(pr.StringIdx(idx)) } +// relocPkg copies the specified package from pr into the output +// export data file, rewriting its import path to match how it was +// imported. +// +// TODO(mdempsky): Since CL 391014, we already have the compilation +// unit's import path, so there should be no need to rewrite packages +// anymore. func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { path := pr.PeekPkgPath(idx) @@ -114,6 +129,9 @@ func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { return w.Flush() } +// relocObj copies the specified object from pr into the output export +// data file, rewriting its compiler-private extension data (e.g., +// adding inlining cost and escape analysis results for functions). func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { path, name, tag := pr.PeekObj(idx) sym := types.NewPkg(path, "").Lookup(name) @@ -184,6 +202,8 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { return w.Idx } +// relocCommon copies the specified element from pr into w, +// recursively relocating any referenced elements as well. func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) { r := pr.NewDecoderRaw(k, idx) w.Relocs = l.relocAll(pr, r.Relocs) diff --git a/src/cmd/compile/internal/noder/quirks.go b/src/cmd/compile/internal/noder/quirks.go index c4cb9b9a2c..a22577f965 100644 --- a/src/cmd/compile/internal/noder/quirks.go +++ b/src/cmd/compile/internal/noder/quirks.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. @@ -12,12 +10,12 @@ import ( "cmd/compile/internal/syntax" ) -// This file defines helper functions useful for satisfying toolstash -// -cmp when compared against the legacy frontend behavior, but can be -// removed after that's no longer a concern. - // typeExprEndPos returns the position that noder would leave base.Pos // after parsing the given type expression. +// +// Deprecated: This function exists to emulate position semantics from +// Go 1.17, necessary for compatibility with the backend DWARF +// generation logic that assigns variables to their appropriate scope. func typeExprEndPos(expr0 syntax.Expr) syntax.Pos { for { switch expr := expr0.(type) { diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index e8401c5775..5ebc776605 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. @@ -26,15 +24,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,6 +58,8 @@ 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 @@ -69,6 +79,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 @@ -162,6 +173,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 +190,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 +237,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 +252,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 +279,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 { @@ -530,8 +534,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) @@ -567,6 +575,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() @@ -707,6 +717,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) @@ -955,6 +966,8 @@ var bodyReader = map[*ir.Func]pkgReaderIndex{} // 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 @@ -978,6 +991,8 @@ 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 @@ -1995,6 +2010,8 @@ 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 @@ -2281,18 +2298,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() } diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index f7cf7f90b2..95486af66c 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.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. @@ -226,6 +224,8 @@ func freePackage(pkg *types2.Package) { base.Fatalf("package never finalized") } +// readPackage reads package export data from pr to populate +// importpkg. func readPackage(pr *pkgReader, importpkg *types.Pkg) { r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) @@ -252,6 +252,8 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg) { } } +// writeUnifiedExport writes to `out` the finalized, self-contained +// Unified IR export data file for the current compilation unit. func writeUnifiedExport(out io.Writer) { l := linker{ pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 4d133e033e..8ef63a0085 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.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. @@ -16,6 +14,45 @@ import ( "cmd/compile/internal/types2" ) +// This file implements the Unified IR package writer and defines the +// Unified IR export data format. +// +// Low-level coding details (e.g., byte-encoding of individual +// primitive values, or handling element bitstreams and +// cross-references) are handled by internal/pkgbits, so here we only +// concern ourselves with higher-level worries like mapping Go +// language constructs into elements. + +// There are two central types in the writing process: the "writer" +// type handles writing out individual elements, while the "pkgWriter" +// type keeps track of which elements have already been created. +// +// For each sort of "thing" (e.g., position, package, object, type) +// that can be written into the export data, there are generally +// several methods that work together: +// +// - writer.thing handles writing out a *use* of a thing, which often +// means writing a relocation to that thing's encoded index. +// +// - pkgWriter.thingIdx handles reserving an index for a thing, and +// writing out any elements needed for the thing. +// +// - writer.doThing handles writing out the *definition* of a thing, +// which in general is a mix of low-level coding primitives (e.g., +// ints and strings) or uses of other things. +// +// A design goal of Unified IR is to have a single, canonical writer +// implementation, but multiple reader implementations each tailored +// to their respective needs. For example, within cmd/compile's own +// backend, inlining is implemented largely by just re-running the +// function body reading code. + +// TODO(mdempsky): Add an importer for Unified IR to the x/tools repo, +// and better document the file format boundary between public and +// private data. + +// A pkgWriter constructs Unified IR export data from the results of +// running the types2 type checker on a Go compilation unit. type pkgWriter struct { pkgbits.PkgEncoder @@ -23,18 +60,29 @@ type pkgWriter struct { curpkg *types2.Package info *types2.Info + // Indices for previously written syntax and types2 things. + posBasesIdx map[*syntax.PosBase]pkgbits.Index pkgsIdx map[*types2.Package]pkgbits.Index typsIdx map[types2.Type]pkgbits.Index - globalsIdx map[types2.Object]pkgbits.Index + objsIdx map[types2.Object]pkgbits.Index + + // Maps from types2.Objects back to their syntax.Decl. funDecls map[*types2.Func]*syntax.FuncDecl typDecls map[*types2.TypeName]typeDeclGen - linknames map[types2.Object]string + // linknames maps package-scope objects to their linker symbol name, + // if specified by a //go:linkname directive. + linknames map[types2.Object]string + + // cgoPragmas accumulates any //go:cgo_* pragmas that need to be + // passed through to cmd/link. cgoPragmas [][]string } +// newPkgWriter returns an initialized pkgWriter for the specified +// package. func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { return &pkgWriter{ PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), @@ -43,9 +91,9 @@ func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { curpkg: pkg, info: info, - pkgsIdx: make(map[*types2.Package]pkgbits.Index), - globalsIdx: make(map[types2.Object]pkgbits.Index), - typsIdx: make(map[types2.Type]pkgbits.Index), + pkgsIdx: make(map[*types2.Package]pkgbits.Index), + objsIdx: make(map[types2.Object]pkgbits.Index), + typsIdx: make(map[types2.Type]pkgbits.Index), posBasesIdx: make(map[*syntax.PosBase]pkgbits.Index), @@ -56,18 +104,23 @@ func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { } } +// errorf reports a user error about thing p. func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) { base.ErrorfAt(pw.m.pos(p), msg, args...) } +// fatalf reports an internal compiler error about thing p. func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) { base.FatalfAt(pw.m.pos(p), msg, args...) } +// unexpected reports a fatal error about a thing of unexpected +// dynamic type. func (pw *pkgWriter) unexpected(what string, p poser) { pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p) } +// A writer provides APIs for writing out an individual element. type writer struct { p *pkgWriter @@ -77,13 +130,19 @@ type writer struct { // scope closes, and then maybe we can just use the same map for // storing the TypeParams too (as their TypeName instead). - // variables declared within this function + // localsIdx tracks any local variables declared within this + // function body. It's unused for writing out non-body things. localsIdx map[*types2.Var]int - closureVars []posObj - closureVarsIdx map[*types2.Var]int + // closureVars tracks any free variables that are referenced by this + // function body. It's unused for writing out non-body things. + closureVars []posVar + closureVarsIdx map[*types2.Var]int // index of previously seen free variables - dict *writerDict + dict *writerDict + + // derived tracks whether the type being written out references any + // type parameters. It's unused for writing non-type things. derived bool } @@ -128,16 +187,25 @@ type typeInfo struct { derived bool } +// An objInfo represents a reference to an encoded, instantiated (if +// applicable) Go object. type objInfo struct { idx pkgbits.Index // index for the generic function declaration explicits []typeInfo // info for the type arguments } +// An itabInfo represents a reference to an encoded itab entry (i.e., +// a non-empty interface type along with a concrete type that +// implements that interface). +// +// itabInfo is only used for type itabInfo struct { typIdx pkgbits.Index // always a derived type index iface typeInfo // always a non-empty interface type } +// anyDerived reports whether any of info's explicit type arguments +// are derived types. func (info objInfo) anyDerived() bool { for _, explicit := range info.explicits { if explicit.derived { @@ -147,6 +215,8 @@ func (info objInfo) anyDerived() bool { return false } +// equals reports whether info and other represent the same Go object +// (i.e., same base object and identical type arguments, if any). func (info objInfo) equals(other objInfo) bool { if info.idx != other.idx { return false @@ -169,6 +239,7 @@ func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) * // @@@ Positions +// pos writes the position of p into the element bitstream. func (w *writer) pos(p poser) { w.Sync(pkgbits.SyncPos) pos := p.Pos() @@ -178,17 +249,19 @@ func (w *writer) pos(p poser) { return } - // TODO(mdempsky): Delta encoding. Also, if there's a b-side, update - // its position base too (but not vice versa!). + // TODO(mdempsky): Delta encoding. w.posBase(pos.Base()) w.Uint(pos.Line()) w.Uint(pos.Col()) } +// posBase writes a reference to the given PosBase into the element +// bitstream. func (w *writer) posBase(b *syntax.PosBase) { w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b)) } +// posBaseIdx returns the index for the given PosBase. func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index { if idx, ok := pw.posBasesIdx[b]; ok { return idx @@ -210,11 +283,14 @@ func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index { // @@@ Packages +// pkg writes a use of the given Package into the element bitstream. func (w *writer) pkg(pkg *types2.Package) { w.Sync(pkgbits.SyncPkg) w.Reloc(pkgbits.RelocPkg, w.p.pkgIdx(pkg)) } +// pkgIdx returns the index for the given package, adding it to the +// package export data if needed. func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { if idx, ok := pw.pkgsIdx[pkg]; ok { return idx @@ -256,10 +332,13 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName) +// typ writes a use of the given type into the bitstream. func (w *writer) typ(typ types2.Type) { w.typInfo(w.p.typIdx(typ, w.dict)) } +// typInfo writes a use of the given type (specified as a typeInfo +// instead) into the bitstream. func (w *writer) typInfo(info typeInfo) { w.Sync(pkgbits.SyncType) if w.Bool(info.derived) { @@ -468,6 +547,11 @@ func (w *writer) param(param *types2.Var) { // @@@ Objects +// obj writes a use of the given object into the bitstream. +// +// If obj is a generic object, then explicits are the explicit type +// arguments used to instantiate it (i.e., used to substitute the +// object's own declared type parameters). func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) { explicitInfos := make([]typeInfo, explicits.Len()) for i := range explicitInfos { @@ -515,8 +599,13 @@ func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) { } } +// objIdx returns the index for the given Object, adding it to the +// export data as needed. func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index { - if idx, ok := pw.globalsIdx[obj]; ok { + // TODO(mdempsky): Validate that obj is a global object (or a local + // defined type, which we hoist to global scope anyway). + + if idx, ok := pw.objsIdx[obj]; ok { return idx } @@ -530,12 +619,35 @@ func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index { dict.implicits = decl.implicits } + // We encode objects into 4 elements across different sections, all + // sharing the same index: + // + // - RelocName has just the object's qualified name (i.e., + // Object.Pkg and Object.Name) and the CodeObj indicating what + // specific type of Object it is (Var, Func, etc). + // + // - RelocObj has the remaining public details about the object, + // relevant to go/types importers. + // + // - RelocObjExt has additional private details about the object, + // which are only relevant to cmd/compile itself. This is + // separated from RelocObj so that go/types importers are + // unaffected by internal compiler changes. + // + // - RelocObjDict has public details about the object's type + // parameters and derived type's used by the object. This is + // separated to facilitate the eventual introduction of + // shape-based stenciling. + // + // TODO(mdempsky): Re-evaluate whether RelocName still makes sense + // to keep separate from RelocObj. + w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1) wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1) wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1) wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1) - pw.globalsIdx[obj] = w.Idx // break cycles + pw.objsIdx[obj] = w.Idx // break cycles assert(wext.Idx == w.Idx) assert(wname.Idx == w.Idx) assert(wdict.Idx == w.Idx) @@ -557,6 +669,8 @@ func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index { return w.Idx } +// doObj writes the RelocObj definition for obj to w, and the +// RelocObjExt definition to wext. func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj { if obj.Pkg() != w.p.curpkg { return pkgbits.ObjStub @@ -726,8 +840,8 @@ func (w *writer) qualifiedIdent(obj types2.Object) { // me a little nervous to try it again. // localIdent writes the name of a locally declared object (i.e., -// objects that can only be accessed by name, within the context of a -// particular function). +// objects that can only be accessed by non-qualified name, within the +// context of a particular function). func (w *writer) localIdent(obj types2.Object) { assert(!isGlobal(obj)) w.Sync(pkgbits.SyncLocalIdent) @@ -789,7 +903,7 @@ func (w *writer) funcExt(obj *types2.Func) { } sig, block := obj.Type().(*types2.Signature), decl.Body - body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.dict) + body, closureVars := w.p.bodyIdx(sig, block, w.dict) assert(len(closureVars) == 0) w.Sync(pkgbits.SyncFuncExt) @@ -831,7 +945,9 @@ func (w *writer) pragmaFlag(p ir.PragmaFlag) { // @@@ Function bodies -func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posObj) { +// bodyIdx returns the index for the given function body (specified by +// block), adding it to the export data +func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) { w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody) w.dict = dict @@ -864,6 +980,7 @@ func (w *writer) funcarg(param *types2.Var, result bool) { } } +// addLocal records the declaration of a new local variable. func (w *writer) addLocal(obj *types2.Var) { w.Sync(pkgbits.SyncAddLocal) idx := len(w.localsIdx) @@ -876,6 +993,8 @@ func (w *writer) addLocal(obj *types2.Var) { w.localsIdx[obj] = idx } +// useLocal writes a reference to the given local or free variable +// into the bitstream. func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { w.Sync(pkgbits.SyncUseObjLocal) @@ -890,7 +1009,7 @@ func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { w.closureVarsIdx = make(map[*types2.Var]int) } idx = len(w.closureVars) - w.closureVars = append(w.closureVars, posObj{pos, obj}) + w.closureVars = append(w.closureVars, posVar{pos, obj}) w.closureVarsIdx[obj] = idx } w.Len(idx) @@ -913,6 +1032,7 @@ func (w *writer) closeAnotherScope() { // @@@ Statements +// stmt writes the given statement into the function body bitstream. func (w *writer) stmt(stmt syntax.Stmt) { var stmts []syntax.Stmt if stmt != nil { @@ -1213,6 +1333,7 @@ func (w *writer) optLabel(label *syntax.Name) { // @@@ Expressions +// expr writes the given expression into the function body bitstream. func (w *writer) expr(expr syntax.Expr) { base.Assertf(expr != nil, "missing expression") @@ -1439,7 +1560,7 @@ func (w *writer) funcLit(expr *syntax.FuncLit) { assert(ok) sig := tv.Type.(*types2.Signature) - body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, expr.Body, w.dict) + body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict) w.Sync(pkgbits.SyncFuncLit) w.pos(expr) @@ -1448,15 +1569,15 @@ func (w *writer) funcLit(expr *syntax.FuncLit) { w.Len(len(closureVars)) for _, cv := range closureVars { w.pos(cv.pos) - w.useLocal(cv.pos, cv.obj) + w.useLocal(cv.pos, cv.var_) } w.Reloc(pkgbits.RelocBody, body) } -type posObj struct { - pos syntax.Pos - obj *types2.Var +type posVar struct { + pos syntax.Pos + var_ *types2.Var } func (w *writer) exprList(expr syntax.Expr) { @@ -1509,6 +1630,9 @@ func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { w.typInfo(info) } +// isInterface reports whether typ is known to be an interface type. +// If typ is a type parameter, then isInterface reports an internal +// compiler error instead. func isInterface(typ types2.Type) bool { if _, ok := typ.(*types2.TypeParam); ok { // typ is a type parameter and may be instantiated as either a @@ -1521,6 +1645,7 @@ func isInterface(typ types2.Type) bool { return ok } +// op writes an Op into the bitstream. func (w *writer) op(op ir.Op) { // TODO(mdempsky): Remove in favor of explicit codes? Would make // export data more stable against internal refactorings, but low @@ -1561,6 +1686,12 @@ type fileImports struct { importedEmbed, importedUnsafe bool } +// declCollector is a visitor type that collects compiler-needed +// information about declarations that types2 doesn't track. +// +// Notably, it maps declared types and functions back to their +// declaration statement, keeps track of implicit type parameters, and +// assigns unique type "generation" numbers to local defined types. type declCollector struct { pw *pkgWriter typegen *int -- cgit v1.3-6-g1900 From fc5dad6646b165d0de75bf94956b18e22d00fa0a Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 14 Jun 2022 15:34:05 -0700 Subject: [dev.unified] cmd/compile/internal/walk: minor prep refactoring Two small refactorings that will make it easier to thread through RType parameters later. Behavior preserving, but seemed worth separating out. Passes toolstash -cmp. Change-Id: I77905775015b6582bad2b32dd7700880c415893f Reviewed-on: https://go-review.googlesource.com/c/go/+/413354 Reviewed-by: David Chase Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/walk/complit.go | 4 ++-- src/cmd/compile/internal/walk/range.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/walk/complit.go b/src/cmd/compile/internal/walk/complit.go index 595fe8538c..46744a7130 100644 --- a/src/cmd/compile/internal/walk/complit.go +++ b/src/cmd/compile/internal/walk/complit.go @@ -414,9 +414,9 @@ func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { // make the map var - a := ir.NewCallExpr(base.Pos, ir.OMAKE, nil, nil) + args := []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(n.Len + int64(len(n.List)))} + a := typecheck.Expr(ir.NewCallExpr(base.Pos, ir.OMAKE, nil, args)).(*ir.MakeExpr) a.SetEsc(n.Esc()) - a.Args = []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(n.Len + int64(len(n.List)))} appendWalkStmt(init, ir.NewAssignStmt(base.Pos, m, a)) entries := n.List diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index 6c30fa2877..48155a817c 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -38,11 +38,7 @@ func cheapComputableIndex(width int64) bool { // the returned node. func walkRange(nrange *ir.RangeStmt) ir.Node { if isMapClear(nrange) { - m := nrange.X - lno := ir.SetPos(m) - n := mapClear(m) - base.Pos = lno - return n + return mapClear(nrange) } nfor := ir.NewForStmt(nrange.Pos(), nil, nil, nil, nil) @@ -360,7 +356,11 @@ func isMapClear(n *ir.RangeStmt) bool { } // mapClear constructs a call to runtime.mapclear for the map m. -func mapClear(m ir.Node) ir.Node { +func mapClear(nrange *ir.RangeStmt) ir.Node { + m := nrange.X + origPos := ir.SetPos(m) + defer func() { base.Pos = origPos }() + t := m.Type() // instantiate mapclear(typ *type, hmap map[any]any) -- cgit v1.3-6-g1900 From f70775ff22f4658d6ee65d9ec314687cffe57a84 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 14 Jun 2022 16:11:17 -0700 Subject: [dev.unified] cmd/compile: refactor reflectdata.{TypePtr,ITabAddr} Minor refactoring to decouple from base.Pos and deduplicate some common code paths. Passes toolstash -cmp. Change-Id: I8c0724cf821d28b0ede3b0e8e4b2d02302d9af3b Reviewed-on: https://go-review.googlesource.com/c/go/+/413355 TryBot-Result: Gopher Robot Run-TryBot: Matthew Dempsky Reviewed-by: David Chase --- src/cmd/compile/internal/reflectdata/reflect.go | 23 +++++++++++++++++------ src/cmd/compile/internal/typecheck/subr.go | 8 ++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 21301ab149..59085869eb 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -842,9 +842,15 @@ func TypeLinksym(t *types.Type) *obj.LSym { return TypeSym(t).Linksym() } +// Deprecated: Use TypePtrAt instead. func TypePtr(t *types.Type) *ir.AddrExpr { - n := ir.NewLinksymExpr(base.Pos, TypeLinksym(t), types.Types[types.TUINT8]) - return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr) + return TypePtrAt(base.Pos, t) +} + +// TypePtrAt returns an expression that evaluates to the +// *runtime._type value for t. +func TypePtrAt(pos src.XPos, t *types.Type) *ir.AddrExpr { + return typecheck.LinksymAddr(pos, TypeLinksym(t), types.Types[types.TUINT8]) } // ITabLsym returns the LSym representing the itab for concrete type typ implementing @@ -864,9 +870,15 @@ func ITabLsym(typ, iface *types.Type) *obj.LSym { return lsym } -// ITabAddr returns an expression representing a pointer to the itab -// for concrete type typ implementing interface iface. +// Deprecated: Use ITabAddrAt instead. func ITabAddr(typ, iface *types.Type) *ir.AddrExpr { + return ITabAddrAt(base.Pos, typ, iface) +} + +// ITabAddrAt returns an expression that evaluates to the +// *runtime.itab value for concrete type typ implementing interface +// iface. +func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr { s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString()) lsym := s.Linksym() @@ -874,8 +886,7 @@ func ITabAddr(typ, iface *types.Type) *ir.AddrExpr { writeITab(lsym, typ, iface, false) } - n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8]) - return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr) + return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8]) } // needkeyupdate reports whether map updates with t as a key diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index ffd00ec3a7..3b0075e616 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -13,6 +13,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" + "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" ) @@ -119,6 +120,13 @@ func ComputeAddrtaken(top []ir.Node) { } } +// LinksymAddr returns a new expression that evaluates to the address +// of lsym. typ specifies the type of the addressed memory. +func LinksymAddr(pos src.XPos, lsym *obj.LSym, typ *types.Type) *ir.AddrExpr { + n := ir.NewLinksymExpr(pos, lsym, typ) + return Expr(NodAddrAt(pos, n)).(*ir.AddrExpr) +} + func NodNil() ir.Node { n := ir.NewNilExpr(base.Pos) n.SetType(types.Types[types.TNIL]) -- cgit v1.3-6-g1900 From 93833cd5d8c8b35cd94f743c73cd18712531de4b Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 14 Jun 2022 16:07:46 -0700 Subject: [dev.unified] cmd/compile: extract rtype code from walk This CL removes (almost*) all reflectdata.{TypePtr,ITabAddr} calls from package walk. This will allow us to next start adding RType/ITab fields to IR nodes directly, and have the helpers start returning them when available instead. The one survining ITabAddr call is due to ODOTTYPE{,2}, but we already have ODYNAMICDOTTYPE{,2}, which I plan to have Unified IR always use. (Longer term, once the Go 1.18 frontend is gone, we can get rid of ODOTTYPE*, and rename ODYNAMICDOTTYPE*.) Passes toolstash -cmp. Change-Id: I5e00da06a93d069abf383d7628e692dd7fd2a1c7 Reviewed-on: https://go-review.googlesource.com/c/go/+/413356 Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/reflectdata/helpers.go | 172 ++++++++++++++++++++++++ src/cmd/compile/internal/walk/assign.go | 13 +- src/cmd/compile/internal/walk/builtin.go | 16 +-- src/cmd/compile/internal/walk/compare.go | 9 +- src/cmd/compile/internal/walk/complit.go | 16 ++- src/cmd/compile/internal/walk/convert.go | 26 ++-- src/cmd/compile/internal/walk/expr.go | 2 +- src/cmd/compile/internal/walk/order.go | 7 +- src/cmd/compile/internal/walk/range.go | 4 +- 9 files changed, 225 insertions(+), 40 deletions(-) create mode 100644 src/cmd/compile/internal/reflectdata/helpers.go (limited to 'src') diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go new file mode 100644 index 0000000000..61d1660773 --- /dev/null +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -0,0 +1,172 @@ +// Copyright 2022 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. + +package reflectdata + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// assertOp asserts that n is an op. +func assertOp(n ir.Node, op ir.Op) { + base.AssertfAt(n.Op() == op, n.Pos(), "want %v, have %v", op, n) +} + +// assertOp2 asserts that n is an op1 or op2. +func assertOp2(n ir.Node, op1, op2 ir.Op) { + base.AssertfAt(n.Op() == op1 || n.Op() == op2, n.Pos(), "want %v or %v, have %v", op1, op2, n) +} + +// kindRType asserts that typ has the given kind, and returns an +// expression that yields the *runtime._type value representing typ. +func kindRType(pos src.XPos, typ *types.Type, k types.Kind) ir.Node { + base.AssertfAt(typ.Kind() == k, pos, "want %v type, have %v", k, typ) + return TypePtrAt(pos, typ) +} + +// mapRType asserts that typ is a map type, and returns an expression +// that yields the *runtime._type value representing typ. +func mapRType(pos src.XPos, typ *types.Type) ir.Node { + return kindRType(pos, typ, types.TMAP) +} + +// chanRType asserts that typ is a map type, and returns an expression +// that yields the *runtime._type value representing typ. +func chanRType(pos src.XPos, typ *types.Type) ir.Node { + return kindRType(pos, typ, types.TCHAN) +} + +// sliceElemRType asserts that typ is a slice type, and returns an +// expression that yields the *runtime._type value representing typ's +// element type. +func sliceElemRType(pos src.XPos, typ *types.Type) ir.Node { + base.AssertfAt(typ.IsSlice(), pos, "want slice type, have %v", typ) + return TypePtrAt(pos, typ.Elem()) +} + +// concreteRType asserts that typ is not an interface type, and +// returns an expression that yields the *runtime._type value +// representing typ. +func concreteRType(pos src.XPos, typ *types.Type) ir.Node { + base.AssertfAt(!typ.IsInterface(), pos, "want non-interface type, have %v", typ) + return TypePtrAt(pos, typ) +} + +// AppendElemRType asserts that n is an "append" operation, and +// returns an expression that yields the *runtime._type value +// representing the result slice type's element type. +func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { + assertOp(n, ir.OAPPEND) + return sliceElemRType(pos, n.Type()) +} + +// CompareRType asserts that n is a comparison (== or !=) operation +// between expressions of interface and non-interface type, and +// returns an expression that yields the *runtime._type value +// representing the non-interface type. +func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { + assertOp2(n, ir.OEQ, ir.ONE) + base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y) + typ := n.X.Type() + if typ.IsInterface() { + typ = n.Y.Type() + } + return concreteRType(pos, typ) +} + +// ConvIfaceTypeWord asserts that n is conversion to interface type, +// and returns an expression that yields the *runtime._type or +// *runtime.itab value necessary for implementing the conversion. +// +// - *runtime._type for the destination type, for I2I conversions +// - *runtime.itab, for T2I conversions +// - *runtime._type for the source type, for T2E conversions +func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { + assertOp(n, ir.OCONVIFACE) + src, dst := n.X.Type(), n.Type() + base.AssertfAt(dst.IsInterface(), n.Pos(), "want interface type, have %L", n) + if dst.IsEmptyInterface() { + return concreteRType(pos, src) // direct eface construction + } + if !src.IsInterface() { + return ITabAddr(src, dst) // direct iface construction + } + return TypePtrAt(pos, dst) // convI2I +} + +// ConvIfaceDataWordRType asserts that n is a conversion from +// non-interface type to interface type (or OCONVIDATA operation), and +// returns an expression that yields the *runtime._type for copying +// the convertee value to the heap. +func ConvIfaceDataWordRType(pos src.XPos, n *ir.ConvExpr) ir.Node { + assertOp2(n, ir.OCONVIFACE, ir.OCONVIDATA) + return concreteRType(pos, n.X.Type()) +} + +// CopyElemRType asserts that n is a "copy" operation, and returns an +// expression that yields the *runtime._type value representing the +// destination slice type's element type. +func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { + assertOp(n, ir.OCOPY) + return sliceElemRType(pos, n.X.Type()) +} + +// DeleteMapRType asserts that n is a "delete" operation, and returns +// an expression that yields the *runtime._type value representing the +// map type. +func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { + assertOp(n, ir.ODELETE) + return mapRType(pos, n.Args[0].Type()) +} + +// IndexMapRType asserts that n is a map index operation, and returns +// an expression that yields the *runtime._type value representing the +// map type. +func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { + assertOp(n, ir.OINDEXMAP) + return mapRType(pos, n.X.Type()) +} + +// MakeChanRType asserts that n is a "make" operation for a channel +// type, and returns an expression that yields the *runtime._type +// value representing that channel type. +func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { + assertOp(n, ir.OMAKECHAN) + return chanRType(pos, n.Type()) +} + +// MakeMapRType asserts that n is a "make" operation for a map type, +// and returns an expression that yields the *runtime._type value +// representing that map type. +func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { + assertOp(n, ir.OMAKEMAP) + return mapRType(pos, n.Type()) +} + +// MakeSliceElemRType asserts that n is a "make" operation for a slice +// type, and returns an expression that yields the *runtime._type +// value representing that slice type's element type. +func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { + assertOp2(n, ir.OMAKESLICE, ir.OMAKESLICECOPY) + return sliceElemRType(pos, n.Type()) +} + +// RangeMapRType asserts that n is a "range" loop over a map value, +// and returns an expression that yields the *runtime._type value +// representing that map type. +func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { + assertOp(n, ir.ORANGE) + return mapRType(pos, n.X.Type()) +} + +// UnsafeSliceElemRType asserts that n is an "unsafe.Slice" operation, +// and returns an expression that yields the *runtime._type value +// representing the result slice type's element type. +func UnsafeSliceElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { + assertOp(n, ir.OUNSAFESLICE) + return sliceElemRType(pos, n.Type()) +} diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go index c44d934f21..1d922d983e 100644 --- a/src/cmd/compile/internal/walk/assign.go +++ b/src/cmd/compile/internal/walk/assign.go @@ -99,10 +99,11 @@ func walkAssign(init *ir.Nodes, n ir.Node) ir.Node { } as.Y = r if r.Op() == ir.OAPPEND { + r := r.(*ir.CallExpr) // Left in place for back end. // Do not add a new write barrier. // Set up address of type for back end. - r.(*ir.CallExpr).X = reflectdata.TypePtr(r.Type().Elem()) + r.X = reflectdata.AppendElemRType(base.Pos, r) return as } // Otherwise, lowered for race detector. @@ -169,11 +170,11 @@ func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { var call *ir.CallExpr if w := t.Elem().Size(); w <= zeroValSize { fn := mapfn(mapaccess2[fast], t, false) - call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key) + call = mkcall1(fn, fn.Type().Results(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key) } else { fn := mapfn("mapaccess2_fat", t, true) z := reflectdata.ZeroAddr(w) - call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key, z) + call = mkcall1(fn, fn.Type().Results(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key, z) } // mapaccess2* returns a typed bool, but due to spec changes, @@ -502,7 +503,7 @@ func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { fn = typecheck.SubstArgTypes(fn, elemtype, elemtype) // s = growslice(T, s, n) - nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))} + nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.AppendElemRType(base.Pos, n), s, nn))} nodes.Append(nif) // s = s[:n] @@ -523,7 +524,7 @@ func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { fn = typecheck.SubstArgTypes(fn, l1.Type().Elem(), l2.Type().Elem()) ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes)) ptr2, len2 := backingArrayPtrLen(l2) - ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.TypePtr(elemtype), ptr1, len1, ptr2, len2) + ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.AppendElemRType(base.Pos, n), ptr1, len1, ptr2, len2) } else if base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime { // rely on runtime to instrument: // copy(s[len(l1):], l2) @@ -670,7 +671,7 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { fn = typecheck.SubstArgTypes(fn, elemtype, elemtype) // s = growslice(T, s, n) - nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))} + nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.AppendElemRType(base.Pos, n), s, nn))} nodes = append(nodes, nif) // s = s[:n] diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index a11031b3d0..7e84f28217 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -87,7 +87,7 @@ func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node { fn := typecheck.LookupRuntime("growslice") // growslice(, old []T, mincap int) (ret []T) fn = typecheck.SubstArgTypes(fn, ns.Type().Elem(), ns.Type().Elem()) - nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.TypePtr(ns.Type().Elem()), ns, + nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.AppendElemRType(base.Pos, n), ns, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns), na)))} l = append(l, nif) @@ -141,7 +141,7 @@ func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node { ptrL, lenL := backingArrayPtrLen(n.X) n.Y = cheapExpr(n.Y, init) ptrR, lenR := backingArrayPtrLen(n.Y) - return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.X.Type().Elem()), ptrL, lenL, ptrR, lenR) + return mkcall1(fn, n.Type(), init, reflectdata.CopyElemRType(base.Pos, n), ptrL, lenL, ptrR, lenR) } if runtimecall { @@ -214,7 +214,7 @@ func walkDelete(init *ir.Nodes, n *ir.CallExpr) ir.Node { t := map_.Type() fast := mapfast(t) key = mapKeyArg(fast, n, key, false) - return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.TypePtr(t), map_, key) + return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.DeleteMapRType(base.Pos, n), map_, key) } // walkLenCap walks an OLEN or OCAP node. @@ -258,7 +258,7 @@ func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node { argtype = types.Types[types.TINT] } - return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(size, argtype)) + return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.MakeChanRType(base.Pos, n), typecheck.Conv(size, argtype)) } // walkMakeMap walks an OMAKEMAP node. @@ -356,7 +356,7 @@ func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node { fn := typecheck.LookupRuntime(fnname) fn = typecheck.SubstArgTypes(fn, hmapType, t.Key(), t.Elem()) - return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(hint, argtype), h) + return mkcall1(fn, n.Type(), init, reflectdata.MakeMapRType(base.Pos, n), typecheck.Conv(hint, argtype), h) } // walkMakeSlice walks an OMAKESLICE node. @@ -421,7 +421,7 @@ func walkMakeSlice(n *ir.MakeExpr, init *ir.Nodes) ir.Node { argtype = types.Types[types.TINT] } fn := typecheck.LookupRuntime(fnname) - ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype)) + ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype)) ptr.MarkNonNil() len = typecheck.Conv(len, types.Types[types.TINT]) cap = typecheck.Conv(cap, types.Types[types.TINT]) @@ -475,7 +475,7 @@ func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node { // Replace make+copy with runtime.makeslicecopy. // instantiate makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer fn := typecheck.LookupRuntime("makeslicecopy") - ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR])) + ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR])) ptr.MarkNonNil() sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length) return walkExpr(typecheck.Expr(sh), init) @@ -658,7 +658,7 @@ func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { if ir.ShouldCheckPtr(ir.CurFunc, 1) { fnname := "unsafeslicecheckptr" fn := typecheck.LookupRuntime(fnname) - init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(sliceType.Elem()), unsafePtr, typecheck.Conv(len, lenType))) + init.Append(mkcall1(fn, nil, init, reflectdata.UnsafeSliceElemRType(base.Pos, n), unsafePtr, typecheck.Conv(len, lenType))) } else { // Otherwise, open code unsafe.Slice to prevent runtime call overhead. // Keep this code in sync with runtime.unsafeslice{,64} diff --git a/src/cmd/compile/internal/walk/compare.go b/src/cmd/compile/internal/walk/compare.go index 6a8ad56d75..df7cb731f7 100644 --- a/src/cmd/compile/internal/walk/compare.go +++ b/src/cmd/compile/internal/walk/compare.go @@ -54,6 +54,10 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { // Given mixed interface/concrete comparison, // rewrite into types-equal && data-equal. // This is efficient, avoids allocations, and avoids runtime calls. + // + // TODO(mdempsky): It would be more general and probably overall + // simpler to just extend walkCompareInterface to optimize when one + // operand is an OCONVIFACE. if n.X.Type().IsInterface() != n.Y.Type().IsInterface() { // Preserve side-effects in case of short-circuiting; see #32187. l := cheapExpr(n.X, init) @@ -74,9 +78,12 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { // l.tab == type(r) // For non-empty interface, this is: // l.tab != nil && l.tab._type == type(r) + // + // TODO(mdempsky): For non-empty interface comparisons, just + // compare against the itab address directly? var eqtype ir.Node tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, l) - rtyp := reflectdata.TypePtr(r.Type()) + rtyp := reflectdata.CompareRType(base.Pos, n) if l.Type().IsEmptyInterface() { tab.SetType(types.NewPtr(types.Types[types.TUINT8])) tab.SetTypecheck(1) diff --git a/src/cmd/compile/internal/walk/complit.go b/src/cmd/compile/internal/walk/complit.go index 46744a7130..d8e5a955c2 100644 --- a/src/cmd/compile/internal/walk/complit.go +++ b/src/cmd/compile/internal/walk/complit.go @@ -467,14 +467,17 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { kidx := ir.NewIndexExpr(base.Pos, vstatk, i) kidx.SetBounded(true) - lhs := ir.NewIndexExpr(base.Pos, m, kidx) + + // typechecker rewrites OINDEX to OINDEXMAP + lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, kidx)).(*ir.IndexExpr) + base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) zero := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0)) cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(tk.NumElem())) incr := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1))) var body ir.Node = ir.NewAssignStmt(base.Pos, lhs, rhs) - body = typecheck.Stmt(body) // typechecker rewrites OINDEX to OINDEXMAP + body = typecheck.Stmt(body) body = orderStmtInPlace(body, map[string][]*ir.Name{}) loop := ir.NewForStmt(base.Pos, nil, cond, incr, nil) @@ -503,8 +506,13 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmpelem, elem)) ir.SetPos(tmpelem) - var a ir.Node = ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, tmpkey), tmpelem) - a = typecheck.Stmt(a) // typechecker rewrites OINDEX to OINDEXMAP + + // typechecker rewrites OINDEX to OINDEXMAP + lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, tmpkey)).(*ir.IndexExpr) + base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + + var a ir.Node = ir.NewAssignStmt(base.Pos, lhs, tmpelem) + a = typecheck.Stmt(a) a = orderStmtInPlace(a, map[string][]*ir.Name{}) appendWalkStmt(init, a) } diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go index 72631e7dfb..e857f325ec 100644 --- a/src/cmd/compile/internal/walk/convert.go +++ b/src/cmd/compile/internal/walk/convert.go @@ -14,7 +14,6 @@ import ( "cmd/compile/internal/ssagen" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" - "cmd/internal/src" "cmd/internal/sys" ) @@ -50,13 +49,8 @@ func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node { } if !fromType.IsInterface() { - var typeWord ir.Node - if toType.IsEmptyInterface() { - typeWord = reflectdata.TypePtr(fromType) - } else { - typeWord = reflectdata.ITabAddr(fromType, toType) - } - l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone)) + typeWord := reflectdata.ConvIfaceTypeWord(base.Pos, n) + l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n, init)) l.SetType(toType) l.SetTypecheck(n.Typecheck()) return l @@ -95,7 +89,7 @@ func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node { fn := typecheck.LookupRuntime("convI2I") types.CalcSize(fn.Type()) call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) - call.Args = []ir.Node{reflectdata.TypePtr(toType), itab} + call.Args = []ir.Node{reflectdata.ConvIfaceTypeWord(base.Pos, n), itab} typeWord = walkExpr(typecheck.Expr(call), init) } @@ -107,10 +101,10 @@ func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node { return e } -// Returns the data word (the second word) used to represent n in an interface. -// n must not be of interface type. -// esc describes whether the result escapes. -func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node { +// Returns the data word (the second word) used to represent conv.X in +// an interface. +func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node { + pos, n := conv.Pos(), conv.X fromType := n.Type() // If it's a pointer, it is its own representation. @@ -150,7 +144,7 @@ func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node { case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly(): // n is a readonly global; use it directly. value = n - case !escapes && fromType.Size() <= 1024: + case conv.Esc() == ir.EscNone && fromType.Size() <= 1024: // n does not escape. Use a stack temporary initialized to n. value = typecheck.Temp(fromType) init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n))) @@ -176,7 +170,7 @@ func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node { n = copyExpr(n, fromType, init) } fn = typecheck.SubstArgTypes(fn, fromType) - args = []ir.Node{reflectdata.TypePtr(fromType), typecheck.NodAddr(n)} + args = []ir.Node{reflectdata.ConvIfaceDataWordRType(base.Pos, conv), typecheck.NodAddr(n)} } else { // Use a specialized conversion routine that takes the type being // converted by value, not by pointer. @@ -211,7 +205,7 @@ func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node { // walkConvIData walks an OCONVIDATA node. func walkConvIData(n *ir.ConvExpr, init *ir.Nodes) ir.Node { n.X = walkExpr(n.X, init) - return dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone) + return dataWord(n, init) } // walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node. diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go index 803a07ae73..83fcea38d5 100644 --- a/src/cmd/compile/internal/walk/expr.go +++ b/src/cmd/compile/internal/walk/expr.go @@ -782,7 +782,7 @@ func walkIndexMap(n *ir.IndexExpr, init *ir.Nodes) ir.Node { t := map_.Type() fast := mapfast(t) key := mapKeyArg(fast, n, n.Index, n.Assigned) - args := []ir.Node{reflectdata.TypePtr(t), map_, key} + args := []ir.Node{reflectdata.IndexMapRType(base.Pos, n), map_, key} var mapFn ir.Node switch { diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index 8d1089dcc1..2602e20563 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -1450,8 +1450,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { // Emit eval+insert of dynamic entries, one at a time. for _, r := range dynamics { - as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value) - typecheck.Stmt(as) // Note: this converts the OINDEX to an OINDEXMAP + lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, r.Key)).(*ir.IndexExpr) + base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + + as := ir.NewAssignStmt(base.Pos, lhs, r.Value) + typecheck.Stmt(as) o.stmt(as) } diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index 48155a817c..16d7595baa 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -168,7 +168,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { fn := typecheck.LookupRuntime("mapiterinit") fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem(), th) - init = append(init, mkcallstmt1(fn, reflectdata.TypePtr(t), ha, typecheck.NodAddr(hit))) + init = append(init, mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), ha, typecheck.NodAddr(hit))) nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil()) fn = typecheck.LookupRuntime("mapiternext") @@ -366,7 +366,7 @@ func mapClear(nrange *ir.RangeStmt) ir.Node { // instantiate mapclear(typ *type, hmap map[any]any) fn := typecheck.LookupRuntime("mapclear") fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem()) - n := mkcallstmt1(fn, reflectdata.TypePtr(t), m) + n := mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), m) return walkStmt(typecheck.Stmt(n)) } -- cgit v1.3-6-g1900 From 5e0258c700cba33db1a41806ff328a9c3f42d4da Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 20 Jun 2022 22:28:15 -0700 Subject: [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 --- src/cmd/compile/internal/ir/expr.go | 15 +++++++++++ src/cmd/compile/internal/ssagen/ssa.go | 46 +++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 12 deletions(-) (limited to 'src') 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 { -- cgit v1.3-6-g1900 From 5960f4ec10e175714145d5ffa1b37d282b7a2157 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 20 Jun 2022 23:21:16 -0700 Subject: [dev.unified] cmd/compile: add RType fields This CL adds RType/ITab fields to IR nodes that (may) ultimately become runtime calls that require a *runtime._type or *runtime.itab argument. It also updates the corresponding reflectdata IR helpers to use these fields in preference of calling TypePtr/ITabAddr. Subsequent CLs will start updating the GOEXPERIMENT=unified frontend to set the RType fields, and incrementally switch the reflectdata helpers to require them. Passes toolstash -cmp. Change-Id: I30e31d91f0a53961e3d6d872d7b5f9df2ec5074c Reviewed-on: https://go-review.googlesource.com/c/go/+/413358 Reviewed-by: David Chase Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Run-TryBot: Matthew Dempsky --- src/cmd/compile/internal/ir/expr.go | 25 ++++++++++--- src/cmd/compile/internal/ir/stmt.go | 1 + src/cmd/compile/internal/reflectdata/helpers.go | 50 ++++++++++++++++++++++++- src/cmd/compile/internal/walk/convert.go | 2 +- 4 files changed, 70 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index 27dd390efc..4a8db70904 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -119,8 +119,9 @@ func (n *BasicLit) SetVal(val constant.Value) { n.val = val } // or Op(X, Y) for builtin functions that do not become calls. type BinaryExpr struct { miniExpr - X Node - Y Node + X Node + Y Node + RType Node `mknode:"-"` // see reflectdata/helpers.go } func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr { @@ -148,6 +149,7 @@ type CallExpr struct { origNode X Node Args Nodes + RType Node `mknode:"-"` // see reflectdata/helpers.go KeepAlive []*Name // vars to be kept alive until call returns IsDDD bool NoInline bool @@ -247,6 +249,17 @@ type ConvExpr struct { miniExpr X Node + // For implementing OCONVIFACE expressions. + // + // TypeWord is an expression yielding a *runtime._type or + // *runtime.itab value to go in the type word of the iface/eface + // result. See reflectdata.ConvIfaceTypeWord for further details. + // + // SrcRType is an expression yielding a *runtime._type value for X, + // if it's not pointer-shaped and needs to be heap allocated. + TypeWord Node `mknode:"-"` + SrcRType Node `mknode:"-"` + // For -d=checkptr instrumentation of conversions from // unsafe.Pointer to *Elem or *[Len]Elem. // @@ -285,6 +298,7 @@ type IndexExpr struct { miniExpr X Node Index Node + RType Node `mknode:"-"` // see reflectdata/helpers.go Assigned bool } @@ -395,8 +409,9 @@ func (n *LogicalExpr) SetOp(op Op) { // but *not* OMAKE (that's a pre-typechecking CallExpr). type MakeExpr struct { miniExpr - Len Node - Cap Node + RType Node `mknode:"-"` // see reflectdata/helpers.go + Len Node + Cap Node } func NewMakeExpr(pos src.XPos, op Op, len, cap Node) *MakeExpr { @@ -633,7 +648,7 @@ type TypeAssertExpr struct { // Runtime type information provided by walkDotType for // assertions from non-empty interface to concrete type. - ITab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type + ITab Node `mknode:"-"` // *runtime.itab for Type implementing X's type } func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr { diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index c46debce36..6a82df58f8 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -333,6 +333,7 @@ type RangeStmt struct { Label *types.Sym Def bool X Node + RType Node `mknode:"-"` // see reflectdata/helpers.go Key Node Value Node Body Nodes diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go index 61d1660773..4d85dea74d 100644 --- a/src/cmd/compile/internal/reflectdata/helpers.go +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -11,6 +11,16 @@ import ( "cmd/internal/src" ) +func haveRType(n, rtype ir.Node, fieldName string, required bool) bool { + if rtype != nil { + return true + } + if base.Debug.Unified != 0 && required { + base.FatalfAt(n.Pos(), "missing %s: %+v", fieldName, n) + } + return false +} + // assertOp asserts that n is an op. func assertOp(n ir.Node, op ir.Op) { base.AssertfAt(n.Op() == op, n.Pos(), "want %v, have %v", op, n) @@ -61,6 +71,9 @@ func concreteRType(pos src.XPos, typ *types.Type) ir.Node { // representing the result slice type's element type. func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.OAPPEND) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return sliceElemRType(pos, n.Type()) } @@ -71,6 +84,9 @@ func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp2(n, ir.OEQ, ir.ONE) base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } typ := n.X.Type() if typ.IsInterface() { typ = n.Y.Type() @@ -89,6 +105,9 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { assertOp(n, ir.OCONVIFACE) src, dst := n.X.Type(), n.Type() base.AssertfAt(dst.IsInterface(), n.Pos(), "want interface type, have %L", n) + if haveRType(n, n.TypeWord, "TypeWord", false) { + return n.TypeWord + } if dst.IsEmptyInterface() { return concreteRType(pos, src) // direct eface construction } @@ -98,12 +117,15 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { return TypePtrAt(pos, dst) // convI2I } -// ConvIfaceDataWordRType asserts that n is a conversion from +// ConvIfaceSrcRType asserts that n is a conversion from // non-interface type to interface type (or OCONVIDATA operation), and // returns an expression that yields the *runtime._type for copying // the convertee value to the heap. -func ConvIfaceDataWordRType(pos src.XPos, n *ir.ConvExpr) ir.Node { +func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { assertOp2(n, ir.OCONVIFACE, ir.OCONVIDATA) + if haveRType(n, n.SrcRType, "SrcRType", false) { + return n.SrcRType + } return concreteRType(pos, n.X.Type()) } @@ -112,6 +134,9 @@ func ConvIfaceDataWordRType(pos src.XPos, n *ir.ConvExpr) ir.Node { // destination slice type's element type. func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OCOPY) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return sliceElemRType(pos, n.X.Type()) } @@ -120,6 +145,9 @@ func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { // map type. func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.ODELETE) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return mapRType(pos, n.Args[0].Type()) } @@ -128,6 +156,9 @@ func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { // map type. func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { assertOp(n, ir.OINDEXMAP) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return mapRType(pos, n.X.Type()) } @@ -136,6 +167,9 @@ func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { // value representing that channel type. func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKECHAN) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return chanRType(pos, n.Type()) } @@ -144,6 +178,9 @@ func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKEMAP) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return mapRType(pos, n.Type()) } @@ -152,6 +189,9 @@ func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // value representing that slice type's element type. func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp2(n, ir.OMAKESLICE, ir.OMAKESLICECOPY) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return sliceElemRType(pos, n.Type()) } @@ -160,6 +200,9 @@ func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { assertOp(n, ir.ORANGE) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return mapRType(pos, n.X.Type()) } @@ -168,5 +211,8 @@ func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { // representing the result slice type's element type. func UnsafeSliceElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OUNSAFESLICE) + if haveRType(n, n.RType, "RType", false) { + return n.RType + } return sliceElemRType(pos, n.Type()) } diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go index e857f325ec..753dbc3e88 100644 --- a/src/cmd/compile/internal/walk/convert.go +++ b/src/cmd/compile/internal/walk/convert.go @@ -170,7 +170,7 @@ func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node { n = copyExpr(n, fromType, init) } fn = typecheck.SubstArgTypes(fn, fromType) - args = []ir.Node{reflectdata.ConvIfaceDataWordRType(base.Pos, conv), typecheck.NodAddr(n)} + args = []ir.Node{reflectdata.ConvIfaceSrcRType(base.Pos, conv), typecheck.NodAddr(n)} } else { // Use a specialized conversion routine that takes the type being // converted by value, not by pointer. -- cgit v1.3-6-g1900 From 7368647ac6eecd57a476616ff1f3ac1aa7f445b6 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 00:57:18 -0700 Subject: [dev.unified] cmd/compile: start setting RType fields for Unified IR This CL switches the GOEXPERIMENT=unified frontend to set RType fields in the simpler cases, and to make it fatal if they're missing. Subsequent CLs will handle the remaining more complex cases (e.g., expressions from later desugaring, and implicit conversions to interface type). Change-Id: If6257dcb3916905afd9b8371ea64b85f108ebbfb Reviewed-on: https://go-review.googlesource.com/c/go/+/413359 TryBot-Result: Gopher Robot Reviewed-by: Keith Randall Run-TryBot: Matthew Dempsky Reviewed-by: David Chase --- src/cmd/compile/internal/noder/reader.go | 54 +++++++++++++++++++++++-- src/cmd/compile/internal/reflectdata/helpers.go | 22 ++++++---- 2 files changed, 65 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 5ebc776605..7588e52d96 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1390,6 +1390,9 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { r.closeAnotherScope() rang := ir.NewRangeStmt(pos, nil, nil, x, body) + if x.Type().IsMap() { + rang.RType = reflectdata.TypePtrAt(pos, x.Type()) + } if len(lhs) >= 1 { rang.Key = lhs[0] if len(lhs) >= 2 { @@ -1632,7 +1635,13 @@ 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 = reflectdata.TypePtrAt(pos, x.Type()) + } + return n case exprSlice: x := r.expr() @@ -1654,6 +1663,7 @@ func (r *reader) expr() (res ir.Node) { if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE { assert := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.RType) + assert.SrcRType = reflectdata.TypePtrAt(pos, x.Type()) assert.ITab = typ.ITab return typed(typ.Type(), assert) } @@ -1682,7 +1692,19 @@ func (r *reader) expr() (res ir.Node) { case ir.OANDAND, ir.OOROR: return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y)) } - return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y)) + n := typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y)) + switch n.Op() { + case ir.OEQ, ir.ONE: + n := n.(*ir.BinaryExpr) + if n.X.Type().IsInterface() != n.Y.Type().IsInterface() { + typ := n.X.Type() + if typ.IsInterface() { + typ = n.Y.Type() + } + n.RType = reflectdata.TypePtrAt(pos, typ) + } + } + return n case exprCall: fun := r.expr() @@ -1694,13 +1716,37 @@ func (r *reader) expr() (res ir.Node) { pos := r.pos() args := r.exprs() 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 = reflectdata.TypePtrAt(pos, n.Type().Elem()) + case ir.OCOPY: + n := n.(*ir.BinaryExpr) + n.RType = reflectdata.TypePtrAt(pos, n.X.Type().Elem()) + case ir.ODELETE: + n := n.(*ir.CallExpr) + n.RType = reflectdata.TypePtrAt(pos, n.Args[0].Type()) + case ir.OUNSAFESLICE: + n := n.(*ir.BinaryExpr) + n.RType = reflectdata.TypePtrAt(pos, n.Type().Elem()) + } + return n case exprMake: pos := r.pos() typ := r.exprType(false) extra := r.exprs() - return typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))) + n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr) + switch n.Op() { + case ir.OMAKECHAN: + n.RType = reflectdata.TypePtrAt(pos, typ.Type()) + case ir.OMAKEMAP: + n.RType = reflectdata.TypePtrAt(pos, typ.Type()) + case ir.OMAKESLICE: + n.RType = reflectdata.TypePtrAt(pos, typ.Type().Elem()) + } + return n case exprNew: pos := r.pos() diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go index 4d85dea74d..22431a2bcb 100644 --- a/src/cmd/compile/internal/reflectdata/helpers.go +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -71,7 +71,7 @@ func concreteRType(pos src.XPos, typ *types.Type) ir.Node { // representing the result slice type's element type. func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.OAPPEND) - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.Type()) @@ -84,6 +84,8 @@ func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp2(n, ir.OEQ, ir.ONE) base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y) + // TODO(mdempsky): Need to propagate RType from OSWITCH/OCASE + // clauses to emitted OEQ nodes. if haveRType(n, n.RType, "RType", false) { return n.RType } @@ -105,6 +107,7 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { assertOp(n, ir.OCONVIFACE) src, dst := n.X.Type(), n.Type() base.AssertfAt(dst.IsInterface(), n.Pos(), "want interface type, have %L", n) + // TODO(mdempsky): Need to handle implicit interface conversions. if haveRType(n, n.TypeWord, "TypeWord", false) { return n.TypeWord } @@ -123,6 +126,7 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { // the convertee value to the heap. func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { assertOp2(n, ir.OCONVIFACE, ir.OCONVIDATA) + // TODO(mdempsky): Need to handle implicit interface conversions. if haveRType(n, n.SrcRType, "SrcRType", false) { return n.SrcRType } @@ -134,7 +138,7 @@ func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { // destination slice type's element type. func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OCOPY) - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.X.Type()) @@ -145,7 +149,7 @@ func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { // map type. func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.ODELETE) - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.Args[0].Type()) @@ -156,6 +160,8 @@ func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { // map type. func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { assertOp(n, ir.OINDEXMAP) + // TODO(mdempsky): Need to propagate RType from OMAPLIT nodes to + // emitted OINDEXMAP nodes. if haveRType(n, n.RType, "RType", false) { return n.RType } @@ -167,7 +173,7 @@ func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { // value representing that channel type. func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKECHAN) - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return chanRType(pos, n.Type()) @@ -178,6 +184,8 @@ func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKEMAP) + // TODO(mdempsky): Need to propagate RType from OMAPLIT nodes to + // emitted OMAKEMAP nodes. if haveRType(n, n.RType, "RType", false) { return n.RType } @@ -189,7 +197,7 @@ func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // value representing that slice type's element type. func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp2(n, ir.OMAKESLICE, ir.OMAKESLICECOPY) - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.Type()) @@ -200,7 +208,7 @@ func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { assertOp(n, ir.ORANGE) - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.X.Type()) @@ -211,7 +219,7 @@ func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { // representing the result slice type's element type. func UnsafeSliceElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OUNSAFESLICE) - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.Type()) -- cgit v1.3-6-g1900 From 3d432b6c4b86a5fcd1ccce0f914193b8e0e9e79e Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 02:29:49 -0700 Subject: [dev.unified] cmd/compile: plumb rtype through for OMAPLIT OMAPLIT gets lowered into a bunch of OINDEXMAP operations, which in general may require a *runtime._type argument. This CL adds CompLitExpr.RType, updates the GOEXPERIMENT=unified frontend to start setting it, and updates walk to propagate it through to any generated OINDEXMAP operations. Change-Id: I278e7e8e615ea6d01f65a5eba6d6fc8e00045735 Reviewed-on: https://go-review.googlesource.com/c/go/+/413360 Reviewed-by: David Chase Run-TryBot: Matthew Dempsky Reviewed-by: Keith Randall TryBot-Result: Gopher Robot --- src/cmd/compile/internal/ir/expr.go | 1 + src/cmd/compile/internal/noder/reader.go | 5 +++++ src/cmd/compile/internal/reflectdata/helpers.go | 8 ++------ src/cmd/compile/internal/walk/complit.go | 3 +++ src/cmd/compile/internal/walk/order.go | 1 + 5 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index 4a8db70904..0058a98824 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -194,6 +194,7 @@ type CompLitExpr struct { miniExpr origNode List Nodes // initialized values + RType Node `mknode:"-"` // *runtime._type for OMAPLIT map types Prealloc *Name // For OSLICELIT, Len is the backing array length. // For OMAPLIT, Len is the number of entries that we've removed from List and diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 7588e52d96..32276e7553 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1812,6 +1812,11 @@ func (r *reader) compLit() ir.Node { } lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems)) + switch lit.Op() { + case ir.OMAPLIT: + lit := lit.(*ir.CompLitExpr) + lit.RType = reflectdata.TypePtrAt(pos, typ) + } if typ0.IsPtr() { lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit)) lit.SetType(typ0) diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go index 22431a2bcb..66f1864474 100644 --- a/src/cmd/compile/internal/reflectdata/helpers.go +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -160,9 +160,7 @@ func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { // map type. func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { assertOp(n, ir.OINDEXMAP) - // TODO(mdempsky): Need to propagate RType from OMAPLIT nodes to - // emitted OINDEXMAP nodes. - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.X.Type()) @@ -184,9 +182,7 @@ func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKEMAP) - // TODO(mdempsky): Need to propagate RType from OMAPLIT nodes to - // emitted OMAKEMAP nodes. - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.Type()) diff --git a/src/cmd/compile/internal/walk/complit.go b/src/cmd/compile/internal/walk/complit.go index d8e5a955c2..7dec9ae6d8 100644 --- a/src/cmd/compile/internal/walk/complit.go +++ b/src/cmd/compile/internal/walk/complit.go @@ -416,6 +416,7 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { // make the map var args := []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(n.Len + int64(len(n.List)))} a := typecheck.Expr(ir.NewCallExpr(base.Pos, ir.OMAKE, nil, args)).(*ir.MakeExpr) + a.RType = n.RType a.SetEsc(n.Esc()) appendWalkStmt(init, ir.NewAssignStmt(base.Pos, m, a)) @@ -471,6 +472,7 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { // typechecker rewrites OINDEX to OINDEXMAP lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, kidx)).(*ir.IndexExpr) base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + lhs.RType = n.RType zero := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0)) cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(tk.NumElem())) @@ -510,6 +512,7 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { // typechecker rewrites OINDEX to OINDEXMAP lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, tmpkey)).(*ir.IndexExpr) base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + lhs.RType = n.RType var a ir.Node = ir.NewAssignStmt(base.Pos, lhs, tmpelem) a = typecheck.Stmt(a) diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index 2602e20563..525c29b96f 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -1452,6 +1452,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { for _, r := range dynamics { lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, r.Key)).(*ir.IndexExpr) base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + lhs.RType = n.RType as := ir.NewAssignStmt(base.Pos, lhs, r.Value) typecheck.Stmt(as) -- cgit v1.3-6-g1900 From 61ae2b734cdbc0db342036a2a026fe1fccdccde3 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 02:30:21 -0700 Subject: [dev.unified] cmd/compile: plumb rtype through OSWITCH/OCASE clauses For (value) switch statements, we may generate OEQ comparisons between values of interface and concrete type, which in turn may require access to the concrete type's RType. To plumb this through, this CL adds CaseClause.RTypes to hold the rtype values, updates the GOEXPERIMENT=unified frontend to set it, and updates walk to plumb rtypes through into generated OEQ nodes. Change-Id: I6f1de2a1167ce54f5770147498a0a591efb3f012 Reviewed-on: https://go-review.googlesource.com/c/go/+/413361 Reviewed-by: Keith Randall Reviewed-by: David Chase Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/ir/stmt.go | 11 +++++++++++ src/cmd/compile/internal/noder/reader.go | 23 ++++++++++++++++++++++- src/cmd/compile/internal/reflectdata/helpers.go | 4 +--- src/cmd/compile/internal/walk/switch.go | 19 +++++++++++++------ 4 files changed, 47 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index 6a82df58f8..10f8b5e394 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -170,6 +170,17 @@ type CaseClause struct { miniStmt Var *Name // declared variable for this case in type switch List Nodes // list of expressions for switch, early select + + // RTypes is a list of RType expressions, which are copied to the + // corresponding OEQ nodes that are emitted when switch statements + // are desugared. RTypes[i] must be non-nil if the emitted + // comparison for List[i] will be a mixed interface/concrete + // comparison; see reflectdata.CompareRType for details. + // + // Because mixed interface/concrete switch cases are rare, we allow + // len(RTypes) < len(List). Missing entries are implicitly nil. + RTypes Nodes + Body Nodes } diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 32276e7553..aa2cccf86b 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1487,7 +1487,7 @@ 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 { @@ -1498,9 +1498,30 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node { } } else { cases = r.exprList() + + tagType := types.Types[types.TBOOL] + if tag != nil { + tagType = tag.Type() + } + for i, cas := range cases { + if cas.Op() == ir.ONIL { + continue // never needs rtype + } + if tagType.IsInterface() != cas.Type().IsInterface() { + typ := tagType + if typ.IsInterface() { + typ = cas.Type() + } + for len(rtypes) < i { + rtypes = append(rtypes, nil) + } + rtypes = append(rtypes, reflectdata.TypePtr(typ)) + } + } } clause := ir.NewCaseStmt(pos, cases, nil) + clause.RTypes = rtypes if ident != nil { pos := r.pos() diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go index 66f1864474..5edb495a81 100644 --- a/src/cmd/compile/internal/reflectdata/helpers.go +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -84,9 +84,7 @@ func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp2(n, ir.OEQ, ir.ONE) base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y) - // TODO(mdempsky): Need to propagate RType from OSWITCH/OCASE - // clauses to emitted OEQ nodes. - if haveRType(n, n.RType, "RType", false) { + if haveRType(n, n.RType, "RType", true) { return n.RType } typ := n.X.Type() diff --git a/src/cmd/compile/internal/walk/switch.go b/src/cmd/compile/internal/walk/switch.go index 6cac8f2937..257903c0b3 100644 --- a/src/cmd/compile/internal/walk/switch.go +++ b/src/cmd/compile/internal/walk/switch.go @@ -85,8 +85,12 @@ func walkSwitchExpr(sw *ir.SwitchStmt) { defaultGoto = jmp } - for _, n1 := range ncase.List { - s.Add(ncase.Pos(), n1, jmp) + for i, n1 := range ncase.List { + var rtype ir.Node + if i < len(ncase.RTypes) { + rtype = ncase.RTypes[i] + } + s.Add(ncase.Pos(), n1, rtype, jmp) } // Process body. @@ -124,11 +128,12 @@ type exprSwitch struct { type exprClause struct { pos src.XPos lo, hi ir.Node + rtype ir.Node // *runtime._type for OEQ node jmp ir.Node } -func (s *exprSwitch) Add(pos src.XPos, expr, jmp ir.Node) { - c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp} +func (s *exprSwitch) Add(pos src.XPos, expr, rtype, jmp ir.Node) { + c := exprClause{pos: pos, lo: expr, hi: expr, rtype: rtype, jmp: jmp} if types.IsOrdered[s.exprname.Type().Kind()] && expr.Op() == ir.OLITERAL { s.clauses = append(s.clauses, c) return @@ -233,7 +238,7 @@ func (s *exprSwitch) flush() { // Add length case to outer switch. cas := ir.NewBasicLit(pos, constant.MakeInt64(runLen(run))) jmp := ir.NewBranchStmt(pos, ir.OGOTO, label) - outer.Add(pos, cas, jmp) + outer.Add(pos, cas, nil, jmp) } s.done.Append(ir.NewLabelStmt(s.pos, outerLabel)) outer.Emit(&s.done) @@ -342,7 +347,9 @@ func (c *exprClause) test(exprname ir.Node) ir.Node { } } - return ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo) + n := ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo) + n.RType = c.rtype + return n } func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool { -- cgit v1.3-6-g1900 From 20e1d5ac8cc269c8fc40d08e0b9e14ffe99d19d3 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 04:41:38 -0700 Subject: [dev.unified] cmd/compile: special case f(g()) calls in Unified IR For f(g()) calls where g() is multi-valued, we may need to insert implicit conversions to convert g()'s result values to f()'s parameter types. This CL refactors code slightly so this will be easier to handle. Change-Id: I3a432220dcb62daecf9a66030e8fa1f097e95f95 Reviewed-on: https://go-review.googlesource.com/c/go/+/413362 Reviewed-by: David Chase TryBot-Result: Gopher Robot Run-TryBot: Matthew Dempsky --- src/cmd/compile/internal/noder/reader.go | 11 +++++++++-- src/cmd/compile/internal/noder/writer.go | 23 +++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index aa2cccf86b..bed56d1be7 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1735,8 +1735,15 @@ func (r *reader) expr() (res ir.Node) { fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym)) } pos := r.pos() - args := r.exprs() - dots := r.Bool() + var args ir.Nodes + var dots bool + if r.Bool() { // f(g()) + call := r.expr() + args = []ir.Node{call} + } else { + args = r.exprs() + dots = r.Bool() + } n := typecheck.Call(pos, fun, args, dots) switch n.Op() { case ir.OAPPEND: diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 8ef63a0085..7bbd3abc5d 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1505,8 +1505,14 @@ func (w *writer) expr(expr syntax.Expr) { w.Code(exprCall) writeFunExpr() w.pos(expr) - w.exprs(expr.ArgList) - w.Bool(expr.HasDots) + if w.Bool(len(expr.ArgList) == 1 && isMultiValueExpr(w.p.info, expr.ArgList[0])) { + // f(g()) call + assert(!expr.HasDots) + w.expr(expr.ArgList[0]) + } else { + w.exprs(expr.ArgList) + w.Bool(expr.HasDots) + } } } @@ -1999,6 +2005,19 @@ func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool { return false } +// isMultiValueExpr reports whether expr is a function call expression +// that yields multiple values. +func isMultiValueExpr(info *types2.Info, expr syntax.Expr) bool { + tv, ok := info.Types[expr] + assert(ok) + assert(tv.IsValue()) + if tuple, ok := tv.Type.(*types2.Tuple); ok { + assert(tuple.Len() > 1) + return true + } + return false +} + // recvBase returns the base type for the given receiver parameter. func recvBase(recv *types2.Var) *types2.Named { typ := recv.Type() -- cgit v1.3-6-g1900 From c70e93ff3d1855b0d0a0508ab83dd751ee65f5b3 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 05:22:00 -0700 Subject: [dev.unified] cmd/compile/internal/typecheck: replace unreachable code with assert Since the removal of -G=0 mode, IR is always well-typed. And in well-typed IR, convlit will always returns expressions having real types (i.e., not untyped). Change-Id: I1ac99a88c94777829852519347a716d19af7948c Reviewed-on: https://go-review.googlesource.com/c/go/+/413363 Run-TryBot: Matthew Dempsky Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: David Chase --- src/cmd/compile/internal/typecheck/subr.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 3b0075e616..8295a4e560 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -301,24 +301,14 @@ func assignconvfn(n ir.Node, t *types.Type, context func() string) ir.Node { n = convlit1(n, t, false, context) if n.Type() == nil { - return n + base.Fatalf("cannot assign %v to %v", n, t) + } + if n.Type().IsUntyped() { + base.Fatalf("%L has untyped type", n) } if t.Kind() == types.TBLANK { return n } - - // Convert ideal bool from comparison to plain bool - // if the next step is non-bool (like interface{}). - if n.Type() == types.UntypedBool && !t.IsBoolean() { - if n.Op() == ir.ONAME || n.Op() == ir.OLITERAL { - r := ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n) - r.SetType(types.Types[types.TBOOL]) - r.SetTypecheck(1) - r.SetImplicit(true) - n = r - } - } - if types.Identical(n.Type(), t) { return n } -- cgit v1.3-6-g1900 From 9cb784ac6926a8b91c1a0dce99612e713150b30c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 06:36:28 -0700 Subject: [dev.unified] cmd/compile/internal/noder: add pkgWriter.typeOf helper Getting the type of a value expression is already a very common operation during writing, and it's going to become more common to handle implicit conversions. Change-Id: I5401c6b01546bbf8e85b1ed3fe4acf2835925e2f Reviewed-on: https://go-review.googlesource.com/c/go/+/413395 Reviewed-by: Keith Randall Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/writer.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 7bbd3abc5d..a562bec26f 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -120,6 +120,14 @@ func (pw *pkgWriter) unexpected(what string, p poser) { pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p) } +// typeOf returns the Type of the given value expression. +func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type { + tv, ok := pw.info.Types[expr] + assert(ok) + assert(tv.IsValue()) + return tv.Type +} + // A writer provides APIs for writing out an individual element. type writer struct { p *pkgWriter @@ -1258,9 +1266,7 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { var iface types2.Type if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) { - tv, ok := w.p.info.Types[guard.X] - assert(ok && tv.IsValue()) - iface = tv.Type + iface = w.p.typeOf(guard.X) w.pos(guard) if tag := guard.Lhs; w.Bool(tag != nil) { @@ -1410,8 +1416,7 @@ func (w *writer) expr(expr syntax.Expr) { w.selector(sel.Obj()) case *syntax.IndexExpr: - tv, ok := w.p.info.Types[expr.Index] - assert(ok && tv.IsValue()) + _ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation w.Code(exprIndex) w.expr(expr.X) @@ -1427,13 +1432,12 @@ func (w *writer) expr(expr syntax.Expr) { } case *syntax.AssertExpr: - tv, ok := w.p.info.Types[expr.X] - assert(ok && tv.IsValue()) + iface := w.p.typeOf(expr.X) w.Code(exprAssert) w.expr(expr.X) w.pos(expr) - w.exprType(tv.Type, expr.Type, false) + w.exprType(iface, expr.Type, false) case *syntax.Operation: if expr.Y == nil { @@ -1523,14 +1527,12 @@ func (w *writer) optExpr(expr syntax.Expr) { } func (w *writer) compLit(lit *syntax.CompositeLit) { - tv, ok := w.p.info.Types[lit] - assert(ok) + typ := w.p.typeOf(lit) w.Sync(pkgbits.SyncCompLit) w.pos(lit) - w.typ(tv.Type) + w.typ(typ) - typ := tv.Type if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { typ = ptr.Elem() } @@ -1562,9 +1564,7 @@ func (w *writer) compLit(lit *syntax.CompositeLit) { } func (w *writer) funcLit(expr *syntax.FuncLit) { - tv, ok := w.p.info.Types[expr] - assert(ok) - sig := tv.Type.(*types2.Signature) + sig := w.p.typeOf(expr).(*types2.Signature) body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict) -- cgit v1.3-6-g1900 From 5f5422a2ddcfcec8282bc2fde0729083eeeb2926 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 07:07:41 -0700 Subject: [dev.unified] cmd/compile/internal/noder: start writing implicit conversions This CL adds support for implicit conversions to the unified IR export data format, and starts inserting them in a few low-hanging places (send statements, index expressions). Subsequentl CLs will handle the remaining trickier cases. Change-Id: Iaea9d1c5df8432b61bd82578ab2ef02adaf26367 Reviewed-on: https://go-review.googlesource.com/c/go/+/413396 Run-TryBot: Matthew Dempsky Reviewed-by: Keith Randall Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/reader.go | 7 +++- src/cmd/compile/internal/noder/writer.go | 58 ++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index bed56d1be7..fed500bcf1 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1782,6 +1782,7 @@ func (r *reader) expr() (res ir.Node) { return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ)) case exprConvert: + implicit := r.Bool() typ := r.typ() pos := r.pos() x := r.expr() @@ -1799,7 +1800,11 @@ 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 := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x)) + if implicit && n.Op() != ir.OLITERAL { + n.(ImplicitNode).SetImplicit(true) + } + return n } } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index a562bec26f..77a40e526a 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1079,13 +1079,18 @@ func (w *writer) stmt1(stmt syntax.Stmt) { w.op(binOps[stmt.Op]) w.expr(stmt.Lhs) w.pos(stmt) - w.expr(stmt.Rhs) + + var typ types2.Type + if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr { + typ = w.p.typeOf(stmt.Lhs) + } + w.implicitExpr(stmt, typ, stmt.Rhs) default: w.Code(stmtAssign) w.pos(stmt) w.assignList(stmt.Lhs) - w.exprList(stmt.Rhs) + w.exprList(stmt.Rhs) // TODO(mdempsky): Implicit conversions to Lhs types. } case *syntax.BlockStmt: @@ -1130,17 +1135,19 @@ func (w *writer) stmt1(stmt syntax.Stmt) { case *syntax.ReturnStmt: w.Code(stmtReturn) w.pos(stmt) - w.exprList(stmt.Results) + w.exprList(stmt.Results) // TODO(mdempsky): Implicit conversions to result types. case *syntax.SelectStmt: w.Code(stmtSelect) w.selectStmt(stmt) case *syntax.SendStmt: + chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan) + w.Code(stmtSend) w.pos(stmt) w.expr(stmt.Chan) - w.expr(stmt.Value) + w.implicitExpr(stmt, chanType.Elem(), stmt.Value) case *syntax.SwitchStmt: w.Code(stmtSwitch) @@ -1196,7 +1203,7 @@ func (w *writer) declStmt(decl syntax.Decl) { w.Code(stmtAssign) w.pos(decl) w.assignList(namesAsExpr(decl.NameList)) - w.exprList(decl.Values) + w.exprList(decl.Values) // TODO(mdempsky): Implicit conversions to Lhs types. } } @@ -1213,6 +1220,11 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) { w.pos(rang) + // TODO(mdempsky): For !rang.Def, we need to handle implicit + // conversions; e.g., see #53328. + // + // This is tricky, because the assignments aren't introduced until + // lowering in walk. w.assignList(rang.Lhs) w.expr(rang.X) } else { @@ -1294,6 +1306,7 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.exprType(iface, cas, true) } } else { + // TODO(mdempsky): Implicit conversions to tagType, if appropriate. w.exprList(clause.Cases) } @@ -1418,10 +1431,15 @@ func (w *writer) expr(expr syntax.Expr) { case *syntax.IndexExpr: _ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation + var keyType types2.Type + if mapType, ok := types2.CoreType(w.p.typeOf(expr.X)).(*types2.Map); ok { + keyType = mapType.Key() + } + w.Code(exprIndex) w.expr(expr.X) w.pos(expr) - w.expr(expr.Index) + w.implicitExpr(expr, keyType, expr.Index) case *syntax.SliceExpr: w.Code(exprSlice) @@ -1448,6 +1466,7 @@ func (w *writer) expr(expr syntax.Expr) { break } + // TODO(mdempsky): Implicit conversions to common type. w.Code(exprBinaryOp) w.op(binOps[expr.Op]) w.expr(expr.X) @@ -1462,6 +1481,7 @@ func (w *writer) expr(expr syntax.Expr) { assert(!expr.HasDots) w.Code(exprConvert) + w.Bool(false) // explicit w.typ(tv.Type) w.pos(expr) w.expr(expr.ArgList[0]) @@ -1512,9 +1532,9 @@ func (w *writer) expr(expr syntax.Expr) { if w.Bool(len(expr.ArgList) == 1 && isMultiValueExpr(w.p.info, expr.ArgList[0])) { // f(g()) call assert(!expr.HasDots) - w.expr(expr.ArgList[0]) + w.expr(expr.ArgList[0]) // TODO(mdempsky): Implicit conversions to parameter types. } else { - w.exprs(expr.ArgList) + w.exprs(expr.ArgList) // TODO(mdempsky): Implicit conversions to parameter types. w.Bool(expr.HasDots) } } @@ -1526,6 +1546,24 @@ func (w *writer) optExpr(expr syntax.Expr) { } } +// implicitExpr is like expr, but if dst is non-nil and different from +// expr's type, then an implicit conversion operation is inserted at +// pos. +func (w *writer) implicitExpr(pos poser, dst types2.Type, expr syntax.Expr) { + src := w.p.typeOf(expr) + if dst != nil && !types2.Identical(src, dst) { + if !types2.AssignableTo(src, dst) { + w.p.fatalf(pos, "%v is not assignable to %v", src, dst) + } + w.Code(exprConvert) + w.Bool(true) // implicit + w.typ(dst) + w.pos(pos) + // fallthrough + } + w.expr(expr) +} + func (w *writer) compLit(lit *syntax.CompositeLit) { typ := w.p.typeOf(lit) @@ -1554,12 +1592,12 @@ func (w *writer) compLit(lit *syntax.CompositeLit) { if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) { // use position of expr.Key rather than of elem (which has position of ':') w.pos(kv.Key) - w.expr(kv.Key) + w.expr(kv.Key) // TODO(mdempsky): Implicit conversion to (map) key type. elem = kv.Value } } w.pos(elem) - w.expr(elem) + w.expr(elem) // TODO(mdempsky): Implicit conversion to element type. } } -- cgit v1.3-6-g1900 From a3e474f86746924022c92f66bfb465f7ad6ea8a5 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 07:39:23 -0700 Subject: [dev.unified] cmd/compile/internal/noder: implicit conversions for complits Operands within a composite literal must be implicitly converted to their respective key/element type. Change-Id: Idc12eba1559e9c9ffebd03395cd91473dd5fc2db Reviewed-on: https://go-review.googlesource.com/c/go/+/413364 Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: David Chase Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/writer.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 77a40e526a..e2d2aba072 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1574,30 +1574,45 @@ func (w *writer) compLit(lit *syntax.CompositeLit) { if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { typ = ptr.Elem() } - str, isStruct := types2.CoreType(typ).(*types2.Struct) + var keyType, elemType types2.Type + var structType *types2.Struct + switch typ := types2.CoreType(typ).(type) { + default: + w.p.fatalf(lit, "unexpected composite literal type: %v", typ) + case *types2.Array: + elemType = typ.Elem() + case *types2.Map: + keyType, elemType = typ.Key(), typ.Elem() + case *types2.Slice: + elemType = typ.Elem() + case *types2.Struct: + structType = typ + } w.Len(len(lit.ElemList)) for i, elem := range lit.ElemList { - if isStruct { + elemType := elemType + if structType != nil { if kv, ok := elem.(*syntax.KeyValueExpr); ok { // use position of expr.Key rather than of elem (which has position of ':') w.pos(kv.Key) - w.Len(fieldIndex(w.p.info, str, kv.Key.(*syntax.Name))) + i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name)) elem = kv.Value } else { w.pos(elem) - w.Len(i) } + elemType = structType.Field(i).Type() + w.Len(i) } else { if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) { // use position of expr.Key rather than of elem (which has position of ':') w.pos(kv.Key) - w.expr(kv.Key) // TODO(mdempsky): Implicit conversion to (map) key type. + w.implicitExpr(kv.Key, keyType, kv.Key) elem = kv.Value } } w.pos(elem) - w.expr(elem) // TODO(mdempsky): Implicit conversion to element type. + w.implicitExpr(elem, elemType, elem) } } -- cgit v1.3-6-g1900 From 46b01ec667c05fb8eb8f382b173e126282acd80c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 07:56:25 -0700 Subject: [dev.unified] cmd/compile/internal/noder: remove needType logic This logic is a holdover from very early on when it wasn't as clear how we would handle dictionary entries for derived types, particularly ones that are emitted during desugaring. However, the current plan is to explicitly wire runtime type info through IR nodes, so we can drop this logic. Notably, the "needed" bit is exposed to the go/types importers, so removing it would break the x/tools importer. To minimize churn for now, we can just leave the bools in place. Change-Id: I374927887d4f3d6d711d3355607849a407d717c4 Reviewed-on: https://go-review.googlesource.com/c/go/+/413367 Reviewed-by: David Chase Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Run-TryBot: Matthew Dempsky --- src/cmd/compile/internal/noder/writer.go | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index e2d2aba072..e773b8973d 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -179,7 +179,7 @@ type writerDict struct { // A derivedInfo represents a reference to an encoded generic Go type. type derivedInfo struct { idx pkgbits.Index - needed bool + needed bool // TODO(mdempsky): Remove; will break x/tools importer } // A typeInfo represents a reference to an encoded Go type. @@ -1362,13 +1362,6 @@ func (w *writer) expr(expr syntax.Expr) { targs := inst.TypeArgs if tv, ok := w.p.info.Types[expr]; ok { - // TODO(mdempsky): Be more judicious about which types are marked as "needed". - if inst.Type != nil { - w.needType(inst.Type) - } else { - w.needType(tv.Type) - } - if tv.IsType() { w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr)) } @@ -1714,20 +1707,6 @@ func (w *writer) op(op ir.Op) { w.Len(int(op)) } -func (w *writer) needType(typ types2.Type) { - // Decompose tuple into component element types. - if typ, ok := typ.(*types2.Tuple); ok { - for i := 0; i < typ.Len(); i++ { - w.needType(typ.At(i).Type()) - } - return - } - - if info := w.p.typIdx(typ, w.dict); info.derived { - w.dict.derived[info.idx].needed = true - } -} - // @@@ Package initialization // Caution: This code is still clumsy, because toolstash -cmp is -- cgit v1.3-6-g1900 From 711dacd8cf52d0c06624c4af3563d3b728c50b57 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 11:31:11 -0700 Subject: [dev.unified] cmd/compile/internal/noder: implicit conversion of call arguments Function call arguments need to be implicitly converted to their respective parameter types. This CL updates the Unified IR writer to handle this case, at least for typical function calls. I'll handle f(g()) calls is a subsequent CL. Change-Id: I7c031d21f57885c9516eaf89eca517977bf9e39a Reviewed-on: https://go-review.googlesource.com/c/go/+/413514 Reviewed-by: Keith Randall Reviewed-by: David Chase Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/writer.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index e773b8973d..5160cfaac6 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1519,6 +1519,9 @@ func (w *writer) expr(expr syntax.Expr) { w.Bool(false) // not a method call (i.e., normal function call) } + sigType := types2.CoreType(tv.Type).(*types2.Signature) + paramTypes := sigType.Params() + w.Code(exprCall) writeFunExpr() w.pos(expr) @@ -1527,7 +1530,20 @@ func (w *writer) expr(expr syntax.Expr) { assert(!expr.HasDots) w.expr(expr.ArgList[0]) // TODO(mdempsky): Implicit conversions to parameter types. } else { - w.exprs(expr.ArgList) // TODO(mdempsky): Implicit conversions to parameter types. + // Like w.exprs(expr.ArgList), but with implicit conversions to parameter types. + args := expr.ArgList + w.Sync(pkgbits.SyncExprs) + w.Len(len(args)) + for i, arg := range args { + var paramType types2.Type + if sigType.Variadic() && !expr.HasDots && i+1 >= paramTypes.Len() { + paramType = paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem() + } else { + paramType = paramTypes.At(i).Type() + } + w.implicitExpr(expr, paramType, arg) + } + w.Bool(expr.HasDots) } } -- cgit v1.3-6-g1900 From 82a958a661660fed6f126e41884a8c75fb983902 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 11:49:27 -0700 Subject: [dev.unified] cmd/compile/internal/noder: refactor stmtAssign generation Eliminate some code duplication between assignment statements and variable declarations, so they're easier to extend with implicit conversions. Change-Id: I605cf7817e3cb230f2c4612b777d8023c926e8b2 Reviewed-on: https://go-review.googlesource.com/c/go/+/413515 Reviewed-by: Keith Randall Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/writer.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 5160cfaac6..942bab4b2b 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1087,10 +1087,7 @@ func (w *writer) stmt1(stmt syntax.Stmt) { w.implicitExpr(stmt, typ, stmt.Rhs) default: - w.Code(stmtAssign) - w.pos(stmt) - w.assignList(stmt.Lhs) - w.exprList(stmt.Rhs) // TODO(mdempsky): Implicit conversions to Lhs types. + w.assignStmt(stmt, stmt.Lhs, stmt.Rhs) } case *syntax.BlockStmt: @@ -1200,13 +1197,18 @@ func (w *writer) declStmt(decl syntax.Decl) { case *syntax.ConstDecl, *syntax.TypeDecl: case *syntax.VarDecl: - w.Code(stmtAssign) - w.pos(decl) - w.assignList(namesAsExpr(decl.NameList)) - w.exprList(decl.Values) // TODO(mdempsky): Implicit conversions to Lhs types. + w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values) } } +// assignStmt writes out an assignment for "lhs = rhs". +func (w *writer) assignStmt(pos poser, lhs, rhs syntax.Expr) { + w.Code(stmtAssign) + w.pos(pos) + w.assignList(lhs) + w.exprList(rhs) // TODO(mdempsky): Implicit conversions to Lhs types. +} + func (w *writer) blockStmt(stmt *syntax.BlockStmt) { w.Sync(pkgbits.SyncBlockStmt) w.openScope(stmt.Pos()) -- cgit v1.3-6-g1900 From a3fea7796aed2437b222708e73299dc57bd409df Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 12:51:01 -0700 Subject: [dev.unified] cmd/compile/internal/noder: implicit conversions for writer.assignStmt This CL inserts implicit conversions for simple N:N assignment statements within Unified IR. A subsequent CL will handle N:1 assignments. Change-Id: I7e204c6ee9ffdb9fa2bc9146315fd79735c04628 Reviewed-on: https://go-review.googlesource.com/c/go/+/413516 Reviewed-by: David Chase Run-TryBot: Matthew Dempsky Reviewed-by: Keith Randall TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/writer.go | 48 ++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 942bab4b2b..7d4cdb014b 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1202,11 +1202,53 @@ func (w *writer) declStmt(decl syntax.Decl) { } // assignStmt writes out an assignment for "lhs = rhs". -func (w *writer) assignStmt(pos poser, lhs, rhs syntax.Expr) { +func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) { + lhs := unpackListExpr(lhs0) + rhs := unpackListExpr(rhs0) + w.Code(stmtAssign) w.pos(pos) - w.assignList(lhs) - w.exprList(rhs) // TODO(mdempsky): Implicit conversions to Lhs types. + + // As if w.assignList(lhs0). + w.Len(len(lhs)) + for _, expr := range lhs { + w.assign(expr) + } + + // As if w.exprList(rhs0), but with implicit conversions. + w.Sync(pkgbits.SyncExprList) + w.Sync(pkgbits.SyncExprs) + w.Len(len(rhs)) + if len(lhs) == len(rhs) { + for i, expr := range rhs { + dst := lhs[i] + + // Finding dstType is somewhat involved, because for VarDecl + // statements, the Names are only added to the info.{Defs,Uses} + // maps, not to info.Types. + var dstType types2.Type + if name, ok := unparen(dst).(*syntax.Name); ok { + if name.Value == "_" { + // ok: no implicit conversion + } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok { + dstType = def.Type() + } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok { + dstType = use.Type() + } else { + w.p.fatalf(dst, "cannot find type of destination object: %v", dst) + } + } else { + dstType = w.p.typeOf(dst) + } + + w.implicitExpr(pos, dstType, expr) + } + } else if len(rhs) == 0 { + // ok: variable declaration without values + } else { + assert(len(rhs) == 1) + w.expr(rhs[0]) // TODO(mdempsky): Implicit conversions to lhs types. + } } func (w *writer) blockStmt(stmt *syntax.BlockStmt) { -- cgit v1.3-6-g1900 From 421e9e9db21363a196f9c5d736749a6754803bff Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 21 Jun 2022 12:02:03 -0700 Subject: [dev.unified] cmd/compile: implicit conversions for return statements This CL inserts implicit conversions for simple N:N return statements. A subsequent CL will handle N:1 return statements. Change-Id: Ia672db3e214025510485e17d3d50d42ff01bc74e Reviewed-on: https://go-review.googlesource.com/c/go/+/413517 Reviewed-by: Keith Randall Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/writer.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 7d4cdb014b..ec744f4122 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -134,6 +134,9 @@ type writer struct { pkgbits.Encoder + // sig holds the signature for the current function body, if any. + sig *types2.Signature + // TODO(mdempsky): We should be able to prune localsIdx whenever a // scope closes, and then maybe we can just use the same map for // storing the TypeParams too (as their TypeName instead). @@ -957,6 +960,7 @@ func (w *writer) pragmaFlag(p ir.PragmaFlag) { // block), adding it to the export data func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) { w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody) + w.sig = sig w.dict = dict w.funcargs(sig) @@ -1132,7 +1136,25 @@ func (w *writer) stmt1(stmt syntax.Stmt) { case *syntax.ReturnStmt: w.Code(stmtReturn) w.pos(stmt) - w.exprList(stmt.Results) // TODO(mdempsky): Implicit conversions to result types. + + // As if w.exprList(stmt.Results), but with implicit conversions to result types. + w.Sync(pkgbits.SyncExprList) + exprs := unpackListExpr(stmt.Results) + w.Sync(pkgbits.SyncExprs) + w.Len(len(exprs)) + + resultTypes := w.sig.Results() + if len(exprs) == resultTypes.Len() { + for i, expr := range exprs { + w.implicitExpr(stmt, resultTypes.At(i).Type(), expr) + } + } else if len(exprs) == 0 { + // ok: bare "return" with named result parameters + } else { + // TODO(mdempsky): Implicit conversions for "return g()", where g() is multi-valued. + assert(len(exprs) == 1) + w.expr(exprs[0]) + } case *syntax.SelectStmt: w.Code(stmtSelect) -- cgit v1.3-6-g1900 From 09a838ad86880150f4e297f7b2dec7c7d116623b Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 23 Jun 2022 13:07:32 -0700 Subject: [dev.unified] cmd/compile: rename haveRType and implicitExpr This CL renames: 1. "haveRType" to "hasRType", suggested by drchase@ during review of CL 413358; and 2. "implicitExpr" to "implicitConvExpr", suggested by khr@ during review of CL 413396. Change-Id: Ibb4deae20908d960706640991ea44d1b9c0b9e3c Reviewed-on: https://go-review.googlesource.com/c/go/+/413854 Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: Keith Randall Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/writer.go | 20 +++++++++---------- src/cmd/compile/internal/reflectdata/helpers.go | 26 ++++++++++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index ec744f4122..0537d1d3b2 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1088,7 +1088,7 @@ func (w *writer) stmt1(stmt syntax.Stmt) { if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr { typ = w.p.typeOf(stmt.Lhs) } - w.implicitExpr(stmt, typ, stmt.Rhs) + w.implicitConvExpr(stmt, typ, stmt.Rhs) default: w.assignStmt(stmt, stmt.Lhs, stmt.Rhs) @@ -1146,7 +1146,7 @@ func (w *writer) stmt1(stmt syntax.Stmt) { resultTypes := w.sig.Results() if len(exprs) == resultTypes.Len() { for i, expr := range exprs { - w.implicitExpr(stmt, resultTypes.At(i).Type(), expr) + w.implicitConvExpr(stmt, resultTypes.At(i).Type(), expr) } } else if len(exprs) == 0 { // ok: bare "return" with named result parameters @@ -1166,7 +1166,7 @@ func (w *writer) stmt1(stmt syntax.Stmt) { w.Code(stmtSend) w.pos(stmt) w.expr(stmt.Chan) - w.implicitExpr(stmt, chanType.Elem(), stmt.Value) + w.implicitConvExpr(stmt, chanType.Elem(), stmt.Value) case *syntax.SwitchStmt: w.Code(stmtSwitch) @@ -1263,7 +1263,7 @@ func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) { dstType = w.p.typeOf(dst) } - w.implicitExpr(pos, dstType, expr) + w.implicitConvExpr(pos, dstType, expr) } } else if len(rhs) == 0 { // ok: variable declaration without values @@ -1498,7 +1498,7 @@ func (w *writer) expr(expr syntax.Expr) { w.Code(exprIndex) w.expr(expr.X) w.pos(expr) - w.implicitExpr(expr, keyType, expr.Index) + w.implicitConvExpr(expr, keyType, expr.Index) case *syntax.SliceExpr: w.Code(exprSlice) @@ -1607,7 +1607,7 @@ func (w *writer) expr(expr syntax.Expr) { } else { paramType = paramTypes.At(i).Type() } - w.implicitExpr(expr, paramType, arg) + w.implicitConvExpr(expr, paramType, arg) } w.Bool(expr.HasDots) @@ -1621,10 +1621,10 @@ func (w *writer) optExpr(expr syntax.Expr) { } } -// implicitExpr is like expr, but if dst is non-nil and different from +// implicitConvExpr is like expr, but if dst is non-nil and different from // expr's type, then an implicit conversion operation is inserted at // pos. -func (w *writer) implicitExpr(pos poser, dst types2.Type, expr syntax.Expr) { +func (w *writer) implicitConvExpr(pos poser, dst types2.Type, expr syntax.Expr) { src := w.p.typeOf(expr) if dst != nil && !types2.Identical(src, dst) { if !types2.AssignableTo(src, dst) { @@ -1682,12 +1682,12 @@ func (w *writer) compLit(lit *syntax.CompositeLit) { if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) { // use position of expr.Key rather than of elem (which has position of ':') w.pos(kv.Key) - w.implicitExpr(kv.Key, keyType, kv.Key) + w.implicitConvExpr(kv.Key, keyType, kv.Key) elem = kv.Value } } w.pos(elem) - w.implicitExpr(elem, elemType, elem) + w.implicitConvExpr(elem, elemType, elem) } } diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go index 5edb495a81..03d1ae3dc2 100644 --- a/src/cmd/compile/internal/reflectdata/helpers.go +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -11,7 +11,7 @@ import ( "cmd/internal/src" ) -func haveRType(n, rtype ir.Node, fieldName string, required bool) bool { +func hasRType(n, rtype ir.Node, fieldName string, required bool) bool { if rtype != nil { return true } @@ -71,7 +71,7 @@ func concreteRType(pos src.XPos, typ *types.Type) ir.Node { // representing the result slice type's element type. func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.OAPPEND) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.Type()) @@ -84,7 +84,7 @@ func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp2(n, ir.OEQ, ir.ONE) base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } typ := n.X.Type() @@ -106,7 +106,7 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { src, dst := n.X.Type(), n.Type() base.AssertfAt(dst.IsInterface(), n.Pos(), "want interface type, have %L", n) // TODO(mdempsky): Need to handle implicit interface conversions. - if haveRType(n, n.TypeWord, "TypeWord", false) { + if hasRType(n, n.TypeWord, "TypeWord", false) { return n.TypeWord } if dst.IsEmptyInterface() { @@ -125,7 +125,7 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { assertOp2(n, ir.OCONVIFACE, ir.OCONVIDATA) // TODO(mdempsky): Need to handle implicit interface conversions. - if haveRType(n, n.SrcRType, "SrcRType", false) { + if hasRType(n, n.SrcRType, "SrcRType", false) { return n.SrcRType } return concreteRType(pos, n.X.Type()) @@ -136,7 +136,7 @@ func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { // destination slice type's element type. func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OCOPY) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.X.Type()) @@ -147,7 +147,7 @@ func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { // map type. func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.ODELETE) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.Args[0].Type()) @@ -158,7 +158,7 @@ func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { // map type. func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { assertOp(n, ir.OINDEXMAP) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.X.Type()) @@ -169,7 +169,7 @@ func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { // value representing that channel type. func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKECHAN) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return chanRType(pos, n.Type()) @@ -180,7 +180,7 @@ func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKEMAP) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.Type()) @@ -191,7 +191,7 @@ func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // value representing that slice type's element type. func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp2(n, ir.OMAKESLICE, ir.OMAKESLICECOPY) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.Type()) @@ -202,7 +202,7 @@ func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { assertOp(n, ir.ORANGE) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return mapRType(pos, n.X.Type()) @@ -213,7 +213,7 @@ func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { // representing the result slice type's element type. func UnsafeSliceElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OUNSAFESLICE) - if haveRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType", true) { return n.RType } return sliceElemRType(pos, n.Type()) -- cgit v1.3-6-g1900 From 398d46d538e70852c6e8c50dc4fb9e1ef3c3a97c Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 6 Jun 2022 10:52:12 +0700 Subject: [dev.unified] cmd/compile/internal/types2: remove package height Same as CL 410342, but for types2. Updates #51734 Change-Id: I6d6cb8fbb7567d3acf0b8cec0fa74f1344b56a1c Reviewed-on: https://go-review.googlesource.com/c/go/+/410347 Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: Robert Griesemer Auto-Submit: Cuong Manh Le --- src/cmd/compile/internal/importer/iimport.go | 7 ++----- src/cmd/compile/internal/importer/ureader.go | 4 ++-- src/cmd/compile/internal/types2/object.go | 5 +---- src/cmd/compile/internal/types2/package.go | 12 +----------- src/cmd/compile/internal/types2/resolver.go | 10 ---------- src/cmd/compile/internal/types2/sizeof_test.go | 2 +- 6 files changed, 7 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index 576036bdce..440f557a80 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -139,22 +139,19 @@ func ImportData(imports map[string]*types2.Package, data, path string) (pkg *typ pkgPathOff := r.uint64() pkgPath := p.stringAt(pkgPathOff) pkgName := p.stringAt(r.uint64()) - pkgHeight := int(r.uint64()) + _ = int(r.uint64()) // was package height, but not necessary anymore. if pkgPath == "" { pkgPath = path } pkg := imports[pkgPath] if pkg == nil { - pkg = types2.NewPackageHeight(pkgPath, pkgName, pkgHeight) + pkg = types2.NewPackage(pkgPath, pkgName) imports[pkgPath] = pkg } else { if pkg.Name() != pkgName { errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) } - if pkg.Height() != pkgHeight { - errorf("conflicting heights %v and %v for package %q", pkg.Height(), pkgHeight, path) - } } p.pkgCache[pkgPathOff] = pkg diff --git a/src/cmd/compile/internal/importer/ureader.go b/src/cmd/compile/internal/importer/ureader.go index e5547b6d44..dc5614c841 100644 --- a/src/cmd/compile/internal/importer/ureader.go +++ b/src/cmd/compile/internal/importer/ureader.go @@ -162,9 +162,9 @@ func (r *reader) doPkg() *types2.Package { } name := r.String() - height := r.Len() + _ = r.Len() // was package height, but not necessary anymore. - pkg := types2.NewPackageHeight(path, name, height) + pkg := types2.NewPackage(path, name) r.p.imports[path] = pkg // TODO(mdempsky): The list of imported packages is important for diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index df080f071c..f373561809 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -189,7 +189,7 @@ func (obj *object) sameId(pkg *Package, name string) bool { // // Objects are ordered nil before non-nil, exported before // non-exported, then by name, and finally (for non-exported -// functions) by package height and path. +// functions) by package path. func (a *object) less(b *object) bool { if a == b { return false @@ -215,9 +215,6 @@ func (a *object) less(b *object) bool { return a.name < b.name } if !ea { - if a.pkg.height != b.pkg.height { - return a.pkg.height < b.pkg.height - } return a.pkg.path < b.pkg.path } diff --git a/src/cmd/compile/internal/types2/package.go b/src/cmd/compile/internal/types2/package.go index 8044e7e6a7..26f10645d2 100644 --- a/src/cmd/compile/internal/types2/package.go +++ b/src/cmd/compile/internal/types2/package.go @@ -14,7 +14,6 @@ type Package struct { name string scope *Scope imports []*Package - height int complete bool fake bool // scope lookup errors are silently dropped if package is fake (internal use only) cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go @@ -23,14 +22,8 @@ type Package struct { // NewPackage returns a new Package for the given package path and name. // The package is not complete and contains no explicit imports. func NewPackage(path, name string) *Package { - return NewPackageHeight(path, name, 0) -} - -// NewPackageHeight is like NewPackage, but allows specifying the -// package's height. -func NewPackageHeight(path, name string, height int) *Package { scope := NewScope(Universe, nopos, nopos, fmt.Sprintf("package %q", path)) - return &Package{path: path, name: name, scope: scope, height: height} + return &Package{path: path, name: name, scope: scope} } // Path returns the package path. @@ -39,9 +32,6 @@ func (pkg *Package) Path() string { return pkg.path } // Name returns the package name. func (pkg *Package) Name() string { return pkg.name } -// Height returns the package height. -func (pkg *Package) Height() int { return pkg.height } - // SetName sets the package name. func (pkg *Package) SetName(name string) { pkg.name = name } diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go index 5d498b6b2b..77881f493f 100644 --- a/src/cmd/compile/internal/types2/resolver.go +++ b/src/cmd/compile/internal/types2/resolver.go @@ -197,7 +197,6 @@ func (check *Checker) importPackage(pos syntax.Pos, path, dir string) *Package { // methods with receiver base type names. func (check *Checker) collectObjects() { pkg := check.pkg - pkg.height = 0 // pkgImports is the set of packages already imported by any package file seen // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate @@ -255,15 +254,6 @@ func (check *Checker) collectObjects() { continue } - if imp == Unsafe { - // typecheck ignores imports of package unsafe for - // calculating height. - // TODO(mdempsky): Revisit this. This seems fine, but I - // don't remember explicitly considering this case. - } else if h := imp.height + 1; h > pkg.height { - pkg.height = h - } - // local name overrides imported package name name := imp.name if s.LocalPkgName != nil { diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index 17876d1f3c..af82b3fa7a 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -47,7 +47,7 @@ func TestSizeof(t *testing.T) { // Misc {Scope{}, 60, 104}, - {Package{}, 40, 80}, + {Package{}, 36, 72}, {_TypeSet{}, 28, 56}, } -- cgit v1.3-6-g1900 From 4b78ece3d7cc34ae7a5175177159599d381400c4 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 8 Jun 2022 11:05:28 +0700 Subject: [dev.unified] cmd/compile: drop package height from Unified IR importer CL 410342 removed package height, but still needs to keep writing out 0 for iexport for compatibility with existing importers. With Unified IR, we don't have to, so get rid of the package height completely. Change-Id: I84a285cbaddd7bb0833d45a24a6818231b4d2b71 Reviewed-on: https://go-review.googlesource.com/c/go/+/411014 Reviewed-by: Matthew Dempsky Run-TryBot: Cuong Manh Le Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Auto-Submit: Cuong Manh Le --- src/cmd/compile/internal/importer/ureader.go | 2 -- src/cmd/compile/internal/noder/reader.go | 1 - src/cmd/compile/internal/noder/writer.go | 1 - src/go/internal/gcimporter/ureader.go | 4 +--- 4 files changed, 1 insertion(+), 7 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/importer/ureader.go b/src/cmd/compile/internal/importer/ureader.go index dc5614c841..ccd4c7c502 100644 --- a/src/cmd/compile/internal/importer/ureader.go +++ b/src/cmd/compile/internal/importer/ureader.go @@ -162,8 +162,6 @@ func (r *reader) doPkg() *types2.Package { } name := r.String() - _ = r.Len() // was package height, but not necessary anymore. - pkg := types2.NewPackage(path, name) r.p.imports[path] = pkg diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index fed500bcf1..c8ed8552cd 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -292,7 +292,6 @@ func (r *reader) doPkg() *types.Pkg { } name := r.String() - _ = r.Len() // was package height, but not necessary anymore. pkg := types.NewPkg(path, "") diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 0537d1d3b2..ff026ba5ca 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -328,7 +328,6 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path) w.String(path) w.String(pkg.Name()) - w.Len(0) // was package height, but not necessary anymore. w.Len(len(pkg.Imports())) for _, imp := range pkg.Imports() { diff --git a/src/go/internal/gcimporter/ureader.go b/src/go/internal/gcimporter/ureader.go index 3b14232c81..63718a59e1 100644 --- a/src/go/internal/gcimporter/ureader.go +++ b/src/go/internal/gcimporter/ureader.go @@ -198,10 +198,8 @@ func (r *reader) doPkg() *types.Package { } name := r.String() - height := r.Len() - // Was: "pkg := types.NewPackageHeight(path, name, height)" - pkg, _ := types.NewPackage(path, name), height + pkg := types.NewPackage(path, name) r.p.imports[path] = pkg imports := make([]*types.Package, r.Len()) -- cgit v1.3-6-g1900 From e3cdc981c802350780a3eec109980acf28fc8746 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 29 Jun 2022 17:22:15 -0700 Subject: [dev.unified] cmd/compile/internal/walk: fix typo in debug print We want to print `init` here. We called `ir.TakeInit(r)` earlier, so `r.Init()` always evaluates to nil at this point. Change-Id: I196fdcfbf5e63c80b7bff0cce1881c9e58302501 Reviewed-on: https://go-review.googlesource.com/c/go/+/415239 Auto-Submit: Matthew Dempsky Reviewed-by: David Chase Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/walk/order.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index 525c29b96f..19376f5bda 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -987,7 +987,7 @@ func (o *orderState) stmt(n ir.Node) { do(0, recv.X.Type().Elem()) do(1, types.Types[types.TBOOL]) if len(init) != 0 { - ir.DumpList("ninit", r.Init()) + ir.DumpList("ninit", init) base.Fatalf("ninit on select recv") } orderBlock(ncas.PtrInit(), o.free) -- cgit v1.3-6-g1900 From e7219cc093aca07bdb7179fa1a42d44e56eaf9d4 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 24 Jun 2022 14:19:06 -0700 Subject: [dev.unified] cmd/compile/internal/noder: refactor N:1 expression handling Pull all multi-value expression handling logic into a new multiExpr helper method. Change-Id: I78ec2dfc523abcfa3368a1064df7045aade8e468 Reviewed-on: https://go-review.googlesource.com/c/go/+/415243 Reviewed-by: Cuong Manh Le Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: David Chase --- src/cmd/compile/internal/noder/reader.go | 29 +++++--- src/cmd/compile/internal/noder/writer.go | 124 +++++++++++++++---------------- src/internal/pkgbits/sync.go | 2 + 3 files changed, 79 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index c8ed8552cd..ea1465693c 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1240,7 +1240,7 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node { pos := r.pos() names, lhs := r.assignList() - rhs := r.exprList() + rhs := r.multiExpr() if len(rhs) == 0 { for _, name := range names { @@ -1308,7 +1308,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: @@ -1734,15 +1734,8 @@ func (r *reader) expr() (res ir.Node) { fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym)) } pos := r.pos() - var args ir.Nodes - var dots bool - if r.Bool() { // f(g()) - call := r.expr() - args = []ir.Node{call} - } else { - args = r.exprs() - dots = r.Bool() - } + args := r.multiExpr() + dots := r.Bool() n := typecheck.Call(pos, fun, args, dots) switch n.Op() { case ir.OAPPEND: @@ -1814,6 +1807,20 @@ func (r *reader) optExpr() ir.Node { return nil } +func (r *reader) multiExpr() []ir.Node { + r.Sync(pkgbits.SyncMultiExpr) + + exprs := make([]ir.Node, r.Len()) + if len(exprs) == 0 { + return nil + } + + for i := range exprs { + exprs[i] = r.expr() + } + return exprs +} + func (r *reader) compLit() ir.Node { r.Sync(pkgbits.SyncCompLit) pos := r.pos() diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index ff026ba5ca..7020a02616 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1136,24 +1136,11 @@ func (w *writer) stmt1(stmt syntax.Stmt) { w.Code(stmtReturn) w.pos(stmt) - // As if w.exprList(stmt.Results), but with implicit conversions to result types. - w.Sync(pkgbits.SyncExprList) - exprs := unpackListExpr(stmt.Results) - w.Sync(pkgbits.SyncExprs) - w.Len(len(exprs)) - resultTypes := w.sig.Results() - if len(exprs) == resultTypes.Len() { - for i, expr := range exprs { - w.implicitConvExpr(stmt, resultTypes.At(i).Type(), expr) - } - } else if len(exprs) == 0 { - // ok: bare "return" with named result parameters - } else { - // TODO(mdempsky): Implicit conversions for "return g()", where g() is multi-valued. - assert(len(exprs) == 1) - w.expr(exprs[0]) + dstType := func(i int) types2.Type { + return resultTypes.At(i).Type() } + w.multiExpr(stmt, dstType, unpackListExpr(stmt.Results)) case *syntax.SelectStmt: w.Code(stmtSelect) @@ -1236,40 +1223,28 @@ func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) { w.assign(expr) } - // As if w.exprList(rhs0), but with implicit conversions. - w.Sync(pkgbits.SyncExprList) - w.Sync(pkgbits.SyncExprs) - w.Len(len(rhs)) - if len(lhs) == len(rhs) { - for i, expr := range rhs { - dst := lhs[i] - - // Finding dstType is somewhat involved, because for VarDecl - // statements, the Names are only added to the info.{Defs,Uses} - // maps, not to info.Types. - var dstType types2.Type - if name, ok := unparen(dst).(*syntax.Name); ok { - if name.Value == "_" { - // ok: no implicit conversion - } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok { - dstType = def.Type() - } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok { - dstType = use.Type() - } else { - w.p.fatalf(dst, "cannot find type of destination object: %v", dst) - } + dstType := func(i int) types2.Type { + dst := lhs[i] + + // Finding dstType is somewhat involved, because for VarDecl + // statements, the Names are only added to the info.{Defs,Uses} + // maps, not to info.Types. + if name, ok := unparen(dst).(*syntax.Name); ok { + if name.Value == "_" { + return nil // ok: no implicit conversion + } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok { + return def.Type() + } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok { + return use.Type() } else { - dstType = w.p.typeOf(dst) + w.p.fatalf(dst, "cannot find type of destination object: %v", dst) } - - w.implicitConvExpr(pos, dstType, expr) } - } else if len(rhs) == 0 { - // ok: variable declaration without values - } else { - assert(len(rhs) == 1) - w.expr(rhs[0]) // TODO(mdempsky): Implicit conversions to lhs types. + + return w.p.typeOf(dst) } + + w.multiExpr(pos, dstType, rhs) } func (w *writer) blockStmt(stmt *syntax.BlockStmt) { @@ -1590,27 +1565,16 @@ func (w *writer) expr(expr syntax.Expr) { w.Code(exprCall) writeFunExpr() w.pos(expr) - if w.Bool(len(expr.ArgList) == 1 && isMultiValueExpr(w.p.info, expr.ArgList[0])) { - // f(g()) call - assert(!expr.HasDots) - w.expr(expr.ArgList[0]) // TODO(mdempsky): Implicit conversions to parameter types. - } else { - // Like w.exprs(expr.ArgList), but with implicit conversions to parameter types. - args := expr.ArgList - w.Sync(pkgbits.SyncExprs) - w.Len(len(args)) - for i, arg := range args { - var paramType types2.Type - if sigType.Variadic() && !expr.HasDots && i+1 >= paramTypes.Len() { - paramType = paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem() - } else { - paramType = paramTypes.At(i).Type() - } - w.implicitConvExpr(expr, paramType, arg) - } - w.Bool(expr.HasDots) + paramType := func(i int) types2.Type { + if sigType.Variadic() && !expr.HasDots && i >= paramTypes.Len()-1 { + return paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem() + } + return paramTypes.At(i).Type() } + + w.multiExpr(expr, paramType, expr.ArgList) + w.Bool(expr.HasDots) } } @@ -1620,6 +1584,30 @@ func (w *writer) optExpr(expr syntax.Expr) { } } +// multiExpr writes a sequence of expressions, where the i'th value is +// implicitly converted to dstType(i). It also handles when exprs is a +// single, multi-valued expression (e.g., the multi-valued argument in +// an f(g()) call, or the RHS operand in a comma-ok assignment). +func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) { + w.Sync(pkgbits.SyncMultiExpr) + w.Len(len(exprs)) + + if len(exprs) == 1 { + expr := exprs[0] + if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok { + // N:1 assignment + assert(tuple.Len() > 1) + w.expr(expr) // TODO(mdempsky): Implicit conversions to dstTypes. + return + } + } + + // N:N assignment + for i, expr := range exprs { + w.implicitConvExpr(pos, dstType(i), expr) + } +} + // implicitConvExpr is like expr, but if dst is non-nil and different from // expr's type, then an implicit conversion operation is inserted at // pos. @@ -2032,6 +2020,12 @@ func (w *writer) pkgDecl(decl syntax.Decl) { w.Code(declVar) w.pos(decl) w.pkgObjs(decl.NameList...) + + // TODO(mdempsky): It would make sense to use multiExpr here, but + // that results in IR that confuses pkginit/initorder.go. So we + // continue using exprList, and let typecheck handle inserting any + // implicit conversions. That's okay though, because package-scope + // assignments never require dictionaries. w.exprList(decl.Values) var embeds []pragmaEmbed diff --git a/src/internal/pkgbits/sync.go b/src/internal/pkgbits/sync.go index 77178af6ce..90301c32b7 100644 --- a/src/internal/pkgbits/sync.go +++ b/src/internal/pkgbits/sync.go @@ -121,4 +121,6 @@ const ( SyncStmtsEnd SyncLabel SyncOptLabel + + SyncMultiExpr ) -- cgit v1.3-6-g1900 From 3635b07d16c9fe3f344b3271fb3bf7029d8b4001 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 28 Jun 2022 16:31:29 -0700 Subject: [dev.unified] cmd/compile/internal/noder: implicit conversions for multi-valued expressions This CL changes GOEXPERIMENT=unified to insert implicit conversions for multi-valued expressions. Unfortunately, IR doesn't have strong, first-class support for multi-valued expressions, so this CL takes the approach of spilling them to temporary variables, which can then be implicitly converted. This is the same approach taken by walk, but doing it this early does introduce some minor complications: 1. For select case clauses with comma-ok assignments (e.g., `case x, ok := <-ch:`), the compiler middle end wants to see the OAS2RECV assignment is the CommClause.Comm statement. So when constructing select statements, we need to massage this around a little. 2. The extra temporary variables and assignments skew the existing inlining heuristics. As mentioned, the temporaries/assignments will eventually be added (and often optimized away again) anyway, but now they're visible to the inliner. So this CL also kludges the inlining heuristics in this case to keep things comparable. Change-Id: I3e3ea756ad92472ebe28bae3963be61ed7684a75 Reviewed-on: https://go-review.googlesource.com/c/go/+/415244 Reviewed-by: Cuong Manh Le Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/inline/inl.go | 30 +++++++++++++++++++++ src/cmd/compile/internal/noder/reader.go | 45 ++++++++++++++++++++++++++++++-- src/cmd/compile/internal/noder/writer.go | 26 +++++++++++++++--- test/escape_iface_unified.go | 5 ++-- 4 files changed, 97 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index 9ef016ab73..b6f80a1723 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -430,6 +430,36 @@ func (v *hairyVisitor) doNode(n ir.Node) bool { case ir.OMETHEXPR: v.budget++ // Hack for toolstash -cmp. + + case ir.OAS2: + n := n.(*ir.AssignListStmt) + + // Unified IR unconditionally rewrites: + // + // a, b = f() + // + // into: + // + // DCL tmp1 + // DCL tmp2 + // tmp1, tmp2 = f() + // a, b = tmp1, tmp2 + // + // so that it can insert implicit conversions as necessary. To + // minimize impact to the existing inlining heuristics (in + // particular, to avoid breaking the existing inlinability regress + // tests), we need to compensate for this here. + if base.Debug.Unified != 0 { + if init := n.Rhs[0].Init(); len(init) == 1 { + if _, ok := init[0].(*ir.AssignListStmt); ok { + // 4 for each value, because each temporary variable now + // appears 3 times (DCL, LHS, RHS), plus an extra DCL node. + // + // 1 for the extra "tmp1, tmp2 = f()" assignment statement. + v.budget += 4*int32(len(n.Lhs)) + 1 + } + } + } } v.budget-- diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index ea1465693c..c56c658bef 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1238,7 +1238,6 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node { case stmtAssign: pos := r.pos() - names, lhs := r.assignList() rhs := r.multiExpr() @@ -1444,6 +1443,18 @@ func (r *reader) selectStmt(label *types.Sym) ir.Node { comm := r.stmt() body := r.stmts() + // 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 { @@ -1810,11 +1821,41 @@ func (r *reader) optExpr() ir.Node { func (r *reader) multiExpr() []ir.Node { r.Sync(pkgbits.SyncMultiExpr) + if r.Bool() { // N:1 + pos := r.pos() + expr := r.expr() + + // See typecheck.typecheckargs. + curfn := r.curfn + if curfn == nil { + curfn = typecheck.InitTodoFunc + } + + results := make([]ir.Node, r.Len()) + as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr}) + as.Def = true + for i := range results { + tmp := typecheck.TempAt(pos, curfn, r.typ()) + as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp)) + as.Lhs.Append(tmp) + + res := ir.Node(tmp) + if r.Bool() { + res = typecheck.Expr(Implicit(ir.NewConvExpr(pos, ir.OCONV, r.typ(), res))) + } + 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() } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 7020a02616..3c247dff4e 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1590,19 +1590,37 @@ func (w *writer) optExpr(expr syntax.Expr) { // an f(g()) call, or the RHS operand in a comma-ok assignment). func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) { w.Sync(pkgbits.SyncMultiExpr) - w.Len(len(exprs)) if len(exprs) == 1 { expr := exprs[0] if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok { - // N:1 assignment assert(tuple.Len() > 1) - w.expr(expr) // TODO(mdempsky): Implicit conversions to dstTypes. + w.Bool(true) // N:1 assignment + w.pos(pos) + w.expr(expr) + + w.Len(tuple.Len()) + for i := 0; i < tuple.Len(); i++ { + src := tuple.At(i).Type() + // TODO(mdempsky): Investigate not writing src here. I think + // the reader should be able to infer it from expr anyway. + w.typ(src) + if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) { + if src == nil || dst == nil { + w.p.fatalf(pos, "src is %v, dst is %v", src, dst) + } + if !types2.AssignableTo(src, dst) { + w.p.fatalf(pos, "%v is not assignable to %v", src, dst) + } + w.typ(dst) + } + } return } } - // N:N assignment + w.Bool(false) // N:N assignment + w.Len(len(exprs)) for i, expr := range exprs { w.implicitConvExpr(pos, dstType(i), expr) } diff --git a/test/escape_iface_unified.go b/test/escape_iface_unified.go index 7ac8e00151..80222dae5f 100644 --- a/test/escape_iface_unified.go +++ b/test/escape_iface_unified.go @@ -18,8 +18,7 @@ func dotTypeEscape2() { // #13805, #15796 var x interface{} = i // ERROR "i does not escape" var y interface{} = j // ERROR "j does not escape" - sink = x.(int) // ERROR "x.\(int\) escapes to heap" - // BAD: should be "y.\(int\) escapes to heap" too - sink, *(&ok) = y.(int) + sink = x.(int) // ERROR "x.\(int\) escapes to heap" + sink, *(&ok) = y.(int) // ERROR "autotmp_.* escapes to heap" } } -- cgit v1.3-6-g1900 From 0a503cf43a58e3a514f254c7dba1daf654557b17 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 29 Jun 2022 19:23:34 -0700 Subject: [dev.unified] cmd/compile: refactor `range` desugaring This CL refactors the code responsible for emitting the user-visible assignments within a range statement. This will make it easier to propagate RTTI from the frontend into any implicit conversions. Updates #53328. Change-Id: Ibed15e3b4951b0a6a726067b401a630977f4c6c2 Reviewed-on: https://go-review.googlesource.com/c/go/+/415158 Reviewed-by: David Chase Reviewed-by: Cuong Manh Le Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/walk/range.go | 37 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index 16d7595baa..60eec25bcf 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -103,7 +103,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // for v1 := range ha { body } if v2 == nil { - body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)} + body = []ir.Node{rangeAssign(nrange, hv1)} break } @@ -112,10 +112,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // v1, v2 = hv1, ha[hv1] tmp := ir.NewIndexExpr(base.Pos, ha, hv1) tmp.SetBounded(true) - // Use OAS2 to correctly handle assignments - // of the form "v1, a[v1] := range". - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1, tmp}) - body = []ir.Node{a} + body = []ir.Node{rangeAssign2(nrange, hv1, tmp)} break } @@ -140,9 +137,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { tmp.SetBounded(true) init = append(init, ir.NewAssignStmt(base.Pos, hp, typecheck.NodAddr(tmp))) - // Use OAS2 to correctly handle assignments - // of the form "v1, a[v1] := range". - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1, ir.NewStarExpr(base.Pos, hp)}) + a := rangeAssign2(nrange, hv1, ir.NewStarExpr(base.Pos, hp)) body = append(body, a) // Advance pointer as part of the late increment. @@ -179,11 +174,10 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { if v1 == nil { body = nil } else if v2 == nil { - body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, key)} + body = []ir.Node{rangeAssign(nrange, key)} } else { elem := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym)) - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{key, elem}) - body = []ir.Node{a} + body = []ir.Node{rangeAssign2(nrange, key, elem)} } case types.TCHAN: @@ -206,7 +200,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { if v1 == nil { body = nil } else { - body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)} + body = []ir.Node{rangeAssign(nrange, hv1)} } // Zero hv1. This prevents hv1 from being the sole, inaccessible // reference to an otherwise GC-able value during the next channel receive. @@ -271,11 +265,10 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { if v1 != nil { if v2 != nil { // v1, v2 = hv1t, hv2 - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1t, hv2}) - body = append(body, a) + body = append(body, rangeAssign2(nrange, hv1t, hv2)) } else { // v1 = hv1t - body = append(body, ir.NewAssignStmt(base.Pos, v1, hv1t)) + body = append(body, rangeAssign(nrange, hv1t)) } } } @@ -310,6 +303,20 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { return n } +// rangeAssign returns "n.Key = key". +func rangeAssign(n *ir.RangeStmt, key ir.Node) ir.Node { + // TODO(mdempsky): Implicit conversions for test/typeparam/mdempsky/17.go. + return ir.NewAssignStmt(n.Pos(), n.Key, key) +} + +// rangeAssign2 returns "n.Key, n.Value = key, value". +func rangeAssign2(n *ir.RangeStmt, key, value ir.Node) ir.Node { + // Use OAS2 to correctly handle assignments + // of the form "v1, a[v1] = range". + // TODO(mdempsky): Implicit conversions for test/typeparam/mdempsky/17.go. + return ir.NewAssignListStmt(n.Pos(), ir.OAS2, []ir.Node{n.Key, n.Value}, []ir.Node{key, value}) +} + // isMapClear checks if n is of the form: // // for k := range m { -- cgit v1.3-6-g1900 From d667be88310de6f91d9f1a88d949ea6c5b48650d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 30 Jun 2022 14:49:28 -0700 Subject: [dev.unified] cmd/compile/internal/walk: RType fields for range assignments This CL adds extra fields to RangeStmt that can be used when desugaring into primitive assignment statements. This will allow the frontend to wire up all of the RTTI necessary, pulling from dictionaries as necessary. Updates #53328. Change-Id: Iab0e3029ff18c947782ff24f71ef20b2b5cb8305 Reviewed-on: https://go-review.googlesource.com/c/go/+/415518 TryBot-Result: Gopher Robot Reviewed-by: Cuong Manh Le Auto-Submit: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: David Chase --- src/cmd/compile/internal/ir/stmt.go | 8 ++++++++ src/cmd/compile/internal/walk/range.go | 20 ++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index 10f8b5e394..cae773b722 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -350,6 +350,14 @@ type RangeStmt struct { Body Nodes HasBreak bool Prealloc *Name + + // When desugaring the RangeStmt during walk, the assignments to Key + // and Value may require OCONVIFACE operations. If so, these fields + // will be copied to their respective ConvExpr fields. + KeyTypeWord Node `mknode:"-"` + KeySrcRType Node `mknode:"-"` + ValueTypeWord Node `mknode:"-"` + ValueSrcRType Node `mknode:"-"` } func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt { diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index 60eec25bcf..b697c243c7 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -305,7 +305,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // rangeAssign returns "n.Key = key". func rangeAssign(n *ir.RangeStmt, key ir.Node) ir.Node { - // TODO(mdempsky): Implicit conversions for test/typeparam/mdempsky/17.go. + key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType) return ir.NewAssignStmt(n.Pos(), n.Key, key) } @@ -313,10 +313,26 @@ func rangeAssign(n *ir.RangeStmt, key ir.Node) ir.Node { func rangeAssign2(n *ir.RangeStmt, key, value ir.Node) ir.Node { // Use OAS2 to correctly handle assignments // of the form "v1, a[v1] = range". - // TODO(mdempsky): Implicit conversions for test/typeparam/mdempsky/17.go. + key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType) + value = rangeConvert(n, n.Value.Type(), value, n.ValueTypeWord, n.ValueSrcRType) return ir.NewAssignListStmt(n.Pos(), ir.OAS2, []ir.Node{n.Key, n.Value}, []ir.Node{key, value}) } +// rangeConvert returns src, converted to dst if necessary. If a +// conversion is necessary, then typeWord and srcRType are copied to +// their respective ConvExpr fields. +func rangeConvert(nrange *ir.RangeStmt, dst *types.Type, src, typeWord, srcRType ir.Node) ir.Node { + src = typecheck.Expr(src) + if dst.Kind() == types.TBLANK || types.Identical(dst, src.Type()) { + return src + } + + n := ir.NewConvExpr(nrange.Pos(), ir.OCONV, dst, src) + n.TypeWord = typeWord + n.SrcRType = srcRType + return typecheck.Expr(n) +} + // isMapClear checks if n is of the form: // // for k := range m { -- cgit v1.3-6-g1900 From 9371a6558412f621b5fbb54c42b43b93f8080e68 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 12 Jul 2022 11:04:44 -0700 Subject: internal/pkgbits: change EnableSync into a dynamic knob Rather than requiring users to recompile the compiler and all tools to enable/disable sync markers, this CL adds a flag word into the Unified IR file format to allow indicating whether they're enabled or not. This in turn requires bumping the file format version. Thanks to drchase@ for benchmarks showing this isn't as expensive as I feared it would be. Change-Id: I99afa0ee0b6ef5f30ed8ca840805ff9fd46b1857 Reviewed-on: https://go-review.googlesource.com/c/go/+/417097 Reviewed-by: David Chase Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/base/flag.go | 1 + src/cmd/compile/internal/noder/reader.go | 2 +- src/cmd/compile/internal/noder/writer.go | 2 +- src/internal/pkgbits/decoder.go | 26 ++++++++++++++++++++++---- src/internal/pkgbits/encoder.go | 25 ++++++++++++++++++++++--- src/internal/pkgbits/flags.go | 9 +++++++++ src/internal/pkgbits/sync.go | 11 ----------- 7 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 src/internal/pkgbits/flags.go (limited to 'src') diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go index 4de0df21cb..df828940ac 100644 --- a/src/cmd/compile/internal/base/flag.go +++ b/src/cmd/compile/internal/base/flag.go @@ -165,6 +165,7 @@ func ParseFlags() { if buildcfg.Experiment.Unified { Debug.Unified = 1 } + Debug.SyncFrames = -1 // disable sync markers by default Debug.Checkptr = -1 // so we can tell whether it is set explicitly diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index c56c658bef..00aafff2d9 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1072,7 +1072,7 @@ 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 { + if r.p.SyncMarkers() { want := r.Int() if have := len(r.locals); have != want { base.FatalfAt(name.Pos(), "locals table has desynced") diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 3c247dff4e..7830b94cd8 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -995,7 +995,7 @@ func (w *writer) funcarg(param *types2.Var, result bool) { func (w *writer) addLocal(obj *types2.Var) { w.Sync(pkgbits.SyncAddLocal) idx := len(w.localsIdx) - if pkgbits.EnableSync { + if w.p.SyncMarkers() { w.Int(idx) } if w.localsIdx == nil { diff --git a/src/internal/pkgbits/decoder.go b/src/internal/pkgbits/decoder.go index 0b5fd9705c..5e233b8770 100644 --- a/src/internal/pkgbits/decoder.go +++ b/src/internal/pkgbits/decoder.go @@ -18,6 +18,12 @@ import ( // A PkgDecoder provides methods for decoding a package's Unified IR // export data. type PkgDecoder struct { + // version is the file format version. + version uint32 + + // sync indicates whether the file uses sync markers. + sync bool + // pkgPath is the package path for the package to be decoded. // // TODO(mdempsky): Remove; unneeded since CL 391014. @@ -52,6 +58,9 @@ type PkgDecoder struct { // TODO(mdempsky): Remove; unneeded since CL 391014. func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath } +// SyncMarkers reports whether pr uses sync markers. +func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } + // NewPkgDecoder returns a PkgDecoder initialized to read the Unified // IR export data from input. pkgPath is the package path for the // compilation unit that produced the export data. @@ -67,9 +76,18 @@ func NewPkgDecoder(pkgPath, input string) PkgDecoder { r := strings.NewReader(input) - var version uint32 - assert(binary.Read(r, binary.LittleEndian, &version) == nil) - assert(version == 0) + assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil) + + switch pr.version { + default: + panic(fmt.Errorf("unsupported version: %v", pr.version)) + case 0: + // no flags + case 1: + var flags uint32 + assert(binary.Read(r, binary.LittleEndian, &flags) == nil) + pr.sync = flags&flagSyncMarkers != 0 + } assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) @@ -215,7 +233,7 @@ func (r *Decoder) rawReloc(k RelocKind, idx int) Index { // // If EnableSync is false, then Sync is a no-op. func (r *Decoder) Sync(mWant SyncMarker) { - if !EnableSync { + if !r.common.sync { return } diff --git a/src/internal/pkgbits/encoder.go b/src/internal/pkgbits/encoder.go index 1326a135cf..c50c838caa 100644 --- a/src/internal/pkgbits/encoder.go +++ b/src/internal/pkgbits/encoder.go @@ -14,6 +14,13 @@ import ( "runtime" ) +// currentVersion is the current version number. +// +// - v0: initial prototype +// +// - v1: adds the flags uint32 word +const currentVersion uint32 = 1 + // A PkgEncoder provides methods for encoding a package's Unified IR // export data. type PkgEncoder struct { @@ -25,15 +32,21 @@ type PkgEncoder struct { // elems[RelocString][stringsIdx[s]] == s (if present). stringsIdx map[string]Index + // syncFrames is the number of frames to write at each sync + // marker. A negative value means sync markers are omitted. syncFrames int } +// SyncMarkers reports whether pw uses sync markers. +func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 } + // NewPkgEncoder returns an initialized PkgEncoder. // // syncFrames is the number of caller frames that should be serialized // at Sync points. Serializing additional frames results in larger // export data files, but can help diagnosing desync errors in -// higher-level Unified IR reader/writer code. +// higher-level Unified IR reader/writer code. If syncFrames is +// negative, then sync markers are omitted entirely. func NewPkgEncoder(syncFrames int) PkgEncoder { return PkgEncoder{ stringsIdx: make(map[string]Index), @@ -51,7 +64,13 @@ func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { assert(binary.Write(out, binary.LittleEndian, x) == nil) } - writeUint32(0) // version + writeUint32(currentVersion) + + var flags uint32 + if pw.SyncMarkers() { + flags |= flagSyncMarkers + } + writeUint32(flags) // Write elemEndsEnds. var sum uint32 @@ -204,7 +223,7 @@ func (w *Encoder) rawReloc(r RelocKind, idx Index) int { } func (w *Encoder) Sync(m SyncMarker) { - if !EnableSync { + if !w.p.SyncMarkers() { return } diff --git a/src/internal/pkgbits/flags.go b/src/internal/pkgbits/flags.go new file mode 100644 index 0000000000..654222745f --- /dev/null +++ b/src/internal/pkgbits/flags.go @@ -0,0 +1,9 @@ +// Copyright 2022 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. + +package pkgbits + +const ( + flagSyncMarkers = 1 << iota // file format contains sync markers +) diff --git a/src/internal/pkgbits/sync.go b/src/internal/pkgbits/sync.go index 90301c32b7..6a5999eb6b 100644 --- a/src/internal/pkgbits/sync.go +++ b/src/internal/pkgbits/sync.go @@ -9,17 +9,6 @@ import ( "strings" ) -// EnableSync controls whether sync markers are written into unified -// IR's export data format and also whether they're expected when -// reading them back in. They're inessential to the correct -// functioning of unified IR, but are helpful during development to -// detect mistakes. -// -// When sync is enabled, writer stack frames will also be included in -// the export data. Currently, a fixed number of frames are included, -// controlled by -d=syncframes (default 0). -const EnableSync = true - // fmtFrames formats a backtrace for reporting reader/writer desyncs. func fmtFrames(pcs ...uintptr) []string { res := make([]string, 0, len(pcs)) -- cgit v1.3-6-g1900 From 2cf632cd57fac7edacb9ad4621ce0234b81d0bc3 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 30 Jun 2022 19:38:10 -0700 Subject: [dev.unified] cmd/compile/internal/reflectdata: prefer ITabAddrAt in ConvIfaceTypeWord We already have an explicit `pos` parameter, so we should use ITabAddrAt instead of ITabAddr (which uses `base.Pos` instead). Change-Id: I7c8c5ae93d0ae7a6467cc972575cb547981576f0 Reviewed-on: https://go-review.googlesource.com/c/go/+/415578 Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/reflectdata/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go index 03d1ae3dc2..81bb18788f 100644 --- a/src/cmd/compile/internal/reflectdata/helpers.go +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -113,7 +113,7 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { return concreteRType(pos, src) // direct eface construction } if !src.IsInterface() { - return ITabAddr(src, dst) // direct iface construction + return ITabAddrAt(pos, src, dst) // direct iface construction } return TypePtrAt(pos, dst) // convI2I } -- cgit v1.3-6-g1900 From 76a82f09d6da7e6fe18b703e5497795a19a780df Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 18 Jul 2022 12:15:09 -0700 Subject: [dev.unified] cmd/compile/internal/noder: prefer *At functions Unified IR tries to avoid depending on base.Pos, so we should prefer explicit position arguments wherever possible. Change-Id: I7163b1b8c5244fe7c2a7989e6a3f459a21a23e81 Reviewed-on: https://go-review.googlesource.com/c/go/+/418096 Reviewed-by: Cuong Manh Le Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/reader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 00aafff2d9..d93859f6ef 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1525,7 +1525,7 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node { for len(rtypes) < i { rtypes = append(rtypes, nil) } - rtypes = append(rtypes, reflectdata.TypePtr(typ)) + rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), typ)) } } } @@ -1976,7 +1976,7 @@ func (r *reader) exprType(nilOK bool) ir.Node { 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 -- cgit v1.3-6-g1900 From c846fd8e136dce06b213cae1cf3b9ada423c078a Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 30 Jun 2022 19:16:23 -0700 Subject: [dev.unified] cmd/compile/internal/noder: implicit conversions for binary exprs Binary operations (except for shifts) require one operand to be assignable to the other's type. In particular, for equality comparisons, this can imply a conversion to interface type. Change-Id: Ic973c8287a40fdaefcf11458378574fdcd243b17 Reviewed-on: https://go-review.googlesource.com/c/go/+/415577 Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: David Chase Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/noder/writer.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 7830b94cd8..6036695de1 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1499,12 +1499,28 @@ func (w *writer) expr(expr syntax.Expr) { break } - // TODO(mdempsky): Implicit conversions to common type. + var commonType types2.Type + switch expr.Op { + case syntax.Shl, syntax.Shr: + // ok: operands are allowed to have different types + default: + xtyp := w.p.typeOf(expr.X) + ytyp := w.p.typeOf(expr.Y) + switch { + case types2.AssignableTo(xtyp, ytyp): + commonType = ytyp + case types2.AssignableTo(ytyp, xtyp): + commonType = xtyp + default: + w.p.fatalf(expr, "failed to find common type between %v and %v", xtyp, ytyp) + } + } + w.Code(exprBinaryOp) w.op(binOps[expr.Op]) - w.expr(expr.X) + w.implicitConvExpr(expr, commonType, expr.X) w.pos(expr) - w.expr(expr.Y) + w.implicitConvExpr(expr, commonType, expr.Y) case *syntax.CallExpr: tv, ok := w.p.info.Types[expr.Fun] -- cgit v1.3-6-g1900 From e376746e54aae4fb519f50bbe42656a2d34df285 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 30 Jun 2022 17:58:47 -0700 Subject: [dev.unified] cmd/compile/internal/noder: wire RTTI for implicit conversions This CL updates Unified IR to set the TypeWord and SrcRType fields on interface conversions, which will be necessary for dictionary support shortly. Change-Id: I9486b417f514ba4ec2ee8036194aa9ae3ad0ad93 Reviewed-on: https://go-review.googlesource.com/c/go/+/415575 Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/reader.go | 75 +++++++++++++++++++++++++++++--- src/cmd/compile/internal/noder/writer.go | 5 --- 2 files changed, 70 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index d93859f6ef..6b47c11749 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1399,6 +1399,18 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { } rang.Def = r.initDefn(rang, names) rang.Label = label + + { + keyType, valueType := rangeTypes(pos, x.Type()) + + if rang.Key != nil { + rang.KeyTypeWord, rang.KeySrcRType = convRTTI(pos, rang.Key.Type(), keyType) + } + if rang.Value != nil { + rang.ValueTypeWord, rang.ValueSrcRType = convRTTI(pos, rang.Value.Type(), valueType) + } + } + return rang } @@ -1414,6 +1426,28 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { return stmt } +// rangeTypes returns the types of values produced by ranging over a +// value of type typ. +func rangeTypes(pos src.XPos, typ *types.Type) (key, value *types.Type) { + switch typ.Kind() { + default: + base.FatalfAt(pos, "unexpected range type: %v", typ) + panic("unreachable") + case types.TPTR: // must be pointer to array + typ = typ.Elem() + base.AssertfAt(typ.Kind() == types.TARRAY, pos, "want array type, have %v", typ) + fallthrough + case types.TARRAY, types.TSLICE: + return types.Types[types.TINT], typ.Elem() + case types.TSTRING: + return types.Types[types.TINT], types.RuneType + case types.TMAP: + return typ.Key(), typ.Elem() + case types.TCHAN: + return typ.Elem(), nil + } +} + func (r *reader) ifStmt() ir.Node { r.Sync(pkgbits.SyncIfStmt) r.openScope() @@ -1803,14 +1837,42 @@ func (r *reader) expr() (res ir.Node) { base.ErrorExit() // harsh, but prevents constructing invalid IR } - n := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x)) - if implicit && n.Op() != ir.OLITERAL { - n.(ImplicitNode).SetImplicit(true) + n := ir.NewConvExpr(pos, ir.OCONV, typ, x) + n.TypeWord, n.SrcRType = convRTTI(pos, typ, x.Type()) + if implicit { + n.SetImplicit(true) } - return n + return typecheck.Expr(n) } } +// convRTTI returns the TypeWord and SrcRType expressions appropriate +// for a conversion from src to dst. +func convRTTI(pos src.XPos, dst, src *types.Type) (typeWord, srcRType ir.Node) { + if !dst.IsInterface() { + return + } + + // See reflectdata.ConvIfaceTypeWord. + switch { + case dst.IsEmptyInterface(): + if !src.IsInterface() { + typeWord = reflectdata.TypePtrAt(pos, src) // direct eface construction + } + case !src.IsInterface(): + typeWord = reflectdata.ITabAddrAt(pos, src, dst) // direct iface construction + default: + typeWord = reflectdata.TypePtrAt(pos, dst) // convI2I + } + + // See reflectdata.ConvIfaceSrcRType. + if !src.IsInterface() { + srcRType = reflectdata.TypePtrAt(pos, src) + } + + return +} + func (r *reader) optExpr() ir.Node { if r.Bool() { return r.expr() @@ -1841,7 +1903,10 @@ func (r *reader) multiExpr() []ir.Node { res := ir.Node(tmp) if r.Bool() { - res = typecheck.Expr(Implicit(ir.NewConvExpr(pos, ir.OCONV, r.typ(), res))) + n := ir.NewConvExpr(pos, ir.OCONV, r.typ(), res) + n.TypeWord, n.SrcRType = convRTTI(pos, n.Type(), n.X.Type()) + n.SetImplicit(true) + res = typecheck.Expr(n) } results[i] = res } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 6036695de1..7ad87146fb 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1260,11 +1260,6 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) { w.pos(rang) - // TODO(mdempsky): For !rang.Def, we need to handle implicit - // conversions; e.g., see #53328. - // - // This is tricky, because the assignments aren't introduced until - // lowering in walk. w.assignList(rang.Lhs) w.expr(rang.X) } else { -- cgit v1.3-6-g1900 From 878439cfe592165bdeaeed037bf1d3351e638853 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 18 Jul 2022 12:41:56 -0700 Subject: [dev.unified] cmd/compile/internal/noder: preserve RTTI for select statements In a select statement, `case i = <-c: ...` may require an implicit conversion of the received value to i's type, but walk does not expect a conversion here. Instead, typecheck actually discards the conversion (resulting in ill-typed IR), and then relies on it being reinserted later when walk desugars the assignment. However, that might lose the explicit RTTI operands we've set for conversions to interface type, so explicitly introduce a temporary variable and rewrite as `case tmp := <-c: i = tmp; ...`, which is semantically equivalent and allows the `i = tmp` assignment to maintain the explicit RTTI without confusing the rest of the compiler frontend. Change-Id: Ie6c4dc9b19437e83970cd3ce83420813b8a47dc4 Reviewed-on: https://go-review.googlesource.com/c/go/+/418098 Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/reader.go | 45 +++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 6b47c11749..8cb0df182c 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1477,6 +1477,32 @@ 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 @@ -1887,17 +1913,11 @@ func (r *reader) multiExpr() []ir.Node { pos := r.pos() expr := r.expr() - // See typecheck.typecheckargs. - curfn := r.curfn - if curfn == nil { - curfn = typecheck.InitTodoFunc - } - results := make([]ir.Node, r.Len()) as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr}) as.Def = true for i := range results { - tmp := typecheck.TempAt(pos, curfn, r.typ()) + tmp := r.temp(pos, r.typ()) as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp)) as.Lhs.Append(tmp) @@ -1927,6 +1947,17 @@ func (r *reader) multiExpr() []ir.Node { 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 { r.Sync(pkgbits.SyncCompLit) pos := r.pos() -- cgit v1.3-6-g1900 From 318027044aa33c109a6f3e6ac12792f129ff2d6a Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 18 Jul 2022 13:13:46 -0700 Subject: [dev.unified] cmd/compile/internal/noder: explicit nil handling Currently, uses of "nil" are handling as references to cmd/compile's own untyped "nil" object, and then we rely on implicitly converting that to its appropriate type. But there are cases where this can subtly go wrong (e.g., the switch test case added in the previous CL). Instead, explicitly handling "nil" expressions so that we can construct them directly with the appropriate type, as computed already by types2. Change-Id: I587f044f60f24e87525dde6d7dad6c58f14478de Reviewed-on: https://go-review.googlesource.com/c/go/+/418100 Reviewed-by: Cuong Manh Le Run-TryBot: Matthew Dempsky Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/codes.go | 1 + src/cmd/compile/internal/noder/reader.go | 5 +++++ src/cmd/compile/internal/noder/writer.go | 7 +++++++ 3 files changed, 13 insertions(+) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go index f7ad2503c2..1a60ea39bb 100644 --- a/src/cmd/compile/internal/noder/codes.go +++ b/src/cmd/compile/internal/noder/codes.go @@ -53,6 +53,7 @@ const ( exprConvert exprNew exprMake + exprNil ) type codeAssign int diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 8cb0df182c..4c90f9dc54 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1684,6 +1684,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() diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 7ad87146fb..47384c6c64 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1413,6 +1413,13 @@ func (w *writer) expr(expr syntax.Expr) { w.String(syntax.String(expr)) return } + + if _, isNil := obj.(*types2.Nil); isNil { + w.Code(exprNil) + w.pos(expr) + w.typ(tv.Type) + return + } } if obj != nil { -- cgit v1.3-6-g1900 From a4c5198a3c8befa2f126fd365de4dc09c32b7886 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 18 Jul 2022 12:28:11 -0700 Subject: [dev.unified] cmd/compile/internal/noder: better switch statements Walk desugars switch statements into a bunch of OEQ comparisons, and sometimes (although rarely in practice) this currently requires converting the tag value to the case value's type. And because this conversion is inserted during walk, unified IR can't wire up appropriate RTTI operands for the conversion. As a simple solution, if any of the case values are *not* assignable to the tag value's type, we instead convert them all to `any`. This works because `any(x) == any(y)` yields the same value as `x == y`, as long as neither `x` nor `y` are `nil`. We never have to worry about `x` or `y` being `nil` either, because: 1. `switch nil` is invalid, so `x` can never be `nil`. 2. If the tag type is a channel, map, or function type, they can *only* be compared against `nil`; so the case values will always be assignable to the tag value's type, and so we won't convert to `any`. 3. For other nullable types, the previous commit (adding explicit `nil` handling to unified IR) ensures that `case nil:` is actually treated as `case tagType(nil):`. Change-Id: I3adcb9cf0d42a91a12b1a163c58d4133a24fca5e Reviewed-on: https://go-review.googlesource.com/c/go/+/418101 Reviewed-by: Keith Randall Reviewed-by: Cuong Manh Le Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/writer.go | 42 ++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 47384c6c64..09afbcdffb 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1311,7 +1311,7 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.pos(stmt) w.stmt(stmt.Init) - var iface types2.Type + var iface, tagType types2.Type if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) { iface = w.p.typeOf(guard.X) @@ -1322,7 +1322,32 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { } w.expr(guard.X) } else { - w.optExpr(stmt.Tag) + tag := stmt.Tag + + if tag != nil { + tagType = w.p.typeOf(tag) + } else { + tagType = types2.Typ[types2.Bool] + } + + // Walk is going to emit comparisons between the tag value and + // each case expression, and we want these comparisons to always + // have the same type. If there are any case values that can't be + // converted to the tag value's type, then convert everything to + // `any` instead. + Outer: + for _, clause := range stmt.Body { + for _, cas := range unpackListExpr(clause.Cases) { + if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) { + tagType = types2.NewInterfaceType(nil, nil) + break Outer + } + } + } + + if w.Bool(tag != nil) { + w.implicitConvExpr(tag, tagType, tag) + } } w.Len(len(stmt.Body)) @@ -1334,15 +1359,22 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.pos(clause) + cases := unpackListExpr(clause.Cases) if iface != nil { - cases := unpackListExpr(clause.Cases) w.Len(len(cases)) for _, cas := range cases { w.exprType(iface, cas, true) } } else { - // TODO(mdempsky): Implicit conversions to tagType, if appropriate. - w.exprList(clause.Cases) + // As if w.exprList(clause.Cases), + // but with implicit conversions to tagType. + + w.Sync(pkgbits.SyncExprList) + w.Sync(pkgbits.SyncExprs) + w.Len(len(cases)) + for _, cas := range cases { + w.implicitConvExpr(cas, tagType, cas) + } } if obj, ok := w.p.info.Implicits[clause]; ok { -- cgit v1.3-6-g1900 From 64cd6faa13e9155e7942f3f51127c1d61a38fcf7 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 18 Jul 2022 13:45:57 -0700 Subject: [dev.unified] cmd/compile/internal/noder: simplify mixed tag/case RTTI wiring The previous CL largely removed the need for worrying about mixed tag/case comparisons in switch statements by ensuring they're always converted to a common type, except for one annoying case: switch statements with an implicit `true` tag, and case values of interface type (which must be empty interface, because `bool`'s method set is empty). It would be simpler to have writer.go desugar the implicit `true` itself, because we already handle explicit `true` correctly. But the existing code already works fine, and I don't want to add further complexity to writer.go until dictionaries and stenciling is done. Change-Id: Ia8d44c425b1be7fc578cd570d15a7560fe9d2674 Reviewed-on: https://go-review.googlesource.com/c/go/+/418102 Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/reader.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 4c90f9dc54..d7ec9f2ebb 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1569,23 +1569,21 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node { } else { cases = r.exprList() - tagType := types.Types[types.TBOOL] - if tag != nil { - tagType = tag.Type() - } - for i, cas := range cases { - if cas.Op() == ir.ONIL { - continue // never needs rtype - } - if tagType.IsInterface() != cas.Type().IsInterface() { - typ := tagType - if typ.IsInterface() { - typ = cas.Type() - } - for len(rtypes) < i { - rtypes = append(rtypes, nil) + // 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])) } - rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), typ)) } } } -- cgit v1.3-6-g1900 From 7a8ba83b729e37d0bdddc9a3c93ae866d1ef199a Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 30 Jun 2022 18:48:02 -0700 Subject: [dev.unified] cmd/compile/internal/reflectdata: remove hasRType's `required` param MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unified IR now always provides RTTI needed by the backend, no need to allow exceptions anymore. 🥳 Change-Id: Ie1ba42c81f92cc43e1b01b3289de10e261ccef57 Reviewed-on: https://go-review.googlesource.com/c/go/+/415576 TryBot-Result: Gopher Robot Run-TryBot: Matthew Dempsky Reviewed-by: David Chase Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/reflectdata/helpers.go | 40 ++++++++++++++----------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go index 81bb18788f..99461cff52 100644 --- a/src/cmd/compile/internal/reflectdata/helpers.go +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -11,13 +11,21 @@ import ( "cmd/internal/src" ) -func hasRType(n, rtype ir.Node, fieldName string, required bool) bool { +func hasRType(n, rtype ir.Node, fieldName string) bool { if rtype != nil { return true } - if base.Debug.Unified != 0 && required { - base.FatalfAt(n.Pos(), "missing %s: %+v", fieldName, n) + + // We make an exception for `init`, because we still depend on + // pkginit for sorting package initialization statements, and it + // gets confused by implicit conversions. Also, because + // package-scope statements can never be generic, so they'll never + // require dictionary lookups. + if base.Debug.Unified != 0 && ir.CurFunc.Nname.Sym().Name != "init" { + ir.Dump("CurFunc", ir.CurFunc) + base.FatalfAt(n.Pos(), "missing %s in %v: %+v", fieldName, ir.CurFunc, n) } + return false } @@ -71,7 +79,7 @@ func concreteRType(pos src.XPos, typ *types.Type) ir.Node { // representing the result slice type's element type. func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.OAPPEND) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return sliceElemRType(pos, n.Type()) @@ -84,7 +92,7 @@ func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp2(n, ir.OEQ, ir.ONE) base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } typ := n.X.Type() @@ -105,8 +113,7 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { assertOp(n, ir.OCONVIFACE) src, dst := n.X.Type(), n.Type() base.AssertfAt(dst.IsInterface(), n.Pos(), "want interface type, have %L", n) - // TODO(mdempsky): Need to handle implicit interface conversions. - if hasRType(n, n.TypeWord, "TypeWord", false) { + if hasRType(n, n.TypeWord, "TypeWord") { return n.TypeWord } if dst.IsEmptyInterface() { @@ -124,8 +131,7 @@ func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { // the convertee value to the heap. func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { assertOp2(n, ir.OCONVIFACE, ir.OCONVIDATA) - // TODO(mdempsky): Need to handle implicit interface conversions. - if hasRType(n, n.SrcRType, "SrcRType", false) { + if hasRType(n, n.SrcRType, "SrcRType") { return n.SrcRType } return concreteRType(pos, n.X.Type()) @@ -136,7 +142,7 @@ func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { // destination slice type's element type. func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OCOPY) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return sliceElemRType(pos, n.X.Type()) @@ -147,7 +153,7 @@ func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { // map type. func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { assertOp(n, ir.ODELETE) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return mapRType(pos, n.Args[0].Type()) @@ -158,7 +164,7 @@ func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { // map type. func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { assertOp(n, ir.OINDEXMAP) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return mapRType(pos, n.X.Type()) @@ -169,7 +175,7 @@ func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { // value representing that channel type. func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKECHAN) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return chanRType(pos, n.Type()) @@ -180,7 +186,7 @@ func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp(n, ir.OMAKEMAP) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return mapRType(pos, n.Type()) @@ -191,7 +197,7 @@ func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // value representing that slice type's element type. func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { assertOp2(n, ir.OMAKESLICE, ir.OMAKESLICECOPY) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return sliceElemRType(pos, n.Type()) @@ -202,7 +208,7 @@ func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { // representing that map type. func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { assertOp(n, ir.ORANGE) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return mapRType(pos, n.X.Type()) @@ -213,7 +219,7 @@ func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { // representing the result slice type's element type. func UnsafeSliceElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { assertOp(n, ir.OUNSAFESLICE) - if hasRType(n, n.RType, "RType", true) { + if hasRType(n, n.RType, "RType") { return n.RType } return sliceElemRType(pos, n.Type()) -- cgit v1.3-6-g1900 From 131f981df0a7d17361839b55fd4d588e652f16a9 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 20 Jul 2022 20:27:58 +0700 Subject: [dev.unified] cmd/compile: make Unified IR always writes concrete type for const exprs So we don't have to depend on typecheck pass to fixup the concrete type for some constant expressions. Previously, the problem won't show up, until CL 418475 sent, which removes an un-necessary type conversion in "append(a, b...) to help the optimization kicks in. For #53888 Change-Id: Idaecd38b7abbaa3ad5b00ff3b1fb0fd8bbeb6726 Reviewed-on: https://go-review.googlesource.com/c/go/+/418514 Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky Reviewed-by: Dmitri Shuralyov TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/expr.go | 29 +++-------------------------- src/cmd/compile/internal/noder/helpers.go | 31 +++++++++++++++++++++++++++++++ src/cmd/compile/internal/noder/writer.go | 4 +++- 3 files changed, 37 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go index a1160d42c4..54b07c39f4 100644 --- a/src/cmd/compile/internal/noder/expr.go +++ b/src/cmd/compile/internal/noder/expr.go @@ -6,7 +6,6 @@ package noder import ( "fmt" - "go/constant" "cmd/compile/internal/base" "cmd/compile/internal/ir" @@ -53,31 +52,9 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node { base.Assert(g.exprStmtOK) - // The gc backend expects all expressions to have a concrete type, and - // types2 mostly satisfies this expectation already. But there are a few - // cases where the Go spec doesn't require converting to concrete type, - // and so types2 leaves them untyped. So we need to fix those up here. - typ := tv.Type - if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 { - switch basic.Kind() { - case types2.UntypedNil: - // ok; can appear in type switch case clauses - // TODO(mdempsky): Handle as part of type switches instead? - case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex: - // Untyped rhs of non-constant shift, e.g. x << 1.0. - // If we have a constant value, it must be an int >= 0. - if tv.Value != nil { - s := constant.ToInt(tv.Value) - assert(s.Kind() == constant.Int && constant.Sign(s) >= 0) - } - typ = types2.Typ[types2.Uint] - case types2.UntypedBool: - typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition - case types2.UntypedString: - typ = types2.Typ[types2.String] // argument to "append" or "copy" calls - default: - base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", basic) - } + typ := idealType(tv) + if typ == nil { + base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", tv.Type) } // Constant expression. diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go index 8efcef26cf..40f80ab528 100644 --- a/src/cmd/compile/internal/noder/helpers.go +++ b/src/cmd/compile/internal/noder/helpers.go @@ -11,6 +11,7 @@ import ( "cmd/compile/internal/ir" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/compile/internal/types2" "cmd/internal/src" ) @@ -220,3 +221,33 @@ func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt { } return ir.NewAssignOpStmt(pos, op, x, bl) } + +func idealType(tv types2.TypeAndValue) types2.Type { + // The gc backend expects all expressions to have a concrete type, and + // types2 mostly satisfies this expectation already. But there are a few + // cases where the Go spec doesn't require converting to concrete type, + // and so types2 leaves them untyped. So we need to fix those up here. + typ := tv.Type + if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 { + switch basic.Kind() { + case types2.UntypedNil: + // ok; can appear in type switch case clauses + // TODO(mdempsky): Handle as part of type switches instead? + case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex: + // Untyped rhs of non-constant shift, e.g. x << 1.0. + // If we have a constant value, it must be an int >= 0. + if tv.Value != nil { + s := constant.ToInt(tv.Value) + assert(s.Kind() == constant.Int && constant.Sign(s) >= 0) + } + typ = types2.Typ[types2.Uint] + case types2.UntypedBool: + typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition + case types2.UntypedString: + typ = types2.Typ[types2.String] // argument to "append" or "copy" calls + default: + return nil + } + } + return typ +} diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 09afbcdffb..ac08022c34 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1436,7 +1436,9 @@ func (w *writer) expr(expr syntax.Expr) { if tv.Value != nil { w.Code(exprConst) w.pos(expr) - w.typ(tv.Type) + typ := idealType(tv) + assert(typ != nil) + w.typ(typ) w.Value(tv.Value) // TODO(mdempsky): These details are only important for backend -- cgit v1.3-6-g1900 From f48fa643f1d8519da42faad1f838d4b2bd035269 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sat, 23 Jul 2022 00:00:49 -0700 Subject: [dev.unified] cmd/compile: remove obsolete RTTI wiring Comparisons between interface-typed and non-interface-typed expressions no longer happen within Unified IR since CL 415577, so this code path is no longer needed. Change-Id: I075dfd1e6c34799f32766ed052eab0710bc6cbd5 Reviewed-on: https://go-review.googlesource.com/c/go/+/419454 Reviewed-by: David Chase Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/reader.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index d7ec9f2ebb..fbbce7e80f 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1786,19 +1786,7 @@ func (r *reader) expr() (res ir.Node) { case ir.OANDAND, ir.OOROR: return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y)) } - n := typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y)) - switch n.Op() { - case ir.OEQ, ir.ONE: - n := n.(*ir.BinaryExpr) - if n.X.Type().IsInterface() != n.Y.Type().IsInterface() { - typ := n.X.Type() - if typ.IsInterface() { - typ = n.Y.Type() - } - n.RType = reflectdata.TypePtrAt(pos, typ) - } - } - return n + return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y)) case exprCall: fun := r.expr() -- cgit v1.3-6-g1900 From fc72b7705d67f432fb6a570f84df9e8840eec226 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sat, 23 Jul 2022 23:54:15 -0700 Subject: [dev.unified] cmd/compile: add method expressions to dictionaries This CL changes method expressions that use derived-type receiver parameters to use dictionary lookups. Change-Id: Iacd09b6d77a2d3000438ec8bc9b5af2a0b068aa7 Reviewed-on: https://go-review.googlesource.com/c/go/+/419455 Reviewed-by: David Chase Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot --- src/cmd/compile/internal/noder/reader.go | 24 ++++++---- src/cmd/compile/internal/noder/writer.go | 76 +++++++++++++++++++++++++++++--- src/internal/pkgbits/encoder.go | 8 +++- 3 files changed, 93 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index fbbce7e80f..6b7ac5494f 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -154,6 +154,8 @@ type readerDict struct { funcsObj []ir.Node itabs []itabInfo2 + + methodExprs []ir.Node } type itabInfo2 struct { @@ -776,6 +778,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 } @@ -1696,15 +1706,13 @@ func (r *reader) expr() (res ir.Node) { case exprSelector: var x ir.Node if r.Bool() { // MethodExpr - x = r.exprType(false) - - // Method expression with derived receiver type. - if x.Op() == ir.ODYNAMICTYPE { - // TODO(mdempsky): Handle with runtime dictionary lookup. - n := ir.TypeNode(x.Type()) - n.SetTypecheck(1) - x = n + if r.Bool() { + return r.dict.methodExprs[r.Len()] } + + n := ir.TypeNode(r.typ()) + n.SetTypecheck(1) + x = n } else { // FieldVal, MethodVal x = r.expr() } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index ac08022c34..93e81bdc82 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -177,6 +177,10 @@ type writerDict struct { // itabs lists itabs that are needed for dynamic type assertions // (including type switches). itabs []itabInfo + + // methodsExprs lists method expressions with derived-type receiver + // parameters. + methodExprs []methodExprInfo } // A derivedInfo represents a reference to an encoded generic Go type. @@ -208,13 +212,26 @@ type objInfo struct { // An itabInfo represents a reference to an encoded itab entry (i.e., // a non-empty interface type along with a concrete type that // implements that interface). -// -// itabInfo is only used for type itabInfo struct { typIdx pkgbits.Index // always a derived type index iface typeInfo // always a non-empty interface type } +// A methodExprInfo represents a reference to an encoded method +// expression, whose receiver parameter is a derived type. +type methodExprInfo struct { + recvIdx pkgbits.Index // always a derived type index + methodInfo selectorInfo +} + +// A selectorInfo represents a reference to an encoded field or method +// name (i.e., objects that can only be accessed using selector +// expressions). +type selectorInfo struct { + pkgIdx pkgbits.Index + nameIdx pkgbits.Index +} + // anyDerived reports whether any of info's explicit type arguments // are derived types. func (info objInfo) anyDerived() bool { @@ -296,8 +313,12 @@ func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index { // pkg writes a use of the given Package into the element bitstream. func (w *writer) pkg(pkg *types2.Package) { + w.pkgRef(w.p.pkgIdx(pkg)) +} + +func (w *writer) pkgRef(idx pkgbits.Index) { w.Sync(pkgbits.SyncPkg) - w.Reloc(pkgbits.RelocPkg, w.p.pkgIdx(pkg)) + w.Reloc(pkgbits.RelocPkg, idx) } // pkgIdx returns the index for the given package, adding it to the @@ -793,6 +814,12 @@ func (w *writer) objDict(obj types2.Object, dict *writerDict) { w.typInfo(itab.iface) } + w.Len(len(dict.methodExprs)) + for _, methodExpr := range dict.methodExprs { + w.Len(int(methodExpr.recvIdx)) + w.selectorInfo(methodExpr.methodInfo) + } + assert(len(dict.derived) == nderived) assert(len(dict.funcs) == nfuncs) } @@ -862,9 +889,19 @@ func (w *writer) localIdent(obj types2.Object) { // selector writes the name of a field or method (i.e., objects that // can only be accessed using selector expressions). func (w *writer) selector(obj types2.Object) { + w.selectorInfo(w.p.selectorIdx(obj)) +} + +func (w *writer) selectorInfo(info selectorInfo) { w.Sync(pkgbits.SyncSelector) - w.pkg(obj.Pkg()) - w.String(obj.Name()) + w.pkgRef(info.pkgIdx) + w.StringRef(info.nameIdx) +} + +func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo { + pkgIdx := pw.pkgIdx(obj.Pkg()) + nameIdx := pw.StringIdx(obj.Name()) + return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx} } // @@@ Compiler extensions @@ -1490,7 +1527,19 @@ func (w *writer) expr(expr syntax.Expr) { w.Code(exprSelector) if w.Bool(sel.Kind() == types2.MethodExpr) { - w.exprType(nil, expr.X, false) + tv, ok := w.p.info.Types[expr.X] + assert(ok) + assert(tv.IsType()) + + typInfo := w.p.typIdx(tv.Type, w.dict) + if w.Bool(typInfo.derived) { + methodInfo := w.p.selectorIdx(sel.Obj()) + idx := w.dict.methodExprIdx(typInfo, methodInfo) + w.Len(idx) + break + } + + w.typInfo(typInfo) } else { w.expr(expr.X) } @@ -1821,6 +1870,21 @@ func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { w.typInfo(info) } +func (dict *writerDict) methodExprIdx(recvInfo typeInfo, methodInfo selectorInfo) int { + assert(recvInfo.derived) + newInfo := methodExprInfo{recvIdx: recvInfo.idx, methodInfo: methodInfo} + + for idx, oldInfo := range dict.methodExprs { + if oldInfo == newInfo { + return idx + } + } + + idx := len(dict.methodExprs) + dict.methodExprs = append(dict.methodExprs, newInfo) + return idx +} + // isInterface reports whether typ is known to be an interface type. // If typ is a type parameter, then isInterface reports an internal // compiler error instead. diff --git a/src/internal/pkgbits/encoder.go b/src/internal/pkgbits/encoder.go index c50c838caa..c0f2252909 100644 --- a/src/internal/pkgbits/encoder.go +++ b/src/internal/pkgbits/encoder.go @@ -316,8 +316,14 @@ func (w *Encoder) Code(c Code) { // section (if not already present), and then writing a relocation // into the element bitstream. func (w *Encoder) String(s string) { + w.StringRef(w.p.StringIdx(s)) +} + +// StringRef writes a reference to the given index, which must be a +// previously encoded string value. +func (w *Encoder) StringRef(idx Index) { w.Sync(SyncString) - w.Reloc(RelocString, w.p.StringIdx(s)) + w.Reloc(RelocString, idx) } // Strings encodes and writes a variable-length slice of strings into -- cgit v1.3-6-g1900 From 9b70178d58060764cbaedf3d4542bd67f9aafede Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 22 Jul 2022 23:33:30 -0700 Subject: [dev.unified] cmd/compile: write RTTI into unified IR export data This CL adds `rtype` methods for unified IR for writing/reading types that need to have their *runtime._type value available. For now, this just builds on the existing type writing/reading mechanics and calling reflectdata.TypePtrAt; but longer term, reading of derived types can be changed to use dictionary lookups instead. Change-Id: I6f803b84546fa7df2877a8a3bcbf2623e4b03449 Reviewed-on: https://go-review.googlesource.com/c/go/+/419456 Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: Keith Randall Reviewed-by: David Chase --- src/cmd/compile/internal/noder/reader.go | 59 ++++++++++++++++-------------- src/cmd/compile/internal/noder/writer.go | 63 ++++++++++++++++++++++++++++++-- src/internal/pkgbits/sync.go | 1 + 3 files changed, 91 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 6b7ac5494f..6692446792 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1390,17 +1390,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 names, lhs := r.assignList() - x := r.expr() - - body := r.blockStmt() - r.closeAnotherScope() - - rang := ir.NewRangeStmt(pos, nil, nil, x, body) - if x.Type().IsMap() { - rang.RType = reflectdata.TypePtrAt(pos, x.Type()) - } if len(lhs) >= 1 { rang.Key = lhs[0] if len(lhs) >= 2 { @@ -1408,10 +1401,13 @@ 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) + } { - keyType, valueType := rangeTypes(pos, x.Type()) + keyType, valueType := rangeTypes(pos, rang.X.Type()) if rang.Key != nil { rang.KeyTypeWord, rang.KeySrcRType = convRTTI(pos, rang.Key.Type(), keyType) @@ -1421,6 +1417,9 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { } } + rang.Body = r.blockStmt() + r.closeAnotherScope() + return rang } @@ -1741,7 +1740,7 @@ func (r *reader) expr() (res ir.Node) { switch n.Op() { case ir.OINDEXMAP: n := n.(*ir.IndexExpr) - n.RType = reflectdata.TypePtrAt(pos, x.Type()) + n.RType = r.rtype(pos) } return n @@ -1762,10 +1761,12 @@ func (r *reader) expr() (res ir.Node) { x := r.expr() pos := r.pos() typ := r.exprType(false) + 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 = reflectdata.TypePtrAt(pos, x.Type()) + assert.SrcRType = srcRType assert.ITab = typ.ITab return typed(typ.Type(), assert) } @@ -1810,16 +1811,16 @@ func (r *reader) expr() (res ir.Node) { switch n.Op() { case ir.OAPPEND: n := n.(*ir.CallExpr) - n.RType = reflectdata.TypePtrAt(pos, n.Type().Elem()) + n.RType = r.rtype(pos) case ir.OCOPY: n := n.(*ir.BinaryExpr) - n.RType = reflectdata.TypePtrAt(pos, n.X.Type().Elem()) + n.RType = r.rtype(pos) case ir.ODELETE: n := n.(*ir.CallExpr) - n.RType = reflectdata.TypePtrAt(pos, n.Args[0].Type()) + n.RType = r.rtype(pos) case ir.OUNSAFESLICE: n := n.(*ir.BinaryExpr) - n.RType = reflectdata.TypePtrAt(pos, n.Type().Elem()) + n.RType = r.rtype(pos) } return n @@ -1828,14 +1829,7 @@ func (r *reader) expr() (res ir.Node) { typ := r.exprType(false) extra := r.exprs() n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr) - switch n.Op() { - case ir.OMAKECHAN: - n.RType = reflectdata.TypePtrAt(pos, typ.Type()) - case ir.OMAKEMAP: - n.RType = reflectdata.TypePtrAt(pos, typ.Type()) - case ir.OMAKESLICE: - n.RType = reflectdata.TypePtrAt(pos, typ.Type().Elem()) - } + n.RType = r.rtype(pos) return n case exprNew: @@ -1969,6 +1963,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()) @@ -1987,10 +1985,9 @@ func (r *reader) compLit() ir.Node { } lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems)) - switch lit.Op() { - case ir.OMAPLIT: + if rtype != nil { lit := lit.(*ir.CompLitExpr) - lit.RType = reflectdata.TypePtrAt(pos, typ) + lit.RType = rtype } if typ0.IsPtr() { lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit)) @@ -2060,6 +2057,12 @@ func (r *reader) exprs() []ir.Node { return nodes } +func (r *reader) rtype(pos src.XPos) ir.Node { + r.Sync(pkgbits.SyncRType) + // TODO(mdempsky): For derived types, use dictionary instead. + return reflectdata.TypePtrAt(pos, r.typ()) +} + func (r *reader) exprType(nilOK bool) ir.Node { r.Sync(pkgbits.SyncExprType) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 93e81bdc82..5dd252a2a5 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1299,6 +1299,11 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { w.pos(rang) w.assignList(rang.Lhs) w.expr(rang.X) + + xtyp := w.p.typeOf(rang.X) + if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap { + w.rtype(xtyp) + } } else { w.pos(stmt) w.stmt(stmt.Init) @@ -1549,8 +1554,10 @@ func (w *writer) expr(expr syntax.Expr) { case *syntax.IndexExpr: _ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation + xtyp := w.p.typeOf(expr.X) + var keyType types2.Type - if mapType, ok := types2.CoreType(w.p.typeOf(expr.X)).(*types2.Map); ok { + if mapType, ok := types2.CoreType(xtyp).(*types2.Map); ok { keyType = mapType.Key() } @@ -1558,6 +1565,9 @@ func (w *writer) expr(expr syntax.Expr) { w.expr(expr.X) w.pos(expr) w.implicitConvExpr(expr, keyType, expr.Index) + if keyType != nil { + w.rtype(xtyp) + } case *syntax.SliceExpr: w.Code(exprSlice) @@ -1574,6 +1584,7 @@ func (w *writer) expr(expr syntax.Expr) { w.expr(expr.X) w.pos(expr) w.exprType(iface, expr.Type, false) + w.rtype(iface) case *syntax.Operation: if expr.Y == nil { @@ -1622,8 +1633,9 @@ func (w *writer) expr(expr syntax.Expr) { break } - if name, ok := unparen(expr.Fun).(*syntax.Name); ok && tv.IsBuiltin() { - switch name.Value { + var rtype types2.Type + if tv.IsBuiltin() { + switch obj, _ := lookupObj(w.p.info, expr.Fun); obj.Name() { case "make": assert(len(expr.ArgList) >= 1) assert(!expr.HasDots) @@ -1632,6 +1644,19 @@ func (w *writer) expr(expr syntax.Expr) { w.pos(expr) w.exprType(nil, expr.ArgList[0], false) w.exprs(expr.ArgList[1:]) + + typ := w.p.typeOf(expr) + switch coreType := types2.CoreType(typ).(type) { + default: + w.p.fatalf(expr, "unexpected core type: %v", coreType) + case *types2.Chan: + w.rtype(typ) + case *types2.Map: + w.rtype(typ) + case *types2.Slice: + w.rtype(sliceElem(typ)) + } + return case "new": @@ -1642,6 +1667,23 @@ func (w *writer) expr(expr syntax.Expr) { w.pos(expr) w.exprType(nil, expr.ArgList[0], false) return + + case "append": + rtype = sliceElem(w.p.typeOf(expr)) + case "copy": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "copy(g())" + typ = tuple.At(0).Type() + } + rtype = sliceElem(typ) + case "delete": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "delete(g())" + typ = tuple.At(0).Type() + } + rtype = typ + case "Slice": + rtype = sliceElem(w.p.typeOf(expr)) } } @@ -1676,9 +1718,16 @@ func (w *writer) expr(expr syntax.Expr) { w.multiExpr(expr, paramType, expr.ArgList) w.Bool(expr.HasDots) + if rtype != nil { + w.rtype(rtype) + } } } +func sliceElem(typ types2.Type) types2.Type { + return types2.CoreType(typ).(*types2.Slice).Elem() +} + func (w *writer) optExpr(expr syntax.Expr) { if w.Bool(expr != nil) { w.expr(expr) @@ -1757,12 +1806,13 @@ func (w *writer) compLit(lit *syntax.CompositeLit) { } var keyType, elemType types2.Type var structType *types2.Struct - switch typ := types2.CoreType(typ).(type) { + switch typ0 := typ; typ := types2.CoreType(typ).(type) { default: w.p.fatalf(lit, "unexpected composite literal type: %v", typ) case *types2.Array: elemType = typ.Elem() case *types2.Map: + w.rtype(typ0) keyType, elemType = typ.Key(), typ.Elem() case *types2.Slice: elemType = typ.Elem() @@ -1833,6 +1883,11 @@ func (w *writer) exprs(exprs []syntax.Expr) { } } +func (w *writer) rtype(typ types2.Type) { + w.Sync(pkgbits.SyncRType) + w.typ(typ) +} + func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface) diff --git a/src/internal/pkgbits/sync.go b/src/internal/pkgbits/sync.go index 6a5999eb6b..54e478d932 100644 --- a/src/internal/pkgbits/sync.go +++ b/src/internal/pkgbits/sync.go @@ -112,4 +112,5 @@ const ( SyncOptLabel SyncMultiExpr + SyncRType ) -- cgit v1.3-6-g1900 From 92798176e76f4ab69d4f18adf40b1a2aab4e3ea1 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sat, 23 Jul 2022 00:46:10 -0700 Subject: [dev.unified] cmd/compile: write iface conversion RTTI into unified IR This CL changes convRTTI into a serialization method too, like the previous CL's rtype method. And again, currently this just builds on the existing type serialization logic, but will eventually be changed to use dictionary lookups where appropriate. Change-Id: I551aef8ade24b08dc6206f06ace86d91e665f5c1 Reviewed-on: https://go-review.googlesource.com/c/go/+/419457 Reviewed-by: Keith Randall Reviewed-by: David Chase TryBot-Result: Gopher Robot Run-TryBot: Matthew Dempsky --- src/cmd/compile/internal/noder/reader.go | 100 +++++++++++++------------------ src/cmd/compile/internal/noder/writer.go | 78 +++++++++++++++++++++++- src/internal/pkgbits/sync.go | 1 + 3 files changed, 116 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 6692446792..afc2705909 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1406,15 +1406,11 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { if rang.X.Type().IsMap() { rang.RType = r.rtype(pos) } - { - keyType, valueType := rangeTypes(pos, rang.X.Type()) - - if rang.Key != nil { - rang.KeyTypeWord, rang.KeySrcRType = convRTTI(pos, rang.Key.Type(), keyType) - } - if rang.Value != nil { - rang.ValueTypeWord, rang.ValueSrcRType = convRTTI(pos, rang.Value.Type(), valueType) - } + 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() @@ -1435,28 +1431,6 @@ func (r *reader) forStmt(label *types.Sym) ir.Node { return stmt } -// rangeTypes returns the types of values produced by ranging over a -// value of type typ. -func rangeTypes(pos src.XPos, typ *types.Type) (key, value *types.Type) { - switch typ.Kind() { - default: - base.FatalfAt(pos, "unexpected range type: %v", typ) - panic("unreachable") - case types.TPTR: // must be pointer to array - typ = typ.Elem() - base.AssertfAt(typ.Kind() == types.TARRAY, pos, "want array type, have %v", typ) - fallthrough - case types.TARRAY, types.TSLICE: - return types.Types[types.TINT], typ.Elem() - case types.TSTRING: - return types.Types[types.TINT], types.RuneType - case types.TMAP: - return typ.Key(), typ.Elem() - case types.TCHAN: - return typ.Elem(), nil - } -} - func (r *reader) ifStmt() ir.Node { r.Sync(pkgbits.SyncIfStmt) r.openScope() @@ -1841,6 +1815,7 @@ func (r *reader) expr() (res ir.Node) { 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. @@ -1857,7 +1832,7 @@ func (r *reader) expr() (res ir.Node) { } n := ir.NewConvExpr(pos, ir.OCONV, typ, x) - n.TypeWord, n.SrcRType = convRTTI(pos, typ, x.Type()) + n.TypeWord, n.SrcRType = typeWord, srcRType if implicit { n.SetImplicit(true) } @@ -1865,33 +1840,6 @@ func (r *reader) expr() (res ir.Node) { } } -// convRTTI returns the TypeWord and SrcRType expressions appropriate -// for a conversion from src to dst. -func convRTTI(pos src.XPos, dst, src *types.Type) (typeWord, srcRType ir.Node) { - if !dst.IsInterface() { - return - } - - // See reflectdata.ConvIfaceTypeWord. - switch { - case dst.IsEmptyInterface(): - if !src.IsInterface() { - typeWord = reflectdata.TypePtrAt(pos, src) // direct eface construction - } - case !src.IsInterface(): - typeWord = reflectdata.ITabAddrAt(pos, src, dst) // direct iface construction - default: - typeWord = reflectdata.TypePtrAt(pos, dst) // convI2I - } - - // See reflectdata.ConvIfaceSrcRType. - if !src.IsInterface() { - srcRType = reflectdata.TypePtrAt(pos, src) - } - - return -} - func (r *reader) optExpr() ir.Node { if r.Bool() { return r.expr() @@ -1917,7 +1865,7 @@ func (r *reader) multiExpr() []ir.Node { res := ir.Node(tmp) if r.Bool() { n := ir.NewConvExpr(pos, ir.OCONV, r.typ(), res) - n.TypeWord, n.SrcRType = convRTTI(pos, n.Type(), n.X.Type()) + n.TypeWord, n.SrcRType = r.convRTTI(pos) n.SetImplicit(true) res = typecheck.Expr(n) } @@ -2057,12 +2005,44 @@ func (r *reader) exprs() []ir.Node { return nodes } +// rtype returns an expression of type *runtime._type. func (r *reader) rtype(pos src.XPos) ir.Node { r.Sync(pkgbits.SyncRType) // TODO(mdempsky): For derived types, use dictionary instead. return reflectdata.TypePtrAt(pos, r.typ()) } +// 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) + src := r.typ() + dst := r.typ() + + if !dst.IsInterface() { + return + } + + // See reflectdata.ConvIfaceTypeWord. + switch { + case dst.IsEmptyInterface(): + if !src.IsInterface() { + typeWord = reflectdata.TypePtrAt(pos, src) // direct eface construction + } + case !src.IsInterface(): + typeWord = reflectdata.ITabAddrAt(pos, src, dst) // direct iface construction + default: + typeWord = reflectdata.TypePtrAt(pos, dst) // convI2I + } + + // See reflectdata.ConvIfaceSrcRType. + if !src.IsInterface() { + srcRType = reflectdata.TypePtrAt(pos, src) + } + + return +} + func (r *reader) exprType(nilOK bool) ir.Node { r.Sync(pkgbits.SyncExprType) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 5dd252a2a5..d96ba0202f 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -123,8 +123,12 @@ func (pw *pkgWriter) unexpected(what string, p poser) { // typeOf returns the Type of the given value expression. func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type { tv, ok := pw.info.Types[expr] - assert(ok) - assert(tv.IsValue()) + if !ok { + pw.fatalf(expr, "missing Types entry: %v", syntax.String(expr)) + } + if !tv.IsValue() { + pw.fatalf(expr, "expected value: %v", syntax.String(expr)) + } return tv.Type } @@ -361,7 +365,10 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { // @@@ Types -var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName) +var ( + anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName) + runeTypeName = types2.Universe.Lookup("rune").(*types2.TypeName) +) // typ writes a use of the given type into the bitstream. func (w *writer) typ(typ types2.Type) { @@ -1304,6 +1311,34 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap { w.rtype(xtyp) } + { + lhs := unpackListExpr(rang.Lhs) + assign := func(i int, src types2.Type) { + if i >= len(lhs) { + return + } + dst := unparen(lhs[i]) + if name, ok := dst.(*syntax.Name); ok && name.Value == "_" { + return + } + + var dstType types2.Type + if rang.Def { + // For `:=` assignments, the LHS names only appear in Defs, + // not Types (as used by typeOf). + dstType = w.p.info.Defs[dst.(*syntax.Name)].(*types2.Var).Type() + } else { + dstType = w.p.typeOf(dst) + } + + w.convRTTI(src, dstType) + } + + keyType, valueType := w.p.rangeTypes(rang.X) + assign(0, keyType) + assign(1, valueType) + } + } else { w.pos(stmt) w.stmt(stmt.Init) @@ -1315,6 +1350,30 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { w.closeAnotherScope() } +// rangeTypes returns the types of values produced by ranging over +// expr. +func (pw *pkgWriter) rangeTypes(expr syntax.Expr) (key, value types2.Type) { + typ := pw.typeOf(expr) + switch typ := types2.CoreType(typ).(type) { + case *types2.Pointer: // must be pointer to array + return types2.Typ[types2.Int], types2.CoreType(typ.Elem()).(*types2.Array).Elem() + case *types2.Array: + return types2.Typ[types2.Int], typ.Elem() + case *types2.Slice: + return types2.Typ[types2.Int], typ.Elem() + case *types2.Basic: + if typ.Info()&types2.IsString != 0 { + return types2.Typ[types2.Int], runeTypeName.Type() + } + case *types2.Map: + return typ.Key(), typ.Elem() + case *types2.Chan: + return typ.Elem(), nil + } + pw.fatalf(expr, "unexpected range type: %v", typ) + panic("unreachable") +} + func (w *writer) ifStmt(stmt *syntax.IfStmt) { w.Sync(pkgbits.SyncIfStmt) w.openScope(stmt.Pos()) @@ -1629,6 +1688,7 @@ func (w *writer) expr(expr syntax.Expr) { w.Bool(false) // explicit w.typ(tv.Type) w.pos(expr) + w.convRTTI(w.p.typeOf(expr.ArgList[0]), tv.Type) w.expr(expr.ArgList[0]) break } @@ -1763,6 +1823,7 @@ func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syn w.p.fatalf(pos, "%v is not assignable to %v", src, dst) } w.typ(dst) + w.convRTTI(src, dst) } } return @@ -1789,6 +1850,7 @@ func (w *writer) implicitConvExpr(pos poser, dst types2.Type, expr syntax.Expr) w.Bool(true) // implicit w.typ(dst) w.pos(pos) + w.convRTTI(src, dst) // fallthrough } w.expr(expr) @@ -1883,11 +1945,21 @@ func (w *writer) exprs(exprs []syntax.Expr) { } } +// rtype writes information so that the reader can construct an +// expression of type *runtime._type representing typ. func (w *writer) rtype(typ types2.Type) { w.Sync(pkgbits.SyncRType) w.typ(typ) } +// convRTTI writes information so that the reader can construct +// expressions for converting from src to dst. +func (w *writer) convRTTI(src, dst types2.Type) { + w.Sync(pkgbits.SyncConvRTTI) + w.typ(src) + w.typ(dst) +} + func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface) diff --git a/src/internal/pkgbits/sync.go b/src/internal/pkgbits/sync.go index 54e478d932..a17a0088f7 100644 --- a/src/internal/pkgbits/sync.go +++ b/src/internal/pkgbits/sync.go @@ -113,4 +113,5 @@ const ( SyncMultiExpr SyncRType + SyncConvRTTI ) -- cgit v1.3-6-g1900 From 831fdf1dff5e38c6c23922880d97ac99fe30f311 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 25 Jul 2022 12:21:21 -0700 Subject: [dev.unified] cmd/compile: extract nil handling from exprType Type switches are the only context where exprType was used and `nilOK` was true. It'll simplify subsequent dictionary work somewhat if exprType doesn't need to worry about `nil`, so extract this logic and move it into switchStmt instead. Change-Id: I3d810f465173f5bb2e2dee7bbc7843fff6a62ee5 Reviewed-on: https://go-review.googlesource.com/c/go/+/419474 Run-TryBot: Matthew Dempsky Reviewed-by: Cherry Mui TryBot-Result: Gopher Robot Reviewed-by: David Chase Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/reader.go | 18 +++++++++--------- src/cmd/compile/internal/noder/writer.go | 29 +++++++++++++++++------------ 2 files changed, 26 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index afc2705909..a8ef0a8e25 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1547,7 +1547,11 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node { 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() @@ -1734,7 +1738,7 @@ 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? @@ -1800,7 +1804,7 @@ func (r *reader) expr() (res ir.Node) { case exprMake: pos := r.pos() - typ := r.exprType(false) + 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) @@ -1808,7 +1812,7 @@ func (r *reader) expr() (res ir.Node) { case exprNew: pos := r.pos() - typ := r.exprType(false) + typ := r.exprType() return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ)) case exprConvert: @@ -2043,13 +2047,9 @@ func (r *reader) convRTTI(pos src.XPos) (typeWord, srcRType ir.Node) { return } -func (r *reader) exprType(nilOK bool) ir.Node { +func (r *reader) exprType() ir.Node { r.Sync(pkgbits.SyncExprType) - if nilOK && r.Bool() { - return typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr)) - } - pos := r.pos() setBasePos(pos) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index d96ba0202f..0005c2e7fa 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1464,7 +1464,10 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { if iface != nil { w.Len(len(cases)) for _, cas := range cases { - w.exprType(iface, cas, true) + if w.Bool(isNil(w.p.info, cas)) { + continue + } + w.exprType(iface, cas) } } else { // As if w.exprList(clause.Cases), @@ -1642,7 +1645,7 @@ func (w *writer) expr(expr syntax.Expr) { w.Code(exprAssert) w.expr(expr.X) w.pos(expr) - w.exprType(iface, expr.Type, false) + w.exprType(iface, expr.Type) w.rtype(iface) case *syntax.Operation: @@ -1702,7 +1705,7 @@ func (w *writer) expr(expr syntax.Expr) { w.Code(exprMake) w.pos(expr) - w.exprType(nil, expr.ArgList[0], false) + w.exprType(nil, expr.ArgList[0]) w.exprs(expr.ArgList[1:]) typ := w.p.typeOf(expr) @@ -1725,7 +1728,7 @@ func (w *writer) expr(expr syntax.Expr) { w.Code(exprNew) w.pos(expr) - w.exprType(nil, expr.ArgList[0], false) + w.exprType(nil, expr.ArgList[0]) return case "append": @@ -1960,21 +1963,15 @@ func (w *writer) convRTTI(src, dst types2.Type) { w.typ(dst) } -func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { +func (w *writer) exprType(iface types2.Type, typ syntax.Expr) { base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface) tv, ok := w.p.info.Types[typ] assert(ok) - - w.Sync(pkgbits.SyncExprType) - - if nilOK && w.Bool(tv.IsNil()) { - return - } - assert(tv.IsType()) info := w.p.typIdx(tv.Type, w.dict) + w.Sync(pkgbits.SyncExprType) w.pos(typ) if w.Bool(info.derived && iface != nil && !iface.Underlying().(*types2.Interface).Empty()) { @@ -2386,6 +2383,14 @@ func isMultiValueExpr(info *types2.Info, expr syntax.Expr) bool { return false } +// isNil reports whether expr is a (possibly parenthesized) reference +// to the predeclared nil value. +func isNil(info *types2.Info, expr syntax.Expr) bool { + tv, ok := info.Types[expr] + assert(ok) + return tv.IsNil() +} + // recvBase returns the base type for the given receiver parameter. func recvBase(recv *types2.Var) *types2.Named { typ := recv.Type() -- cgit v1.3-6-g1900 From f2851c67fd103b8dd7e84e3d35b896ea49ea4af5 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 25 Jul 2022 21:37:30 -0700 Subject: [dev.unified] cmd/compile: allow inlining to fail gracefully Change-Id: I20c7df52d110fb88eb22d57bdad9264d0c5e22fe Reviewed-on: https://go-review.googlesource.com/c/go/+/419674 Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/inline/inl.go | 7 +++---- src/cmd/compile/internal/noder/reader.go | 7 ++++++- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index b6f80a1723..77848577c6 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -685,9 +685,8 @@ var inlgen int var SSADumpInline = func(*ir.Func) {} // NewInline allows the inliner implementation to be overridden. -// If it returns nil, the legacy inliner will handle this call -// instead. -var NewInline = func(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { return nil } +// If it returns nil, the function will not be inlined. +var NewInline = oldInline // If n is a OCALLFUNC node, and fn is an ONAME node for a // function with an inlinable body, return an OINLCALL node that can replace n. @@ -807,7 +806,7 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b res := NewInline(n, fn, inlIndex) if res == nil { - res = oldInline(n, fn, inlIndex) + return n } // transitive inlining diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index a8ef0a8e25..0a382e1c9b 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -2227,7 +2227,12 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp pri, ok := bodyReader[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 { -- cgit v1.3-6-g1900 From f9959460940140b280be1f5591ae38b9ab74182e Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 26 Jul 2022 21:52:42 -0700 Subject: [dev.unified] cmd/compile: implement simple inline body pruning heuristic An important optimization in the existing export data format is the pruning of unreachable inline bodies. That is, when re-exporting transitively imported types, omitting the inline bodies for methods that can't actually be needed due to importing that package. The existing logic (implemented in typecheck/crawler.go) is fairly sophisticated, but also relies on actually expanding inline bodies in the process, which is undesirable. However, including all inline bodies is also prohibitive for testing GOEXPERIMENT=unified against very large Go code bases that impose size limits on build action inputs. As a short-term solution, this CL implements a simple heuristic for GOEXPERIMENT=unified: include the inline bodies for all locally-declared functions/methods, and for any imported functions/methods that were inlined into this package. Change-Id: I686964a0cd9262b77d3d5587f89cfbcfe8b2e521 Reviewed-on: https://go-review.googlesource.com/c/go/+/419675 Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Reviewed-by: David Chase --- src/cmd/compile/internal/noder/import.go | 2 +- src/cmd/compile/internal/noder/linker.go | 83 +++++++++++++++++++++------- src/cmd/compile/internal/noder/reader.go | 35 ++++++++---- src/cmd/compile/internal/noder/unified.go | 92 ++++++++++++++++++++++++------- 4 files changed, 161 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go index 2cef9f75e8..49b8fd142a 100644 --- a/src/cmd/compile/internal/noder/import.go +++ b/src/cmd/compile/internal/noder/import.go @@ -241,7 +241,7 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag pr := pkgbits.NewPkgDecoder(pkg1.Path, data) // Read package descriptors for both types2 and compiler backend. - readPackage(newPkgReader(pr), pkg1) + readPackage(newPkgReader(pr), pkg1, false) pkg2 = importer.ReadPackage(env, packages, pr) case 'i': diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go index 1626c04090..0f39fdec05 100644 --- a/src/cmd/compile/internal/noder/linker.go +++ b/src/cmd/compile/internal/noder/linker.go @@ -38,8 +38,9 @@ import ( type linker struct { pw pkgbits.PkgEncoder - pkgs map[string]pkgbits.Index - decls map[*types.Sym]pkgbits.Index + pkgs map[string]pkgbits.Index + decls map[*types.Sym]pkgbits.Index + bodies map[*types.Sym]pkgbits.Index } // relocAll ensures that all elements specified by pr and relocs are @@ -170,21 +171,12 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { l.relocCommon(pr, &wname, pkgbits.RelocName, idx) l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx) - var obj *ir.Name - if sym.Pkg == types.LocalPkg { - var ok bool - obj, ok = sym.Def.(*ir.Name) - - // Generic types and functions and declared constraint types won't - // have definitions. - // For now, just generically copy their extension data. - // TODO(mdempsky): Restore assertion. - if !ok && false { - base.Fatalf("missing definition for %v", sym) - } - } + // Generic types and functions won't have definitions, and imported + // objects may not either. + obj, _ := sym.Def.(*ir.Name) + local := sym.Pkg == types.LocalPkg - if obj != nil { + if local && obj != nil { wext.Sync(pkgbits.SyncObject1) switch tag { case pkgbits.ObjFunc: @@ -199,9 +191,64 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx) } + // Check if we need to export the inline bodies for functions and + // methods. + if obj != nil { + if obj.Op() == ir.ONAME && obj.Class == ir.PFUNC { + l.exportBody(obj, local) + } + + if obj.Op() == ir.OTYPE { + if typ := obj.Type(); !typ.IsInterface() { + for _, method := range typ.Methods().Slice() { + l.exportBody(method.Nname.(*ir.Name), local) + } + } + } + } + return w.Idx } +// exportBody exports the given function or method's body, if +// appropriate. local indicates whether it's a local function or +// method available on a locally declared type. (Due to cross-package +// type aliases, a method may be imported, but still available on a +// locally declared type.) +func (l *linker) exportBody(obj *ir.Name, local bool) { + assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC) + + fn := obj.Func + if fn.Inl == nil { + return // not inlinable anyway + } + + // As a simple heuristic, if the function was declared in this + // package or we inlined it somewhere in this package, then we'll + // (re)export the function body. This isn't perfect, but seems + // reasonable in practice. In particular, it has the nice property + // that in the worst case, adding a blank import ensures the + // function body is available for inlining. + // + // TODO(mdempsky): Reimplement the reachable method crawling logic + // from typecheck/crawler.go. + exportBody := local || fn.Inl.Body != nil + if !exportBody { + return + } + + sym := obj.Sym() + if _, ok := l.bodies[sym]; ok { + // Due to type aliases, we might visit methods multiple times. + base.AssertfAt(obj.Type().Recv() != nil, obj.Pos(), "expected method: %v", obj) + return + } + + pri, ok := bodyReaderFor(fn) + assert(ok) + l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx) +} + // relocCommon copies the specified element from pr into w, // recursively relocating any referenced elements as well. func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) { @@ -240,10 +287,6 @@ func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) { if inl := name.Func.Inl; w.Bool(inl != nil) { w.Len(int(inl.Cost)) w.Bool(inl.CanDelayResults) - - pri, ok := bodyReader[name.Func] - assert(ok) - w.Reloc(pkgbits.RelocBody, l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx)) } w.Sync(pkgbits.SyncEOF) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 0a382e1c9b..9458332fc8 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -897,6 +897,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. @@ -911,7 +913,6 @@ func (r *reader) funcExt(name *ir.Name) { Cost: int32(r.Len()), CanDelayResults: r.Bool(), } - r.addBody(name.Func) } } else { r.addBody(name.Func) @@ -967,10 +968,26 @@ 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] + assert(ok) // 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 @@ -978,15 +995,13 @@ 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) { + // addBody should only be called for local functions or imported + // generic functions; see comment in funcExt. + assert(fn.Nname.Defn != nil) + pri := pkgReaderIndex{r.p, r.Reloc(pkgbits.RelocBody), r.dict} bodyReader[fn] = pri - if fn.Nname.Defn == nil { - // Don't read in function body for imported functions. - // See comment in funcExt. - return - } - if r.curfn == nil { todoBodies = append(todoBodies, fn) return @@ -2225,7 +2240,7 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp // TODO(mdempsky): Turn callerfn into an explicit parameter. callerfn := ir.CurFunc - pri, ok := bodyReader[fn] + pri, ok := bodyReaderFor(fn) if !ok { // TODO(mdempsky): Reconsider this diagnostic's wording, if it's // to be included in Go 1.20. diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index 95486af66c..d9b15ab385 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -85,7 +85,7 @@ func unified(noders []*noder) { typecheck.TypecheckAllowed = true localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data)) - readPackage(localPkgReader, types.LocalPkg) + readPackage(localPkgReader, types.LocalPkg, true) r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate) r.pkgInit(types.LocalPkg, target) @@ -226,29 +226,54 @@ func freePackage(pkg *types2.Package) { // readPackage reads package export data from pr to populate // importpkg. -func readPackage(pr *pkgReader, importpkg *types.Pkg) { - r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) +// +// localStub indicates whether pr is reading the stub export data for +// the local package, as opposed to relocated export data for an +// import. +func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { + { + r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) + + pkg := r.pkg() + base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg) + + if r.Bool() { + sym := pkg.Lookup(".inittask") + task := ir.NewNameAt(src.NoXPos, sym) + task.Class = ir.PEXTERN + sym.Def = task + } + + for i, n := 0, r.Len(); i < n; i++ { + r.Sync(pkgbits.SyncObject) + assert(!r.Bool()) + idx := r.Reloc(pkgbits.RelocObj) + assert(r.Len() == 0) - pkg := r.pkg() - base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg) + path, name, code := r.p.PeekObj(idx) + if code != pkgbits.ObjStub { + objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil} + } + } - if r.Bool() { - sym := pkg.Lookup(".inittask") - task := ir.NewNameAt(src.NoXPos, sym) - task.Class = ir.PEXTERN - sym.Def = task + r.Sync(pkgbits.SyncEOF) } - for i, n := 0, r.Len(); i < n; i++ { - r.Sync(pkgbits.SyncObject) - assert(!r.Bool()) - idx := r.Reloc(pkgbits.RelocObj) - assert(r.Len() == 0) + if !localStub { + r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate) + + for i, n := 0, r.Len(); i < n; i++ { + path := r.String() + name := r.String() + idx := r.Reloc(pkgbits.RelocBody) - path, name, code := r.p.PeekObj(idx) - if code != pkgbits.ObjStub { - objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil} + sym := types.NewPkg(path, "").Lookup(name) + if _, ok := importBodyReader[sym]; !ok { + importBodyReader[sym] = pkgReaderIndex{pr, idx, nil} + } } + + r.Sync(pkgbits.SyncEOF) } } @@ -258,12 +283,15 @@ func writeUnifiedExport(out io.Writer) { l := linker{ pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), - pkgs: make(map[string]pkgbits.Index), - decls: make(map[*types.Sym]pkgbits.Index), + pkgs: make(map[string]pkgbits.Index), + decls: make(map[*types.Sym]pkgbits.Index), + bodies: make(map[*types.Sym]pkgbits.Index), } publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic) + privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate) assert(publicRootWriter.Idx == pkgbits.PublicRootIdx) + assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx) var selfPkgIdx pkgbits.Index @@ -320,5 +348,29 @@ func writeUnifiedExport(out io.Writer) { w.Flush() } + { + type symIdx struct { + sym *types.Sym + idx pkgbits.Index + } + var bodies []symIdx + for sym, idx := range l.bodies { + bodies = append(bodies, symIdx{sym, idx}) + } + sort.Slice(bodies, func(i, j int) bool { return bodies[i].idx < bodies[j].idx }) + + w := privateRootWriter + + w.Len(len(bodies)) + for _, body := range bodies { + w.String(body.sym.Pkg.Path) + w.String(body.sym.Name) + w.Reloc(pkgbits.RelocBody, body.idx) + } + + w.Sync(pkgbits.SyncEOF) + w.Flush() + } + base.Ctxt.Fingerprint = l.pw.DumpTo(out) } -- cgit v1.3-6-g1900 From ac0844ec274bf6cfd64bc8fa1e2cc8b24c789dee Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 27 Jul 2022 00:33:56 -0700 Subject: [dev.unified] cmd/compile: move "has init" to private metadata Currently, there's a "has init" bool in the public metadata section, which is only needed by cmd/compile; but because it's in the public metadata section, it's known to the go/types importers too. This CL moves it instead to the new compiler-only private metadata section added in the last CL for the inline bodies index. The existing bool in the public metadata section is left in place, and just always set to false, to avoid breaking the x/tools importer. The next time we bump the export version number, we can remove the bool properly. But no urgency just yet. Change-Id: I380f358652374b5a221f85020a53dc65912ddb29 Reviewed-on: https://go-review.googlesource.com/c/go/+/419676 Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/importer/ureader.go | 2 +- src/cmd/compile/internal/noder/unified.go | 23 +++++++++++++---------- src/go/internal/gcimporter/ureader.go | 2 +- src/internal/pkgbits/encoder.go | 4 ++++ 4 files changed, 19 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/importer/ureader.go b/src/cmd/compile/internal/importer/ureader.go index ccd4c7c502..d00b765859 100644 --- a/src/cmd/compile/internal/importer/ureader.go +++ b/src/cmd/compile/internal/importer/ureader.go @@ -39,7 +39,7 @@ func ReadPackage(ctxt *types2.Context, imports map[string]*types2.Package, input r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) pkg := r.pkg() - r.Bool() // has init + r.Bool() // TODO(mdempsky): Remove; was "has init" for i, n := 0, r.Len(); i < n; i++ { // As if r.obj(), but avoiding the Scope.Lookup call, diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index d9b15ab385..eebbb03742 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -161,7 +161,7 @@ func writePkgStub(noders []*noder) string { { w := publicRootWriter w.pkg(pkg) - w.Bool(false) // has init; XXX + w.Bool(false) // TODO(mdempsky): Remove; was "has init" scope := pkg.Scope() names := scope.Names() @@ -237,12 +237,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { pkg := r.pkg() base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg) - if r.Bool() { - sym := pkg.Lookup(".inittask") - task := ir.NewNameAt(src.NoXPos, sym) - task.Class = ir.PEXTERN - sym.Def = task - } + r.Bool() // TODO(mdempsky): Remove; was "has init" for i, n := 0, r.Len(); i < n; i++ { r.Sync(pkgbits.SyncObject) @@ -262,6 +257,13 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { if !localStub { r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate) + if r.Bool() { + sym := importpkg.Lookup(".inittask") + task := ir.NewNameAt(src.NoXPos, sym) + task.Class = ir.PEXTERN + sym.Def = task + } + for i, n := 0, r.Len(); i < n; i++ { path := r.String() name := r.String() @@ -302,7 +304,7 @@ func writeUnifiedExport(out io.Writer) { r.Sync(pkgbits.SyncPkg) selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg)) - r.Bool() // has init + r.Bool() // TODO(mdempsky): Remove; was "has init" for i, n := 0, r.Len(); i < n; i++ { r.Sync(pkgbits.SyncObject) @@ -333,8 +335,7 @@ func writeUnifiedExport(out io.Writer) { w.Sync(pkgbits.SyncPkg) w.Reloc(pkgbits.RelocPkg, selfPkgIdx) - - w.Bool(typecheck.Lookup(".inittask").Def != nil) + w.Bool(false) // TODO(mdempsky): Remove; was "has init" w.Len(len(idxs)) for _, idx := range idxs { @@ -361,6 +362,8 @@ func writeUnifiedExport(out io.Writer) { w := privateRootWriter + w.Bool(typecheck.Lookup(".inittask").Def != nil) + w.Len(len(bodies)) for _, body := range bodies { w.String(body.sym.Pkg.Path) diff --git a/src/go/internal/gcimporter/ureader.go b/src/go/internal/gcimporter/ureader.go index 63718a59e1..2047ad8ae9 100644 --- a/src/go/internal/gcimporter/ureader.go +++ b/src/go/internal/gcimporter/ureader.go @@ -60,7 +60,7 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) pkg := r.pkg() - r.Bool() // has init + r.Bool() // TODO(mdempsky): Remove; was "has init" for i, n := 0, r.Len(); i < n; i++ { // As if r.obj(), but avoiding the Scope.Lookup call, diff --git a/src/internal/pkgbits/encoder.go b/src/internal/pkgbits/encoder.go index c0f2252909..f1bc8367ef 100644 --- a/src/internal/pkgbits/encoder.go +++ b/src/internal/pkgbits/encoder.go @@ -19,6 +19,10 @@ import ( // - v0: initial prototype // // - v1: adds the flags uint32 word +// +// TODO(mdempsky): For the next version bump: +// - remove the unused dict.derived.needed bool +// - remove the legacy "has init" bool from the public root const currentVersion uint32 = 1 // A PkgEncoder provides methods for encoding a package's Unified IR -- cgit v1.3-6-g1900 From c8d5ccf82ec24ff3855c1717d46df6a0c60684de Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 27 Jul 2022 14:24:46 -0700 Subject: [dev.unified] go/internal/gcimporter: flatten imports The current documentation for go/types.(*Packages).Imports requires that the import graph be flattened when read from export data. I think this is a documentation bug (incorrectly codifying the existing behavior, rather than documenting it as a known bug), but until that's decided, we can at least flatten imports ourselves. Updates #54096. Change-Id: Idc054a2efc908b3e6651e6567d0ea0e89bb0c54d Reviewed-on: https://go-review.googlesource.com/c/go/+/419596 Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/go/internal/gcimporter/ureader.go | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/go/internal/gcimporter/ureader.go b/src/go/internal/gcimporter/ureader.go index 2047ad8ae9..5e133f890b 100644 --- a/src/go/internal/gcimporter/ureader.go +++ b/src/go/internal/gcimporter/ureader.go @@ -206,11 +206,41 @@ func (r *reader) doPkg() *types.Package { for i := range imports { imports[i] = r.pkg() } - pkg.SetImports(imports) + + // The documentation for (*types.Package).Imports requires + // flattening the import graph when reading from export data, as + // obviously incorrect as that is. + // + // TODO(mdempsky): Remove this if go.dev/issue/54096 is accepted. + pkg.SetImports(flattenImports(imports)) return pkg } +// flattenImports returns the transitive closure of all imported +// packages rooted from pkgs. +func flattenImports(pkgs []*types.Package) []*types.Package { + var res []*types.Package + + seen := make(map[*types.Package]bool) + var add func(pkg *types.Package) + add = func(pkg *types.Package) { + if seen[pkg] { + return + } + seen[pkg] = true + res = append(res, pkg) + for _, imp := range pkg.Imports() { + add(imp) + } + } + + for _, pkg := range pkgs { + add(pkg) + } + return res +} + // @@@ Types func (r *reader) typ() types.Type { -- cgit v1.3-6-g1900 From 994ff78ba01b921870866f9ce9db7563bd89494f Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 29 Jul 2022 12:06:50 -0400 Subject: [dev.unified] go/internal: set underlying types in proper order This problem appeared in google-internal testing. If the run-later functions are run in the wrong order, type definitions won't resolve properly. Change-Id: I9da0775976282e92ca036d20fd9fd6650900daf9 Reviewed-on: https://go-review.googlesource.com/c/go/+/419996 Run-TryBot: David Chase TryBot-Result: Gopher Robot Reviewed-by: Matthew Dempsky --- src/go/internal/gcimporter/gcimporter_test.go | 24 ++++++++++++++++++++++++ src/go/internal/gcimporter/testdata/g.go | 23 +++++++++++++++++++++++ src/go/internal/gcimporter/ureader.go | 21 ++++++++++++++++++++- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/go/internal/gcimporter/testdata/g.go (limited to 'src') diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index b32de17910..68a077c190 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -583,6 +583,30 @@ func TestIssue13566(t *testing.T) { } } +func TestTypeNamingOrder(t *testing.T) { + skipSpecialPlatforms(t) + + // This package only handles gc export data. + if runtime.Compiler != "gc" { + t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) + } + + // On windows, we have to set the -D option for the compiler to avoid having a drive + // letter and an illegal ':' in the import path - just skip it (see also issue #3483). + if runtime.GOOS == "windows" { + t.Skip("avoid dealing with relative paths/drive letters on windows") + } + + tmpdir := mktmpdir(t) + defer os.RemoveAll(tmpdir) + testoutdir := filepath.Join(tmpdir, "testdata") + + compile(t, "testdata", "g.go", testoutdir) + + // import must succeed (test for issue at hand) + _ = importPkg(t, "./testdata/g", tmpdir) +} + func TestIssue13898(t *testing.T) { skipSpecialPlatforms(t) diff --git a/src/go/internal/gcimporter/testdata/g.go b/src/go/internal/gcimporter/testdata/g.go new file mode 100644 index 0000000000..301c1429e6 --- /dev/null +++ b/src/go/internal/gcimporter/testdata/g.go @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +// Input for TestTypeNamingOrder + +// ensures that the order in which "type A B" declarations are +// processed is correct; this was a problem for unified IR imports. + +package g + +type Client struct { + common service + A *AService + B *BService +} + +type service struct { + client *Client +} + +type AService service +type BService service diff --git a/src/go/internal/gcimporter/ureader.go b/src/go/internal/gcimporter/ureader.go index 5e133f890b..97f0664fe3 100644 --- a/src/go/internal/gcimporter/ureader.go +++ b/src/go/internal/gcimporter/ureader.go @@ -31,6 +31,8 @@ type pkgReader struct { // laterFns holds functions that need to be invoked at the end of // import reading. laterFns []func() + // laterFors is used in case of 'type A B' to ensure that B is processed before A. + laterFors map[types.Type]int } // later adds a function to be invoked at the end of import reading. @@ -38,6 +40,15 @@ func (pr *pkgReader) later(fn func()) { pr.laterFns = append(pr.laterFns, fn) } +// laterFor adds a function to be invoked at the end of import reading, and records the type that function is finishing. +func (pr *pkgReader) laterFor(t types.Type, fn func()) { + if pr.laterFors == nil { + pr.laterFors = make(map[types.Type]int) + } + pr.laterFors[t] = len(pr.laterFns) + pr.laterFns = append(pr.laterFns, fn) +} + // readUnifiedPackage reads a package description from the given // unified IR export data decoder. func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package { @@ -487,7 +498,15 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { // unit tests expected that), but cmd/compile doesn't care // about it, so maybe we can avoid worrying about that here. rhs := r.typ() - r.p.later(func() { + pk := r.p + pk.laterFor(named, func() { + // First be sure that the rhs is initialized, if it needs to be initialized. + delete(pk.laterFors, named) // prevent cycles + if i, ok := pk.laterFors[rhs]; ok { + f := pk.laterFns[i] + pk.laterFns[i] = func() {} // function is running now, so replace it with a no-op + f() // initialize RHS + } underlying := rhs.Underlying() named.SetUnderlying(underlying) }) -- cgit v1.3-6-g1900 From c9f2150cfb3c1db87f6434f727c25403d985a6e4 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sun, 31 Jul 2022 18:48:16 -0700 Subject: [dev.unified] cmd/compile: start using runtime dictionaries This CL switches unified IR to start using runtime dictionaries, rather than pure stenciling. In particular, for each instantiated function `F[T]`, it now: 1. Generates a global variable `F[T]-dict` of type `[N]uintptr`, with all of the `*runtime._type` values needed by `F[T]`. 2. Generates a function `F[T]-shaped`, with an extra `.dict *[N]uintptr` parameter and indexing into that parameter for derived types. (N.B., this function is not yet actually using shape types.) 3. Changes `F[T]` to instead be a wrapper function that calls `F[T]-shaped` passing `&F[T]-dict` as the `.dict` parameter. This is done in one pass to make sure the overall wiring is all working (especially, function literals and inlining). Subsequent CLs will write more information into `F[T]-dict` and update `F[T]-shaped` to use it instead of relying on `T`-derived information itself. Once that's done, `F[T]-shaped` can be changed to `F[shapify(T)]` (e.g., `F[go.shape.int]`) and deduplicated. Change-Id: I0e802a4d9934794e01a6bfc367820af893335155 Reviewed-on: https://go-review.googlesource.com/c/go/+/420416 Run-TryBot: Matthew Dempsky TryBot-Result: Gopher Robot Auto-Submit: Matthew Dempsky Reviewed-by: Cuong Manh Le Reviewed-by: Keith Randall Reviewed-by: Keith Randall Reviewed-by: David Chase --- src/cmd/compile/internal/noder/reader.go | 318 ++++++++++++++++++++--- src/cmd/compile/internal/noder/unified.go | 4 +- src/cmd/compile/internal/noder/writer.go | 22 +- src/cmd/compile/internal/ssa/debug_lines_test.go | 4 +- src/internal/pkgbits/encoder.go | 1 - 5 files changed, 308 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 9458332fc8..d02d05bc5d 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -17,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" @@ -61,14 +62,16 @@ 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 } @@ -98,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 @@ -119,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 { @@ -737,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()) @@ -806,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()) @@ -981,7 +989,7 @@ var importBodyReader = map[*types.Sym]pkgReaderIndex{} func bodyReaderFor(fn *ir.Func) (pri pkgReaderIndex, ok bool) { if fn.Nname.Defn != nil { pri, ok = bodyReader[fn] - assert(ok) // must always be available + base.AssertfAt(ok, base.Pos, "must have bodyReader for %v", fn) // must always be available } else { pri, ok = importBodyReader[fn.Sym()] } @@ -999,7 +1007,38 @@ func (r *reader) addBody(fn *ir.Func) { // generic functions; see comment in funcExt. assert(fn.Nname.Defn != nil) - pri := pkgReaderIndex{r.p, r.Reloc(pkgbits.RelocBody), r.dict} + 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 { @@ -1020,6 +1059,9 @@ func (pri pkgReaderIndex) funcBody(fn *ir.Func) { 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) @@ -1028,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))} @@ -1039,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() @@ -1096,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 r.p.SyncMarkers() { - 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) { @@ -2000,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) @@ -2024,39 +2213,60 @@ func (r *reader) exprs() []ir.Node { return nodes } -// rtype returns an expression of type *runtime._type. +// 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)))) +} + +// 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) - // TODO(mdempsky): For derived types, use dictionary instead. - return reflectdata.TypePtrAt(pos, r.typ()) + 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) - src := r.typ() - dst := r.typ() + 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 = reflectdata.TypePtrAt(pos, src) // direct eface construction + typeWord = r.rtypeInfo(pos, srcInfo) // direct eface construction } case !src.IsInterface(): typeWord = reflectdata.ITabAddrAt(pos, src, dst) // direct iface construction default: - typeWord = reflectdata.TypePtrAt(pos, dst) // convI2I + typeWord = r.rtypeInfo(pos, dstInfo) // convI2I } // See reflectdata.ConvIfaceSrcRType. if !src.IsInterface() { - srcRType = reflectdata.TypePtrAt(pos, src) + srcRType = r.rtypeInfo(pos, srcInfo) } return @@ -2096,7 +2306,7 @@ func (r *reader) exprType() ir.Node { return n } - rtype = lsymPtr(reflectdata.TypeLinksym(typ)) + rtype = r.rtypeInfo(pos, info) } dt := ir.NewDynamicType(pos, rtype) @@ -2275,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) @@ -2337,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 @@ -2800,3 +3017,40 @@ 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) +} diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index eebbb03742..1ded367383 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -247,7 +247,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { path, name, code := r.p.PeekObj(idx) if code != pkgbits.ObjStub { - objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil} + objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil, nil} } } @@ -271,7 +271,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { sym := types.NewPkg(path, "").Lookup(name) if _, ok := importBodyReader[sym]; !ok { - importBodyReader[sym] = pkgReaderIndex{pr, idx, nil} + importBodyReader[sym] = pkgReaderIndex{pr, idx, nil, nil} } } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 0005c2e7fa..5f8767bf83 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -190,7 +190,7 @@ type writerDict struct { // A derivedInfo represents a reference to an encoded generic Go type. type derivedInfo struct { idx pkgbits.Index - needed bool // TODO(mdempsky): Remove; will break x/tools importer + needed bool } // A typeInfo represents a reference to an encoded Go type. @@ -1952,15 +1952,26 @@ func (w *writer) exprs(exprs []syntax.Expr) { // expression of type *runtime._type representing typ. func (w *writer) rtype(typ types2.Type) { w.Sync(pkgbits.SyncRType) - w.typ(typ) + w.typNeeded(typ) +} + +// typNeeded writes a reference to typ, and records that its +// *runtime._type is needed. +func (w *writer) typNeeded(typ types2.Type) { + info := w.p.typIdx(typ, w.dict) + w.typInfo(info) + + if info.derived { + w.dict.derived[info.idx].needed = true + } } // convRTTI writes information so that the reader can construct // expressions for converting from src to dst. func (w *writer) convRTTI(src, dst types2.Type) { w.Sync(pkgbits.SyncConvRTTI) - w.typ(src) - w.typ(dst) + w.typNeeded(src) + w.typNeeded(dst) } func (w *writer) exprType(iface types2.Type, typ syntax.Expr) { @@ -1992,6 +2003,9 @@ func (w *writer) exprType(iface types2.Type, typ syntax.Expr) { } w.typInfo(info) + if info.derived { + w.dict.derived[info.idx].needed = true + } } func (dict *writerDict) methodExprIdx(recvInfo typeInfo, methodInfo selectorInfo) int { diff --git a/src/cmd/compile/internal/ssa/debug_lines_test.go b/src/cmd/compile/internal/ssa/debug_lines_test.go index a76358967d..1b564055d3 100644 --- a/src/cmd/compile/internal/ssa/debug_lines_test.go +++ b/src/cmd/compile/internal/ssa/debug_lines_test.go @@ -76,7 +76,7 @@ func TestDebugLinesPushback(t *testing.T) { fn := "(*List[go.shape.int_0]).PushBack" if buildcfg.Experiment.Unified { // Unified mangles differently - fn = "(*List[int]).PushBack" + fn = "(*List[int]).PushBack-shaped" } testDebugLines(t, "-N -l", "pushback.go", fn, []int{17, 18, 19, 20, 21, 22, 24}, true) } @@ -95,7 +95,7 @@ func TestDebugLinesConvert(t *testing.T) { fn := "G[go.shape.int_0]" if buildcfg.Experiment.Unified { // Unified mangles differently - fn = "G[int]" + fn = "G[int]-shaped" } testDebugLines(t, "-N -l", "convertline.go", fn, []int{9, 10, 11}, true) } diff --git a/src/internal/pkgbits/encoder.go b/src/internal/pkgbits/encoder.go index f1bc8367ef..ec47e352cb 100644 --- a/src/internal/pkgbits/encoder.go +++ b/src/internal/pkgbits/encoder.go @@ -21,7 +21,6 @@ import ( // - v1: adds the flags uint32 word // // TODO(mdempsky): For the next version bump: -// - remove the unused dict.derived.needed bool // - remove the legacy "has init" bool from the public root const currentVersion uint32 = 1 -- cgit v1.3-6-g1900