mirror of
https://github.com/golang/go
synced 2024-09-05 07:54:46 +00:00
[dev.typeparams] cmd/compile: incremental typecheck during unified IR
This CL changes unified IR to incrementally typecheck the IR as it's constructed. This is significant, because it means reader can now use typecheck.Expr to typecheck sub-expressions when it's needed. This should be helpful for construction and insertion of dictionaries. This CL does introduce two quirks outside of unified IR itself, which simplify preserving binary output: 1. Top-level declarations are sorted after they're constructed, to avoid worrying about the order that closures are added. 2. Zero-padding autotmp_N variable names. Interleaving typechecking means autotmp variables are sometimes named differently (since their naming depends on the number of variables declared so far), and this ensures that code that sorts variables by names doesn't suddenly sort autotmp_8/autotmp_9 differently than it would have sorted autotmp_9/autotmp_10. While at it, this CL also updated reader to use ir.WithFunc instead of manually setting and restoring ir.CurFunc. There's now only one remaining direct use of ir.CurFunc. Change-Id: I6233b4c059596e471c53166f94750917d710462f Reviewed-on: https://go-review.googlesource.com/c/go/+/332469 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
2aea44204e
commit
a18726a648
|
@ -32,6 +32,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func hidePanic() {
|
func hidePanic() {
|
||||||
|
@ -202,6 +203,20 @@ func Main(archInit func(*ssagen.ArchInfo)) {
|
||||||
typecheck.Export(initTask)
|
typecheck.Export(initTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stability quirk: sort top-level declarations, so we're not
|
||||||
|
// sensitive to the order that functions are added. In particular,
|
||||||
|
// the order that noder+typecheck add function closures is very
|
||||||
|
// subtle, and not important to reproduce.
|
||||||
|
//
|
||||||
|
// Note: This needs to happen after pkginit.Task, otherwise it risks
|
||||||
|
// changing the order in which top-level variables are initialized.
|
||||||
|
if base.Debug.UnifiedQuirks != 0 {
|
||||||
|
s := typecheck.Target.Decls
|
||||||
|
sort.SliceStable(s, func(i, j int) bool {
|
||||||
|
return s[i].Pos().Before(s[j].Pos())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Eliminate some obviously dead code.
|
// Eliminate some obviously dead code.
|
||||||
// Must happen after typechecking.
|
// Must happen after typechecking.
|
||||||
for _, n := range typecheck.Target.Decls {
|
for _, n := range typecheck.Target.Decls {
|
||||||
|
|
|
@ -886,27 +886,25 @@ func (r *reader) funcBody(fn *ir.Func) {
|
||||||
r.curfn = fn
|
r.curfn = fn
|
||||||
r.closureVars = fn.ClosureVars
|
r.closureVars = fn.ClosureVars
|
||||||
|
|
||||||
// TODO(mdempsky): Get rid of uses of typecheck.NodAddrAt so we
|
ir.WithFunc(fn, func() {
|
||||||
// don't have to set ir.CurFunc.
|
r.funcargs(fn)
|
||||||
outerCurFunc := ir.CurFunc
|
|
||||||
ir.CurFunc = fn
|
|
||||||
|
|
||||||
r.funcargs(fn)
|
if !r.bool() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if r.bool() {
|
|
||||||
body := r.stmts()
|
body := r.stmts()
|
||||||
if body == nil {
|
if body == nil {
|
||||||
pos := src.NoXPos
|
pos := src.NoXPos
|
||||||
if quirksMode() {
|
if quirksMode() {
|
||||||
pos = funcParamsEndPos(fn)
|
pos = funcParamsEndPos(fn)
|
||||||
}
|
}
|
||||||
body = []ir.Node{ir.NewBlockStmt(pos, nil)}
|
body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(pos, nil))}
|
||||||
}
|
}
|
||||||
fn.Body = body
|
fn.Body = body
|
||||||
fn.Endlineno = r.pos()
|
fn.Endlineno = r.pos()
|
||||||
}
|
})
|
||||||
|
|
||||||
ir.CurFunc = outerCurFunc
|
|
||||||
r.marker.WriteTo(fn)
|
r.marker.WriteTo(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1045,7 +1043,42 @@ func (r *reader) closeAnotherScope() {
|
||||||
scopeVars := r.scopeVars[len(r.scopeVars)-1]
|
scopeVars := r.scopeVars[len(r.scopeVars)-1]
|
||||||
r.scopeVars = r.scopeVars[:len(r.scopeVars)-1]
|
r.scopeVars = r.scopeVars[:len(r.scopeVars)-1]
|
||||||
|
|
||||||
if scopeVars == len(r.curfn.Dcl) {
|
// Quirkish: noder decides which scopes to keep before
|
||||||
|
// typechecking, whereas incremental typechecking during IR
|
||||||
|
// construction can result in new autotemps being allocated. To
|
||||||
|
// produce identical output, we ignore autotemps here for the
|
||||||
|
// purpose of deciding whether to retract the scope.
|
||||||
|
//
|
||||||
|
// This is important for net/http/fcgi, because it contains:
|
||||||
|
//
|
||||||
|
// var body io.ReadCloser
|
||||||
|
// if len(content) > 0 {
|
||||||
|
// body, req.pw = io.Pipe()
|
||||||
|
// } else { … }
|
||||||
|
//
|
||||||
|
// Notably, io.Pipe is inlinable, and inlining it introduces a ~R0
|
||||||
|
// variable at the call site.
|
||||||
|
//
|
||||||
|
// Noder does not preserve the scope where the io.Pipe() call
|
||||||
|
// resides, because it doesn't contain any declared variables in
|
||||||
|
// source. So the ~R0 variable ends up being assigned to the
|
||||||
|
// enclosing scope instead.
|
||||||
|
//
|
||||||
|
// However, typechecking this assignment also introduces
|
||||||
|
// autotemps, because io.Pipe's results need conversion before
|
||||||
|
// they can be assigned to their respective destination variables.
|
||||||
|
//
|
||||||
|
// TODO(mdempsky): We should probably just keep all scopes, and
|
||||||
|
// let dwarfgen take care of pruning them instead.
|
||||||
|
retract := true
|
||||||
|
for _, n := range r.curfn.Dcl[scopeVars:] {
|
||||||
|
if !n.AutoTemp() {
|
||||||
|
retract = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if retract {
|
||||||
// no variables were declared in this scope, so we can retract it.
|
// no variables were declared in this scope, so we can retract it.
|
||||||
r.marker.Unpush()
|
r.marker.Unpush()
|
||||||
} else {
|
} else {
|
||||||
|
@ -1068,6 +1101,7 @@ func (r *reader) stmt() ir.Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reader) stmts() []ir.Node {
|
func (r *reader) stmts() []ir.Node {
|
||||||
|
assert(ir.CurFunc == r.curfn)
|
||||||
var res ir.Nodes
|
var res ir.Nodes
|
||||||
|
|
||||||
r.sync(syncStmts)
|
r.sync(syncStmts)
|
||||||
|
@ -1079,7 +1113,7 @@ func (r *reader) stmts() []ir.Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
if n := r.stmt1(tag, &res); n != nil {
|
if n := r.stmt1(tag, &res); n != nil {
|
||||||
res.Append(n)
|
res.Append(typecheck.Stmt(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1108,7 +1142,7 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
as := ir.NewAssignStmt(pos, name, nil)
|
as := ir.NewAssignStmt(pos, name, nil)
|
||||||
as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name))
|
as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name))
|
||||||
out.Append(as)
|
out.Append(typecheck.Stmt(as))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1488,6 +1522,9 @@ func (r *reader) expr() ir.Node {
|
||||||
|
|
||||||
case exprCall:
|
case exprCall:
|
||||||
fun := r.expr()
|
fun := r.expr()
|
||||||
|
if clo, ok := fun.(*ir.ClosureExpr); ok {
|
||||||
|
clo.Func.SetClosureCalled(true)
|
||||||
|
}
|
||||||
pos := r.pos()
|
pos := r.pos()
|
||||||
args := r.exprs()
|
args := r.exprs()
|
||||||
dots := r.bool()
|
dots := r.bool()
|
||||||
|
@ -1574,11 +1611,15 @@ func (r *reader) funcLit() ir.Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn := ir.NewClosureFunc(opos, r.curfn != nil)
|
fn := ir.NewClosureFunc(opos, r.curfn != nil)
|
||||||
|
clo := fn.OClosure
|
||||||
|
ir.NameClosure(clo, r.curfn)
|
||||||
|
|
||||||
r.setType(fn.Nname, xtype2)
|
r.setType(fn.Nname, xtype2)
|
||||||
if quirksMode() {
|
if quirksMode() {
|
||||||
fn.Nname.Ntype = ir.TypeNodeAt(typPos, xtype2)
|
fn.Nname.Ntype = ir.TypeNodeAt(typPos, xtype2)
|
||||||
}
|
}
|
||||||
|
typecheck.Func(fn)
|
||||||
|
r.setType(clo, fn.Type())
|
||||||
|
|
||||||
fn.ClosureVars = make([]*ir.Name, 0, r.len())
|
fn.ClosureVars = make([]*ir.Name, 0, r.len())
|
||||||
for len(fn.ClosureVars) < cap(fn.ClosureVars) {
|
for len(fn.ClosureVars) < cap(fn.ClosureVars) {
|
||||||
|
@ -1591,7 +1632,8 @@ func (r *reader) funcLit() ir.Node {
|
||||||
|
|
||||||
r.addBody(fn)
|
r.addBody(fn)
|
||||||
|
|
||||||
return fn.OClosure
|
// TODO(mdempsky): Remove hard-coding of typecheck.Target.
|
||||||
|
return ir.UseClosure(clo, typecheck.Target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reader) exprList() []ir.Node {
|
func (r *reader) exprList() []ir.Node {
|
||||||
|
@ -1788,7 +1830,7 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
|
||||||
r.setType(tmpfn.Nname, fn.Type())
|
r.setType(tmpfn.Nname, fn.Type())
|
||||||
r.curfn = tmpfn
|
r.curfn = tmpfn
|
||||||
|
|
||||||
r.inlCaller = ir.CurFunc
|
r.inlCaller = callerfn
|
||||||
r.inlCall = call
|
r.inlCall = call
|
||||||
r.inlFunc = fn
|
r.inlFunc = fn
|
||||||
r.inlTreeIndex = inlIndex
|
r.inlTreeIndex = inlIndex
|
||||||
|
@ -1872,17 +1914,13 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
|
||||||
|
|
||||||
nparams := len(r.curfn.Dcl)
|
nparams := len(r.curfn.Dcl)
|
||||||
|
|
||||||
oldcurfn := ir.CurFunc
|
ir.WithFunc(r.curfn, func() {
|
||||||
ir.CurFunc = r.curfn
|
r.curfn.Body = r.stmts()
|
||||||
|
r.curfn.Endlineno = r.pos()
|
||||||
|
|
||||||
r.curfn.Body = r.stmts()
|
deadcode.Func(r.curfn)
|
||||||
r.curfn.Endlineno = r.pos()
|
|
||||||
|
|
||||||
typecheck.Stmts(r.curfn.Body)
|
// Replace any "return" statements within the function body.
|
||||||
deadcode.Func(r.curfn)
|
|
||||||
|
|
||||||
// Replace any "return" statements within the function body.
|
|
||||||
{
|
|
||||||
var edit func(ir.Node) ir.Node
|
var edit func(ir.Node) ir.Node
|
||||||
edit = func(n ir.Node) ir.Node {
|
edit = func(n ir.Node) ir.Node {
|
||||||
if ret, ok := n.(*ir.ReturnStmt); ok {
|
if ret, ok := n.(*ir.ReturnStmt); ok {
|
||||||
|
@ -1892,9 +1930,7 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
edit(r.curfn)
|
edit(r.curfn)
|
||||||
}
|
})
|
||||||
|
|
||||||
ir.CurFunc = oldcurfn
|
|
||||||
|
|
||||||
body := ir.Nodes(r.curfn.Body)
|
body := ir.Nodes(r.curfn.Body)
|
||||||
|
|
||||||
|
@ -1998,16 +2034,12 @@ func expandInline(fn *ir.Func, pri pkgReaderIndex) {
|
||||||
r.funarghack = true
|
r.funarghack = true
|
||||||
|
|
||||||
r.funcBody(tmpfn)
|
r.funcBody(tmpfn)
|
||||||
|
|
||||||
|
ir.WithFunc(tmpfn, func() {
|
||||||
|
deadcode.Func(tmpfn)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
oldcurfn := ir.CurFunc
|
|
||||||
ir.CurFunc = tmpfn
|
|
||||||
|
|
||||||
typecheck.Stmts(tmpfn.Body)
|
|
||||||
deadcode.Func(tmpfn)
|
|
||||||
|
|
||||||
ir.CurFunc = oldcurfn
|
|
||||||
|
|
||||||
used := usedLocals(tmpfn.Body)
|
used := usedLocals(tmpfn.Body)
|
||||||
|
|
||||||
for _, name := range tmpfn.Dcl {
|
for _, name := range tmpfn.Dcl {
|
||||||
|
|
|
@ -138,23 +138,6 @@ func unified(noders []*noder) {
|
||||||
}
|
}
|
||||||
todoBodies = nil
|
todoBodies = nil
|
||||||
|
|
||||||
// Don't use range--typecheck can add closures to Target.Decls.
|
|
||||||
for i := 0; i < len(target.Decls); i++ {
|
|
||||||
if fn, ok := target.Decls[i].(*ir.Func); ok {
|
|
||||||
if base.Flag.W > 1 {
|
|
||||||
s := fmt.Sprintf("\nbefore typecheck %v", fn)
|
|
||||||
ir.Dump(s, fn)
|
|
||||||
}
|
|
||||||
ir.WithFunc(fn, func() {
|
|
||||||
typecheck.Stmts(fn.Body)
|
|
||||||
})
|
|
||||||
if base.Flag.W > 1 {
|
|
||||||
s := fmt.Sprintf("\nafter typecheck %v", fn)
|
|
||||||
ir.Dump(s, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !quirksMode() {
|
if !quirksMode() {
|
||||||
// TODO(mdempsky): Investigate generating wrappers in quirks mode too.
|
// TODO(mdempsky): Investigate generating wrappers in quirks mode too.
|
||||||
r.wrapTypes(target)
|
r.wrapTypes(target)
|
||||||
|
|
|
@ -54,7 +54,7 @@ func TestUnifiedCompare(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgs1 := loadPackages(t, goos, goarch, "-d=unified=0 -d=inlfuncswithclosures=0")
|
pkgs1 := loadPackages(t, goos, goarch, "-d=unified=0 -d=inlfuncswithclosures=0 -d=unifiedquirks=1")
|
||||||
pkgs2 := loadPackages(t, goos, goarch, "-d=unified=1 -d=inlfuncswithclosures=0 -d=unifiedquirks=1")
|
pkgs2 := loadPackages(t, goos, goarch, "-d=unified=1 -d=inlfuncswithclosures=0 -d=unifiedquirks=1")
|
||||||
|
|
||||||
if len(pkgs1) != len(pkgs2) {
|
if len(pkgs1) != len(pkgs2) {
|
||||||
|
|
|
@ -450,7 +450,16 @@ func autotmpname(n int) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
// Give each tmp a different name so that they can be registerized.
|
// Give each tmp a different name so that they can be registerized.
|
||||||
// Add a preceding . to avoid clashing with legal names.
|
// Add a preceding . to avoid clashing with legal names.
|
||||||
s = fmt.Sprintf(".autotmp_%d", n)
|
prefix := ".autotmp_%d"
|
||||||
|
|
||||||
|
// In quirks mode, pad out the number to stabilize variable
|
||||||
|
// sorting. This ensures autotmps 8 and 9 sort the same way even
|
||||||
|
// if they get renumbered to 9 and 10, respectively.
|
||||||
|
if base.Debug.UnifiedQuirks != 0 {
|
||||||
|
prefix = ".autotmp_%06d"
|
||||||
|
}
|
||||||
|
|
||||||
|
s = fmt.Sprintf(prefix, n)
|
||||||
autotmpnames[n] = s
|
autotmpnames[n] = s
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
|
|
Loading…
Reference in a new issue