summaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/staticinit/sched.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/staticinit/sched.go')
-rw-r--r--src/cmd/compile/internal/staticinit/sched.go368
1 files changed, 289 insertions, 79 deletions
diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go
index e9b97e6c87..8ad340c046 100644
--- a/src/cmd/compile/internal/staticinit/sched.go
+++ b/src/cmd/compile/internal/staticinit/sched.go
@@ -48,7 +48,7 @@ func (s *Schedule) append(n ir.Node) {
func (s *Schedule) StaticInit(n ir.Node) {
if !s.tryStaticInit(n) {
if base.Flag.Percent != 0 {
- ir.Dump("nonstatic", n)
+ ir.Dump("StaticInit failed", n)
}
s.append(n)
}
@@ -364,9 +364,15 @@ func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Ty
}
return true
+
+ case ir.OINLCALL:
+ r := r.(*ir.InlinedCallExpr)
+ return s.staticAssignInlinedCall(l, loff, r, typ)
}
- //dump("not static", r);
+ if base.Flag.Percent != 0 {
+ ir.Dump("not static", r)
+ }
return false
}
@@ -443,6 +449,163 @@ func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
}
+func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool {
+ // Handle the special case of an inlined call of
+ // a function body with a single return statement,
+ // which turns into a single assignment plus a goto.
+ //
+ // For example code like this:
+ //
+ // type T struct{ x int }
+ // func F(x int) *T { return &T{x} }
+ // var Global = F(400)
+ //
+ // turns into IR like this:
+ //
+ // INLCALL-init
+ // . AS2-init
+ // . . DCL # x.go:18:13
+ // . . . NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
+ // . AS2 Def tc(1) # x.go:18:13
+ // . AS2-Lhs
+ // . . NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
+ // . AS2-Rhs
+ // . . LITERAL-400 int tc(1) # x.go:18:14
+ // . INLMARK Index:1 # +x.go:18:13
+ // INLCALL PTR-*T tc(1) # x.go:18:13
+ // INLCALL-Body
+ // . BLOCK tc(1) # x.go:18:13
+ // . BLOCK-List
+ // . . DCL tc(1) # x.go:18:13
+ // . . . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
+ // . . AS2 tc(1) # x.go:18:13
+ // . . AS2-Lhs
+ // . . . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
+ // . . AS2-Rhs
+ // . . . INLINED RETURN ARGUMENT HERE
+ // . . GOTO p..i1 tc(1) # x.go:18:13
+ // . LABEL p..i1 # x.go:18:13
+ // INLCALL-ReturnVars
+ // . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
+ //
+ // In non-unified IR, the tree is slightly different:
+ // - if there are no arguments to the inlined function,
+ // the INLCALL-init omits the AS2.
+ // - the DCL inside BLOCK is on the AS2's init list,
+ // not its own statement in the top level of the BLOCK.
+ //
+ // If the init values are side-effect-free and each either only
+ // appears once in the function body or is safely repeatable,
+ // then we inline the value expressions into the return argument
+ // and then call StaticAssign to handle that copy.
+ //
+ // This handles simple cases like
+ //
+ // var myError = errors.New("mine")
+ //
+ // where errors.New is
+ //
+ // func New(text string) error {
+ // return &errorString{text}
+ // }
+ //
+ // We could make things more sophisticated but this kind of initializer
+ // is the most important case for us to get right.
+
+ init := call.Init()
+ var as2init *ir.AssignListStmt
+ if len(init) == 2 && init[0].Op() == ir.OAS2 && init[1].Op() == ir.OINLMARK {
+ as2init = init[0].(*ir.AssignListStmt)
+ } else if len(init) == 1 && init[0].Op() == ir.OINLMARK {
+ as2init = new(ir.AssignListStmt)
+ } else {
+ return false
+ }
+ if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL {
+ return false
+ }
+ label := call.Body[1].(*ir.LabelStmt).Label
+ block := call.Body[0].(*ir.BlockStmt)
+ list := block.List
+ var dcl *ir.Decl
+ if len(list) == 3 && list[0].Op() == ir.ODCL {
+ dcl = list[0].(*ir.Decl)
+ list = list[1:]
+ }
+ if len(list) != 2 ||
+ list[0].Op() != ir.OAS2 ||
+ list[1].Op() != ir.OGOTO ||
+ list[1].(*ir.BranchStmt).Label != label {
+ return false
+ }
+ as2body := list[0].(*ir.AssignListStmt)
+ if dcl == nil {
+ ainit := as2body.Init()
+ if len(ainit) != 1 || ainit[0].Op() != ir.ODCL {
+ return false
+ }
+ dcl = ainit[0].(*ir.Decl)
+ }
+ if len(as2body.Lhs) != 1 || as2body.Lhs[0] != dcl.X {
+ return false
+ }
+
+ // Can't remove the parameter variables if an address is taken.
+ for _, v := range as2init.Lhs {
+ if v.(*ir.Name).Addrtaken() {
+ return false
+ }
+ }
+ // Can't move the computation of the args if they have side effects.
+ for _, r := range as2init.Rhs {
+ if AnySideEffects(r) {
+ return false
+ }
+ }
+
+ // Can only substitute arg for param if param is used
+ // at most once or is repeatable.
+ count := make(map[*ir.Name]int)
+ for _, x := range as2init.Lhs {
+ count[x.(*ir.Name)] = 0
+ }
+ ir.Visit(as2body.Rhs[0], func(n ir.Node) {
+ if name, ok := n.(*ir.Name); ok {
+ if c, ok := count[name]; ok {
+ count[name] = c + 1
+ }
+ }
+ })
+ for name, c := range count {
+ if c > 1 {
+ // Check whether corresponding initializer can be repeated.
+ // Something like 1 can be; make(chan int) or &T{} cannot,
+ // because they need to evaluate to the same result in each use.
+ for i, n := range as2init.Lhs {
+ if n == name && !canRepeat(as2init.Rhs[i]) {
+ return false
+ }
+ }
+ }
+ }
+
+ // Possible static init.
+ // Build tree with args substituted for params and try it.
+ args := make(map[*ir.Name]ir.Node)
+ for i, v := range as2init.Lhs {
+ args[v.(*ir.Name)] = as2init.Rhs[i]
+ }
+ r := subst(as2body.Rhs[0], args)
+ ok := s.StaticAssign(l, loff, r, typ)
+
+ if ok && base.Flag.Percent != 0 {
+ ir.Dump("static inlined-LEFT", l)
+ ir.Dump("static inlined-ORIG", call)
+ ir.Dump("static inlined-RIGHT", r)
+ }
+ return ok
+}
+
// from here down is the walk analysis
// of composite literals.
// most of the work is to generate
@@ -510,91 +673,118 @@ func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
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:
+func isSideEffect(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.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
+ }
- // No side effects here (arguments are checked separately).
- case ir.ONAME,
- ir.ONONAME,
- ir.OTYPE,
- 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 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 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.
+ // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
+ case ir.OMAKESLICE, ir.OMAKESLICECOPY:
+ return true
+ }
+ return false
+}
- // 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
- }
+// AnySideEffects reports whether n contains any operations that could have observable side effects.
+func AnySideEffects(n ir.Node) bool {
+ return ir.Any(n, isSideEffect)
+}
- // Only possible side effect is panic on invalid size.
- // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
- case ir.OMAKESLICE, ir.OMAKESLICECOPY:
+// canRepeat reports whether executing n multiple times has the same effect as
+// assigning n to a single variable and using that variable multiple times.
+func canRepeat(n ir.Node) bool {
+ bad := func(n ir.Node) bool {
+ if isSideEffect(n) {
+ return true
+ }
+ switch n.Op() {
+ case ir.OMAKECHAN,
+ ir.OMAKEMAP,
+ ir.OMAKESLICE,
+ ir.OMAKESLICECOPY,
+ ir.OMAPLIT,
+ ir.ONEW,
+ ir.OPTRLIT,
+ ir.OSLICELIT,
+ ir.OSTR2BYTES,
+ ir.OSTR2RUNES:
return true
}
return false
- })
+ }
+ return !ir.Any(n, bad)
}
func getlit(lit ir.Node) int {
@@ -607,3 +797,23 @@ func getlit(lit ir.Node) int {
func isvaluelit(n ir.Node) bool {
return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
}
+
+func subst(n ir.Node, m map[*ir.Name]ir.Node) ir.Node {
+ var edit func(ir.Node) ir.Node
+ edit = func(x ir.Node) ir.Node {
+ switch x.Op() {
+ case ir.ONAME:
+ x := x.(*ir.Name)
+ if v, ok := m[x]; ok {
+ return ir.DeepCopy(v.Pos(), v)
+ }
+ return x
+ case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE:
+ return x
+ }
+ x = ir.Copy(x)
+ ir.EditChildren(x, edit)
+ return typecheck.EvalConst(x)
+ }
+ return edit(n)
+}