mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
cmd/compile: remove unified IR quirks mode
Unified IR quirks mode existed to help bootstrap unified IR by forcing it to produce bit-for-bit identical output to the original gc noder and typechecker. However, I believe it's far enough along now to stand on its own, plus we have good test coverage of generics already for -G=3 mode. Change-Id: I8bf412c8bb5d720eadeac3fe31f49dc73679da70 Reviewed-on: https://go-review.googlesource.com/c/go/+/385998 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
936c7fbc1c
commit
4f04e1d99f
|
@ -39,7 +39,6 @@ type DebugFlags struct {
|
|||
TypeAssert int `help:"print information about type assertion inlining"`
|
||||
TypecheckInl int `help:"eager typechecking of inline function bodies"`
|
||||
Unified int `help:"enable unified IR construction"`
|
||||
UnifiedQuirks int `help:"enable unified IR construction's quirks mode"`
|
||||
WB int `help:"print information about write barriers"`
|
||||
ABIWrap int `help:"print information about ABI wrapper generation"`
|
||||
MayMoreStack string `help:"call named function before all stack growth checks"`
|
||||
|
|
|
@ -32,7 +32,6 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// handlePanic ensures that we print out an "internal compiler error" for any panic
|
||||
|
@ -205,17 +204,6 @@ func Main(archInit func(*ssagen.ArchInfo)) {
|
|||
// removal can skew the results (e.g., #43444).
|
||||
pkginit.MakeInit()
|
||||
|
||||
// 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.
|
||||
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.
|
||||
// Must happen after typechecking.
|
||||
for _, n := range typecheck.Target.Decls {
|
||||
|
|
|
@ -9,254 +9,13 @@ package noder
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/types2"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
// 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.
|
||||
|
||||
// quirksMode controls whether behavior specific to satisfying
|
||||
// toolstash -cmp is used.
|
||||
func quirksMode() bool {
|
||||
return base.Debug.UnifiedQuirks != 0
|
||||
}
|
||||
|
||||
// posBasesOf returns all of the position bases in the source files,
|
||||
// as seen in a straightforward traversal.
|
||||
//
|
||||
// This is necessary to ensure position bases (and thus file names)
|
||||
// get registered in the same order as noder would visit them.
|
||||
func posBasesOf(noders []*noder) []*syntax.PosBase {
|
||||
seen := make(map[*syntax.PosBase]bool)
|
||||
var bases []*syntax.PosBase
|
||||
|
||||
for _, p := range noders {
|
||||
syntax.Crawl(p.file, func(n syntax.Node) bool {
|
||||
if b := n.Pos().Base(); !seen[b] {
|
||||
bases = append(bases, b)
|
||||
seen[b] = true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
return bases
|
||||
}
|
||||
|
||||
// importedObjsOf returns the imported objects (i.e., referenced
|
||||
// objects not declared by curpkg) from the parsed source files, in
|
||||
// the order that typecheck used to load their definitions.
|
||||
//
|
||||
// This is needed because loading the definitions for imported objects
|
||||
// can also add file names.
|
||||
func importedObjsOf(curpkg *types2.Package, info *types2.Info, noders []*noder) []types2.Object {
|
||||
// This code is complex because it matches the precise order that
|
||||
// typecheck recursively and repeatedly traverses the IR. It's meant
|
||||
// to be thrown away eventually anyway.
|
||||
|
||||
seen := make(map[types2.Object]bool)
|
||||
var objs []types2.Object
|
||||
|
||||
var phase int
|
||||
|
||||
decls := make(map[types2.Object]syntax.Decl)
|
||||
assoc := func(decl syntax.Decl, names ...*syntax.Name) {
|
||||
for _, name := range names {
|
||||
obj, ok := info.Defs[name]
|
||||
assert(ok)
|
||||
decls[obj] = decl
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range noders {
|
||||
syntax.Crawl(p.file, func(n syntax.Node) bool {
|
||||
switch n := n.(type) {
|
||||
case *syntax.ConstDecl:
|
||||
assoc(n, n.NameList...)
|
||||
case *syntax.FuncDecl:
|
||||
assoc(n, n.Name)
|
||||
case *syntax.TypeDecl:
|
||||
assoc(n, n.Name)
|
||||
case *syntax.VarDecl:
|
||||
assoc(n, n.NameList...)
|
||||
case *syntax.BlockStmt:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
var visited map[syntax.Decl]bool
|
||||
|
||||
var resolveDecl func(n syntax.Decl)
|
||||
var resolveNode func(n syntax.Node, top bool)
|
||||
|
||||
resolveDecl = func(n syntax.Decl) {
|
||||
if visited[n] {
|
||||
return
|
||||
}
|
||||
visited[n] = true
|
||||
|
||||
switch n := n.(type) {
|
||||
case *syntax.ConstDecl:
|
||||
resolveNode(n.Type, true)
|
||||
resolveNode(n.Values, true)
|
||||
|
||||
case *syntax.FuncDecl:
|
||||
if n.Recv != nil {
|
||||
resolveNode(n.Recv, true)
|
||||
}
|
||||
resolveNode(n.Type, true)
|
||||
|
||||
case *syntax.TypeDecl:
|
||||
resolveNode(n.Type, true)
|
||||
|
||||
case *syntax.VarDecl:
|
||||
if n.Type != nil {
|
||||
resolveNode(n.Type, true)
|
||||
} else {
|
||||
resolveNode(n.Values, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolveObj := func(pos syntax.Pos, obj types2.Object) {
|
||||
switch obj.Pkg() {
|
||||
case nil:
|
||||
// builtin; nothing to do
|
||||
|
||||
case curpkg:
|
||||
if decl, ok := decls[obj]; ok {
|
||||
resolveDecl(decl)
|
||||
}
|
||||
|
||||
default:
|
||||
if obj.Parent() == obj.Pkg().Scope() && !seen[obj] {
|
||||
seen[obj] = true
|
||||
objs = append(objs, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkdefat := func(pos syntax.Pos, n *syntax.Name) {
|
||||
if n.Value == "_" {
|
||||
return
|
||||
}
|
||||
obj, ok := info.Uses[n]
|
||||
if !ok {
|
||||
obj, ok = info.Defs[n]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
resolveObj(pos, obj)
|
||||
}
|
||||
checkdef := func(n *syntax.Name) { checkdefat(n.Pos(), n) }
|
||||
|
||||
var later []syntax.Node
|
||||
|
||||
resolveNode = func(n syntax.Node, top bool) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
syntax.Crawl(n, func(n syntax.Node) bool {
|
||||
switch n := n.(type) {
|
||||
case *syntax.Name:
|
||||
checkdef(n)
|
||||
|
||||
case *syntax.SelectorExpr:
|
||||
if name, ok := n.X.(*syntax.Name); ok {
|
||||
if _, isPkg := info.Uses[name].(*types2.PkgName); isPkg {
|
||||
checkdefat(n.X.Pos(), n.Sel)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *syntax.AssignStmt:
|
||||
resolveNode(n.Rhs, top)
|
||||
resolveNode(n.Lhs, top)
|
||||
return true
|
||||
|
||||
case *syntax.VarDecl:
|
||||
resolveNode(n.Values, top)
|
||||
|
||||
case *syntax.FuncLit:
|
||||
if top {
|
||||
resolveNode(n.Type, top)
|
||||
later = append(later, n.Body)
|
||||
return true
|
||||
}
|
||||
|
||||
case *syntax.BlockStmt:
|
||||
if phase >= 3 {
|
||||
for _, stmt := range n.List {
|
||||
resolveNode(stmt, false)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
for phase = 1; phase <= 5; phase++ {
|
||||
visited = map[syntax.Decl]bool{}
|
||||
|
||||
for _, p := range noders {
|
||||
for _, decl := range p.file.DeclList {
|
||||
switch decl := decl.(type) {
|
||||
case *syntax.ConstDecl:
|
||||
resolveDecl(decl)
|
||||
|
||||
case *syntax.FuncDecl:
|
||||
resolveDecl(decl)
|
||||
if phase >= 3 && decl.Body != nil {
|
||||
resolveNode(decl.Body, true)
|
||||
}
|
||||
|
||||
case *syntax.TypeDecl:
|
||||
if !decl.Alias || phase >= 2 {
|
||||
resolveDecl(decl)
|
||||
}
|
||||
|
||||
case *syntax.VarDecl:
|
||||
if phase >= 2 {
|
||||
resolveNode(decl.Values, true)
|
||||
resolveDecl(decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if phase >= 5 {
|
||||
syntax.Crawl(p.file, func(n syntax.Node) bool {
|
||||
if name, ok := n.(*syntax.Name); ok {
|
||||
if obj, ok := info.Uses[name]; ok {
|
||||
resolveObj(name.Pos(), obj)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(later); i++ {
|
||||
resolveNode(later[i], true)
|
||||
}
|
||||
later = nil
|
||||
}
|
||||
|
||||
return objs
|
||||
}
|
||||
|
||||
// typeExprEndPos returns the position that noder would leave base.Pos
|
||||
// after parsing the given type expression.
|
||||
func typeExprEndPos(expr0 syntax.Expr) syntax.Pos {
|
||||
|
@ -320,131 +79,3 @@ func lastFieldType(fields []*syntax.Field) syntax.Expr {
|
|||
}
|
||||
return fields[len(fields)-1].Type
|
||||
}
|
||||
|
||||
// sumPos returns the position that noder.sum would produce for
|
||||
// constant expression x.
|
||||
func sumPos(x syntax.Expr) syntax.Pos {
|
||||
orig := x
|
||||
for {
|
||||
switch x1 := x.(type) {
|
||||
case *syntax.BasicLit:
|
||||
assert(x1.Kind == syntax.StringLit)
|
||||
return x1.Pos()
|
||||
case *syntax.Operation:
|
||||
assert(x1.Op == syntax.Add && x1.Y != nil)
|
||||
if r, ok := x1.Y.(*syntax.BasicLit); ok {
|
||||
assert(r.Kind == syntax.StringLit)
|
||||
x = x1.X
|
||||
continue
|
||||
}
|
||||
}
|
||||
return orig.Pos()
|
||||
}
|
||||
}
|
||||
|
||||
// funcParamsEndPos returns the value of base.Pos left by noder after
|
||||
// processing a function signature.
|
||||
func funcParamsEndPos(fn *ir.Func) src.XPos {
|
||||
sig := fn.Nname.Type()
|
||||
|
||||
fields := sig.Results().FieldSlice()
|
||||
if len(fields) == 0 {
|
||||
fields = sig.Params().FieldSlice()
|
||||
if len(fields) == 0 {
|
||||
fields = sig.Recvs().FieldSlice()
|
||||
if len(fields) == 0 {
|
||||
if fn.OClosure != nil {
|
||||
return fn.Nname.Ntype.Pos()
|
||||
}
|
||||
return fn.Pos()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields[len(fields)-1].Pos
|
||||
}
|
||||
|
||||
type dupTypes struct {
|
||||
origs map[types2.Type]types2.Type
|
||||
}
|
||||
|
||||
func (d *dupTypes) orig(t types2.Type) types2.Type {
|
||||
if orig, ok := d.origs[t]; ok {
|
||||
return orig
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (d *dupTypes) add(t, orig types2.Type) {
|
||||
if t == orig {
|
||||
return
|
||||
}
|
||||
|
||||
if d.origs == nil {
|
||||
d.origs = make(map[types2.Type]types2.Type)
|
||||
}
|
||||
assert(d.origs[t] == nil)
|
||||
d.origs[t] = orig
|
||||
|
||||
switch t := t.(type) {
|
||||
case *types2.Pointer:
|
||||
orig := orig.(*types2.Pointer)
|
||||
d.add(t.Elem(), orig.Elem())
|
||||
|
||||
case *types2.Slice:
|
||||
orig := orig.(*types2.Slice)
|
||||
d.add(t.Elem(), orig.Elem())
|
||||
|
||||
case *types2.Map:
|
||||
orig := orig.(*types2.Map)
|
||||
d.add(t.Key(), orig.Key())
|
||||
d.add(t.Elem(), orig.Elem())
|
||||
|
||||
case *types2.Array:
|
||||
orig := orig.(*types2.Array)
|
||||
assert(t.Len() == orig.Len())
|
||||
d.add(t.Elem(), orig.Elem())
|
||||
|
||||
case *types2.Chan:
|
||||
orig := orig.(*types2.Chan)
|
||||
assert(t.Dir() == orig.Dir())
|
||||
d.add(t.Elem(), orig.Elem())
|
||||
|
||||
case *types2.Struct:
|
||||
orig := orig.(*types2.Struct)
|
||||
assert(t.NumFields() == orig.NumFields())
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
d.add(t.Field(i).Type(), orig.Field(i).Type())
|
||||
}
|
||||
|
||||
case *types2.Interface:
|
||||
orig := orig.(*types2.Interface)
|
||||
assert(t.NumExplicitMethods() == orig.NumExplicitMethods())
|
||||
assert(t.NumEmbeddeds() == orig.NumEmbeddeds())
|
||||
for i := 0; i < t.NumExplicitMethods(); i++ {
|
||||
d.add(t.ExplicitMethod(i).Type(), orig.ExplicitMethod(i).Type())
|
||||
}
|
||||
for i := 0; i < t.NumEmbeddeds(); i++ {
|
||||
d.add(t.EmbeddedType(i), orig.EmbeddedType(i))
|
||||
}
|
||||
|
||||
case *types2.Signature:
|
||||
orig := orig.(*types2.Signature)
|
||||
assert((t.Recv() == nil) == (orig.Recv() == nil))
|
||||
if t.Recv() != nil {
|
||||
d.add(t.Recv().Type(), orig.Recv().Type())
|
||||
}
|
||||
d.add(t.Params(), orig.Params())
|
||||
d.add(t.Results(), orig.Results())
|
||||
|
||||
case *types2.Tuple:
|
||||
orig := orig.(*types2.Tuple)
|
||||
assert(t.Len() == orig.Len())
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
d.add(t.At(i).Type(), orig.At(i).Type())
|
||||
}
|
||||
|
||||
default:
|
||||
assert(types2.Identical(t, orig))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -968,11 +968,7 @@ func (r *reader) funcBody(fn *ir.Func) {
|
|||
|
||||
body := r.stmts()
|
||||
if body == nil {
|
||||
pos := src.NoXPos
|
||||
if quirksMode() {
|
||||
pos = funcParamsEndPos(fn)
|
||||
}
|
||||
body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(pos, nil))}
|
||||
body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(src.NoXPos, nil))}
|
||||
}
|
||||
fn.Body = body
|
||||
fn.Endlineno = r.pos()
|
||||
|
@ -1291,18 +1287,6 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
|
|||
|
||||
case stmtSwitch:
|
||||
return r.switchStmt(label)
|
||||
|
||||
case stmtTypeDeclHack:
|
||||
// fake "type _ = int" declaration to prevent inlining in quirks mode.
|
||||
assert(quirksMode())
|
||||
|
||||
name := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.BlankNode.Sym())
|
||||
name.SetAlias(true)
|
||||
setType(name, types.Types[types.TINT])
|
||||
|
||||
n := ir.NewDecl(src.NoXPos, ir.ODCLTYPE, name)
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1712,22 +1696,15 @@ func (r *reader) funcLit() ir.Node {
|
|||
r.sync(syncFuncLit)
|
||||
|
||||
pos := r.pos()
|
||||
typPos := r.pos()
|
||||
xtype2 := r.signature(types.LocalPkg, nil)
|
||||
|
||||
opos := pos
|
||||
if quirksMode() {
|
||||
opos = r.origPos(pos)
|
||||
}
|
||||
|
||||
fn := ir.NewClosureFunc(opos, r.curfn != nil)
|
||||
clo := fn.OClosure
|
||||
ir.NameClosure(clo, r.curfn)
|
||||
|
||||
setType(fn.Nname, xtype2)
|
||||
if quirksMode() {
|
||||
fn.Nname.Ntype = ir.TypeNodeAt(typPos, xtype2)
|
||||
}
|
||||
typecheck.Func(fn)
|
||||
setType(clo, fn.Type())
|
||||
|
||||
|
@ -1767,23 +1744,6 @@ func (r *reader) op() ir.Op {
|
|||
// @@@ Package initialization
|
||||
|
||||
func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
|
||||
if quirksMode() {
|
||||
for i, n := 0, r.len(); i < n; i++ {
|
||||
// Eagerly register position bases, so their filenames are
|
||||
// assigned stable indices.
|
||||
posBase := r.posBase()
|
||||
_ = base.Ctxt.PosTable.XPos(src.MakePos(posBase, 0, 0))
|
||||
}
|
||||
|
||||
for i, n := 0, r.len(); i < n; i++ {
|
||||
// Eagerly resolve imported objects, so any filenames registered
|
||||
// in the process are assigned stable indices too.
|
||||
_, sym := r.qualifiedIdent()
|
||||
typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
|
||||
assert(sym.Def != nil)
|
||||
}
|
||||
}
|
||||
|
||||
cgoPragmas := make([][]string, r.len())
|
||||
for i := range cgoPragmas {
|
||||
cgoPragmas[i] = r.strings()
|
||||
|
@ -2027,17 +1987,6 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
|
|||
|
||||
body := ir.Nodes(r.curfn.Body)
|
||||
|
||||
// Quirk: If deadcode elimination turned a non-empty function into
|
||||
// an empty one, we need to set the position for the empty block
|
||||
// left behind to the inlined position for src.NoXPos, so that
|
||||
// an empty string gets added into the DWARF file name listing at
|
||||
// the appropriate index.
|
||||
if quirksMode() && len(body) == 1 {
|
||||
if block, ok := body[0].(*ir.BlockStmt); ok && len(block.List) == 0 {
|
||||
block.SetPos(r.updatePos(src.NoXPos))
|
||||
}
|
||||
}
|
||||
|
||||
// Quirkish: We need to eagerly prune variables added during
|
||||
// inlining, but removed by deadcode.FuncBody above. Unused
|
||||
// variables will get removed during stack frame layout anyway, but
|
||||
|
@ -2218,8 +2167,8 @@ func (r *reader) importedDef() bool {
|
|||
}
|
||||
|
||||
func MakeWrappers(target *ir.Package) {
|
||||
// Only unified IR in non-quirks mode emits its own wrappers.
|
||||
if base.Debug.Unified == 0 || quirksMode() {
|
||||
// Only unified IR emits its own wrappers.
|
||||
if base.Debug.Unified == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -72,11 +72,7 @@ var localPkgReader *pkgReader
|
|||
func unified(noders []*noder) {
|
||||
inline.NewInline = InlineCall
|
||||
|
||||
if !quirksMode() {
|
||||
writeNewExportFunc = writeNewExport
|
||||
} else if base.Flag.G != 0 {
|
||||
base.Errorf("cannot use -G and -d=quirksmode together")
|
||||
}
|
||||
writeNewExportFunc = writeNewExport
|
||||
|
||||
newReadImportFunc = func(data string, pkg1 *types.Pkg, ctxt *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
|
||||
pr := newPkgDecoder(pkg1.Path, data)
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
// 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.
|
||||
|
||||
package noder_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
exec "internal/execabs"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
flagCmp = flag.Bool("cmp", false, "enable TestUnifiedCompare")
|
||||
flagPkgs = flag.String("pkgs", "std", "list of packages to compare (ignored in -short mode)")
|
||||
flagAll = flag.Bool("all", false, "enable testing of all GOOS/GOARCH targets")
|
||||
flagParallel = flag.Bool("parallel", false, "test GOOS/GOARCH targets in parallel")
|
||||
)
|
||||
|
||||
// TestUnifiedCompare implements a test similar to running:
|
||||
//
|
||||
// $ go build -toolexec="toolstash -cmp" std
|
||||
//
|
||||
// The -pkgs flag controls the list of packages tested.
|
||||
//
|
||||
// By default, only the native GOOS/GOARCH target is enabled. The -all
|
||||
// flag enables testing of non-native targets. The -parallel flag
|
||||
// additionally enables testing of targets in parallel.
|
||||
//
|
||||
// Caution: Testing all targets is very resource intensive! On an IBM
|
||||
// P920 (dual Intel Xeon Gold 6154 CPUs; 36 cores, 192GB RAM), testing
|
||||
// all targets in parallel takes about 5 minutes. Using the 'go test'
|
||||
// command's -run flag for subtest matching is recommended for less
|
||||
// powerful machines.
|
||||
func TestUnifiedCompare(t *testing.T) {
|
||||
// TODO(mdempsky): Either re-enable or delete. Disabled for now to
|
||||
// avoid impeding others' forward progress.
|
||||
if !*flagCmp {
|
||||
t.Skip("skipping TestUnifiedCompare (use -cmp to enable)")
|
||||
}
|
||||
|
||||
targets, err := exec.Command("go", "tool", "dist", "list").Output()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, target := range strings.Fields(string(targets)) {
|
||||
t.Run(target, func(t *testing.T) {
|
||||
parts := strings.Split(target, "/")
|
||||
goos, goarch := parts[0], parts[1]
|
||||
|
||||
if !(*flagAll || goos == runtime.GOOS && goarch == runtime.GOARCH) {
|
||||
t.Skip("skipping non-native target (use -all to enable)")
|
||||
}
|
||||
if *flagParallel {
|
||||
t.Parallel()
|
||||
}
|
||||
|
||||
pkgs1 := loadPackages(t, goos, goarch, "-d=unified=0 -d=inlfuncswithclosures=0 -d=unifiedquirks=1 -G=0")
|
||||
pkgs2 := loadPackages(t, goos, goarch, "-d=unified=1 -d=inlfuncswithclosures=0 -d=unifiedquirks=1 -G=0")
|
||||
|
||||
if len(pkgs1) != len(pkgs2) {
|
||||
t.Fatalf("length mismatch: %v != %v", len(pkgs1), len(pkgs2))
|
||||
}
|
||||
|
||||
for i := range pkgs1 {
|
||||
pkg1 := pkgs1[i]
|
||||
pkg2 := pkgs2[i]
|
||||
|
||||
path := pkg1.ImportPath
|
||||
if path != pkg2.ImportPath {
|
||||
t.Fatalf("mismatched paths: %q != %q", path, pkg2.ImportPath)
|
||||
}
|
||||
|
||||
// Packages that don't have any source files (e.g., packages
|
||||
// unsafe, embed/internal/embedtest, and cmd/internal/moddeps).
|
||||
if pkg1.Export == "" && pkg2.Export == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if pkg1.BuildID == pkg2.BuildID {
|
||||
t.Errorf("package %q: build IDs unexpectedly matched", path)
|
||||
}
|
||||
|
||||
// Unlike toolstash -cmp, we're comparing the same compiler
|
||||
// binary against itself, just with different flags. So we
|
||||
// don't need to worry about skipping over mismatched version
|
||||
// strings, but we do need to account for differing build IDs.
|
||||
//
|
||||
// Fortunately, build IDs are cryptographic 256-bit hashes,
|
||||
// and cmd/go provides us with them up front. So we can just
|
||||
// use them as delimeters to split the files, and then check
|
||||
// that the substrings are all equal.
|
||||
file1 := strings.Split(readFile(t, pkg1.Export), pkg1.BuildID)
|
||||
file2 := strings.Split(readFile(t, pkg2.Export), pkg2.BuildID)
|
||||
if !reflect.DeepEqual(file1, file2) {
|
||||
t.Errorf("package %q: compile output differs", path)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type pkg struct {
|
||||
ImportPath string
|
||||
Export string
|
||||
BuildID string
|
||||
Incomplete bool
|
||||
}
|
||||
|
||||
func loadPackages(t *testing.T, goos, goarch, gcflags string) []pkg {
|
||||
args := []string{"list", "-e", "-export", "-json", "-gcflags=all=" + gcflags, "--"}
|
||||
if testing.Short() {
|
||||
t.Log("short testing mode; only testing package runtime")
|
||||
args = append(args, "runtime")
|
||||
} else {
|
||||
args = append(args, strings.Fields(*flagPkgs)...)
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Env = append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch)
|
||||
cmd.Stderr = os.Stderr
|
||||
t.Logf("running %v", cmd)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var res []pkg
|
||||
for dec := json.NewDecoder(stdout); dec.More(); {
|
||||
var pkg pkg
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkg.Incomplete {
|
||||
t.Fatalf("incomplete package: %q", pkg.ImportPath)
|
||||
}
|
||||
res = append(res, pkg)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func readFile(t *testing.T, name string) string {
|
||||
buf, err := os.ReadFile(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return string(buf)
|
||||
}
|
|
@ -8,7 +8,6 @@ package noder
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
|
@ -33,8 +32,6 @@ type pkgWriter struct {
|
|||
|
||||
linknames map[types2.Object]string
|
||||
cgoPragmas [][]string
|
||||
|
||||
dups dupTypes
|
||||
}
|
||||
|
||||
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
|
||||
|
@ -251,10 +248,6 @@ func (w *writer) typInfo(info typeInfo) {
|
|||
// typIdx also reports whether typ is a derived type; that is, whether
|
||||
// its identity depends on type parameters.
|
||||
func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
|
||||
if quirksMode() {
|
||||
typ = pw.dups.orig(typ)
|
||||
}
|
||||
|
||||
if idx, ok := pw.typsIdx[typ]; ok {
|
||||
return typeInfo{idx: idx, derived: false}
|
||||
}
|
||||
|
@ -999,36 +992,9 @@ func (w *writer) declStmt(decl syntax.Decl) {
|
|||
default:
|
||||
w.p.unexpected("declaration", decl)
|
||||
|
||||
case *syntax.ConstDecl:
|
||||
|
||||
case *syntax.TypeDecl:
|
||||
// Quirk: The legacy inliner doesn't support inlining functions
|
||||
// with type declarations. Unified IR doesn't have any need to
|
||||
// write out type declarations explicitly (they're always looked
|
||||
// up via global index tables instead), so we just write out a
|
||||
// marker so the reader knows to synthesize a fake declaration to
|
||||
// prevent inlining.
|
||||
if quirksMode() {
|
||||
w.code(stmtTypeDeclHack)
|
||||
}
|
||||
case *syntax.ConstDecl, *syntax.TypeDecl:
|
||||
|
||||
case *syntax.VarDecl:
|
||||
values := unpackListExpr(decl.Values)
|
||||
|
||||
// Quirk: When N variables are declared with N initialization
|
||||
// values, we need to decompose that into N interleaved
|
||||
// declarations+initializations, because it leads to different
|
||||
// (albeit semantically equivalent) code generation.
|
||||
if quirksMode() && len(decl.NameList) == len(values) {
|
||||
for i, name := range decl.NameList {
|
||||
w.code(stmtAssign)
|
||||
w.pos(decl)
|
||||
w.exprList(values[i])
|
||||
w.assignList(name)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
w.code(stmtAssign)
|
||||
w.pos(decl)
|
||||
w.exprList(decl.Values)
|
||||
|
@ -1184,21 +1150,8 @@ func (w *writer) expr(expr syntax.Expr) {
|
|||
}
|
||||
|
||||
if tv.Value != nil {
|
||||
pos := expr.Pos()
|
||||
if quirksMode() {
|
||||
if obj != nil {
|
||||
// Quirk: IR (and thus iexport) doesn't track position
|
||||
// information for uses of declared objects.
|
||||
pos = syntax.Pos{}
|
||||
} else if tv.Value.Kind() == constant.String {
|
||||
// Quirk: noder.sum picks a particular position for certain
|
||||
// string concatenations.
|
||||
pos = sumPos(expr)
|
||||
}
|
||||
}
|
||||
|
||||
w.code(exprConst)
|
||||
w.pos(pos)
|
||||
w.pos(expr.Pos())
|
||||
w.typ(tv.Type)
|
||||
w.value(tv.Value)
|
||||
|
||||
|
@ -1377,15 +1330,11 @@ func (w *writer) funcLit(expr *syntax.FuncLit) {
|
|||
|
||||
w.sync(syncFuncLit)
|
||||
w.pos(expr)
|
||||
w.pos(expr.Type) // for QuirksMode
|
||||
w.signature(sig)
|
||||
|
||||
w.len(len(closureVars))
|
||||
for _, cv := range closureVars {
|
||||
w.pos(cv.pos)
|
||||
if quirksMode() {
|
||||
cv.pos = expr.Body.Rbrace
|
||||
}
|
||||
w.useLocal(cv.pos, cv.obj)
|
||||
}
|
||||
|
||||
|
@ -1538,21 +1487,6 @@ func (c *declCollector) Visit(n syntax.Node) syntax.Visitor {
|
|||
}
|
||||
}
|
||||
|
||||
// Workaround for #46208. For variable declarations that
|
||||
// declare multiple variables and have an explicit type
|
||||
// expression, the type expression is evaluated multiple
|
||||
// times. This affects toolstash -cmp, because iexport is
|
||||
// sensitive to *types.Type pointer identity.
|
||||
if quirksMode() && n.Type != nil {
|
||||
tv, ok := pw.info.Types[n.Type]
|
||||
assert(ok)
|
||||
assert(tv.IsType())
|
||||
for _, name := range n.NameList {
|
||||
obj := pw.info.Defs[name].(*types2.Var)
|
||||
pw.dups.add(obj.Type(), tv.Type)
|
||||
}
|
||||
}
|
||||
|
||||
case *syntax.BlockStmt:
|
||||
if !c.withinFunc {
|
||||
copy := *c
|
||||
|
@ -1621,20 +1555,6 @@ func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedO
|
|||
}
|
||||
|
||||
func (w *writer) pkgInit(noders []*noder) {
|
||||
if quirksMode() {
|
||||
posBases := posBasesOf(noders)
|
||||
w.len(len(posBases))
|
||||
for _, posBase := range posBases {
|
||||
w.posBase(posBase)
|
||||
}
|
||||
|
||||
objs := importedObjsOf(w.p.curpkg, w.p.info, noders)
|
||||
w.len(len(objs))
|
||||
for _, obj := range objs {
|
||||
w.qualifiedIdent(obj)
|
||||
}
|
||||
}
|
||||
|
||||
w.len(len(w.p.cgoPragmas))
|
||||
for _, cgoPragma := range w.p.cgoPragmas {
|
||||
w.strings(cgoPragma)
|
||||
|
|
|
@ -1826,8 +1826,8 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
|||
}
|
||||
newnam.SetSiggen(true)
|
||||
|
||||
// Except in quirks mode, unified IR creates its own wrappers.
|
||||
if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
|
||||
// Unified IR creates its own wrappers.
|
||||
if base.Debug.Unified != 0 {
|
||||
return lsym
|
||||
}
|
||||
|
||||
|
|
|
@ -455,13 +455,6 @@ func autotmpname(n int) string {
|
|||
// Add a preceding . to avoid clashing with legal names.
|
||||
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
|
||||
}
|
||||
|
|
|
@ -621,12 +621,7 @@ func (p *iexporter) doDecl(n *ir.Name) {
|
|||
break
|
||||
}
|
||||
|
||||
// Sort methods, for consistency with types2.
|
||||
methods := append([]*types.Field(nil), t.Methods().Slice()...)
|
||||
if base.Debug.UnifiedQuirks != 0 {
|
||||
sort.Sort(types.MethodsByName(methods))
|
||||
}
|
||||
|
||||
methods := t.Methods().Slice()
|
||||
w.uint64(uint64(len(methods)))
|
||||
for _, m := range methods {
|
||||
w.pos(m.Pos)
|
||||
|
@ -1052,14 +1047,6 @@ func (w *exportWriter) doTyp(t *types.Type) {
|
|||
}
|
||||
}
|
||||
|
||||
// Sort methods and embedded types, for consistency with types2.
|
||||
// Note: embedded types may be anonymous, and types2 sorts them
|
||||
// with sort.Stable too.
|
||||
if base.Debug.UnifiedQuirks != 0 {
|
||||
sort.Sort(types.MethodsByName(methods))
|
||||
sort.Stable(types.EmbeddedsByName(embeddeds))
|
||||
}
|
||||
|
||||
w.startType(interfaceType)
|
||||
w.setPkg(t.Pkg(), true)
|
||||
|
||||
|
|
|
@ -227,7 +227,7 @@ func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
|
|||
}
|
||||
sym.SetUniq(true)
|
||||
|
||||
if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
|
||||
if base.Debug.Unified != 0 {
|
||||
base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue