aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/compile/fmtmap_test.go2
-rw-r--r--src/cmd/compile/internal/gc/compile.go3
-rw-r--r--src/cmd/compile/internal/gc/initorder.go11
-rw-r--r--src/cmd/compile/internal/gc/main.go3
-rw-r--r--src/cmd/compile/internal/gc/obj.go57
-rw-r--r--src/cmd/compile/internal/ir/func.go21
-rw-r--r--src/cmd/compile/internal/reflectdata/reflect.go26
-rw-r--r--src/cmd/compile/internal/staticdata/data.go57
-rw-r--r--src/cmd/compile/internal/staticinit/sched.go596
-rw-r--r--src/cmd/compile/internal/walk/closure.go (renamed from src/cmd/compile/internal/gc/closure.go)31
-rw-r--r--src/cmd/compile/internal/walk/order.go (renamed from src/cmd/compile/internal/gc/order.go)80
-rw-r--r--src/cmd/compile/internal/walk/race.go (renamed from src/cmd/compile/internal/gc/racewalk.go)2
-rw-r--r--src/cmd/compile/internal/walk/range.go (renamed from src/cmd/compile/internal/gc/range.go)5
-rw-r--r--src/cmd/compile/internal/walk/select.go (renamed from src/cmd/compile/internal/gc/select.go)2
-rw-r--r--src/cmd/compile/internal/walk/sinit.go (renamed from src/cmd/compile/internal/gc/sinit.go)509
-rw-r--r--src/cmd/compile/internal/walk/subr.go (renamed from src/cmd/compile/internal/gc/subr.go)5
-rw-r--r--src/cmd/compile/internal/walk/switch.go (renamed from src/cmd/compile/internal/gc/swt.go)9
-rw-r--r--src/cmd/compile/internal/walk/walk.go (renamed from src/cmd/compile/internal/gc/walk.go)135
18 files changed, 790 insertions, 764 deletions
diff --git a/src/cmd/compile/fmtmap_test.go b/src/cmd/compile/fmtmap_test.go
index 9bc059c2e4..a925ec05ac 100644
--- a/src/cmd/compile/fmtmap_test.go
+++ b/src/cmd/compile/fmtmap_test.go
@@ -37,7 +37,6 @@ var knownFormats = map[string]string{
"[]cmd/compile/internal/syntax.token %s": "",
"cmd/compile/internal/arm.shift %d": "",
"cmd/compile/internal/gc.RegIndex %d": "",
- "cmd/compile/internal/gc.initKind %d": "",
"cmd/compile/internal/ir.Class %d": "",
"cmd/compile/internal/ir.Node %+v": "",
"cmd/compile/internal/ir.Node %L": "",
@@ -68,6 +67,7 @@ var knownFormats = map[string]string{
"cmd/compile/internal/syntax.token %s": "",
"cmd/compile/internal/types.Kind %d": "",
"cmd/compile/internal/types.Kind %s": "",
+ "cmd/compile/internal/walk.initKind %d": "",
"go/constant.Value %#v": "",
"math/big.Accuracy %s": "",
"reflect.Type %s": "",
diff --git a/src/cmd/compile/internal/gc/compile.go b/src/cmd/compile/internal/gc/compile.go
index c2a6a9e327..926b2dee95 100644
--- a/src/cmd/compile/internal/gc/compile.go
+++ b/src/cmd/compile/internal/gc/compile.go
@@ -17,6 +17,7 @@ import (
"cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/compile/internal/walk"
)
// "Portable" code generation.
@@ -61,7 +62,7 @@ func compile(fn *ir.Func) {
ssagen.InitLSym(fn, true)
errorsBefore := base.Errors()
- walk(fn)
+ walk.Walk(fn)
if base.Errors() > errorsBefore {
return
}
diff --git a/src/cmd/compile/internal/gc/initorder.go b/src/cmd/compile/internal/gc/initorder.go
index 5caa2e769f..4ac468fb4e 100644
--- a/src/cmd/compile/internal/gc/initorder.go
+++ b/src/cmd/compile/internal/gc/initorder.go
@@ -11,6 +11,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/staticinit"
)
// Package initialization
@@ -77,9 +78,9 @@ type InitOrder struct {
// corresponding list of statements to include in the init() function
// body.
func initOrder(l []ir.Node) []ir.Node {
- s := InitSchedule{
- initplans: make(map[ir.Node]*InitPlan),
- inittemps: make(map[ir.Node]*ir.Name),
+ s := staticinit.Schedule{
+ Plans: make(map[ir.Node]*staticinit.Plan),
+ Temps: make(map[ir.Node]*ir.Name),
}
o := InitOrder{
blocking: make(map[ir.Node][]ir.Node),
@@ -91,7 +92,7 @@ func initOrder(l []ir.Node) []ir.Node {
switch n.Op() {
case ir.OAS, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
o.processAssign(n)
- o.flushReady(s.staticInit)
+ o.flushReady(s.StaticInit)
case ir.ODCLCONST, ir.ODCLFUNC, ir.ODCLTYPE:
// nop
default:
@@ -124,7 +125,7 @@ func initOrder(l []ir.Node) []ir.Node {
base.Fatalf("expected empty map: %v", o.blocking)
}
- return s.out
+ return s.Out
}
func (o *InitOrder) processAssign(n ir.Node) {
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 2a8012b462..aeb58a3310 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -22,6 +22,7 @@ import (
"cmd/compile/internal/staticdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/compile/internal/walk"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/objabi"
@@ -268,7 +269,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
n := n.(*ir.Func)
if n.OClosure != nil {
ir.CurFunc = n
- transformclosure(n)
+ walk.Closure(n)
}
}
}
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index f159256da6..0ab3a8dad4 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -18,7 +18,6 @@ import (
"cmd/internal/objabi"
"encoding/json"
"fmt"
- "go/constant"
)
// These modes say which kind of object file to generate.
@@ -261,62 +260,6 @@ func addGCLocals() {
}
}
-// litsym writes the static literal c to n.
-// Neither n nor c is modified.
-func litsym(n *ir.Name, noff int64, c ir.Node, wid int) {
- if n.Op() != ir.ONAME {
- base.Fatalf("litsym n op %v", n.Op())
- }
- if n.Sym() == nil {
- base.Fatalf("litsym nil n sym")
- }
- if c.Op() == ir.ONIL {
- return
- }
- if c.Op() != ir.OLITERAL {
- base.Fatalf("litsym c op %v", c.Op())
- }
- s := n.Sym().Linksym()
- switch u := c.Val(); u.Kind() {
- case constant.Bool:
- i := int64(obj.Bool2int(constant.BoolVal(u)))
- s.WriteInt(base.Ctxt, noff, wid, i)
-
- case constant.Int:
- s.WriteInt(base.Ctxt, noff, wid, ir.IntVal(c.Type(), u))
-
- case constant.Float:
- f, _ := constant.Float64Val(u)
- switch c.Type().Kind() {
- case types.TFLOAT32:
- s.WriteFloat32(base.Ctxt, noff, float32(f))
- case types.TFLOAT64:
- s.WriteFloat64(base.Ctxt, noff, f)
- }
-
- case constant.Complex:
- re, _ := constant.Float64Val(constant.Real(u))
- im, _ := constant.Float64Val(constant.Imag(u))
- switch c.Type().Kind() {
- case types.TCOMPLEX64:
- s.WriteFloat32(base.Ctxt, noff, float32(re))
- s.WriteFloat32(base.Ctxt, noff+4, float32(im))
- case types.TCOMPLEX128:
- s.WriteFloat64(base.Ctxt, noff, re)
- s.WriteFloat64(base.Ctxt, noff+8, im)
- }
-
- case constant.String:
- i := constant.StringVal(u)
- symdata := staticdata.StringSym(n.Pos(), i)
- s.WriteAddr(base.Ctxt, noff, types.PtrSize, symdata, 0)
- s.WriteInt(base.Ctxt, noff+int64(types.PtrSize), types.PtrSize, int64(len(i)))
-
- default:
- base.Fatalf("litsym unhandled OLITERAL %v", c)
- }
-}
-
func ggloblnod(nam ir.Node) {
s := nam.Sym().Linksym()
s.Gotype = reflectdata.TypeSym(nam.Type()).Linksym()
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go
index a93516d716..6bc8cd574c 100644
--- a/src/cmd/compile/internal/ir/func.go
+++ b/src/cmd/compile/internal/ir/func.go
@@ -288,3 +288,24 @@ func MarkFunc(n *Name) {
n.Class_ = PFUNC
n.Sym().SetFunc(true)
}
+
+// ClosureDebugRuntimeCheck applies boilerplate checks for debug flags
+// and compiling runtime
+func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
+ if base.Debug.Closure > 0 {
+ if clo.Esc() == EscHeap {
+ base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
+ } else {
+ base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
+ }
+ }
+ if base.Flag.CompilingRuntime && clo.Esc() == EscHeap {
+ base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime")
+ }
+}
+
+// IsTrivialClosure reports whether closure clo has an
+// empty list of captured vars.
+func IsTrivialClosure(clo *ClosureExpr) bool {
+ return len(clo.Func.ClosureVars) == 0
+}
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index a5e2fb407a..ba3e0fa75e 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -1834,3 +1834,29 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
}
var ZeroSize int64
+
+// MarkTypeUsedInInterface marks that type t is converted to an interface.
+// This information is used in the linker in dead method elimination.
+func MarkTypeUsedInInterface(t *types.Type, from *obj.LSym) {
+ tsym := TypeSym(t).Linksym()
+ // Emit a marker relocation. The linker will know the type is converted
+ // to an interface if "from" is reachable.
+ r := obj.Addrel(from)
+ r.Sym = tsym
+ r.Type = objabi.R_USEIFACE
+}
+
+// MarkUsedIfaceMethod marks that an interface method is used in the current
+// function. n is OCALLINTER node.
+func MarkUsedIfaceMethod(n *ir.CallExpr) {
+ dot := n.X.(*ir.SelectorExpr)
+ ityp := dot.X.Type()
+ tsym := TypeSym(ityp).Linksym()
+ r := obj.Addrel(ir.CurFunc.LSym)
+ r.Sym = tsym
+ // dot.Xoffset is the method index * Widthptr (the offset of code pointer
+ // in itab).
+ midx := dot.Offset / int64(types.PtrSize)
+ r.Add = InterfaceMethodOffset(ityp, midx)
+ r.Type = objabi.R_USEIFACEMETHOD
+}
diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go
index 7627aaa11a..342a2e2bbc 100644
--- a/src/cmd/compile/internal/staticdata/data.go
+++ b/src/cmd/compile/internal/staticdata/data.go
@@ -7,6 +7,7 @@ package staticdata
import (
"crypto/sha256"
"fmt"
+ "go/constant"
"io"
"io/ioutil"
"os"
@@ -294,3 +295,59 @@ func WriteFuncSyms() {
objw.Global(sf, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
}
}
+
+// InitConst writes the static literal c to n.
+// Neither n nor c is modified.
+func InitConst(n *ir.Name, noff int64, c ir.Node, wid int) {
+ if n.Op() != ir.ONAME {
+ base.Fatalf("litsym n op %v", n.Op())
+ }
+ if n.Sym() == nil {
+ base.Fatalf("litsym nil n sym")
+ }
+ if c.Op() == ir.ONIL {
+ return
+ }
+ if c.Op() != ir.OLITERAL {
+ base.Fatalf("litsym c op %v", c.Op())
+ }
+ s := n.Sym().Linksym()
+ switch u := c.Val(); u.Kind() {
+ case constant.Bool:
+ i := int64(obj.Bool2int(constant.BoolVal(u)))
+ s.WriteInt(base.Ctxt, noff, wid, i)
+
+ case constant.Int:
+ s.WriteInt(base.Ctxt, noff, wid, ir.IntVal(c.Type(), u))
+
+ case constant.Float:
+ f, _ := constant.Float64Val(u)
+ switch c.Type().Kind() {
+ case types.TFLOAT32:
+ s.WriteFloat32(base.Ctxt, noff, float32(f))
+ case types.TFLOAT64:
+ s.WriteFloat64(base.Ctxt, noff, f)
+ }
+
+ case constant.Complex:
+ re, _ := constant.Float64Val(constant.Real(u))
+ im, _ := constant.Float64Val(constant.Imag(u))
+ switch c.Type().Kind() {
+ case types.TCOMPLEX64:
+ s.WriteFloat32(base.Ctxt, noff, float32(re))
+ s.WriteFloat32(base.Ctxt, noff+4, float32(im))
+ case types.TCOMPLEX128:
+ s.WriteFloat64(base.Ctxt, noff, re)
+ s.WriteFloat64(base.Ctxt, noff+8, im)
+ }
+
+ case constant.String:
+ i := constant.StringVal(u)
+ symdata := StringSym(n.Pos(), i)
+ s.WriteAddr(base.Ctxt, noff, types.PtrSize, symdata, 0)
+ s.WriteInt(base.Ctxt, noff+int64(types.PtrSize), types.PtrSize, int64(len(i)))
+
+ default:
+ base.Fatalf("litsym unhandled OLITERAL %v", c)
+ }
+}
diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go
new file mode 100644
index 0000000000..2a499d6eed
--- /dev/null
+++ b/src/cmd/compile/internal/staticinit/sched.go
@@ -0,0 +1,596 @@
+// Copyright 2009 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 staticinit
+
+import (
+ "fmt"
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/staticdata"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+type Entry struct {
+ Xoffset int64 // struct, array only
+ Expr ir.Node // bytes of run-time computed expressions
+}
+
+type Plan struct {
+ E []Entry
+}
+
+// An Schedule is used to decompose assignment statements into
+// static and dynamic initialization parts. Static initializations are
+// handled by populating variables' linker symbol data, while dynamic
+// initializations are accumulated to be executed in order.
+type Schedule struct {
+ // Out is the ordered list of dynamic initialization
+ // statements.
+ Out []ir.Node
+
+ Plans map[ir.Node]*Plan
+ Temps map[ir.Node]*ir.Name
+}
+
+func (s *Schedule) append(n ir.Node) {
+ s.Out = append(s.Out, n)
+}
+
+// StaticInit adds an initialization statement n to the schedule.
+func (s *Schedule) StaticInit(n ir.Node) {
+ if !s.tryStaticInit(n) {
+ if base.Flag.Percent != 0 {
+ ir.Dump("nonstatic", n)
+ }
+ s.append(n)
+ }
+}
+
+// tryStaticInit attempts to statically execute an initialization
+// statement and reports whether it succeeded.
+func (s *Schedule) tryStaticInit(nn ir.Node) bool {
+ // Only worry about simple "l = r" assignments. Multiple
+ // variable/expression OAS2 assignments have already been
+ // replaced by multiple simple OAS assignments, and the other
+ // OAS2* assignments mostly necessitate dynamic execution
+ // anyway.
+ if nn.Op() != ir.OAS {
+ return false
+ }
+ n := nn.(*ir.AssignStmt)
+ if ir.IsBlank(n.X) && !AnySideEffects(n.Y) {
+ // Discard.
+ return true
+ }
+ lno := ir.SetPos(n)
+ defer func() { base.Pos = lno }()
+ nam := n.X.(*ir.Name)
+ return s.StaticAssign(nam, 0, n.Y, nam.Type())
+}
+
+// like staticassign but we are copying an already
+// initialized value r.
+func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool {
+ if rn.Class_ == ir.PFUNC {
+ // TODO if roff != 0 { panic }
+ staticdata.InitFunc(l, loff, rn)
+ return true
+ }
+ if rn.Class_ != ir.PEXTERN || rn.Sym().Pkg != types.LocalPkg {
+ return false
+ }
+ if rn.Defn == nil { // probably zeroed but perhaps supplied externally and of unknown value
+ return false
+ }
+ if rn.Defn.Op() != ir.OAS {
+ return false
+ }
+ if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675)
+ return false
+ }
+ orig := rn
+ r := rn.Defn.(*ir.AssignStmt).Y
+
+ for r.Op() == ir.OCONVNOP && !types.Identical(r.Type(), typ) {
+ r = r.(*ir.ConvExpr).X
+ }
+
+ switch r.Op() {
+ case ir.OMETHEXPR:
+ r = r.(*ir.MethodExpr).FuncName()
+ fallthrough
+ case ir.ONAME:
+ r := r.(*ir.Name)
+ if s.staticcopy(l, loff, r, typ) {
+ return true
+ }
+ // We may have skipped past one or more OCONVNOPs, so
+ // use conv to ensure r is assignable to l (#13263).
+ dst := ir.Node(l)
+ if loff != 0 || !types.Identical(typ, l.Type()) {
+ dst = ir.NewNameOffsetExpr(base.Pos, l, loff, typ)
+ }
+ s.append(ir.NewAssignStmt(base.Pos, dst, typecheck.Conv(r, typ)))
+ return true
+
+ case ir.ONIL:
+ return true
+
+ case ir.OLITERAL:
+ if ir.IsZero(r) {
+ return true
+ }
+ staticdata.InitConst(l, loff, r, int(typ.Width))
+ return true
+
+ case ir.OADDR:
+ r := r.(*ir.AddrExpr)
+ if a := r.X; a.Op() == ir.ONAME {
+ a := a.(*ir.Name)
+ staticdata.InitAddr(l, loff, a, 0)
+ return true
+ }
+
+ case ir.OPTRLIT:
+ r := r.(*ir.AddrExpr)
+ switch r.X.Op() {
+ case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT, ir.OMAPLIT:
+ // copy pointer
+ staticdata.InitAddr(l, loff, s.Temps[r], 0)
+ return true
+ }
+
+ case ir.OSLICELIT:
+ r := r.(*ir.CompLitExpr)
+ // copy slice
+ staticdata.InitSlice(l, loff, s.Temps[r], r.Len)
+ return true
+
+ case ir.OARRAYLIT, ir.OSTRUCTLIT:
+ r := r.(*ir.CompLitExpr)
+ p := s.Plans[r]
+ for i := range p.E {
+ e := &p.E[i]
+ typ := e.Expr.Type()
+ if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
+ staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Width))
+ continue
+ }
+ x := e.Expr
+ if x.Op() == ir.OMETHEXPR {
+ x = x.(*ir.MethodExpr).FuncName()
+ }
+ if x.Op() == ir.ONAME && s.staticcopy(l, loff+e.Xoffset, x.(*ir.Name), typ) {
+ continue
+ }
+ // Requires computation, but we're
+ // copying someone else's computation.
+ ll := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, typ)
+ rr := ir.NewNameOffsetExpr(base.Pos, orig, e.Xoffset, typ)
+ ir.SetPos(rr)
+ s.append(ir.NewAssignStmt(base.Pos, ll, rr))
+ }
+
+ return true
+ }
+
+ return false
+}
+
+func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool {
+ for r.Op() == ir.OCONVNOP {
+ r = r.(*ir.ConvExpr).X
+ }
+
+ switch r.Op() {
+ case ir.ONAME:
+ r := r.(*ir.Name)
+ return s.staticcopy(l, loff, r, typ)
+
+ case ir.OMETHEXPR:
+ r := r.(*ir.MethodExpr)
+ return s.staticcopy(l, loff, r.FuncName(), typ)
+
+ case ir.ONIL:
+ return true
+
+ case ir.OLITERAL:
+ if ir.IsZero(r) {
+ return true
+ }
+ staticdata.InitConst(l, loff, r, int(typ.Width))
+ return true
+
+ case ir.OADDR:
+ r := r.(*ir.AddrExpr)
+ if name, offset, ok := StaticLoc(r.X); ok {
+ staticdata.InitAddr(l, loff, name, offset)
+ return true
+ }
+ fallthrough
+
+ case ir.OPTRLIT:
+ r := r.(*ir.AddrExpr)
+ switch r.X.Op() {
+ case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
+ // Init pointer.
+ a := StaticName(r.X.Type())
+
+ s.Temps[r] = a
+ staticdata.InitAddr(l, loff, a, 0)
+
+ // Init underlying literal.
+ if !s.StaticAssign(a, 0, r.X, a.Type()) {
+ s.append(ir.NewAssignStmt(base.Pos, a, r.X))
+ }
+ return true
+ }
+ //dump("not static ptrlit", r);
+
+ case ir.OSTR2BYTES:
+ r := r.(*ir.ConvExpr)
+ if l.Class_ == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
+ sval := ir.StringVal(r.X)
+ staticdata.InitSliceBytes(l, loff, sval)
+ return true
+ }
+
+ case ir.OSLICELIT:
+ r := r.(*ir.CompLitExpr)
+ s.initplan(r)
+ // Init slice.
+ ta := types.NewArray(r.Type().Elem(), r.Len)
+ ta.SetNoalg(true)
+ a := StaticName(ta)
+ s.Temps[r] = a
+ staticdata.InitSlice(l, loff, a, r.Len)
+ // Fall through to init underlying array.
+ l = a
+ loff = 0
+ fallthrough
+
+ case ir.OARRAYLIT, ir.OSTRUCTLIT:
+ r := r.(*ir.CompLitExpr)
+ s.initplan(r)
+
+ p := s.Plans[r]
+ for i := range p.E {
+ e := &p.E[i]
+ if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
+ staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Width))
+ continue
+ }
+ ir.SetPos(e.Expr)
+ if !s.StaticAssign(l, loff+e.Xoffset, e.Expr, e.Expr.Type()) {
+ a := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, e.Expr.Type())
+ s.append(ir.NewAssignStmt(base.Pos, a, e.Expr))
+ }
+ }
+
+ return true
+
+ case ir.OMAPLIT:
+ break
+
+ case ir.OCLOSURE:
+ r := r.(*ir.ClosureExpr)
+ if ir.IsTrivialClosure(r) {
+ if base.Debug.Closure > 0 {
+ base.WarnfAt(r.Pos(), "closure converted to global")
+ }
+ // Closures with no captured variables are globals,
+ // so the assignment can be done at link time.
+ // TODO if roff != 0 { panic }
+ staticdata.InitFunc(l, loff, r.Func.Nname)
+ return true
+ }
+ ir.ClosureDebugRuntimeCheck(r)
+
+ case ir.OCONVIFACE:
+ // This logic is mirrored in isStaticCompositeLiteral.
+ // If you change something here, change it there, and vice versa.
+
+ // Determine the underlying concrete type and value we are converting from.
+ r := r.(*ir.ConvExpr)
+ val := ir.Node(r)
+ for val.Op() == ir.OCONVIFACE {
+ val = val.(*ir.ConvExpr).X
+ }
+
+ if val.Type().IsInterface() {
+ // val is an interface type.
+ // If val is nil, we can statically initialize l;
+ // both words are zero and so there no work to do, so report success.
+ // If val is non-nil, we have no concrete type to record,
+ // and we won't be able to statically initialize its value, so report failure.
+ return val.Op() == ir.ONIL
+ }
+
+ reflectdata.MarkTypeUsedInInterface(val.Type(), l.Sym().Linksym())
+
+ var itab *ir.AddrExpr
+ if typ.IsEmptyInterface() {
+ itab = reflectdata.TypePtr(val.Type())
+ } else {
+ itab = reflectdata.ITabAddr(val.Type(), typ)
+ }
+
+ // Create a copy of l to modify while we emit data.
+
+ // Emit itab, advance offset.
+ staticdata.InitAddr(l, loff, itab.X.(*ir.Name), 0)
+
+ // Emit data.
+ if types.IsDirectIface(val.Type()) {
+ if val.Op() == ir.ONIL {
+ // Nil is zero, nothing to do.
+ return true
+ }
+ // Copy val directly into n.
+ ir.SetPos(val)
+ if !s.StaticAssign(l, loff+int64(types.PtrSize), val, val.Type()) {
+ a := ir.NewNameOffsetExpr(base.Pos, l, loff+int64(types.PtrSize), val.Type())
+ s.append(ir.NewAssignStmt(base.Pos, a, val))
+ }
+ } else {
+ // Construct temp to hold val, write pointer to temp into n.
+ a := StaticName(val.Type())
+ s.Temps[val] = a
+ if !s.StaticAssign(a, 0, val, val.Type()) {
+ s.append(ir.NewAssignStmt(base.Pos, a, val))
+ }
+ staticdata.InitAddr(l, loff+int64(types.PtrSize), a, 0)
+ }
+
+ return true
+ }
+
+ //dump("not static", r);
+ return false
+}
+
+func (s *Schedule) initplan(n ir.Node) {
+ if s.Plans[n] != nil {
+ return
+ }
+ p := new(Plan)
+ s.Plans[n] = p
+ switch n.Op() {
+ default:
+ base.Fatalf("initplan")
+
+ case ir.OARRAYLIT, ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ var k int64
+ for _, a := range n.List {
+ if a.Op() == ir.OKEY {
+ kv := a.(*ir.KeyExpr)
+ k = typecheck.IndexConst(kv.Key)
+ if k < 0 {
+ base.Fatalf("initplan arraylit: invalid index %v", kv.Key)
+ }
+ a = kv.Value
+ }
+ s.addvalue(p, k*n.Type().Elem().Width, a)
+ k++
+ }
+
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, a := range n.List {
+ if a.Op() != ir.OSTRUCTKEY {
+ base.Fatalf("initplan structlit")
+ }
+ a := a.(*ir.StructKeyExpr)
+ if a.Field.IsBlank() {
+ continue
+ }
+ s.addvalue(p, a.Offset, a.Value)
+ }
+
+ case ir.OMAPLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, a := range n.List {
+ if a.Op() != ir.OKEY {
+ base.Fatalf("initplan maplit")
+ }
+ a := a.(*ir.KeyExpr)
+ s.addvalue(p, -1, a.Value)
+ }
+ }
+}
+
+func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
+ // special case: zero can be dropped entirely
+ if ir.IsZero(n) {
+ return
+ }
+
+ // special case: inline struct and array (not slice) literals
+ if isvaluelit(n) {
+ s.initplan(n)
+ q := s.Plans[n]
+ for _, qe := range q.E {
+ // qe is a copy; we are not modifying entries in q.E
+ qe.Xoffset += xoffset
+ p.E = append(p.E, qe)
+ }
+ return
+ }
+
+ // add to plan
+ p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
+}
+
+// from here down is the walk analysis
+// of composite literals.
+// most of the work is to generate
+// data statements for the constant
+// part of the composite literal.
+
+var statuniqgen int // name generator for static temps
+
+// StaticName returns a name backed by a (writable) static data symbol.
+// Use readonlystaticname for read-only node.
+func StaticName(t *types.Type) *ir.Name {
+ // Don't use lookupN; it interns the resulting string, but these are all unique.
+ n := typecheck.NewName(typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen)))
+ statuniqgen++
+ typecheck.Declare(n, ir.PEXTERN)
+ n.SetType(t)
+ n.Sym().Linksym().Set(obj.AttrLocal, true)
+ return n
+}
+
+// StaticLoc returns the static address of n, if n has one, or else nil.
+func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
+ if n == nil {
+ return nil, 0, false
+ }
+
+ switch n.Op() {
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ return n, 0, true
+
+ case ir.OMETHEXPR:
+ n := n.(*ir.MethodExpr)
+ return StaticLoc(n.FuncName())
+
+ case ir.ODOT:
+ n := n.(*ir.SelectorExpr)
+ if name, offset, ok = StaticLoc(n.X); !ok {
+ break
+ }
+ offset += n.Offset
+ return name, offset, true
+
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ if n.X.Type().IsSlice() {
+ break
+ }
+ if name, offset, ok = StaticLoc(n.X); !ok {
+ break
+ }
+ l := getlit(n.Index)
+ if l < 0 {
+ break
+ }
+
+ // Check for overflow.
+ if n.Type().Width != 0 && types.MaxWidth/n.Type().Width <= int64(l) {
+ break
+ }
+ offset += int64(l) * n.Type().Width
+ return name, offset, true
+ }
+
+ return nil, 0, false
+}
+
+// AnySideEffects reports whether n contains any operations that could have observable side effects.
+func AnySideEffects(n ir.Node) bool {
+ return ir.Any(n, func(n ir.Node) bool {
+ switch n.Op() {
+ // Assume side effects unless we know otherwise.
+ default:
+ return true
+
+ // No side effects here (arguments are checked separately).
+ case ir.ONAME,
+ ir.ONONAME,
+ ir.OTYPE,
+ ir.OPACK,
+ ir.OLITERAL,
+ ir.ONIL,
+ ir.OADD,
+ ir.OSUB,
+ ir.OOR,
+ ir.OXOR,
+ ir.OADDSTR,
+ ir.OADDR,
+ ir.OANDAND,
+ ir.OBYTES2STR,
+ ir.ORUNES2STR,
+ ir.OSTR2BYTES,
+ ir.OSTR2RUNES,
+ ir.OCAP,
+ ir.OCOMPLIT,
+ ir.OMAPLIT,
+ ir.OSTRUCTLIT,
+ ir.OARRAYLIT,
+ ir.OSLICELIT,
+ ir.OPTRLIT,
+ ir.OCONV,
+ ir.OCONVIFACE,
+ ir.OCONVNOP,
+ ir.ODOT,
+ ir.OEQ,
+ ir.ONE,
+ ir.OLT,
+ ir.OLE,
+ ir.OGT,
+ ir.OGE,
+ ir.OKEY,
+ ir.OSTRUCTKEY,
+ ir.OLEN,
+ ir.OMUL,
+ ir.OLSH,
+ ir.ORSH,
+ ir.OAND,
+ ir.OANDNOT,
+ ir.ONEW,
+ ir.ONOT,
+ ir.OBITNOT,
+ ir.OPLUS,
+ ir.ONEG,
+ ir.OOROR,
+ ir.OPAREN,
+ ir.ORUNESTR,
+ ir.OREAL,
+ ir.OIMAG,
+ ir.OCOMPLEX:
+ return false
+
+ // Only possible side effect is division by zero.
+ case ir.ODIV, ir.OMOD:
+ n := n.(*ir.BinaryExpr)
+ if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 {
+ return true
+ }
+
+ // Only possible side effect is panic on invalid size,
+ // but many makechan and makemap use size zero, which is definitely OK.
+ case ir.OMAKECHAN, ir.OMAKEMAP:
+ n := n.(*ir.MakeExpr)
+ if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 {
+ return true
+ }
+
+ // Only possible side effect is panic on invalid size.
+ // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
+ case ir.OMAKESLICE, ir.OMAKESLICECOPY:
+ return true
+ }
+ return false
+ })
+}
+
+func getlit(lit ir.Node) int {
+ if ir.IsSmallIntConst(lit) {
+ return int(ir.Int64Val(lit))
+ }
+ return -1
+}
+
+func isvaluelit(n ir.Node) bool {
+ return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
+}
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/walk/closure.go
index 4679b6535b..545c762ac7 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/walk/closure.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
"cmd/compile/internal/base"
@@ -12,9 +12,9 @@ import (
"cmd/internal/src"
)
-// transformclosure is called in a separate phase after escape analysis.
+// Closure is called in a separate phase after escape analysis.
// It transform closure bodies to properly reference captured variables.
-func transformclosure(fn *ir.Func) {
+func Closure(fn *ir.Func) {
lno := base.Pos
base.Pos = fn.Pos()
@@ -115,38 +115,17 @@ func transformclosure(fn *ir.Func) {
base.Pos = lno
}
-// hasemptycvars reports whether closure clo has an
-// empty list of captured vars.
-func hasemptycvars(clo *ir.ClosureExpr) bool {
- return len(clo.Func.ClosureVars) == 0
-}
-
-// closuredebugruntimecheck applies boilerplate checks for debug flags
-// and compiling runtime
-func closuredebugruntimecheck(clo *ir.ClosureExpr) {
- if base.Debug.Closure > 0 {
- if clo.Esc() == ir.EscHeap {
- base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
- } else {
- base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
- }
- }
- if base.Flag.CompilingRuntime && clo.Esc() == ir.EscHeap {
- base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime")
- }
-}
-
func walkclosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
fn := clo.Func
// If no closure vars, don't bother wrapping.
- if hasemptycvars(clo) {
+ if ir.IsTrivialClosure(clo) {
if base.Debug.Closure > 0 {
base.WarnfAt(clo.Pos(), "closure converted to global")
}
return fn.Nname
}
- closuredebugruntimecheck(clo)
+ ir.ClosureDebugRuntimeCheck(clo)
typ := typecheck.ClosureType(clo)
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/walk/order.go
index d1c5bb04a1..03310a50c6 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/walk/order.go
@@ -2,17 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
+ "fmt"
+
"cmd/compile/internal/base"
"cmd/compile/internal/escape"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/staticinit"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/src"
- "fmt"
)
// Rewrite tree to use separate statements to enforce
@@ -45,8 +47,8 @@ import (
// it can result in unnecessary zeroing of those variables in the function
// prologue.
-// Order holds state during the ordering process.
-type Order struct {
+// orderState holds state during the ordering process.
+type orderState struct {
out []ir.Node // list of generated statements
temp []*ir.Name // stack of temporary variables
free map[string][]*ir.Name // free list of unused temporaries, by type.LongString().
@@ -65,14 +67,14 @@ func order(fn *ir.Func) {
}
// append typechecks stmt and appends it to out.
-func (o *Order) append(stmt ir.Node) {
+func (o *orderState) append(stmt ir.Node) {
o.out = append(o.out, typecheck.Stmt(stmt))
}
// newTemp allocates a new temporary with the given type,
// pushes it onto the temp stack, and returns it.
// If clear is true, newTemp emits code to zero the temporary.
-func (o *Order) newTemp(t *types.Type, clear bool) *ir.Name {
+func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
var v *ir.Name
// Note: LongString is close to the type equality we want,
// but not exactly. We still need to double-check with types.Identical.
@@ -100,7 +102,7 @@ func (o *Order) newTemp(t *types.Type, clear bool) *ir.Name {
// copyExpr behaves like newTemp but also emits
// code to initialize the temporary to the value n.
-func (o *Order) copyExpr(n ir.Node) ir.Node {
+func (o *orderState) copyExpr(n ir.Node) ir.Node {
return o.copyExpr1(n, false)
}
@@ -114,11 +116,11 @@ func (o *Order) copyExpr(n ir.Node) ir.Node {
// (The other candidate would be map access, but map access
// returns a pointer to the result data instead of taking a pointer
// to be filled in.)
-func (o *Order) copyExprClear(n ir.Node) *ir.Name {
+func (o *orderState) copyExprClear(n ir.Node) *ir.Name {
return o.copyExpr1(n, true)
}
-func (o *Order) copyExpr1(n ir.Node, clear bool) *ir.Name {
+func (o *orderState) copyExpr1(n ir.Node, clear bool) *ir.Name {
t := n.Type()
v := o.newTemp(t, clear)
o.append(ir.NewAssignStmt(base.Pos, v, n))
@@ -129,7 +131,7 @@ func (o *Order) copyExpr1(n ir.Node, clear bool) *ir.Name {
// The definition of cheap is that n is a variable or constant.
// If not, cheapExpr allocates a new tmp, emits tmp = n,
// and then returns tmp.
-func (o *Order) cheapExpr(n ir.Node) ir.Node {
+func (o *orderState) cheapExpr(n ir.Node) ir.Node {
if n == nil {
return nil
}
@@ -158,7 +160,7 @@ func (o *Order) cheapExpr(n ir.Node) ir.Node {
// as assigning to the original n.
//
// The intended use is to apply to x when rewriting x += y into x = x + y.
-func (o *Order) safeExpr(n ir.Node) ir.Node {
+func (o *orderState) safeExpr(n ir.Node) ir.Node {
switch n.Op() {
case ir.ONAME, ir.OLITERAL, ir.ONIL:
return n
@@ -241,15 +243,15 @@ func isaddrokay(n ir.Node) bool {
// tmp = n, and then returns tmp.
// The result of addrTemp MUST be assigned back to n, e.g.
// n.Left = o.addrTemp(n.Left)
-func (o *Order) addrTemp(n ir.Node) ir.Node {
+func (o *orderState) addrTemp(n ir.Node) ir.Node {
if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL {
// TODO: expand this to all static composite literal nodes?
n = typecheck.DefaultLit(n, nil)
types.CalcSize(n.Type())
vstat := readonlystaticname(n.Type())
- var s InitSchedule
- s.staticassign(vstat, 0, n, n.Type())
- if s.out != nil {
+ var s staticinit.Schedule
+ s.StaticAssign(vstat, 0, n, n.Type())
+ if s.Out != nil {
base.Fatalf("staticassign of const generated code: %+v", n)
}
vstat = typecheck.Expr(vstat).(*ir.Name)
@@ -263,7 +265,7 @@ func (o *Order) addrTemp(n ir.Node) ir.Node {
// mapKeyTemp prepares n to be a key in a map runtime call and returns n.
// It should only be used for map runtime calls which have *_fast* versions.
-func (o *Order) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
+func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
// Most map calls need to take the address of the key.
// Exception: map*_fast* calls. See golang.org/issue/19015.
if mapfast(t) == mapslow {
@@ -318,13 +320,13 @@ func mapKeyReplaceStrConv(n ir.Node) bool {
type ordermarker int
// markTemp returns the top of the temporary variable stack.
-func (o *Order) markTemp() ordermarker {
+func (o *orderState) markTemp() ordermarker {
return ordermarker(len(o.temp))
}
// popTemp pops temporaries off the stack until reaching the mark,
// which must have been returned by markTemp.
-func (o *Order) popTemp(mark ordermarker) {
+func (o *orderState) popTemp(mark ordermarker) {
for _, n := range o.temp[mark:] {
key := n.Type().LongString()
o.free[key] = append(o.free[key], n)
@@ -335,7 +337,7 @@ func (o *Order) popTemp(mark ordermarker) {
// cleanTempNoPop emits VARKILL instructions to *out
// for each temporary above the mark on the temporary stack.
// It does not pop the temporaries from the stack.
-func (o *Order) cleanTempNoPop(mark ordermarker) []ir.Node {
+func (o *orderState) cleanTempNoPop(mark ordermarker) []ir.Node {
var out []ir.Node
for i := len(o.temp) - 1; i >= int(mark); i-- {
n := o.temp[i]
@@ -346,13 +348,13 @@ func (o *Order) cleanTempNoPop(mark ordermarker) []ir.Node {
// cleanTemp emits VARKILL instructions for each temporary above the
// mark on the temporary stack and removes them from the stack.
-func (o *Order) cleanTemp(top ordermarker) {
+func (o *orderState) cleanTemp(top ordermarker) {
o.out = append(o.out, o.cleanTempNoPop(top)...)
o.popTemp(top)
}
// stmtList orders each of the statements in the list.
-func (o *Order) stmtList(l ir.Nodes) {
+func (o *orderState) stmtList(l ir.Nodes) {
s := l
for i := range s {
orderMakeSliceCopy(s[i:])
@@ -396,14 +398,14 @@ func orderMakeSliceCopy(s []ir.Node) {
}
// edge inserts coverage instrumentation for libfuzzer.
-func (o *Order) edge() {
+func (o *orderState) edge() {
if base.Debug.Libfuzzer == 0 {
return
}
// Create a new uint8 counter to be allocated in section
// __libfuzzer_extra_counters.
- counter := staticname(types.Types[types.TUINT8])
+ counter := staticinit.StaticName(types.Types[types.TUINT8])
counter.Name().SetLibfuzzerExtraCounter(true)
// counter += 1
@@ -415,7 +417,7 @@ func (o *Order) edge() {
// and then replaces the old slice in n with the new slice.
// free is a map that can be used to obtain temporary variables by type.
func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
- var order Order
+ var order orderState
order.free = free
mark := order.markTemp()
order.edge()
@@ -428,8 +430,8 @@ func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
// leaves them as the init list of the final *np.
// The result of exprInPlace MUST be assigned back to n, e.g.
// n.Left = o.exprInPlace(n.Left)
-func (o *Order) exprInPlace(n ir.Node) ir.Node {
- var order Order
+func (o *orderState) exprInPlace(n ir.Node) ir.Node {
+ var order orderState
order.free = o.free
n = order.expr(n, nil)
n = ir.InitExpr(order.out, n)
@@ -446,7 +448,7 @@ func (o *Order) exprInPlace(n ir.Node) ir.Node {
// n.Left = orderStmtInPlace(n.Left)
// free is a map that can be used to obtain temporary variables by type.
func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
- var order Order
+ var order orderState
order.free = free
mark := order.markTemp()
order.stmt(n)
@@ -455,7 +457,7 @@ func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
}
// init moves n's init list to o.out.
-func (o *Order) init(n ir.Node) {
+func (o *orderState) init(n ir.Node) {
if ir.MayBeShared(n) {
// For concurrency safety, don't mutate potentially shared nodes.
// First, ensure that no work is required here.
@@ -470,7 +472,7 @@ func (o *Order) init(n ir.Node) {
// call orders the call expression n.
// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
-func (o *Order) call(nn ir.Node) {
+func (o *orderState) call(nn ir.Node) {
if len(nn.Init()) > 0 {
// Caller should have already called o.init(nn).
base.Fatalf("%v with unexpected ninit", nn.Op())
@@ -551,7 +553,7 @@ func (o *Order) call(nn ir.Node) {
// cases they are also typically registerizable, so not much harm done.
// And this only applies to the multiple-assignment form.
// We could do a more precise analysis if needed, like in walk.go.
-func (o *Order) mapAssign(n ir.Node) {
+func (o *orderState) mapAssign(n ir.Node) {
switch n.Op() {
default:
base.Fatalf("order.mapAssign %v", n.Op())
@@ -596,7 +598,7 @@ func (o *Order) mapAssign(n ir.Node) {
}
}
-func (o *Order) safeMapRHS(r ir.Node) ir.Node {
+func (o *orderState) safeMapRHS(r ir.Node) ir.Node {
// Make sure we evaluate the RHS before starting the map insert.
// We need to make sure the RHS won't panic. See issue 22881.
if r.Op() == ir.OAPPEND {
@@ -613,7 +615,7 @@ func (o *Order) safeMapRHS(r ir.Node) ir.Node {
// stmt orders the statement n, appending to o.out.
// Temporaries created during the statement are cleaned
// up using VARKILL instructions as possible.
-func (o *Order) stmt(n ir.Node) {
+func (o *orderState) stmt(n ir.Node) {
if n == nil {
return
}
@@ -1061,7 +1063,7 @@ func hasDefaultCase(n *ir.SwitchStmt) bool {
}
// exprList orders the expression list l into o.
-func (o *Order) exprList(l ir.Nodes) {
+func (o *orderState) exprList(l ir.Nodes) {
s := l
for i := range s {
s[i] = o.expr(s[i], nil)
@@ -1070,14 +1072,14 @@ func (o *Order) exprList(l ir.Nodes) {
// exprListInPlace orders the expression list l but saves
// the side effects on the individual expression ninit lists.
-func (o *Order) exprListInPlace(l ir.Nodes) {
+func (o *orderState) exprListInPlace(l ir.Nodes) {
s := l
for i := range s {
s[i] = o.exprInPlace(s[i])
}
}
-func (o *Order) exprNoLHS(n ir.Node) ir.Node {
+func (o *orderState) exprNoLHS(n ir.Node) ir.Node {
return o.expr(n, nil)
}
@@ -1088,7 +1090,7 @@ func (o *Order) exprNoLHS(n ir.Node) ir.Node {
// to avoid copying the result of the expression to a temporary.)
// The result of expr MUST be assigned back to n, e.g.
// n.Left = o.expr(n.Left, lhs)
-func (o *Order) expr(n, lhs ir.Node) ir.Node {
+func (o *orderState) expr(n, lhs ir.Node) ir.Node {
if n == nil {
return n
}
@@ -1098,7 +1100,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node {
return n
}
-func (o *Order) expr1(n, lhs ir.Node) ir.Node {
+func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
o.init(n)
switch n.Op() {
@@ -1441,7 +1443,7 @@ func (o *Order) expr1(n, lhs ir.Node) ir.Node {
// tmp1, tmp2, tmp3 = ...
// a, b, a = tmp1, tmp2, tmp3
// This is necessary to ensure left to right assignment order.
-func (o *Order) as2(n *ir.AssignListStmt) {
+func (o *orderState) as2(n *ir.AssignListStmt) {
tmplist := []ir.Node{}
left := []ir.Node{}
for ni, l := range n.Lhs {
@@ -1463,7 +1465,7 @@ func (o *Order) as2(n *ir.AssignListStmt) {
// okAs2 orders OAS2XXX with ok.
// Just like as2, this also adds temporaries to ensure left-to-right assignment.
-func (o *Order) okAs2(n *ir.AssignListStmt) {
+func (o *orderState) okAs2(n *ir.AssignListStmt) {
var tmp1, tmp2 ir.Node
if !ir.IsBlank(n.Lhs[0]) {
typ := n.Rhs[0].Type()
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/walk/race.go
index c52bf1479b..1fe439a99a 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/walk/race.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
"cmd/compile/internal/base"
diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/walk/range.go
index 2b2178a8bd..ea23761a39 100644
--- a/src/cmd/compile/internal/gc/range.go
+++ b/src/cmd/compile/internal/walk/range.go
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
+ "unicode/utf8"
+
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
@@ -12,7 +14,6 @@ import (
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/sys"
- "unicode/utf8"
)
func cheapComputableIndex(width int64) bool {
diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/walk/select.go
index 51bb1e5355..006833eb7b 100644
--- a/src/cmd/compile/internal/gc/select.go
+++ b/src/cmd/compile/internal/walk/select.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
"cmd/compile/internal/base"
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/walk/sinit.go
index 337b67af46..dbb17dfe50 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/walk/sinit.go
@@ -2,358 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
- "cmd/compile/internal/reflectdata"
"cmd/compile/internal/staticdata"
+ "cmd/compile/internal/staticinit"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
- "fmt"
)
-type InitEntry struct {
- Xoffset int64 // struct, array only
- Expr ir.Node // bytes of run-time computed expressions
-}
-
-type InitPlan struct {
- E []InitEntry
-}
-
-// An InitSchedule is used to decompose assignment statements into
-// static and dynamic initialization parts. Static initializations are
-// handled by populating variables' linker symbol data, while dynamic
-// initializations are accumulated to be executed in order.
-type InitSchedule struct {
- // out is the ordered list of dynamic initialization
- // statements.
- out []ir.Node
-
- initplans map[ir.Node]*InitPlan
- inittemps map[ir.Node]*ir.Name
-}
-
-func (s *InitSchedule) append(n ir.Node) {
- s.out = append(s.out, n)
-}
-
-// staticInit adds an initialization statement n to the schedule.
-func (s *InitSchedule) staticInit(n ir.Node) {
- if !s.tryStaticInit(n) {
- if base.Flag.Percent != 0 {
- ir.Dump("nonstatic", n)
- }
- s.append(n)
- }
-}
-
-// tryStaticInit attempts to statically execute an initialization
-// statement and reports whether it succeeded.
-func (s *InitSchedule) tryStaticInit(nn ir.Node) bool {
- // Only worry about simple "l = r" assignments. Multiple
- // variable/expression OAS2 assignments have already been
- // replaced by multiple simple OAS assignments, and the other
- // OAS2* assignments mostly necessitate dynamic execution
- // anyway.
- if nn.Op() != ir.OAS {
- return false
- }
- n := nn.(*ir.AssignStmt)
- if ir.IsBlank(n.X) && !anySideEffects(n.Y) {
- // Discard.
- return true
- }
- lno := ir.SetPos(n)
- defer func() { base.Pos = lno }()
- nam := n.X.(*ir.Name)
- return s.staticassign(nam, 0, n.Y, nam.Type())
-}
-
-// like staticassign but we are copying an already
-// initialized value r.
-func (s *InitSchedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool {
- if rn.Class_ == ir.PFUNC {
- // TODO if roff != 0 { panic }
- staticdata.InitFunc(l, loff, rn)
- return true
- }
- if rn.Class_ != ir.PEXTERN || rn.Sym().Pkg != types.LocalPkg {
- return false
- }
- if rn.Defn == nil { // probably zeroed but perhaps supplied externally and of unknown value
- return false
- }
- if rn.Defn.Op() != ir.OAS {
- return false
- }
- if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675)
- return false
- }
- orig := rn
- r := rn.Defn.(*ir.AssignStmt).Y
-
- for r.Op() == ir.OCONVNOP && !types.Identical(r.Type(), typ) {
- r = r.(*ir.ConvExpr).X
- }
-
- switch r.Op() {
- case ir.OMETHEXPR:
- r = r.(*ir.MethodExpr).FuncName()
- fallthrough
- case ir.ONAME:
- r := r.(*ir.Name)
- if s.staticcopy(l, loff, r, typ) {
- return true
- }
- // We may have skipped past one or more OCONVNOPs, so
- // use conv to ensure r is assignable to l (#13263).
- dst := ir.Node(l)
- if loff != 0 || !types.Identical(typ, l.Type()) {
- dst = ir.NewNameOffsetExpr(base.Pos, l, loff, typ)
- }
- s.append(ir.NewAssignStmt(base.Pos, dst, typecheck.Conv(r, typ)))
- return true
-
- case ir.ONIL:
- return true
-
- case ir.OLITERAL:
- if ir.IsZero(r) {
- return true
- }
- litsym(l, loff, r, int(typ.Width))
- return true
-
- case ir.OADDR:
- r := r.(*ir.AddrExpr)
- if a := r.X; a.Op() == ir.ONAME {
- a := a.(*ir.Name)
- staticdata.InitAddr(l, loff, a, 0)
- return true
- }
-
- case ir.OPTRLIT:
- r := r.(*ir.AddrExpr)
- switch r.X.Op() {
- case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT, ir.OMAPLIT:
- // copy pointer
- staticdata.InitAddr(l, loff, s.inittemps[r], 0)
- return true
- }
-
- case ir.OSLICELIT:
- r := r.(*ir.CompLitExpr)
- // copy slice
- staticdata.InitSlice(l, loff, s.inittemps[r], r.Len)
- return true
-
- case ir.OARRAYLIT, ir.OSTRUCTLIT:
- r := r.(*ir.CompLitExpr)
- p := s.initplans[r]
- for i := range p.E {
- e := &p.E[i]
- typ := e.Expr.Type()
- if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
- litsym(l, loff+e.Xoffset, e.Expr, int(typ.Width))
- continue
- }
- x := e.Expr
- if x.Op() == ir.OMETHEXPR {
- x = x.(*ir.MethodExpr).FuncName()
- }
- if x.Op() == ir.ONAME && s.staticcopy(l, loff+e.Xoffset, x.(*ir.Name), typ) {
- continue
- }
- // Requires computation, but we're
- // copying someone else's computation.
- ll := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, typ)
- rr := ir.NewNameOffsetExpr(base.Pos, orig, e.Xoffset, typ)
- ir.SetPos(rr)
- s.append(ir.NewAssignStmt(base.Pos, ll, rr))
- }
-
- return true
- }
-
- return false
-}
-
-func (s *InitSchedule) staticassign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool {
- for r.Op() == ir.OCONVNOP {
- r = r.(*ir.ConvExpr).X
- }
-
- switch r.Op() {
- case ir.ONAME:
- r := r.(*ir.Name)
- return s.staticcopy(l, loff, r, typ)
-
- case ir.OMETHEXPR:
- r := r.(*ir.MethodExpr)
- return s.staticcopy(l, loff, r.FuncName(), typ)
-
- case ir.ONIL:
- return true
-
- case ir.OLITERAL:
- if ir.IsZero(r) {
- return true
- }
- litsym(l, loff, r, int(typ.Width))
- return true
-
- case ir.OADDR:
- r := r.(*ir.AddrExpr)
- if name, offset, ok := stataddr(r.X); ok {
- staticdata.InitAddr(l, loff, name, offset)
- return true
- }
- fallthrough
-
- case ir.OPTRLIT:
- r := r.(*ir.AddrExpr)
- switch r.X.Op() {
- case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
- // Init pointer.
- a := staticname(r.X.Type())
-
- s.inittemps[r] = a
- staticdata.InitAddr(l, loff, a, 0)
-
- // Init underlying literal.
- if !s.staticassign(a, 0, r.X, a.Type()) {
- s.append(ir.NewAssignStmt(base.Pos, a, r.X))
- }
- return true
- }
- //dump("not static ptrlit", r);
-
- case ir.OSTR2BYTES:
- r := r.(*ir.ConvExpr)
- if l.Class_ == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
- sval := ir.StringVal(r.X)
- staticdata.InitSliceBytes(l, loff, sval)
- return true
- }
-
- case ir.OSLICELIT:
- r := r.(*ir.CompLitExpr)
- s.initplan(r)
- // Init slice.
- ta := types.NewArray(r.Type().Elem(), r.Len)
- ta.SetNoalg(true)
- a := staticname(ta)
- s.inittemps[r] = a
- staticdata.InitSlice(l, loff, a, r.Len)
- // Fall through to init underlying array.
- l = a
- loff = 0
- fallthrough
-
- case ir.OARRAYLIT, ir.OSTRUCTLIT:
- r := r.(*ir.CompLitExpr)
- s.initplan(r)
-
- p := s.initplans[r]
- for i := range p.E {
- e := &p.E[i]
- if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
- litsym(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Width))
- continue
- }
- ir.SetPos(e.Expr)
- if !s.staticassign(l, loff+e.Xoffset, e.Expr, e.Expr.Type()) {
- a := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, e.Expr.Type())
- s.append(ir.NewAssignStmt(base.Pos, a, e.Expr))
- }
- }
-
- return true
-
- case ir.OMAPLIT:
- break
-
- case ir.OCLOSURE:
- r := r.(*ir.ClosureExpr)
- if hasemptycvars(r) {
- if base.Debug.Closure > 0 {
- base.WarnfAt(r.Pos(), "closure converted to global")
- }
- // Closures with no captured variables are globals,
- // so the assignment can be done at link time.
- // TODO if roff != 0 { panic }
- staticdata.InitFunc(l, loff, r.Func.Nname)
- return true
- }
- closuredebugruntimecheck(r)
-
- case ir.OCONVIFACE:
- // This logic is mirrored in isStaticCompositeLiteral.
- // If you change something here, change it there, and vice versa.
-
- // Determine the underlying concrete type and value we are converting from.
- r := r.(*ir.ConvExpr)
- val := ir.Node(r)
- for val.Op() == ir.OCONVIFACE {
- val = val.(*ir.ConvExpr).X
- }
-
- if val.Type().IsInterface() {
- // val is an interface type.
- // If val is nil, we can statically initialize l;
- // both words are zero and so there no work to do, so report success.
- // If val is non-nil, we have no concrete type to record,
- // and we won't be able to statically initialize its value, so report failure.
- return val.Op() == ir.ONIL
- }
-
- markTypeUsedInInterface(val.Type(), l.Sym().Linksym())
-
- var itab *ir.AddrExpr
- if typ.IsEmptyInterface() {
- itab = reflectdata.TypePtr(val.Type())
- } else {
- itab = reflectdata.ITabAddr(val.Type(), typ)
- }
-
- // Create a copy of l to modify while we emit data.
-
- // Emit itab, advance offset.
- staticdata.InitAddr(l, loff, itab.X.(*ir.Name), 0)
-
- // Emit data.
- if types.IsDirectIface(val.Type()) {
- if val.Op() == ir.ONIL {
- // Nil is zero, nothing to do.
- return true
- }
- // Copy val directly into n.
- ir.SetPos(val)
- if !s.staticassign(l, loff+int64(types.PtrSize), val, val.Type()) {
- a := ir.NewNameOffsetExpr(base.Pos, l, loff+int64(types.PtrSize), val.Type())
- s.append(ir.NewAssignStmt(base.Pos, a, val))
- }
- } else {
- // Construct temp to hold val, write pointer to temp into n.
- a := staticname(val.Type())
- s.inittemps[val] = a
- if !s.staticassign(a, 0, val, val.Type()) {
- s.append(ir.NewAssignStmt(base.Pos, a, val))
- }
- staticdata.InitAddr(l, loff+int64(types.PtrSize), a, 0)
- }
-
- return true
- }
-
- //dump("not static", r);
- return false
-}
-
// initContext is the context in which static data is populated.
// It is either in an init function or in any other function.
// Static data populated in an init function will be written either
@@ -378,29 +38,9 @@ func (c initContext) String() string {
return "inNonInitFunction"
}
-// from here down is the walk analysis
-// of composite literals.
-// most of the work is to generate
-// data statements for the constant
-// part of the composite literal.
-
-var statuniqgen int // name generator for static temps
-
-// staticname returns a name backed by a (writable) static data symbol.
-// Use readonlystaticname for read-only node.
-func staticname(t *types.Type) *ir.Name {
- // Don't use lookupN; it interns the resulting string, but these are all unique.
- n := typecheck.NewName(typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen)))
- statuniqgen++
- typecheck.Declare(n, ir.PEXTERN)
- n.SetType(t)
- n.Sym().Linksym().Set(obj.AttrLocal, true)
- return n
-}
-
// readonlystaticname returns a name backed by a (writable) static data symbol.
func readonlystaticname(t *types.Type) *ir.Name {
- n := staticname(t)
+ n := staticinit.StaticName(t)
n.MarkReadonly()
n.Sym().Linksym().Set(obj.AttrContentAddressable, true)
return n
@@ -572,7 +212,7 @@ func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node,
for _, r := range n.List {
a, value := splitnode(r)
- if a == ir.BlankNode && !anySideEffects(value) {
+ if a == ir.BlankNode && !staticinit.AnySideEffects(value) {
// Discard.
continue
}
@@ -629,14 +269,14 @@ func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes)
if ctxt == inNonInitFunction {
// put everything into static array
- vstat := staticname(t)
+ vstat := staticinit.StaticName(t)
fixedlit(ctxt, initKindStatic, n, vstat, init)
fixedlit(ctxt, initKindDynamic, n, vstat, init)
// copy static to slice
var_ = typecheck.AssignExpr(var_)
- name, offset, ok := stataddr(var_)
+ name, offset, ok := staticinit.StaticLoc(var_)
if !ok || name.Class_ != ir.PEXTERN {
base.Fatalf("slicelit: %v", var_)
}
@@ -672,7 +312,7 @@ func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes)
if ctxt == inInitFunction {
vstat = readonlystaticname(t)
} else {
- vstat = staticname(t)
+ vstat = staticinit.StaticName(t)
}
fixedlit(ctxt, initKindStatic, n, vstat, init)
}
@@ -993,150 +633,19 @@ func oaslit(n *ir.AssignStmt, init *ir.Nodes) bool {
return true
}
-func getlit(lit ir.Node) int {
- if ir.IsSmallIntConst(lit) {
- return int(ir.Int64Val(lit))
- }
- return -1
-}
-
-// stataddr returns the static address of n, if n has one, or else nil.
-func stataddr(n ir.Node) (name *ir.Name, offset int64, ok bool) {
- if n == nil {
- return nil, 0, false
- }
-
- switch n.Op() {
- case ir.ONAME:
- n := n.(*ir.Name)
- return n, 0, true
-
- case ir.OMETHEXPR:
- n := n.(*ir.MethodExpr)
- return stataddr(n.FuncName())
-
- case ir.ODOT:
- n := n.(*ir.SelectorExpr)
- if name, offset, ok = stataddr(n.X); !ok {
- break
- }
- offset += n.Offset
- return name, offset, true
-
- case ir.OINDEX:
- n := n.(*ir.IndexExpr)
- if n.X.Type().IsSlice() {
- break
- }
- if name, offset, ok = stataddr(n.X); !ok {
- break
- }
- l := getlit(n.Index)
- if l < 0 {
- break
- }
-
- // Check for overflow.
- if n.Type().Width != 0 && types.MaxWidth/n.Type().Width <= int64(l) {
- break
- }
- offset += int64(l) * n.Type().Width
- return name, offset, true
- }
-
- return nil, 0, false
-}
-
-func (s *InitSchedule) initplan(n ir.Node) {
- if s.initplans[n] != nil {
- return
- }
- p := new(InitPlan)
- s.initplans[n] = p
- switch n.Op() {
- default:
- base.Fatalf("initplan")
-
- case ir.OARRAYLIT, ir.OSLICELIT:
- n := n.(*ir.CompLitExpr)
- var k int64
- for _, a := range n.List {
- if a.Op() == ir.OKEY {
- kv := a.(*ir.KeyExpr)
- k = typecheck.IndexConst(kv.Key)
- if k < 0 {
- base.Fatalf("initplan arraylit: invalid index %v", kv.Key)
- }
- a = kv.Value
- }
- s.addvalue(p, k*n.Type().Elem().Width, a)
- k++
- }
-
- case ir.OSTRUCTLIT:
- n := n.(*ir.CompLitExpr)
- for _, a := range n.List {
- if a.Op() != ir.OSTRUCTKEY {
- base.Fatalf("initplan structlit")
- }
- a := a.(*ir.StructKeyExpr)
- if a.Field.IsBlank() {
- continue
- }
- s.addvalue(p, a.Offset, a.Value)
- }
-
- case ir.OMAPLIT:
- n := n.(*ir.CompLitExpr)
- for _, a := range n.List {
- if a.Op() != ir.OKEY {
- base.Fatalf("initplan maplit")
- }
- a := a.(*ir.KeyExpr)
- s.addvalue(p, -1, a.Value)
- }
- }
-}
-
-func (s *InitSchedule) addvalue(p *InitPlan, xoffset int64, n ir.Node) {
- // special case: zero can be dropped entirely
- if ir.IsZero(n) {
- return
- }
-
- // special case: inline struct and array (not slice) literals
- if isvaluelit(n) {
- s.initplan(n)
- q := s.initplans[n]
- for _, qe := range q.E {
- // qe is a copy; we are not modifying entries in q.E
- qe.Xoffset += xoffset
- p.E = append(p.E, qe)
- }
- return
- }
-
- // add to plan
- p.E = append(p.E, InitEntry{Xoffset: xoffset, Expr: n})
-}
-
-func isvaluelit(n ir.Node) bool {
- return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
-}
-
func genAsStatic(as *ir.AssignStmt) {
if as.X.Type() == nil {
base.Fatalf("genAsStatic as.Left not typechecked")
}
- name, offset, ok := stataddr(as.X)
+ name, offset, ok := staticinit.StaticLoc(as.X)
if !ok || (name.Class_ != ir.PEXTERN && as.X != ir.BlankNode) {
base.Fatalf("genAsStatic: lhs %v", as.X)
}
switch r := as.Y; r.Op() {
case ir.OLITERAL:
- litsym(name, offset, r, int(r.Type().Width))
+ staticdata.InitConst(name, offset, r, int(r.Type().Width))
return
case ir.OMETHEXPR:
r := r.(*ir.MethodExpr)
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/walk/subr.go
index 17bbd1c3a2..bc65432d49 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/walk/subr.go
@@ -2,16 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
+ "fmt"
+
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/src"
- "fmt"
)
// backingArrayPtrLen extracts the pointer and length from a slice or string.
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/walk/switch.go
index 9ffa8b67bb..9becd0e404 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/walk/switch.go
@@ -2,17 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
+ "go/constant"
+ "go/token"
+ "sort"
+
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/src"
- "go/constant"
- "go/token"
- "sort"
)
// walkswitch walks a switch statement.
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/walk/walk.go
index f86dbba2c9..cb3018a4ac 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/walk/walk.go
@@ -2,9 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package walk
import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "strings"
+
"cmd/compile/internal/base"
"cmd/compile/internal/escape"
"cmd/compile/internal/ir"
@@ -17,19 +24,13 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
- "encoding/binary"
- "errors"
- "fmt"
- "go/constant"
- "go/token"
- "strings"
)
// The constant is known to runtime.
const tmpstringbufsize = 32
const zeroValSize = 1024 // must match value of runtime/map.go:maxZero
-func walk(fn *ir.Func) {
+func Walk(fn *ir.Func) {
ir.CurFunc = fn
errorsBefore := base.Errors()
order(fn)
@@ -670,7 +671,7 @@ func walkexpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.CallExpr)
if n.Op() == ir.OCALLINTER {
usemethod(n)
- markUsedIfaceMethod(n)
+ reflectdata.MarkUsedIfaceMethod(n)
}
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.OCLOSURE {
@@ -933,7 +934,7 @@ func walkexpr1(n ir.Node, init *ir.Nodes) ir.Node {
toType := n.Type()
if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) { // skip unnamed functions (func _())
- markTypeUsedInInterface(fromType, ir.CurFunc.LSym)
+ reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym)
}
// typeword generates the type word of the interface value.
@@ -1708,32 +1709,6 @@ func walkexpr1(n ir.Node, init *ir.Nodes) ir.Node {
// in the presence of type assertions.
}
-// markTypeUsedInInterface marks that type t is converted to an interface.
-// This information is used in the linker in dead method elimination.
-func markTypeUsedInInterface(t *types.Type, from *obj.LSym) {
- tsym := reflectdata.TypeSym(t).Linksym()
- // Emit a marker relocation. The linker will know the type is converted
- // to an interface if "from" is reachable.
- r := obj.Addrel(from)
- r.Sym = tsym
- r.Type = objabi.R_USEIFACE
-}
-
-// markUsedIfaceMethod marks that an interface method is used in the current
-// function. n is OCALLINTER node.
-func markUsedIfaceMethod(n *ir.CallExpr) {
- dot := n.X.(*ir.SelectorExpr)
- ityp := dot.X.Type()
- tsym := reflectdata.TypeSym(ityp).Linksym()
- r := obj.Addrel(ir.CurFunc.LSym)
- r.Sym = tsym
- // dot.Xoffset is the method index * Widthptr (the offset of code pointer
- // in itab).
- midx := dot.Offset / int64(types.PtrSize)
- r.Add = reflectdata.InterfaceMethodOffset(ityp, midx)
- r.Type = objabi.R_USEIFACEMETHOD
-}
-
// rtconvfn returns the parameter and result types that will be used by a
// runtime function to convert from type src to type dst. The runtime function
// name can be derived from the names of the returned types.
@@ -3737,94 +3712,6 @@ func usefield(n *ir.SelectorExpr) {
ir.CurFunc.FieldTrack[sym] = struct{}{}
}
-// anySideEffects reports whether n contains any operations that could have observable side effects.
-func anySideEffects(n ir.Node) bool {
- return ir.Any(n, func(n ir.Node) bool {
- switch n.Op() {
- // Assume side effects unless we know otherwise.
- default:
- return true
-
- // No side effects here (arguments are checked separately).
- case ir.ONAME,
- ir.ONONAME,
- ir.OTYPE,
- ir.OPACK,
- ir.OLITERAL,
- ir.ONIL,
- ir.OADD,
- ir.OSUB,
- ir.OOR,
- ir.OXOR,
- ir.OADDSTR,
- ir.OADDR,
- ir.OANDAND,
- ir.OBYTES2STR,
- ir.ORUNES2STR,
- ir.OSTR2BYTES,
- ir.OSTR2RUNES,
- ir.OCAP,
- ir.OCOMPLIT,
- ir.OMAPLIT,
- ir.OSTRUCTLIT,
- ir.OARRAYLIT,
- ir.OSLICELIT,
- ir.OPTRLIT,
- ir.OCONV,
- ir.OCONVIFACE,
- ir.OCONVNOP,
- ir.ODOT,
- ir.OEQ,
- ir.ONE,
- ir.OLT,
- ir.OLE,
- ir.OGT,
- ir.OGE,
- ir.OKEY,
- ir.OSTRUCTKEY,
- ir.OLEN,
- ir.OMUL,
- ir.OLSH,
- ir.ORSH,
- ir.OAND,
- ir.OANDNOT,
- ir.ONEW,
- ir.ONOT,
- ir.OBITNOT,
- ir.OPLUS,
- ir.ONEG,
- ir.OOROR,
- ir.OPAREN,
- ir.ORUNESTR,
- ir.OREAL,
- ir.OIMAG,
- ir.OCOMPLEX:
- return false
-
- // Only possible side effect is division by zero.
- case ir.ODIV, ir.OMOD:
- n := n.(*ir.BinaryExpr)
- if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 {
- return true
- }
-
- // Only possible side effect is panic on invalid size,
- // but many makechan and makemap use size zero, which is definitely OK.
- case ir.OMAKECHAN, ir.OMAKEMAP:
- n := n.(*ir.MakeExpr)
- if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 {
- return true
- }
-
- // Only possible side effect is panic on invalid size.
- // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
- case ir.OMAKESLICE, ir.OMAKESLICECOPY:
- return true
- }
- return false
- })
-}
-
// Rewrite
// go builtin(x, y, z)
// into