go/src/cmd/compile/internal/walk/sinit.go
Russ Cox e4895ab4c0 [dev.regabi] cmd/compile: split out package walk [generated]
[git-generate]
cd src/cmd/compile/internal/gc
rf '
	# Late addition to package ir.
	mv closuredebugruntimecheck ClosureDebugRuntimeCheck
	mv hasemptycvars IsTrivialClosure
	mv ClosureDebugRuntimeCheck IsTrivialClosure func.go
	mv func.go cmd/compile/internal/ir

	# Late addition to package reflectdata.
	mv markTypeUsedInInterface MarkTypeUsedInInterface
	mv markUsedIfaceMethod MarkUsedIfaceMethod
	mv MarkTypeUsedInInterface MarkUsedIfaceMethod reflect.go
	mv reflect.go cmd/compile/internal/reflectdata

	# Late addition to package staticdata.
	mv litsym InitConst
	mv InitConst data.go
	mv data.go cmd/compile/internal/staticdata

	# Extract staticinit out of walk into its own package.
	mv InitEntry InitPlan InitSchedule InitSchedule.append InitSchedule.staticInit \
		InitSchedule.tryStaticInit InitSchedule.staticcopy \
		InitSchedule.staticassign InitSchedule.initplan InitSchedule.addvalue \
		statuniqgen staticname stataddr anySideEffects getlit isvaluelit \
		sched.go
	mv InitSchedule.initplans InitSchedule.Plans
	mv InitSchedule.inittemps InitSchedule.Temps
	mv InitSchedule.out InitSchedule.Out
	mv InitSchedule.staticInit InitSchedule.StaticInit
	mv InitSchedule.staticassign InitSchedule.StaticAssign
	mv InitSchedule Schedule
	mv InitPlan Plan
	mv InitEntry Entry
	mv anySideEffects AnySideEffects
	mv staticname StaticName
	mv stataddr StaticLoc
	mv sched.go cmd/compile/internal/staticinit

	# Export API and unexport non-API.
	mv transformclosure Closure
	mv walk Walk

	mv Order orderState

	mv swt.go switch.go
	mv racewalk.go race.go

	mv closure.go order.go range.go select.go switch.go race.go \
		sinit.go subr.go walk.go \
		cmd/compile/internal/walk
'

: # Update format test.
cd ../../
go install cmd/compile/... cmd/internal/archive
go test -u || go test -u
rm -rf ../../../pkg/darwin_amd64/cmd

Change-Id: I11c7a45f74d4a9e963da15c080e1018caaa99c05
Reviewed-on: https://go-review.googlesource.com/c/go/+/279478
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:43 +00:00

665 lines
18 KiB
Go

// 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 walk
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/staticdata"
"cmd/compile/internal/staticinit"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
)
// 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
// zero times (as a readonly, static data symbol) or
// one time (during init function execution).
// Either way, there is no opportunity for races or further modification,
// so the data can be written to a (possibly readonly) data symbol.
// Static data populated in any other function needs to be local to
// that function to allow multiple instances of that function
// to execute concurrently without clobbering each others' data.
type initContext uint8
const (
inInitFunction initContext = iota
inNonInitFunction
)
func (c initContext) String() string {
if c == inInitFunction {
return "inInitFunction"
}
return "inNonInitFunction"
}
// readonlystaticname returns a name backed by a (writable) static data symbol.
func readonlystaticname(t *types.Type) *ir.Name {
n := staticinit.StaticName(t)
n.MarkReadonly()
n.Sym().Linksym().Set(obj.AttrContentAddressable, true)
return n
}
func isSimpleName(nn ir.Node) bool {
if nn.Op() != ir.ONAME {
return false
}
n := nn.(*ir.Name)
return n.Class_ != ir.PAUTOHEAP && n.Class_ != ir.PEXTERN
}
func litas(l ir.Node, r ir.Node, init *ir.Nodes) {
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, l, r))
}
// initGenType is a bitmap indicating the types of generation that will occur for a static value.
type initGenType uint8
const (
initDynamic initGenType = 1 << iota // contains some dynamic values, for which init code will be generated
initConst // contains some constant values, which may be written into data symbols
)
// getdyn calculates the initGenType for n.
// If top is false, getdyn is recursing.
func getdyn(n ir.Node, top bool) initGenType {
switch n.Op() {
default:
if ir.IsConstNode(n) {
return initConst
}
return initDynamic
case ir.OSLICELIT:
n := n.(*ir.CompLitExpr)
if !top {
return initDynamic
}
if n.Len/4 > int64(len(n.List)) {
// <25% of entries have explicit values.
// Very rough estimation, it takes 4 bytes of instructions
// to initialize 1 byte of result. So don't use a static
// initializer if the dynamic initialization code would be
// smaller than the static value.
// See issue 23780.
return initDynamic
}
case ir.OARRAYLIT, ir.OSTRUCTLIT:
}
lit := n.(*ir.CompLitExpr)
var mode initGenType
for _, n1 := range lit.List {
switch n1.Op() {
case ir.OKEY:
n1 = n1.(*ir.KeyExpr).Value
case ir.OSTRUCTKEY:
n1 = n1.(*ir.StructKeyExpr).Value
}
mode |= getdyn(n1, false)
if mode == initDynamic|initConst {
break
}
}
return mode
}
// isStaticCompositeLiteral reports whether n is a compile-time constant.
func isStaticCompositeLiteral(n ir.Node) bool {
switch n.Op() {
case ir.OSLICELIT:
return false
case ir.OARRAYLIT:
n := n.(*ir.CompLitExpr)
for _, r := range n.List {
if r.Op() == ir.OKEY {
r = r.(*ir.KeyExpr).Value
}
if !isStaticCompositeLiteral(r) {
return false
}
}
return true
case ir.OSTRUCTLIT:
n := n.(*ir.CompLitExpr)
for _, r := range n.List {
r := r.(*ir.StructKeyExpr)
if !isStaticCompositeLiteral(r.Value) {
return false
}
}
return true
case ir.OLITERAL, ir.ONIL:
return true
case ir.OCONVIFACE:
// See staticassign's OCONVIFACE case for comments.
n := n.(*ir.ConvExpr)
val := ir.Node(n)
for val.Op() == ir.OCONVIFACE {
val = val.(*ir.ConvExpr).X
}
if val.Type().IsInterface() {
return val.Op() == ir.ONIL
}
if types.IsDirectIface(val.Type()) && val.Op() == ir.ONIL {
return true
}
return isStaticCompositeLiteral(val)
}
return false
}
// initKind is a kind of static initialization: static, dynamic, or local.
// Static initialization represents literals and
// literal components of composite literals.
// Dynamic initialization represents non-literals and
// non-literal components of composite literals.
// LocalCode initialization represents initialization
// that occurs purely in generated code local to the function of use.
// Initialization code is sometimes generated in passes,
// first static then dynamic.
type initKind uint8
const (
initKindStatic initKind = iota + 1
initKindDynamic
initKindLocalCode
)
// fixedlit handles struct, array, and slice literals.
// TODO: expand documentation.
func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) {
isBlank := var_ == ir.BlankNode
var splitnode func(ir.Node) (a ir.Node, value ir.Node)
switch n.Op() {
case ir.OARRAYLIT, ir.OSLICELIT:
var k int64
splitnode = func(r ir.Node) (ir.Node, ir.Node) {
if r.Op() == ir.OKEY {
kv := r.(*ir.KeyExpr)
k = typecheck.IndexConst(kv.Key)
if k < 0 {
base.Fatalf("fixedlit: invalid index %v", kv.Key)
}
r = kv.Value
}
a := ir.NewIndexExpr(base.Pos, var_, ir.NewInt(k))
k++
if isBlank {
return ir.BlankNode, r
}
return a, r
}
case ir.OSTRUCTLIT:
splitnode = func(rn ir.Node) (ir.Node, ir.Node) {
r := rn.(*ir.StructKeyExpr)
if r.Field.IsBlank() || isBlank {
return ir.BlankNode, r.Value
}
ir.SetPos(r)
return ir.NewSelectorExpr(base.Pos, ir.ODOT, var_, r.Field), r.Value
}
default:
base.Fatalf("fixedlit bad op: %v", n.Op())
}
for _, r := range n.List {
a, value := splitnode(r)
if a == ir.BlankNode && !staticinit.AnySideEffects(value) {
// Discard.
continue
}
switch value.Op() {
case ir.OSLICELIT:
value := value.(*ir.CompLitExpr)
if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) {
slicelit(ctxt, value, a, init)
continue
}
case ir.OARRAYLIT, ir.OSTRUCTLIT:
value := value.(*ir.CompLitExpr)
fixedlit(ctxt, kind, value, a, init)
continue
}
islit := ir.IsConstNode(value)
if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
continue
}
// build list of assignments: var[index] = expr
ir.SetPos(a)
as := ir.NewAssignStmt(base.Pos, a, value)
as = typecheck.Stmt(as).(*ir.AssignStmt)
switch kind {
case initKindStatic:
genAsStatic(as)
case initKindDynamic, initKindLocalCode:
a = orderStmtInPlace(as, map[string][]*ir.Name{})
a = walkstmt(a)
init.Append(a)
default:
base.Fatalf("fixedlit: bad kind %d", kind)
}
}
}
func isSmallSliceLit(n *ir.CompLitExpr) bool {
if n.Op() != ir.OSLICELIT {
return false
}
return n.Type().Elem().Width == 0 || n.Len <= ir.MaxSmallArraySize/n.Type().Elem().Width
}
func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) {
// make an array type corresponding the number of elements we have
t := types.NewArray(n.Type().Elem(), n.Len)
types.CalcSize(t)
if ctxt == inNonInitFunction {
// put everything into static array
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 := staticinit.StaticLoc(var_)
if !ok || name.Class_ != ir.PEXTERN {
base.Fatalf("slicelit: %v", var_)
}
staticdata.InitSlice(name, offset, vstat, t.NumElem())
return
}
// recipe for var = []t{...}
// 1. make a static array
// var vstat [...]t
// 2. assign (data statements) the constant part
// vstat = constpart{}
// 3. make an auto pointer to array and allocate heap to it
// var vauto *[...]t = new([...]t)
// 4. copy the static array to the auto array
// *vauto = vstat
// 5. for each dynamic part assign to the array
// vauto[i] = dynamic part
// 6. assign slice of allocated heap to var
// var = vauto[:]
//
// an optimization is done if there is no constant part
// 3. var vauto *[...]t = new([...]t)
// 5. vauto[i] = dynamic part
// 6. var = vauto[:]
// if the literal contains constants,
// make static initialized array (1),(2)
var vstat ir.Node
mode := getdyn(n, true)
if mode&initConst != 0 && !isSmallSliceLit(n) {
if ctxt == inInitFunction {
vstat = readonlystaticname(t)
} else {
vstat = staticinit.StaticName(t)
}
fixedlit(ctxt, initKindStatic, n, vstat, init)
}
// make new auto *array (3 declare)
vauto := typecheck.Temp(types.NewPtr(t))
// set auto to point at new temp or heap (3 assign)
var a ir.Node
if x := n.Prealloc; x != nil {
// temp allocated during order.go for dddarg
if !types.Identical(t, x.Type()) {
panic("dotdotdot base type does not match order's assigned type")
}
if vstat == nil {
a = ir.NewAssignStmt(base.Pos, x, nil)
a = typecheck.Stmt(a)
init.Append(a) // zero new temp
} else {
// Declare that we're about to initialize all of x.
// (Which happens at the *vauto = vstat below.)
init.Append(ir.NewUnaryExpr(base.Pos, ir.OVARDEF, x))
}
a = typecheck.NodAddr(x)
} else if n.Esc() == ir.EscNone {
a = typecheck.Temp(t)
if vstat == nil {
a = ir.NewAssignStmt(base.Pos, typecheck.Temp(t), nil)
a = typecheck.Stmt(a)
init.Append(a) // zero new temp
a = a.(*ir.AssignStmt).X
} else {
init.Append(ir.NewUnaryExpr(base.Pos, ir.OVARDEF, a))
}
a = typecheck.NodAddr(a)
} else {
a = ir.NewUnaryExpr(base.Pos, ir.ONEW, ir.TypeNode(t))
}
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, vauto, a))
if vstat != nil {
// copy static to heap (4)
a = ir.NewStarExpr(base.Pos, vauto)
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, a, vstat))
}
// put dynamics into array (5)
var index int64
for _, value := range n.List {
if value.Op() == ir.OKEY {
kv := value.(*ir.KeyExpr)
index = typecheck.IndexConst(kv.Key)
if index < 0 {
base.Fatalf("slicelit: invalid index %v", kv.Key)
}
value = kv.Value
}
a := ir.NewIndexExpr(base.Pos, vauto, ir.NewInt(index))
a.SetBounded(true)
index++
// TODO need to check bounds?
switch value.Op() {
case ir.OSLICELIT:
break
case ir.OARRAYLIT, ir.OSTRUCTLIT:
value := value.(*ir.CompLitExpr)
k := initKindDynamic
if vstat == nil {
// Generate both static and dynamic initializations.
// See issue #31987.
k = initKindLocalCode
}
fixedlit(ctxt, k, value, a, init)
continue
}
if vstat != nil && ir.IsConstNode(value) { // already set by copy from static value
continue
}
// build list of vauto[c] = expr
ir.SetPos(value)
as := typecheck.Stmt(ir.NewAssignStmt(base.Pos, a, value))
as = orderStmtInPlace(as, map[string][]*ir.Name{})
as = walkstmt(as)
init.Append(as)
}
// make slice out of heap (6)
a = ir.NewAssignStmt(base.Pos, var_, ir.NewSliceExpr(base.Pos, ir.OSLICE, vauto))
a = typecheck.Stmt(a)
a = orderStmtInPlace(a, map[string][]*ir.Name{})
a = walkstmt(a)
init.Append(a)
}
func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) {
// make the map var
a := ir.NewCallExpr(base.Pos, ir.OMAKE, nil, nil)
a.SetEsc(n.Esc())
a.Args = []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(int64(len(n.List)))}
litas(m, a, init)
entries := n.List
// The order pass already removed any dynamic (runtime-computed) entries.
// All remaining entries are static. Double-check that.
for _, r := range entries {
r := r.(*ir.KeyExpr)
if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
base.Fatalf("maplit: entry is not a literal: %v", r)
}
}
if len(entries) > 25 {
// For a large number of entries, put them in an array and loop.
// build types [count]Tindex and [count]Tvalue
tk := types.NewArray(n.Type().Key(), int64(len(entries)))
te := types.NewArray(n.Type().Elem(), int64(len(entries)))
tk.SetNoalg(true)
te.SetNoalg(true)
types.CalcSize(tk)
types.CalcSize(te)
// make and initialize static arrays
vstatk := readonlystaticname(tk)
vstate := readonlystaticname(te)
datak := ir.NewCompLitExpr(base.Pos, ir.OARRAYLIT, nil, nil)
datae := ir.NewCompLitExpr(base.Pos, ir.OARRAYLIT, nil, nil)
for _, r := range entries {
r := r.(*ir.KeyExpr)
datak.List.Append(r.Key)
datae.List.Append(r.Value)
}
fixedlit(inInitFunction, initKindStatic, datak, vstatk, init)
fixedlit(inInitFunction, initKindStatic, datae, vstate, init)
// loop adding structure elements to map
// for i = 0; i < len(vstatk); i++ {
// map[vstatk[i]] = vstate[i]
// }
i := typecheck.Temp(types.Types[types.TINT])
rhs := ir.NewIndexExpr(base.Pos, vstate, i)
rhs.SetBounded(true)
kidx := ir.NewIndexExpr(base.Pos, vstatk, i)
kidx.SetBounded(true)
lhs := ir.NewIndexExpr(base.Pos, m, kidx)
zero := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0))
cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(tk.NumElem()))
incr := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
body := ir.NewAssignStmt(base.Pos, lhs, rhs)
loop := ir.NewForStmt(base.Pos, nil, cond, incr, nil)
loop.Body = []ir.Node{body}
*loop.PtrInit() = []ir.Node{zero}
appendWalkStmt(init, loop)
return
}
// For a small number of entries, just add them directly.
// Build list of var[c] = expr.
// Use temporaries so that mapassign1 can have addressable key, elem.
// TODO(josharian): avoid map key temporaries for mapfast_* assignments with literal keys.
tmpkey := typecheck.Temp(m.Type().Key())
tmpelem := typecheck.Temp(m.Type().Elem())
for _, r := range entries {
r := r.(*ir.KeyExpr)
index, elem := r.Key, r.Value
ir.SetPos(index)
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmpkey, index))
ir.SetPos(elem)
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmpelem, elem))
ir.SetPos(tmpelem)
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, tmpkey), tmpelem))
}
appendWalkStmt(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, tmpkey))
appendWalkStmt(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, tmpelem))
}
func anylit(n ir.Node, var_ ir.Node, init *ir.Nodes) {
t := n.Type()
switch n.Op() {
default:
base.Fatalf("anylit: not lit, op=%v node=%v", n.Op(), n)
case ir.ONAME:
n := n.(*ir.Name)
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, n))
case ir.OMETHEXPR:
n := n.(*ir.MethodExpr)
anylit(n.FuncName(), var_, init)
case ir.OPTRLIT:
n := n.(*ir.AddrExpr)
if !t.IsPtr() {
base.Fatalf("anylit: not ptr")
}
var r ir.Node
if n.Alloc != nil {
// n.Right is stack temporary used as backing store.
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n.Alloc, nil)) // zero backing store, just in case (#18410)
r = typecheck.NodAddr(n.Alloc)
} else {
r = ir.NewUnaryExpr(base.Pos, ir.ONEW, ir.TypeNode(n.X.Type()))
r.SetEsc(n.Esc())
}
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, r))
var_ = ir.NewStarExpr(base.Pos, var_)
var_ = typecheck.AssignExpr(var_)
anylit(n.X, var_, init)
case ir.OSTRUCTLIT, ir.OARRAYLIT:
n := n.(*ir.CompLitExpr)
if !t.IsStruct() && !t.IsArray() {
base.Fatalf("anylit: not struct/array")
}
if isSimpleName(var_) && len(n.List) > 4 {
// lay out static data
vstat := readonlystaticname(t)
ctxt := inInitFunction
if n.Op() == ir.OARRAYLIT {
ctxt = inNonInitFunction
}
fixedlit(ctxt, initKindStatic, n, vstat, init)
// copy static to var
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, vstat))
// add expressions to automatic
fixedlit(inInitFunction, initKindDynamic, n, var_, init)
break
}
var components int64
if n.Op() == ir.OARRAYLIT {
components = t.NumElem()
} else {
components = int64(t.NumFields())
}
// initialization of an array or struct with unspecified components (missing fields or arrays)
if isSimpleName(var_) || int64(len(n.List)) < components {
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, nil))
}
fixedlit(inInitFunction, initKindLocalCode, n, var_, init)
case ir.OSLICELIT:
n := n.(*ir.CompLitExpr)
slicelit(inInitFunction, n, var_, init)
case ir.OMAPLIT:
n := n.(*ir.CompLitExpr)
if !t.IsMap() {
base.Fatalf("anylit: not map")
}
maplit(n, var_, init)
}
}
// oaslit handles special composite literal assignments.
// It returns true if n's effects have been added to init,
// in which case n should be dropped from the program by the caller.
func oaslit(n *ir.AssignStmt, init *ir.Nodes) bool {
if n.X == nil || n.Y == nil {
// not a special composite literal assignment
return false
}
if n.X.Type() == nil || n.Y.Type() == nil {
// not a special composite literal assignment
return false
}
if !isSimpleName(n.X) {
// not a special composite literal assignment
return false
}
if !types.Identical(n.X.Type(), n.Y.Type()) {
// not a special composite literal assignment
return false
}
switch n.Y.Op() {
default:
// not a special composite literal assignment
return false
case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
if refersToCommonName(n.X, n.Y) {
// not a special composite literal assignment
return false
}
anylit(n.Y, n.X, init)
}
return true
}
func genAsStatic(as *ir.AssignStmt) {
if as.X.Type() == nil {
base.Fatalf("genAsStatic as.Left not typechecked")
}
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:
staticdata.InitConst(name, offset, r, int(r.Type().Width))
return
case ir.OMETHEXPR:
r := r.(*ir.MethodExpr)
staticdata.InitFunc(name, offset, r.FuncName())
return
case ir.ONAME:
r := r.(*ir.Name)
if r.Offset_ != 0 {
base.Fatalf("genAsStatic %+v", as)
}
if r.Class_ == ir.PFUNC {
staticdata.InitFunc(name, offset, r)
return
}
}
base.Fatalf("genAsStatic: rhs %v", as.Y)
}