1
0
mirror of https://github.com/golang/go synced 2024-11-26 02:07:57 -07:00

cmd: remove GOEXPERIMENT=nounified knob

This CL removes the GOEXPERIMENT=nounified knob, and any conditional
statements that depend on that knob. Further CLs to remove unreachable
code follow this one.

Updates #57410.

Change-Id: I39c147e1a83601c73f8316a001705778fee64a91
Reviewed-on: https://go-review.googlesource.com/c/go/+/458615
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
Matthew Dempsky 2022-12-01 16:14:11 -08:00
parent 3d49b683c6
commit 4f467f1082
32 changed files with 71 additions and 533 deletions

View File

@ -47,7 +47,6 @@ type DebugFlags struct {
SyncFrames int `help:"how many writer stack frames to include at sync points in unified export data"` SyncFrames int `help:"how many writer stack frames to include at sync points in unified export data"`
TypeAssert int `help:"print information about type assertion inlining"` TypeAssert int `help:"print information about type assertion inlining"`
TypecheckInl int `help:"eager typechecking of inline function bodies" concurrent:"ok"` TypecheckInl int `help:"eager typechecking of inline function bodies" concurrent:"ok"`
Unified int `help:"enable unified IR construction"`
WB int `help:"print information about write barriers"` WB int `help:"print information about write barriers"`
ABIWrap int `help:"print information about ABI wrapper generation"` ABIWrap int `help:"print information about ABI wrapper generation"`
MayMoreStack string `help:"call named function before all stack growth checks" concurrent:"ok"` MayMoreStack string `help:"call named function before all stack growth checks" concurrent:"ok"`

View File

@ -167,9 +167,6 @@ func ParseFlags() {
Debug.ConcurrentOk = true Debug.ConcurrentOk = true
Debug.InlFuncsWithClosures = 1 Debug.InlFuncsWithClosures = 1
Debug.InlStaticInit = 1 Debug.InlStaticInit = 1
if buildcfg.Experiment.Unified {
Debug.Unified = 1
}
Debug.SyncFrames = -1 // disable sync markers by default Debug.SyncFrames = -1 // disable sync markers by default
Debug.Checkptr = -1 // so we can tell whether it is set explicitly Debug.Checkptr = -1 // so we can tell whether it is set explicitly

View File

@ -57,58 +57,52 @@ func Call(call *ir.CallExpr) {
return return
} }
if base.Debug.Unified != 0 { // If typ is a shape type, then it was a type argument originally
// N.B., stencil.go converts shape-typed values to interface type // and we'd need an indirect call through the dictionary anyway.
// using OEFACE instead of OCONVIFACE, so devirtualization fails // We're unable to devirtualize this call.
// above instead. That's why this code is specific to unified IR. if typ.IsShape() {
return
}
// If typ is a shape type, then it was a type argument originally // If typ *has* a shape type, then it's an shaped, instantiated
// and we'd need an indirect call through the dictionary anyway. // type like T[go.shape.int], and its methods (may) have an extra
// We're unable to devirtualize this call. // dictionary parameter. We could devirtualize this call if we
if typ.IsShape() { // could derive an appropriate dictionary argument.
return //
// TODO(mdempsky): If typ has has a promoted non-generic method,
// then that method won't require a dictionary argument. We could
// still devirtualize those calls.
//
// TODO(mdempsky): We have the *runtime.itab in recv.TypeWord. It
// should be possible to compute the represented type's runtime
// dictionary from this (e.g., by adding a pointer from T[int]'s
// *runtime._type to .dict.T[int]; or by recognizing static
// references to go:itab.T[int],iface and constructing a direct
// reference to .dict.T[int]).
if typ.HasShape() {
if base.Flag.LowerM != 0 {
base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped receiver %v", call, typ)
} }
return
}
// If typ *has* a shape type, then it's an shaped, instantiated // Further, if sel.X's type has a shape type, then it's a shaped
// type like T[go.shape.int], and its methods (may) have an extra // interface type. In this case, the (non-dynamic) TypeAssertExpr
// dictionary parameter. We could devirtualize this call if we // we construct below would attempt to create an itab
// could derive an appropriate dictionary argument. // corresponding to this shaped interface type; but the actual
// // itab pointer in the interface value will correspond to the
// TODO(mdempsky): If typ has has a promoted non-generic method, // original (non-shaped) interface type instead. These are
// then that method won't require a dictionary argument. We could // functionally equivalent, but they have distinct pointer
// still devirtualize those calls. // identities, which leads to the type assertion failing.
// //
// TODO(mdempsky): We have the *runtime.itab in recv.TypeWord. It // TODO(mdempsky): We know the type assertion here is safe, so we
// should be possible to compute the represented type's runtime // could instead set a flag so that walk skips the itab check. For
// dictionary from this (e.g., by adding a pointer from T[int]'s // now, punting is easy and safe.
// *runtime._type to .dict.T[int]; or by recognizing static if sel.X.Type().HasShape() {
// references to go:itab.T[int],iface and constructing a direct if base.Flag.LowerM != 0 {
// reference to .dict.T[int]). base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped interface %v", call, sel.X.Type())
if typ.HasShape() {
if base.Flag.LowerM != 0 {
base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped receiver %v", call, typ)
}
return
}
// Further, if sel.X's type has a shape type, then it's a shaped
// interface type. In this case, the (non-dynamic) TypeAssertExpr
// we construct below would attempt to create an itab
// corresponding to this shaped interface type; but the actual
// itab pointer in the interface value will correspond to the
// original (non-shaped) interface type instead. These are
// functionally equivalent, but they have distinct pointer
// identities, which leads to the type assertion failing.
//
// TODO(mdempsky): We know the type assertion here is safe, so we
// could instead set a flag so that walk skips the itab check. For
// now, punting is easy and safe.
if sel.X.Type().HasShape() {
if base.Flag.LowerM != 0 {
base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped interface %v", call, sel.X.Type())
}
return
} }
return
} }
dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil) dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil)

View File

@ -9,7 +9,6 @@ import (
"cmd/compile/internal/types2" "cmd/compile/internal/types2"
"fmt" "fmt"
"go/build" "go/build"
"internal/goexperiment"
"internal/testenv" "internal/testenv"
"os" "os"
"os/exec" "os/exec"
@ -98,7 +97,7 @@ func TestImportTestdata(t *testing.T) {
"exports.go": {"go/ast", "go/token"}, "exports.go": {"go/ast", "go/token"},
"generics.go": nil, "generics.go": nil,
} }
if goexperiment.Unified { if true /* was goexperiment.Unified */ {
// TODO(mdempsky): Fix test below to flatten the transitive // TODO(mdempsky): Fix test below to flatten the transitive
// Package.Imports graph. Unified IR is more precise about // Package.Imports graph. Unified IR is more precise about
// recreating the package import graph. // recreating the package import graph.
@ -343,8 +342,12 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) {
// The unified IR importer always sets interface method receiver // The unified IR importer always sets interface method receiver
// parameters to point to the Interface type, rather than the Named. // parameters to point to the Interface type, rather than the Named.
// See #49906. // See #49906.
//
// TODO(mdempsky): This is only true for the types2 importer. For
// the go/types importer, we duplicate the Interface and rewrite its
// receiver methods to match historical behavior.
var want types2.Type = named var want types2.Type = named
if goexperiment.Unified { if true /* was goexperiment.Unified */ {
want = iface want = iface
} }

View File

@ -645,15 +645,13 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
// minimize impact to the existing inlining heuristics (in // minimize impact to the existing inlining heuristics (in
// particular, to avoid breaking the existing inlinability regress // particular, to avoid breaking the existing inlinability regress
// tests), we need to compensate for this here. // tests), we need to compensate for this here.
if base.Debug.Unified != 0 { if init := n.Rhs[0].Init(); len(init) == 1 {
if init := n.Rhs[0].Init(); len(init) == 1 { if _, ok := init[0].(*ir.AssignListStmt); ok {
if _, ok := init[0].(*ir.AssignListStmt); ok { // 4 for each value, because each temporary variable now
// 4 for each value, because each temporary variable now // appears 3 times (DCL, LHS, RHS), plus an extra DCL node.
// appears 3 times (DCL, LHS, RHS), plus an extra DCL node. //
// // 1 for the extra "tmp1, tmp2 = f()" assignment statement.
// 1 for the extra "tmp1, tmp2 = f()" assignment statement. v.budget += 4*int32(len(n.Lhs)) + 1
v.budget += 4*int32(len(n.Lhs)) + 1
}
} }
} }
@ -958,49 +956,6 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlCalls *[]*ir.Inlin
return n return n
} }
// The non-unified frontend has issues with inlining and shape parameters.
if base.Debug.Unified == 0 {
// Don't inline a function fn that has no shape parameters, but is passed at
// least one shape arg. This means we must be inlining a non-generic function
// fn that was passed into a generic function, and can be called with a shape
// arg because it matches an appropriate type parameters. But fn may include
// an interface conversion (that may be applied to a shape arg) that was not
// apparent when we first created the instantiation of the generic function.
// We can't handle this if we actually do the inlining, since we want to know
// all interface conversions immediately after stenciling. So, we avoid
// inlining in this case, see issue #49309. (1)
//
// See discussion on go.dev/cl/406475 for more background.
if !fn.Type().Params().HasShape() {
for _, arg := range n.Args {
if arg.Type().HasShape() {
if logopt.Enabled() {
logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
fmt.Sprintf("inlining function %v has no-shape params with shape args", ir.FuncName(fn)))
}
return n
}
}
} else {
// Don't inline a function fn that has shape parameters, but is passed no shape arg.
// See comments (1) above, and issue #51909.
inlineable := len(n.Args) == 0 // Function has shape in type, with no arguments can always be inlined.
for _, arg := range n.Args {
if arg.Type().HasShape() {
inlineable = true
break
}
}
if !inlineable {
if logopt.Enabled() {
logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
fmt.Sprintf("inlining function %v has shape params with no-shape args", ir.FuncName(fn)))
}
return n
}
}
}
if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) { if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) {
// Runtime package must not be instrumented. // Runtime package must not be instrumented.
// Instrument skips runtime package. However, some runtime code can be // Instrument skips runtime package. However, some runtime code can be

View File

@ -10,19 +10,14 @@ import (
"io" "io"
"cmd/compile/internal/base" "cmd/compile/internal/base"
"cmd/compile/internal/typecheck"
"cmd/internal/bio" "cmd/internal/bio"
) )
func WriteExports(out *bio.Writer) { func WriteExports(out *bio.Writer) {
var data bytes.Buffer var data bytes.Buffer
if base.Debug.Unified != 0 { data.WriteByte('u')
data.WriteByte('u') writeUnifiedExport(&data)
writeUnifiedExport(&data)
} else {
typecheck.WriteExports(&data, true)
}
// The linker also looks for the $$ marker - use char after $$ to distinguish format. // The linker also looks for the $$ marker - use char after $$ to distinguish format.
out.WriteString("\n$$B\n") // indicate binary export format out.WriteString("\n$$B\n") // indicate binary export format

View File

@ -231,10 +231,6 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag
switch c { switch c {
case 'u': case 'u':
if !buildcfg.Experiment.Unified {
base.Fatalf("unexpected export data format")
}
// TODO(mdempsky): This seems a bit clunky. // TODO(mdempsky): This seems a bit clunky.
data = strings.TrimSuffix(data, "\n$$\n") data = strings.TrimSuffix(data, "\n$$\n")
@ -244,20 +240,6 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag
readPackage(newPkgReader(pr), pkg1, false) readPackage(newPkgReader(pr), pkg1, false)
pkg2 = importer.ReadPackage(env, packages, pr) pkg2 = importer.ReadPackage(env, packages, pr)
case 'i':
if buildcfg.Experiment.Unified {
base.Fatalf("unexpected export data format")
}
typecheck.ReadImports(pkg1, data)
if packages != nil {
pkg2, err = importer.ImportData(packages, data, path)
if err != nil {
return
}
}
default: default:
// Indexed format is distinguished by an 'i' byte, // Indexed format is distinguished by an 'i' byte,
// whereas previous export formats started with 'c', 'd', or 'v'. // whereas previous export formats started with 'c', 'd', or 'v'.

View File

@ -73,13 +73,7 @@ func LoadPackage(filenames []string) {
} }
base.Timer.AddEvent(int64(lines), "lines") base.Timer.AddEvent(int64(lines), "lines")
if base.Debug.Unified != 0 { unified(noders)
unified(noders)
return
}
// Use types2 to type-check and generate IR.
check2(noders)
} }
func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) { func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) {

View File

@ -3688,11 +3688,6 @@ func (r *reader) importedDef() bool {
} }
func MakeWrappers(target *ir.Package) { func MakeWrappers(target *ir.Package) {
// Only unified IR emits its own wrappers.
if base.Debug.Unified == 0 {
return
}
// always generate a wrapper for error.Error (#29304) // always generate a wrapper for error.Error (#29304)
needWrapperTypes = append(needWrapperTypes, types.ErrorType) needWrapperTypes = append(needWrapperTypes, types.ErrorType)

View File

@ -21,7 +21,7 @@ func hasRType(n, rtype ir.Node, fieldName string) bool {
// gets confused by implicit conversions. Also, because // gets confused by implicit conversions. Also, because
// package-scope statements can never be generic, so they'll never // package-scope statements can never be generic, so they'll never
// require dictionary lookups. // require dictionary lookups.
if base.Debug.Unified != 0 && ir.CurFunc.Nname.Sym().Name != "init" { if ir.CurFunc.Nname.Sym().Name != "init" {
ir.Dump("CurFunc", ir.CurFunc) ir.Dump("CurFunc", ir.CurFunc)
base.FatalfAt(n.Pos(), "missing %s in %v: %+v", fieldName, ir.CurFunc, n) base.FatalfAt(n.Pos(), "missing %s in %v: %+v", fieldName, ir.CurFunc, n)
} }

View File

@ -16,8 +16,6 @@ import (
"cmd/compile/internal/base" "cmd/compile/internal/base"
"cmd/compile/internal/bitvec" "cmd/compile/internal/bitvec"
"cmd/compile/internal/compare" "cmd/compile/internal/compare"
"cmd/compile/internal/escape"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir" "cmd/compile/internal/ir"
"cmd/compile/internal/objw" "cmd/compile/internal/objw"
"cmd/compile/internal/typebits" "cmd/compile/internal/typebits"
@ -1868,199 +1866,14 @@ func NeedEmit(typ *types.Type) bool {
// //
// These wrappers are always fully stenciled. // These wrappers are always fully stenciled.
func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSym { func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSym {
orig := rcvr
if forItab && !types.IsDirectIface(rcvr) { if forItab && !types.IsDirectIface(rcvr) {
rcvr = rcvr.PtrTo() rcvr = rcvr.PtrTo()
} }
generic := false
// We don't need a dictionary if we are reaching a method (possibly via an
// embedded field) which is an interface method.
if !types.IsInterfaceMethod(method.Type) {
rcvr1 := deref(rcvr)
if len(rcvr1.RParams()) > 0 {
// If rcvr has rparams, remember method as generic, which
// means we need to add a dictionary to the wrapper.
generic = true
if rcvr.HasShape() {
base.Fatalf("method on type instantiated with shapes, rcvr:%+v", rcvr)
}
}
}
newnam := ir.MethodSym(rcvr, method.Sym) newnam := ir.MethodSym(rcvr, method.Sym)
lsym := newnam.Linksym() lsym := newnam.Linksym()
// Unified IR creates its own wrappers. // Unified IR creates its own wrappers.
if base.Debug.Unified != 0 {
return lsym
}
if newnam.Siggen() {
return lsym
}
newnam.SetSiggen(true)
methodrcvr := method.Type.Recv().Type
// For generic methods, we need to generate the wrapper even if the receiver
// types are identical, because we want to add the dictionary.
if !generic && types.Identical(rcvr, methodrcvr) {
return lsym
}
if !NeedEmit(rcvr) || rcvr.IsPtr() && !NeedEmit(rcvr.Elem()) {
return lsym
}
base.Pos = base.AutogeneratedPos
typecheck.DeclContext = ir.PEXTERN
// TODO(austin): SelectorExpr may have created one or more
// ir.Names for these already with a nil Func field. We should
// consolidate these and always attach a Func to the Name.
fn := typecheck.DeclFunc(newnam, ir.NewField(base.Pos, typecheck.Lookup(".this"), rcvr),
typecheck.NewFuncParams(method.Type.Params(), true),
typecheck.NewFuncParams(method.Type.Results(), false))
fn.SetDupok(true)
nthis := ir.AsNode(fn.Type().Recv().Nname)
indirect := rcvr.IsPtr() && rcvr.Elem() == methodrcvr
// generate nil pointer check for better error
if indirect {
// generating wrapper from *T to T.
n := ir.NewIfStmt(base.Pos, nil, nil, nil)
n.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, nthis, typecheck.NodNil())
call := ir.NewCallExpr(base.Pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)
n.Body = []ir.Node{call}
fn.Body.Append(n)
}
dot := typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, nthis, method.Sym))
// generate call
// It's not possible to use a tail call when dynamic linking on ppc64le. The
// bad scenario is when a local call is made to the wrapper: the wrapper will
// call the implementation, which might be in a different module and so set
// the TOC to the appropriate value for that module. But if it returns
// directly to the wrapper's caller, nothing will reset it to the correct
// value for that function.
var call *ir.CallExpr
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic {
call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
call.Args = ir.ParamNames(fn.Type())
call.IsDDD = fn.Type().IsVariadic()
fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
} else {
fn.SetWrapper(true) // ignore frame for panic+recover matching
if generic && dot.X != nthis {
// If there is embedding involved, then we should do the
// normal non-generic embedding wrapper below, which calls
// the wrapper for the real receiver type using dot as an
// argument. There is no need for generic processing (adding
// a dictionary) for this wrapper.
generic = false
}
if generic {
targs := deref(rcvr).RParams()
// The wrapper for an auto-generated pointer/non-pointer
// receiver method should share the same dictionary as the
// corresponding original (user-written) method.
baseOrig := orig
if baseOrig.IsPtr() && !methodrcvr.IsPtr() {
baseOrig = baseOrig.Elem()
} else if !baseOrig.IsPtr() && methodrcvr.IsPtr() {
baseOrig = types.NewPtr(baseOrig)
}
args := []ir.Node{getDictionary(ir.MethodSym(baseOrig, method.Sym), targs)}
if indirect {
args = append(args, ir.NewStarExpr(base.Pos, dot.X))
} else if methodrcvr.IsPtr() && methodrcvr.Elem() == dot.X.Type() {
// Case where method call is via a non-pointer
// embedded field with a pointer method.
args = append(args, typecheck.NodAddrAt(base.Pos, dot.X))
} else {
args = append(args, dot.X)
}
args = append(args, ir.ParamNames(fn.Type())...)
// Target method uses shaped names.
targs2 := make([]*types.Type, len(targs))
origRParams := deref(orig).OrigType().RParams()
for i, t := range targs {
targs2[i] = typecheck.Shapify(t, i, origRParams[i])
}
targs = targs2
sym := typecheck.MakeFuncInstSym(ir.MethodSym(methodrcvr, method.Sym), targs, false, true)
if sym.Def == nil {
// Currently we make sure that we have all the
// instantiations we need by generating them all in
// ../noder/stencil.go:instantiateMethods
// Extra instantiations because of an inlined function
// should have been exported, and so available via
// Resolve.
in := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
if in.Op() == ir.ONONAME {
base.Fatalf("instantiation %s not found", sym.Name)
}
sym = in.Sym()
}
target := ir.AsNode(sym.Def)
call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args)
// Fill-in the generic method node that was not filled in
// in instantiateMethod.
method.Nname = fn.Nname
} else {
call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
call.Args = ir.ParamNames(fn.Type())
}
call.IsDDD = fn.Type().IsVariadic()
if method.Type.NumResults() > 0 {
ret := ir.NewReturnStmt(base.Pos, nil)
ret.Results = []ir.Node{call}
fn.Body.Append(ret)
} else {
fn.Body.Append(call)
}
}
typecheck.FinishFuncBody()
if base.Debug.DclStack != 0 {
types.CheckDclstack()
}
typecheck.Func(fn)
ir.CurFunc = fn
typecheck.Stmts(fn.Body)
if AfterGlobalEscapeAnalysis {
// Inlining the method may reveal closures, which require walking all function bodies
// to decide whether to capture free variables by value or by ref. So we only do inline
// if the method do not contain any closures, otherwise, the escape analysis may make
// dead variables resurrected, and causing liveness analysis confused, see issue #53702.
var canInline bool
switch x := call.X.(type) {
case *ir.Name:
canInline = len(x.Func.Closures) == 0
case *ir.SelectorExpr:
if x.Op() == ir.OMETHEXPR {
canInline = x.FuncName().Func != nil && len(x.FuncName().Func.Closures) == 0
}
}
if canInline {
// TODO(prattmic): plumb PGO.
inline.InlineCalls(fn, nil)
}
escape.Batch([]*ir.Func{fn}, false)
}
ir.CurFunc = nil
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
return lsym return lsym
} }

View File

@ -9,7 +9,6 @@ import (
"bytes" "bytes"
"flag" "flag"
"fmt" "fmt"
"internal/buildcfg"
"internal/testenv" "internal/testenv"
"os" "os"
"path/filepath" "path/filepath"
@ -84,7 +83,7 @@ func TestDebugLinesPushback(t *testing.T) {
case "arm64", "amd64": // register ABI case "arm64", "amd64": // register ABI
fn := "(*List[go.shape.int_0]).PushBack" fn := "(*List[go.shape.int_0]).PushBack"
if buildcfg.Experiment.Unified { if true /* was buildcfg.Experiment.Unified */ {
// Unified mangles differently // Unified mangles differently
fn = "(*List[go.shape.int]).PushBack" fn = "(*List[go.shape.int]).PushBack"
} }
@ -101,7 +100,7 @@ func TestDebugLinesConvert(t *testing.T) {
case "arm64", "amd64": // register ABI case "arm64", "amd64": // register ABI
fn := "G[go.shape.int_0]" fn := "G[go.shape.int_0]"
if buildcfg.Experiment.Unified { if true /* was buildcfg.Experiment.Unified */ {
// Unified mangles differently // Unified mangles differently
fn = "G[go.shape.int]" fn = "G[go.shape.int]"
} }

View File

@ -333,7 +333,7 @@ func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Ty
return val.Op() == ir.ONIL return val.Op() == ir.ONIL
} }
if base.Debug.Unified != 0 && val.Type().HasShape() { if val.Type().HasShape() {
// See comment in cmd/compile/internal/walk/convert.go:walkConvInterface // See comment in cmd/compile/internal/walk/convert.go:walkConvInterface
return false return false
} }

View File

@ -6,7 +6,6 @@ package test
import ( import (
"bufio" "bufio"
"internal/buildcfg"
"internal/goexperiment" "internal/goexperiment"
"internal/testenv" "internal/testenv"
"io" "io"
@ -235,7 +234,7 @@ func TestIntendedInlining(t *testing.T) {
// (*Bool).CompareAndSwap is just over budget on 32-bit systems (386, arm). // (*Bool).CompareAndSwap is just over budget on 32-bit systems (386, arm).
want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap") want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
} }
if buildcfg.Experiment.Unified { if true /* was buildcfg.Experiment.Unified */ {
// Non-unified IR does not report "inlining call ..." for atomic.Pointer[T]'s methods. // Non-unified IR does not report "inlining call ..." for atomic.Pointer[T]'s methods.
// TODO(cuonglm): remove once non-unified IR frontend gone. // TODO(cuonglm): remove once non-unified IR frontend gone.
want["sync/atomic"] = append(want["sync/atomic"], "(*Pointer[go.shape.int]).CompareAndSwap") want["sync/atomic"] = append(want["sync/atomic"], "(*Pointer[go.shape.int]).CompareAndSwap")

View File

@ -336,27 +336,6 @@ func (p *crawler) markInlBody(n *ir.Name) {
} else { } else {
p.checkForFullyInst(t) p.checkForFullyInst(t)
} }
if base.Debug.Unified == 0 {
// If a method of un-exported type is promoted and accessible by
// embedding in an exported type, it makes that type reachable.
//
// Example:
//
// type t struct {}
// func (t) M() {}
//
// func F() interface{} { return struct{ t }{} }
//
// We generate the wrapper for "struct{ t }".M, and inline call
// to "struct{ t }".M, which makes "t.M" reachable.
if t.IsStruct() {
for _, f := range t.FieldSlice() {
if f.Embedded != 0 {
p.markEmbed(f.Type)
}
}
}
}
} }
switch n.Op() { switch n.Op() {

View File

@ -382,7 +382,7 @@ func Assignop1(src, dst *types.Type) (ir.Op, string) {
// don't have the methods for them. // don't have the methods for them.
return ir.OCONVIFACE, "" return ir.OCONVIFACE, ""
} }
if base.Debug.Unified != 0 && src.HasShape() { if src.HasShape() {
// Unified IR uses OCONVIFACE for converting all derived types // Unified IR uses OCONVIFACE for converting all derived types
// to interface type, not just type arguments themselves. // to interface type, not just type arguments themselves.
return ir.OCONVIFACE, "" return ir.OCONVIFACE, ""

View File

@ -217,7 +217,6 @@ func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
base.Fatalf("methodValueWrapper: unexpected %v (%v)", dot, dot.Op()) base.Fatalf("methodValueWrapper: unexpected %v (%v)", dot, dot.Op())
} }
t0 := dot.Type()
meth := dot.Sel meth := dot.Sel
rcvrtype := dot.X.Type() rcvrtype := dot.X.Type()
sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm") sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm")
@ -227,48 +226,6 @@ func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
} }
sym.SetUniq(true) sym.SetUniq(true)
if base.Debug.Unified != 0 { base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth)
base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth) panic("unreachable")
}
savecurfn := ir.CurFunc
saveLineNo := base.Pos
ir.CurFunc = nil
base.Pos = base.AutogeneratedPos
fn := typecheck.DeclFunc(sym, nil,
typecheck.NewFuncParams(t0.Params(), true),
typecheck.NewFuncParams(t0.Results(), false))
fn.SetDupok(true)
fn.SetWrapper(true)
// Declare and initialize variable holding receiver.
ptr := ir.NewHiddenParam(base.Pos, fn, typecheck.Lookup(".this"), rcvrtype)
call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil)
call.Args = ir.ParamNames(fn.Type())
call.IsDDD = fn.Type().IsVariadic()
var body ir.Node = call
if t0.NumResults() != 0 {
ret := ir.NewReturnStmt(base.Pos, nil)
ret.Results = []ir.Node{call}
body = ret
}
fn.Body = []ir.Node{body}
typecheck.FinishFuncBody()
typecheck.Func(fn)
// Need to typecheck the body of the just-generated wrapper.
// typecheckslice() requires that Curfn is set when processing an ORETURN.
ir.CurFunc = fn
typecheck.Stmts(fn.Body)
sym.Def = fn.Nname
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
ir.CurFunc = savecurfn
base.Pos = saveLineNo
return fn.Nname
} }

View File

@ -45,7 +45,7 @@ func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
toType := n.Type() toType := n.Type()
if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) { if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) {
// skip unnamed functions (func _()) // skip unnamed functions (func _())
if base.Debug.Unified != 0 && fromType.HasShape() { if fromType.HasShape() {
// Unified IR uses OCONVIFACE for converting all derived types // Unified IR uses OCONVIFACE for converting all derived types
// to interface type. Avoid assertion failure in // to interface type. Avoid assertion failure in
// MarkTypeUsedInInterface, because we've marked used types // MarkTypeUsedInInterface, because we've marked used types

View File

@ -8,7 +8,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"debug/macho" "debug/macho"
"internal/buildcfg"
"internal/platform" "internal/platform"
"internal/testenv" "internal/testenv"
"os" "os"
@ -1094,7 +1093,7 @@ func TestUnlinkableObj(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
t.Parallel() t.Parallel()
if buildcfg.Experiment.Unified { if true /* was buildcfg.Experiment.Unified */ {
t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified") t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
} }

View File

@ -7,7 +7,6 @@ package importer
import ( import (
"go/build" "go/build"
"go/token" "go/token"
"internal/buildcfg"
"internal/testenv" "internal/testenv"
"io" "io"
"os" "os"
@ -68,7 +67,7 @@ func TestForCompiler(t *testing.T) {
// support for it in unified IR. It's not clear that we actually // support for it in unified IR. It's not clear that we actually
// need to support importing "math/big" as "math/bigger", for // need to support importing "math/big" as "math/bigger", for
// example. cmd/link no longer supports that. // example. cmd/link no longer supports that.
if buildcfg.Experiment.Unified { if true /* was buildcfg.Experiment.Unified */ {
t.Skip("not supported by GOEXPERIMENT=unified; see go.dev/cl/406319") t.Skip("not supported by GOEXPERIMENT=unified; see go.dev/cl/406319")
} }

View File

@ -7,7 +7,6 @@ package gcimporter_test
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"internal/goexperiment"
"internal/goroot" "internal/goroot"
"internal/testenv" "internal/testenv"
"os" "os"
@ -108,7 +107,7 @@ func TestImportTestdata(t *testing.T) {
"exports.go": {"go/ast", "go/token"}, "exports.go": {"go/ast", "go/token"},
"generics.go": nil, "generics.go": nil,
} }
if goexperiment.Unified { if true /* was goexperiment.Unified */ {
// TODO(mdempsky): Fix test below to flatten the transitive // TODO(mdempsky): Fix test below to flatten the transitive
// Package.Imports graph. Unified IR is more precise about // Package.Imports graph. Unified IR is more precise about
// recreating the package import graph. // recreating the package import graph.
@ -168,17 +167,6 @@ func TestImportTypeparamTests(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
var skip map[string]string
if !goexperiment.Unified {
// The Go 1.18 frontend still fails several cases.
skip = map[string]string{
"equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this.
"nested.go": "fails to compile", // TODO(rfindley): investigate this.
"issue47631.go": "can not handle local type declarations",
"issue55101.go": "fails to compile",
}
}
for _, entry := range list { for _, entry := range list {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
// For now, only consider standalone go files. // For now, only consider standalone go files.
@ -186,10 +174,6 @@ func TestImportTypeparamTests(t *testing.T) {
} }
t.Run(entry.Name(), func(t *testing.T) { t.Run(entry.Name(), func(t *testing.T) {
if reason, ok := skip[entry.Name()]; ok {
t.Skip(reason)
}
filename := filepath.Join(rootDir, entry.Name()) filename := filepath.Join(rootDir, entry.Name())
src, err := os.ReadFile(filename) src, err := os.ReadFile(filename)
if err != nil { if err != nil {

View File

@ -70,7 +70,6 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
baseline := goexperiment.Flags{ baseline := goexperiment.Flags{
RegabiWrappers: regabiSupported, RegabiWrappers: regabiSupported,
RegabiArgs: regabiSupported, RegabiArgs: regabiSupported,
Unified: true,
CoverageRedesign: true, CoverageRedesign: true,
} }

View File

@ -1,9 +0,0 @@
// Code generated by mkconsts.go. DO NOT EDIT.
//go:build !goexperiment.unified
// +build !goexperiment.unified
package goexperiment
const Unified = false
const UnifiedInt = 0

View File

@ -1,9 +0,0 @@
// Code generated by mkconsts.go. DO NOT EDIT.
//go:build goexperiment.unified
// +build goexperiment.unified
package goexperiment
const Unified = true
const UnifiedInt = 1

View File

@ -60,10 +60,6 @@ type Flags struct {
StaticLockRanking bool StaticLockRanking bool
BoringCrypto bool BoringCrypto bool
// Unified enables the compiler's unified IR construction
// experiment.
Unified bool
// Regabi is split into several sub-experiments that can be // Regabi is split into several sub-experiments that can be
// enabled individually. Not all combinations work. // enabled individually. Not all combinations work.
// The "regabi" GOEXPERIMENT is an alias for all "working" // The "regabi" GOEXPERIMENT is an alias for all "working"

View File

@ -1,25 +0,0 @@
// errorcheck -0 -m -l
//go:build !goexperiment.unified
// +build !goexperiment.unified
// Copyright 2015 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 escape
var sink interface{}
func dotTypeEscape2() { // #13805, #15796
{
i := 0
j := 0
var ok bool
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)
}
}

View File

@ -1,6 +1,4 @@
// errorcheck -0 -m -l // errorcheck -0 -m -l
//go:build goexperiment.unified
// +build goexperiment.unified
// Copyright 2015 The Go Authors. All rights reserved. // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style

View File

@ -1,8 +1,5 @@
// run // run
//go:build goexperiment.unified && cgo //go:build cgo
// TODO(mdempsky): Enable test unconditionally. This test should pass
// for non-unified mode too.
// Copyright 2021 The Go Authors. All rights reserved. // Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style

View File

@ -1,5 +1,4 @@
// compile // compile
//go:build goexperiment.unified
// Copyright 2022 The Go Authors. All rights reserved. // Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style

View File

@ -1,21 +0,0 @@
// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1
//go:build !goexperiment.unified
// +build !goexperiment.unified
// 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 foo
func r(z int) int {
foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape"
return x + z
}
bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2"
return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3"
return 2*y + x*z
}(x) // ERROR "inlining call to r.func2.1"
}
return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3"
}

View File

@ -1,6 +1,4 @@
// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1 // errorcheckwithauto -0 -m -d=inlfuncswithclosures=1
//go:build goexperiment.unified
// +build goexperiment.unified
// Copyright 2022 The Go Authors. All rights reserved. // Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style

View File

@ -76,15 +76,6 @@ var env = func() (res envVars) {
return return
}() }()
var unifiedEnabled = func() bool {
for _, tag := range build.Default.ToolTags {
if tag == "goexperiment.unified" {
return true
}
}
return false
}()
// defaultAllCodeGen returns the default value of the -all_codegen // defaultAllCodeGen returns the default value of the -all_codegen
// flag. By default, we prefer to be fast (returning false), except on // flag. By default, we prefer to be fast (returning false), except on
// the linux-amd64 builder that's already very fast, so we get more // the linux-amd64 builder that's already very fast, so we get more
@ -374,10 +365,6 @@ func (t *test) initExpectFail() {
failureSets = append(failureSets, types2Failures32Bit) failureSets = append(failureSets, types2Failures32Bit)
} }
if !unifiedEnabled {
failureSets = append(failureSets, go118Failures)
}
filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows
for _, set := range failureSets { for _, set := range failureSets {
@ -2037,21 +2024,6 @@ var types2Failures32Bit = setOf(
"fixedbugs/issue23305.go", // large untyped int passed to println (32-bit) "fixedbugs/issue23305.go", // large untyped int passed to println (32-bit)
) )
var go118Failures = setOf(
"fixedbugs/issue54343.go", // 1.18 compiler assigns receiver parameter to global variable
"fixedbugs/issue56280.go", // 1.18 compiler doesn't support inlining generic functions
"typeparam/nested.go", // 1.18 compiler doesn't support function-local types with generics
"typeparam/issue47631.go", // 1.18 can not handle local type declarations
"typeparam/issue51521.go", // 1.18 compiler produces bad panic message and link error
"typeparam/issue54456.go", // 1.18 compiler fails to distinguish local generic types
"typeparam/issue54497.go", // 1.18 compiler is more conservative about inlining due to repeated issues
"typeparam/issue55101.go", // 1.18 compiler ICEs writing export data
"typeparam/mdempsky/16.go", // 1.18 compiler uses interface shape type in failed type assertions
"typeparam/mdempsky/17.go", // 1.18 compiler mishandles implicit conversions from range loops
"typeparam/mdempsky/18.go", // 1.18 compiler mishandles implicit conversions in select statements
"typeparam/mdempsky/20.go", // 1.18 compiler crashes on method expressions promoted to derived types
)
// In all of these cases, the 1.17 compiler reports reasonable errors, but either the // In all of these cases, the 1.17 compiler reports reasonable errors, but either the
// 1.17 or 1.18 compiler report extra errors, so we can't match correctly on both. We // 1.17 or 1.18 compiler report extra errors, so we can't match correctly on both. We
// now set the patterns to match correctly on all the 1.18 errors. // now set the patterns to match correctly on all the 1.18 errors.