mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: delete register maps, completely
Remove go115ReduceLiveness feature gating flag, along with code that only needed when go115ReduceLiveness is false. Change-Id: I7571913cc74cbd17b330a0ee0160fefc9eeee66e Reviewed-on: https://go-review.googlesource.com/c/go/+/264338 Trust: Cherry Zhang <cherryyz@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
89a6540d8a
commit
84d7a85089
8 changed files with 88 additions and 413 deletions
|
|
@ -105,10 +105,8 @@ var knownFormats = map[string]string{
|
||||||
"cmd/compile/internal/ssa.GCNode %v": "",
|
"cmd/compile/internal/ssa.GCNode %v": "",
|
||||||
"cmd/compile/internal/ssa.ID %d": "",
|
"cmd/compile/internal/ssa.ID %d": "",
|
||||||
"cmd/compile/internal/ssa.ID %v": "",
|
"cmd/compile/internal/ssa.ID %v": "",
|
||||||
"cmd/compile/internal/ssa.LocPair %s": "",
|
|
||||||
"cmd/compile/internal/ssa.LocalSlot %s": "",
|
"cmd/compile/internal/ssa.LocalSlot %s": "",
|
||||||
"cmd/compile/internal/ssa.LocalSlot %v": "",
|
"cmd/compile/internal/ssa.LocalSlot %v": "",
|
||||||
"cmd/compile/internal/ssa.Location %T": "",
|
|
||||||
"cmd/compile/internal/ssa.Location %s": "",
|
"cmd/compile/internal/ssa.Location %s": "",
|
||||||
"cmd/compile/internal/ssa.Op %s": "",
|
"cmd/compile/internal/ssa.Op %s": "",
|
||||||
"cmd/compile/internal/ssa.Op %v": "",
|
"cmd/compile/internal/ssa.Op %v": "",
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,8 @@ func newProgs(fn *Node, worker int) *Progs {
|
||||||
pp.pos = fn.Pos
|
pp.pos = fn.Pos
|
||||||
pp.settext(fn)
|
pp.settext(fn)
|
||||||
// PCDATA tables implicitly start with index -1.
|
// PCDATA tables implicitly start with index -1.
|
||||||
pp.prevLive = LivenessIndex{-1, -1, false}
|
pp.prevLive = LivenessIndex{-1, false}
|
||||||
if go115ReduceLiveness {
|
pp.nextLive = pp.prevLive
|
||||||
pp.nextLive = pp.prevLive
|
|
||||||
} else {
|
|
||||||
pp.nextLive = LivenessInvalid
|
|
||||||
}
|
|
||||||
return pp
|
return pp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,31 +116,15 @@ func (pp *Progs) Prog(as obj.As) *obj.Prog {
|
||||||
Addrconst(&p.From, objabi.PCDATA_StackMapIndex)
|
Addrconst(&p.From, objabi.PCDATA_StackMapIndex)
|
||||||
Addrconst(&p.To, int64(idx))
|
Addrconst(&p.To, int64(idx))
|
||||||
}
|
}
|
||||||
if !go115ReduceLiveness {
|
if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint {
|
||||||
|
// Emit unsafe-point marker.
|
||||||
|
pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint
|
||||||
|
p := pp.Prog(obj.APCDATA)
|
||||||
|
Addrconst(&p.From, objabi.PCDATA_UnsafePoint)
|
||||||
if pp.nextLive.isUnsafePoint {
|
if pp.nextLive.isUnsafePoint {
|
||||||
// Unsafe points are encoded as a special value in the
|
Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe)
|
||||||
// register map.
|
} else {
|
||||||
pp.nextLive.regMapIndex = objabi.PCDATA_RegMapUnsafe
|
Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe)
|
||||||
}
|
|
||||||
if pp.nextLive.regMapIndex != pp.prevLive.regMapIndex {
|
|
||||||
// Emit register map index change.
|
|
||||||
idx := pp.nextLive.regMapIndex
|
|
||||||
pp.prevLive.regMapIndex = idx
|
|
||||||
p := pp.Prog(obj.APCDATA)
|
|
||||||
Addrconst(&p.From, objabi.PCDATA_RegMapIndex)
|
|
||||||
Addrconst(&p.To, int64(idx))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint {
|
|
||||||
// Emit unsafe-point marker.
|
|
||||||
pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint
|
|
||||||
p := pp.Prog(obj.APCDATA)
|
|
||||||
Addrconst(&p.From, objabi.PCDATA_UnsafePoint)
|
|
||||||
if pp.nextLive.isUnsafePoint {
|
|
||||||
Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe)
|
|
||||||
} else {
|
|
||||||
Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ func addGCLocals() {
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals, fn.GCRegs} {
|
for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} {
|
||||||
if gcsym != nil && !gcsym.OnList() {
|
if gcsym != nil && !gcsym.OnList() {
|
||||||
ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
|
ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,16 +24,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// go115ReduceLiveness disables register maps and only produces stack
|
|
||||||
// maps at call sites.
|
|
||||||
//
|
|
||||||
// In Go 1.15, we changed debug call injection to use conservative
|
|
||||||
// scanning instead of precise pointer maps, so these are no longer
|
|
||||||
// necessary.
|
|
||||||
//
|
|
||||||
// Keep in sync with runtime/preempt.go:go115ReduceLiveness.
|
|
||||||
const go115ReduceLiveness = true
|
|
||||||
|
|
||||||
// OpVarDef is an annotation for the liveness analysis, marking a place
|
// OpVarDef is an annotation for the liveness analysis, marking a place
|
||||||
// where a complete initialization (definition) of a variable begins.
|
// where a complete initialization (definition) of a variable begins.
|
||||||
// Since the liveness analysis can see initialization of single-word
|
// Since the liveness analysis can see initialization of single-word
|
||||||
|
|
@ -96,15 +86,15 @@ type BlockEffects struct {
|
||||||
//
|
//
|
||||||
// uevar: upward exposed variables (used before set in block)
|
// uevar: upward exposed variables (used before set in block)
|
||||||
// varkill: killed variables (set in block)
|
// varkill: killed variables (set in block)
|
||||||
uevar varRegVec
|
uevar bvec
|
||||||
varkill varRegVec
|
varkill bvec
|
||||||
|
|
||||||
// Computed during Liveness.solve using control flow information:
|
// Computed during Liveness.solve using control flow information:
|
||||||
//
|
//
|
||||||
// livein: variables live at block entry
|
// livein: variables live at block entry
|
||||||
// liveout: variables live at block exit
|
// liveout: variables live at block exit
|
||||||
livein varRegVec
|
livein bvec
|
||||||
liveout varRegVec
|
liveout bvec
|
||||||
}
|
}
|
||||||
|
|
||||||
// A collection of global state used by liveness analysis.
|
// A collection of global state used by liveness analysis.
|
||||||
|
|
@ -128,16 +118,14 @@ type Liveness struct {
|
||||||
// current Block during Liveness.epilogue. Indexed in Value
|
// current Block during Liveness.epilogue. Indexed in Value
|
||||||
// order for that block. Additionally, for the entry block
|
// order for that block. Additionally, for the entry block
|
||||||
// livevars[0] is the entry bitmap. Liveness.compact moves
|
// livevars[0] is the entry bitmap. Liveness.compact moves
|
||||||
// these to stackMaps and regMaps.
|
// these to stackMaps.
|
||||||
livevars []varRegVec
|
livevars []bvec
|
||||||
|
|
||||||
// livenessMap maps from safe points (i.e., CALLs) to their
|
// livenessMap maps from safe points (i.e., CALLs) to their
|
||||||
// liveness map indexes.
|
// liveness map indexes.
|
||||||
livenessMap LivenessMap
|
livenessMap LivenessMap
|
||||||
stackMapSet bvecSet
|
stackMapSet bvecSet
|
||||||
stackMaps []bvec
|
stackMaps []bvec
|
||||||
regMapSet map[liveRegMask]int
|
|
||||||
regMaps []liveRegMask
|
|
||||||
|
|
||||||
cache progeffectscache
|
cache progeffectscache
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +146,7 @@ func (m *LivenessMap) reset() {
|
||||||
delete(m.vals, k)
|
delete(m.vals, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.deferreturn = LivenessInvalid
|
m.deferreturn = LivenessDontCare
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
|
func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
|
||||||
|
|
@ -166,27 +154,17 @@ func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m LivenessMap) Get(v *ssa.Value) LivenessIndex {
|
func (m LivenessMap) Get(v *ssa.Value) LivenessIndex {
|
||||||
if !go115ReduceLiveness {
|
|
||||||
// All safe-points are in the map, so if v isn't in
|
|
||||||
// the map, it's an unsafe-point.
|
|
||||||
if idx, ok := m.vals[v.ID]; ok {
|
|
||||||
return idx
|
|
||||||
}
|
|
||||||
return LivenessInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
// If v isn't in the map, then it's a "don't care" and not an
|
// If v isn't in the map, then it's a "don't care" and not an
|
||||||
// unsafe-point.
|
// unsafe-point.
|
||||||
if idx, ok := m.vals[v.ID]; ok {
|
if idx, ok := m.vals[v.ID]; ok {
|
||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
return LivenessIndex{StackMapDontCare, StackMapDontCare, false}
|
return LivenessIndex{StackMapDontCare, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LivenessIndex stores the liveness map information for a Value.
|
// LivenessIndex stores the liveness map information for a Value.
|
||||||
type LivenessIndex struct {
|
type LivenessIndex struct {
|
||||||
stackMapIndex int
|
stackMapIndex int
|
||||||
regMapIndex int // only for !go115ReduceLiveness
|
|
||||||
|
|
||||||
// isUnsafePoint indicates that this is an unsafe-point.
|
// isUnsafePoint indicates that this is an unsafe-point.
|
||||||
//
|
//
|
||||||
|
|
@ -197,8 +175,10 @@ type LivenessIndex struct {
|
||||||
isUnsafePoint bool
|
isUnsafePoint bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// LivenessInvalid indicates an unsafe point with no stack map.
|
// LivenessDontCare indicates that the liveness information doesn't
|
||||||
var LivenessInvalid = LivenessIndex{StackMapDontCare, StackMapDontCare, true} // only for !go115ReduceLiveness
|
// matter. Currently it is used in deferreturn liveness when we don't
|
||||||
|
// actually need it. It should never be emitted to the PCDATA stream.
|
||||||
|
var LivenessDontCare = LivenessIndex{StackMapDontCare, true}
|
||||||
|
|
||||||
// StackMapDontCare indicates that the stack map index at a Value
|
// StackMapDontCare indicates that the stack map index at a Value
|
||||||
// doesn't matter.
|
// doesn't matter.
|
||||||
|
|
@ -212,46 +192,12 @@ func (idx LivenessIndex) StackMapValid() bool {
|
||||||
return idx.stackMapIndex != StackMapDontCare
|
return idx.stackMapIndex != StackMapDontCare
|
||||||
}
|
}
|
||||||
|
|
||||||
func (idx LivenessIndex) RegMapValid() bool {
|
|
||||||
return idx.regMapIndex != StackMapDontCare
|
|
||||||
}
|
|
||||||
|
|
||||||
type progeffectscache struct {
|
type progeffectscache struct {
|
||||||
retuevar []int32
|
retuevar []int32
|
||||||
tailuevar []int32
|
tailuevar []int32
|
||||||
initialized bool
|
initialized bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// varRegVec contains liveness bitmaps for variables and registers.
|
|
||||||
type varRegVec struct {
|
|
||||||
vars bvec
|
|
||||||
regs liveRegMask
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *varRegVec) Eq(v2 varRegVec) bool {
|
|
||||||
return v.vars.Eq(v2.vars) && v.regs == v2.regs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *varRegVec) Copy(v2 varRegVec) {
|
|
||||||
v.vars.Copy(v2.vars)
|
|
||||||
v.regs = v2.regs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *varRegVec) Clear() {
|
|
||||||
v.vars.Clear()
|
|
||||||
v.regs = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *varRegVec) Or(v1, v2 varRegVec) {
|
|
||||||
v.vars.Or(v1.vars, v2.vars)
|
|
||||||
v.regs = v1.regs | v2.regs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *varRegVec) AndNot(v1, v2 varRegVec) {
|
|
||||||
v.vars.AndNot(v1.vars, v2.vars)
|
|
||||||
v.regs = v1.regs &^ v2.regs
|
|
||||||
}
|
|
||||||
|
|
||||||
// livenessShouldTrack reports whether the liveness analysis
|
// livenessShouldTrack reports whether the liveness analysis
|
||||||
// should track the variable n.
|
// should track the variable n.
|
||||||
// We don't care about variables that have no pointers,
|
// We don't care about variables that have no pointers,
|
||||||
|
|
@ -400,110 +346,6 @@ func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// regEffects returns the registers affected by v.
|
|
||||||
func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
|
|
||||||
if go115ReduceLiveness {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
if v.Op == ssa.OpPhi {
|
|
||||||
// All phi node arguments must come from the same
|
|
||||||
// register and the result must also go to that
|
|
||||||
// register, so there's no overall effect.
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
addLocs := func(mask liveRegMask, v *ssa.Value, ptrOnly bool) liveRegMask {
|
|
||||||
if int(v.ID) >= len(lv.f.RegAlloc) {
|
|
||||||
// v has no allocated registers.
|
|
||||||
return mask
|
|
||||||
}
|
|
||||||
loc := lv.f.RegAlloc[v.ID]
|
|
||||||
if loc == nil {
|
|
||||||
// v has no allocated registers.
|
|
||||||
return mask
|
|
||||||
}
|
|
||||||
if v.Op == ssa.OpGetG {
|
|
||||||
// GetG represents the G register, which is a
|
|
||||||
// pointer, but not a valid GC register. The
|
|
||||||
// current G is always reachable, so it's okay
|
|
||||||
// to ignore this register.
|
|
||||||
return mask
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect registers and types from v's location.
|
|
||||||
var regs [2]*ssa.Register
|
|
||||||
nreg := 0
|
|
||||||
switch loc := loc.(type) {
|
|
||||||
case ssa.LocalSlot:
|
|
||||||
return mask
|
|
||||||
case *ssa.Register:
|
|
||||||
if ptrOnly && !v.Type.HasPointers() {
|
|
||||||
return mask
|
|
||||||
}
|
|
||||||
regs[0] = loc
|
|
||||||
nreg = 1
|
|
||||||
case ssa.LocPair:
|
|
||||||
// The value will have TTUPLE type, and the
|
|
||||||
// children are nil or *ssa.Register.
|
|
||||||
if v.Type.Etype != types.TTUPLE {
|
|
||||||
v.Fatalf("location pair %s has non-tuple type %v", loc, v.Type)
|
|
||||||
}
|
|
||||||
for i, loc1 := range &loc {
|
|
||||||
if loc1 == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ptrOnly && !v.Type.FieldType(i).HasPointers() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
regs[nreg] = loc1.(*ssa.Register)
|
|
||||||
nreg++
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
v.Fatalf("weird RegAlloc location: %s (%T)", loc, loc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add register locations to vars.
|
|
||||||
for _, reg := range regs[:nreg] {
|
|
||||||
if reg.GCNum() == -1 {
|
|
||||||
if ptrOnly {
|
|
||||||
v.Fatalf("pointer in non-pointer register %v", reg)
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mask |= 1 << uint(reg.GCNum())
|
|
||||||
}
|
|
||||||
return mask
|
|
||||||
}
|
|
||||||
|
|
||||||
// v clobbers all registers it writes to (whether or not the
|
|
||||||
// write is pointer-typed).
|
|
||||||
kill = addLocs(0, v, false)
|
|
||||||
for _, arg := range v.Args {
|
|
||||||
// v uses all registers is reads from, but we only
|
|
||||||
// care about marking those containing pointers.
|
|
||||||
uevar = addLocs(uevar, arg, true)
|
|
||||||
}
|
|
||||||
return uevar, kill
|
|
||||||
}
|
|
||||||
|
|
||||||
type liveRegMask uint32 // only if !go115ReduceLiveness
|
|
||||||
|
|
||||||
func (m liveRegMask) niceString(config *ssa.Config) string {
|
|
||||||
if m == 0 {
|
|
||||||
return "<none>"
|
|
||||||
}
|
|
||||||
str := ""
|
|
||||||
for i, reg := range config.GCRegMap {
|
|
||||||
if m&(1<<uint(i)) != 0 {
|
|
||||||
if str != "" {
|
|
||||||
str += ","
|
|
||||||
}
|
|
||||||
str += reg.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
type livenessFuncCache struct {
|
type livenessFuncCache struct {
|
||||||
be []BlockEffects
|
be []BlockEffects
|
||||||
livenessMap LivenessMap
|
livenessMap LivenessMap
|
||||||
|
|
@ -519,8 +361,6 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
|
||||||
vars: vars,
|
vars: vars,
|
||||||
idx: idx,
|
idx: idx,
|
||||||
stkptrsize: stkptrsize,
|
stkptrsize: stkptrsize,
|
||||||
|
|
||||||
regMapSet: make(map[liveRegMask]int),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Significant sources of allocation are kept in the ssa.Cache
|
// Significant sources of allocation are kept in the ssa.Cache
|
||||||
|
|
@ -533,7 +373,7 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
|
||||||
if cap(lc.be) >= f.NumBlocks() {
|
if cap(lc.be) >= f.NumBlocks() {
|
||||||
lv.be = lc.be[:f.NumBlocks()]
|
lv.be = lc.be[:f.NumBlocks()]
|
||||||
}
|
}
|
||||||
lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessInvalid}
|
lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessDontCare}
|
||||||
lc.livenessMap.vals = nil
|
lc.livenessMap.vals = nil
|
||||||
}
|
}
|
||||||
if lv.be == nil {
|
if lv.be == nil {
|
||||||
|
|
@ -546,10 +386,10 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
|
||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
be := lv.blockEffects(b)
|
be := lv.blockEffects(b)
|
||||||
|
|
||||||
be.uevar = varRegVec{vars: bulk.next()}
|
be.uevar = bulk.next()
|
||||||
be.varkill = varRegVec{vars: bulk.next()}
|
be.varkill = bulk.next()
|
||||||
be.livein = varRegVec{vars: bulk.next()}
|
be.livein = bulk.next()
|
||||||
be.liveout = varRegVec{vars: bulk.next()}
|
be.liveout = bulk.next()
|
||||||
}
|
}
|
||||||
lv.livenessMap.reset()
|
lv.livenessMap.reset()
|
||||||
|
|
||||||
|
|
@ -637,20 +477,6 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// usedRegs returns the maximum width of the live register map.
|
|
||||||
func (lv *Liveness) usedRegs() int32 {
|
|
||||||
var any liveRegMask
|
|
||||||
for _, live := range lv.regMaps {
|
|
||||||
any |= live
|
|
||||||
}
|
|
||||||
i := int32(0)
|
|
||||||
for any != 0 {
|
|
||||||
any >>= 1
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates live pointer value maps for arguments and local variables. The
|
// Generates live pointer value maps for arguments and local variables. The
|
||||||
// this argument and the in arguments are always assumed live. The vars
|
// this argument and the in arguments are always assumed live. The vars
|
||||||
// argument is a slice of *Nodes.
|
// argument is a slice of *Nodes.
|
||||||
|
|
@ -851,31 +677,16 @@ func (lv *Liveness) markUnsafePoints() {
|
||||||
// particular, call Values can have a stack map in case the callee
|
// particular, call Values can have a stack map in case the callee
|
||||||
// grows the stack, but not themselves be a safe-point.
|
// grows the stack, but not themselves be a safe-point.
|
||||||
func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
|
func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
|
||||||
// The runtime only has safe-points in function prologues, so
|
if !v.Op.IsCall() {
|
||||||
// we only need stack maps at call sites. go:nosplit functions
|
|
||||||
// are similar.
|
|
||||||
if go115ReduceLiveness || compiling_runtime || lv.f.NoSplit {
|
|
||||||
if !v.Op.IsCall() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// typedmemclr and typedmemmove are write barriers and
|
|
||||||
// deeply non-preemptible. They are unsafe points and
|
|
||||||
// hence should not have liveness maps.
|
|
||||||
if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Op {
|
|
||||||
case ssa.OpInitMem, ssa.OpArg, ssa.OpSP, ssa.OpSB,
|
|
||||||
ssa.OpSelect0, ssa.OpSelect1, ssa.OpGetG,
|
|
||||||
ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive,
|
|
||||||
ssa.OpPhi:
|
|
||||||
// These don't produce code (see genssa).
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return !lv.unsafePoints.Get(int32(v.ID))
|
// typedmemclr and typedmemmove are write barriers and
|
||||||
|
// deeply non-preemptible. They are unsafe points and
|
||||||
|
// hence should not have liveness maps.
|
||||||
|
if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializes the sets for solving the live variables. Visits all the
|
// Initializes the sets for solving the live variables. Visits all the
|
||||||
|
|
@ -891,17 +702,13 @@ func (lv *Liveness) prologue() {
|
||||||
// effects with the each prog effects.
|
// effects with the each prog effects.
|
||||||
for j := len(b.Values) - 1; j >= 0; j-- {
|
for j := len(b.Values) - 1; j >= 0; j-- {
|
||||||
pos, e := lv.valueEffects(b.Values[j])
|
pos, e := lv.valueEffects(b.Values[j])
|
||||||
regUevar, regKill := lv.regEffects(b.Values[j])
|
|
||||||
if e&varkill != 0 {
|
if e&varkill != 0 {
|
||||||
be.varkill.vars.Set(pos)
|
be.varkill.Set(pos)
|
||||||
be.uevar.vars.Unset(pos)
|
be.uevar.Unset(pos)
|
||||||
}
|
}
|
||||||
be.varkill.regs |= regKill
|
|
||||||
be.uevar.regs &^= regKill
|
|
||||||
if e&uevar != 0 {
|
if e&uevar != 0 {
|
||||||
be.uevar.vars.Set(pos)
|
be.uevar.Set(pos)
|
||||||
}
|
}
|
||||||
be.uevar.regs |= regUevar
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -911,8 +718,8 @@ func (lv *Liveness) solve() {
|
||||||
// These temporary bitvectors exist to avoid successive allocations and
|
// These temporary bitvectors exist to avoid successive allocations and
|
||||||
// frees within the loop.
|
// frees within the loop.
|
||||||
nvars := int32(len(lv.vars))
|
nvars := int32(len(lv.vars))
|
||||||
newlivein := varRegVec{vars: bvalloc(nvars)}
|
newlivein := bvalloc(nvars)
|
||||||
newliveout := varRegVec{vars: bvalloc(nvars)}
|
newliveout := bvalloc(nvars)
|
||||||
|
|
||||||
// Walk blocks in postorder ordering. This improves convergence.
|
// Walk blocks in postorder ordering. This improves convergence.
|
||||||
po := lv.f.Postorder()
|
po := lv.f.Postorder()
|
||||||
|
|
@ -930,11 +737,11 @@ func (lv *Liveness) solve() {
|
||||||
switch b.Kind {
|
switch b.Kind {
|
||||||
case ssa.BlockRet:
|
case ssa.BlockRet:
|
||||||
for _, pos := range lv.cache.retuevar {
|
for _, pos := range lv.cache.retuevar {
|
||||||
newliveout.vars.Set(pos)
|
newliveout.Set(pos)
|
||||||
}
|
}
|
||||||
case ssa.BlockRetJmp:
|
case ssa.BlockRetJmp:
|
||||||
for _, pos := range lv.cache.tailuevar {
|
for _, pos := range lv.cache.tailuevar {
|
||||||
newliveout.vars.Set(pos)
|
newliveout.Set(pos)
|
||||||
}
|
}
|
||||||
case ssa.BlockExit:
|
case ssa.BlockExit:
|
||||||
// panic exit - nothing to do
|
// panic exit - nothing to do
|
||||||
|
|
@ -969,7 +776,7 @@ func (lv *Liveness) solve() {
|
||||||
// variables at each safe point locations.
|
// variables at each safe point locations.
|
||||||
func (lv *Liveness) epilogue() {
|
func (lv *Liveness) epilogue() {
|
||||||
nvars := int32(len(lv.vars))
|
nvars := int32(len(lv.vars))
|
||||||
liveout := varRegVec{vars: bvalloc(nvars)}
|
liveout := bvalloc(nvars)
|
||||||
livedefer := bvalloc(nvars) // always-live variables
|
livedefer := bvalloc(nvars) // always-live variables
|
||||||
|
|
||||||
// If there is a defer (that could recover), then all output
|
// If there is a defer (that could recover), then all output
|
||||||
|
|
@ -1025,12 +832,11 @@ func (lv *Liveness) epilogue() {
|
||||||
{
|
{
|
||||||
// Reserve an entry for function entry.
|
// Reserve an entry for function entry.
|
||||||
live := bvalloc(nvars)
|
live := bvalloc(nvars)
|
||||||
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
lv.livevars = append(lv.livevars, live)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, b := range lv.f.Blocks {
|
for _, b := range lv.f.Blocks {
|
||||||
be := lv.blockEffects(b)
|
be := lv.blockEffects(b)
|
||||||
firstBitmapIndex := len(lv.livevars)
|
|
||||||
|
|
||||||
// Walk forward through the basic block instructions and
|
// Walk forward through the basic block instructions and
|
||||||
// allocate liveness maps for those instructions that need them.
|
// allocate liveness maps for those instructions that need them.
|
||||||
|
|
@ -1040,7 +846,7 @@ func (lv *Liveness) epilogue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
live := bvalloc(nvars)
|
live := bvalloc(nvars)
|
||||||
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
lv.livevars = append(lv.livevars, live)
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk backward, construct maps at each safe point
|
// walk backward, construct maps at each safe point
|
||||||
|
|
@ -1056,21 +862,18 @@ func (lv *Liveness) epilogue() {
|
||||||
|
|
||||||
live := &lv.livevars[index]
|
live := &lv.livevars[index]
|
||||||
live.Or(*live, liveout)
|
live.Or(*live, liveout)
|
||||||
live.vars.Or(live.vars, livedefer) // only for non-entry safe points
|
live.Or(*live, livedefer) // only for non-entry safe points
|
||||||
index--
|
index--
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update liveness information.
|
// Update liveness information.
|
||||||
pos, e := lv.valueEffects(v)
|
pos, e := lv.valueEffects(v)
|
||||||
regUevar, regKill := lv.regEffects(v)
|
|
||||||
if e&varkill != 0 {
|
if e&varkill != 0 {
|
||||||
liveout.vars.Unset(pos)
|
liveout.Unset(pos)
|
||||||
}
|
}
|
||||||
liveout.regs &^= regKill
|
|
||||||
if e&uevar != 0 {
|
if e&uevar != 0 {
|
||||||
liveout.vars.Set(pos)
|
liveout.Set(pos)
|
||||||
}
|
}
|
||||||
liveout.regs |= regUevar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if b == lv.f.Entry {
|
if b == lv.f.Entry {
|
||||||
|
|
@ -1080,7 +883,7 @@ func (lv *Liveness) epilogue() {
|
||||||
|
|
||||||
// Check to make sure only input variables are live.
|
// Check to make sure only input variables are live.
|
||||||
for i, n := range lv.vars {
|
for i, n := range lv.vars {
|
||||||
if !liveout.vars.Get(int32(i)) {
|
if !liveout.Get(int32(i)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if n.Class() == PPARAM {
|
if n.Class() == PPARAM {
|
||||||
|
|
@ -1094,32 +897,16 @@ func (lv *Liveness) epilogue() {
|
||||||
live.Or(*live, liveout)
|
live.Or(*live, liveout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that no registers are live across calls.
|
|
||||||
// For closure calls, the CALLclosure is the last use
|
|
||||||
// of the context register, so it's dead after the call.
|
|
||||||
index = int32(firstBitmapIndex)
|
|
||||||
for _, v := range b.Values {
|
|
||||||
if lv.hasStackMap(v) {
|
|
||||||
live := lv.livevars[index]
|
|
||||||
if v.Op.IsCall() && live.regs != 0 {
|
|
||||||
lv.printDebug()
|
|
||||||
v.Fatalf("%v register %s recorded as live at call", lv.fn.Func.Nname, live.regs.niceString(lv.f.Config))
|
|
||||||
}
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The liveness maps for this block are now complete. Compact them.
|
// The liveness maps for this block are now complete. Compact them.
|
||||||
lv.compact(b)
|
lv.compact(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have an open-coded deferreturn call, make a liveness map for it.
|
// If we have an open-coded deferreturn call, make a liveness map for it.
|
||||||
if lv.fn.Func.OpenCodedDeferDisallowed() {
|
if lv.fn.Func.OpenCodedDeferDisallowed() {
|
||||||
lv.livenessMap.deferreturn = LivenessInvalid
|
lv.livenessMap.deferreturn = LivenessDontCare
|
||||||
} else {
|
} else {
|
||||||
lv.livenessMap.deferreturn = LivenessIndex{
|
lv.livenessMap.deferreturn = LivenessIndex{
|
||||||
stackMapIndex: lv.stackMapSet.add(livedefer),
|
stackMapIndex: lv.stackMapSet.add(livedefer),
|
||||||
regMapIndex: 0, // entry regMap, containing no live registers
|
|
||||||
isUnsafePoint: false,
|
isUnsafePoint: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1136,20 +923,10 @@ func (lv *Liveness) epilogue() {
|
||||||
lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Func.Nname, n)
|
lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Func.Nname, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !go115ReduceLiveness {
|
|
||||||
// Check that no registers are live at function entry.
|
|
||||||
// The context register, if any, comes from a
|
|
||||||
// LoweredGetClosurePtr operation first thing in the function,
|
|
||||||
// so it doesn't appear live at entry.
|
|
||||||
if regs := lv.regMaps[0]; regs != 0 {
|
|
||||||
lv.printDebug()
|
|
||||||
lv.f.Fatalf("%v register %s recorded as live on entry", lv.fn.Func.Nname, regs.niceString(lv.f.Config))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compact coalesces identical bitmaps from lv.livevars into the sets
|
// Compact coalesces identical bitmaps from lv.livevars into the sets
|
||||||
// lv.stackMapSet and lv.regMaps.
|
// lv.stackMapSet.
|
||||||
//
|
//
|
||||||
// Compact clears lv.livevars.
|
// Compact clears lv.livevars.
|
||||||
//
|
//
|
||||||
|
|
@ -1165,45 +942,23 @@ func (lv *Liveness) epilogue() {
|
||||||
// PCDATA tables cost about 100k. So for now we keep using a single index for
|
// PCDATA tables cost about 100k. So for now we keep using a single index for
|
||||||
// both bitmap lists.
|
// both bitmap lists.
|
||||||
func (lv *Liveness) compact(b *ssa.Block) {
|
func (lv *Liveness) compact(b *ssa.Block) {
|
||||||
add := func(live varRegVec, isUnsafePoint bool) LivenessIndex { // only if !go115ReduceLiveness
|
|
||||||
// Deduplicate the stack map.
|
|
||||||
stackIndex := lv.stackMapSet.add(live.vars)
|
|
||||||
// Deduplicate the register map.
|
|
||||||
regIndex, ok := lv.regMapSet[live.regs]
|
|
||||||
if !ok {
|
|
||||||
regIndex = len(lv.regMapSet)
|
|
||||||
lv.regMapSet[live.regs] = regIndex
|
|
||||||
lv.regMaps = append(lv.regMaps, live.regs)
|
|
||||||
}
|
|
||||||
return LivenessIndex{stackIndex, regIndex, isUnsafePoint}
|
|
||||||
}
|
|
||||||
pos := 0
|
pos := 0
|
||||||
if b == lv.f.Entry {
|
if b == lv.f.Entry {
|
||||||
// Handle entry stack map.
|
// Handle entry stack map.
|
||||||
if !go115ReduceLiveness {
|
lv.stackMapSet.add(lv.livevars[0])
|
||||||
add(lv.livevars[0], false)
|
|
||||||
} else {
|
|
||||||
lv.stackMapSet.add(lv.livevars[0].vars)
|
|
||||||
}
|
|
||||||
pos++
|
pos++
|
||||||
}
|
}
|
||||||
for _, v := range b.Values {
|
for _, v := range b.Values {
|
||||||
if go115ReduceLiveness {
|
hasStackMap := lv.hasStackMap(v)
|
||||||
hasStackMap := lv.hasStackMap(v)
|
isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
|
||||||
isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
|
idx := LivenessIndex{StackMapDontCare, isUnsafePoint}
|
||||||
idx := LivenessIndex{StackMapDontCare, StackMapDontCare, isUnsafePoint}
|
if hasStackMap {
|
||||||
if hasStackMap {
|
idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos])
|
||||||
idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos].vars)
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
if hasStackMap || isUnsafePoint {
|
|
||||||
lv.livenessMap.set(v, idx)
|
|
||||||
}
|
|
||||||
} else if lv.hasStackMap(v) {
|
|
||||||
isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
|
|
||||||
lv.livenessMap.set(v, add(lv.livevars[pos], isUnsafePoint))
|
|
||||||
pos++
|
pos++
|
||||||
}
|
}
|
||||||
|
if hasStackMap || isUnsafePoint {
|
||||||
|
lv.livenessMap.set(v, idx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset livevars.
|
// Reset livevars.
|
||||||
|
|
@ -1250,8 +1005,8 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
|
||||||
Warnl(pos, s)
|
Warnl(pos, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lv *Liveness) printbvec(printed bool, name string, live varRegVec) bool {
|
func (lv *Liveness) printbvec(printed bool, name string, live bvec) bool {
|
||||||
if live.vars.IsEmpty() && live.regs == 0 {
|
if live.IsEmpty() {
|
||||||
return printed
|
return printed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1264,19 +1019,18 @@ func (lv *Liveness) printbvec(printed bool, name string, live varRegVec) bool {
|
||||||
|
|
||||||
comma := ""
|
comma := ""
|
||||||
for i, n := range lv.vars {
|
for i, n := range lv.vars {
|
||||||
if !live.vars.Get(int32(i)) {
|
if !live.Get(int32(i)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("%s%s", comma, n.Sym.Name)
|
fmt.Printf("%s%s", comma, n.Sym.Name)
|
||||||
comma = ","
|
comma = ","
|
||||||
}
|
}
|
||||||
fmt.Printf("%s%s", comma, live.regs.niceString(lv.f.Config))
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// printeffect is like printbvec, but for valueEffects and regEffects.
|
// printeffect is like printbvec, but for valueEffects.
|
||||||
func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool, regMask liveRegMask) bool {
|
func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
|
||||||
if !x && regMask == 0 {
|
if !x {
|
||||||
return printed
|
return printed
|
||||||
}
|
}
|
||||||
if !printed {
|
if !printed {
|
||||||
|
|
@ -1288,15 +1042,7 @@ func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool, re
|
||||||
if x {
|
if x {
|
||||||
fmt.Printf("%s", lv.vars[pos].Sym.Name)
|
fmt.Printf("%s", lv.vars[pos].Sym.Name)
|
||||||
}
|
}
|
||||||
for j, reg := range lv.f.Config.GCRegMap {
|
|
||||||
if regMask&(1<<uint(j)) != 0 {
|
|
||||||
if x {
|
|
||||||
fmt.Printf(",")
|
|
||||||
}
|
|
||||||
x = true
|
|
||||||
fmt.Printf("%v", reg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1364,15 +1110,14 @@ func (lv *Liveness) printDebug() {
|
||||||
pcdata := lv.livenessMap.Get(v)
|
pcdata := lv.livenessMap.Get(v)
|
||||||
|
|
||||||
pos, effect := lv.valueEffects(v)
|
pos, effect := lv.valueEffects(v)
|
||||||
regUevar, regKill := lv.regEffects(v)
|
|
||||||
printed = false
|
printed = false
|
||||||
printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0, regUevar)
|
printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0)
|
||||||
printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0, regKill)
|
printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0)
|
||||||
if printed {
|
if printed {
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if pcdata.StackMapValid() || pcdata.RegMapValid() {
|
if pcdata.StackMapValid() {
|
||||||
fmt.Printf("\tlive=")
|
fmt.Printf("\tlive=")
|
||||||
printed = false
|
printed = false
|
||||||
if pcdata.StackMapValid() {
|
if pcdata.StackMapValid() {
|
||||||
|
|
@ -1388,16 +1133,6 @@ func (lv *Liveness) printDebug() {
|
||||||
printed = true
|
printed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pcdata.RegMapValid() { // only if !go115ReduceLiveness
|
|
||||||
regLive := lv.regMaps[pcdata.regMapIndex]
|
|
||||||
if regLive != 0 {
|
|
||||||
if printed {
|
|
||||||
fmt.Printf(",")
|
|
||||||
}
|
|
||||||
fmt.Printf("%s", regLive.niceString(lv.f.Config))
|
|
||||||
printed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1423,7 +1158,7 @@ func (lv *Liveness) printDebug() {
|
||||||
// first word dumped is the total number of bitmaps. The second word is the
|
// first word dumped is the total number of bitmaps. The second word is the
|
||||||
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
|
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
|
||||||
// remaining bytes are the raw bitmaps.
|
// remaining bytes are the raw bitmaps.
|
||||||
func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
|
func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
|
||||||
// Size args bitmaps to be just large enough to hold the largest pointer.
|
// Size args bitmaps to be just large enough to hold the largest pointer.
|
||||||
// First, find the largest Xoffset node we care about.
|
// First, find the largest Xoffset node we care about.
|
||||||
// (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.)
|
// (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.)
|
||||||
|
|
@ -1452,7 +1187,7 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
|
||||||
maxLocals := lv.stkptrsize
|
maxLocals := lv.stkptrsize
|
||||||
|
|
||||||
// Temporary symbols for encoding bitmaps.
|
// Temporary symbols for encoding bitmaps.
|
||||||
var argsSymTmp, liveSymTmp, regsSymTmp obj.LSym
|
var argsSymTmp, liveSymTmp obj.LSym
|
||||||
|
|
||||||
args := bvalloc(int32(maxArgs / int64(Widthptr)))
|
args := bvalloc(int32(maxArgs / int64(Widthptr)))
|
||||||
aoff := duint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
aoff := duint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
||||||
|
|
@ -1472,24 +1207,6 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
|
||||||
loff = dbvec(&liveSymTmp, loff, locals)
|
loff = dbvec(&liveSymTmp, loff, locals)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !go115ReduceLiveness {
|
|
||||||
regs := bvalloc(lv.usedRegs())
|
|
||||||
roff := duint32(®sSymTmp, 0, uint32(len(lv.regMaps))) // number of bitmaps
|
|
||||||
roff = duint32(®sSymTmp, roff, uint32(regs.n)) // number of bits in each bitmap
|
|
||||||
if regs.n > 32 {
|
|
||||||
// Our uint32 conversion below won't work.
|
|
||||||
Fatalf("GP registers overflow uint32")
|
|
||||||
}
|
|
||||||
|
|
||||||
if regs.n > 0 {
|
|
||||||
for _, live := range lv.regMaps {
|
|
||||||
regs.Clear()
|
|
||||||
regs.b[0] = uint32(live)
|
|
||||||
roff = dbvec(®sSymTmp, roff, regs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give these LSyms content-addressable names,
|
// Give these LSyms content-addressable names,
|
||||||
// so that they can be de-duplicated.
|
// so that they can be de-duplicated.
|
||||||
// This provides significant binary size savings.
|
// This provides significant binary size savings.
|
||||||
|
|
@ -1502,11 +1219,7 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
|
||||||
lsym.Set(obj.AttrContentAddressable, true)
|
lsym.Set(obj.AttrContentAddressable, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if !go115ReduceLiveness {
|
return makeSym(&argsSymTmp), makeSym(&liveSymTmp)
|
||||||
return makeSym(&argsSymTmp), makeSym(&liveSymTmp), makeSym(®sSymTmp)
|
|
||||||
}
|
|
||||||
// TODO(go115ReduceLiveness): Remove regsSym result
|
|
||||||
return makeSym(&argsSymTmp), makeSym(&liveSymTmp), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry pointer for liveness analysis. Solves for the liveness of
|
// Entry pointer for liveness analysis. Solves for the liveness of
|
||||||
|
|
@ -1553,7 +1266,7 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap {
|
||||||
// Emit the live pointer map data structures
|
// Emit the live pointer map data structures
|
||||||
ls := e.curfn.Func.lsym
|
ls := e.curfn.Func.lsym
|
||||||
fninfo := ls.Func()
|
fninfo := ls.Func()
|
||||||
fninfo.GCArgs, fninfo.GCLocals, fninfo.GCRegs = lv.emit()
|
fninfo.GCArgs, fninfo.GCLocals = lv.emit()
|
||||||
|
|
||||||
p := pp.Prog(obj.AFUNCDATA)
|
p := pp.Prog(obj.AFUNCDATA)
|
||||||
Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps)
|
Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps)
|
||||||
|
|
@ -1567,14 +1280,6 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap {
|
||||||
p.To.Name = obj.NAME_EXTERN
|
p.To.Name = obj.NAME_EXTERN
|
||||||
p.To.Sym = fninfo.GCLocals
|
p.To.Sym = fninfo.GCLocals
|
||||||
|
|
||||||
if !go115ReduceLiveness {
|
|
||||||
p = pp.Prog(obj.AFUNCDATA)
|
|
||||||
Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps)
|
|
||||||
p.To.Type = obj.TYPE_MEM
|
|
||||||
p.To.Name = obj.NAME_EXTERN
|
|
||||||
p.To.Sym = fninfo.GCRegs
|
|
||||||
}
|
|
||||||
|
|
||||||
return lv.livenessMap
|
return lv.livenessMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6265,7 +6265,7 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||||
// instruction. We won't use the actual liveness map on a
|
// instruction. We won't use the actual liveness map on a
|
||||||
// control instruction. Just mark it something that is
|
// control instruction. Just mark it something that is
|
||||||
// preemptible, unless this function is "all unsafe".
|
// preemptible, unless this function is "all unsafe".
|
||||||
s.pp.nextLive = LivenessIndex{-1, -1, allUnsafe(f)}
|
s.pp.nextLive = LivenessIndex{-1, allUnsafe(f)}
|
||||||
|
|
||||||
// Emit values in block
|
// Emit values in block
|
||||||
thearch.SSAMarkMoves(&s, b)
|
thearch.SSAMarkMoves(&s, b)
|
||||||
|
|
|
||||||
|
|
@ -460,7 +460,6 @@ type FuncInfo struct {
|
||||||
|
|
||||||
GCArgs *LSym
|
GCArgs *LSym
|
||||||
GCLocals *LSym
|
GCLocals *LSym
|
||||||
GCRegs *LSym // Only if !go115ReduceLiveness
|
|
||||||
StackObjects *LSym
|
StackObjects *LSym
|
||||||
OpenCodedDeferInfo *LSym
|
OpenCodedDeferInfo *LSym
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
|
||||||
// Prog generated.
|
// Prog generated.
|
||||||
func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
|
func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
|
||||||
pcdata := ctxt.EmitEntryStackMap(s, p, newprog)
|
pcdata := ctxt.EmitEntryStackMap(s, p, newprog)
|
||||||
pcdata = ctxt.EmitEntryRegMap(s, pcdata, newprog)
|
pcdata = ctxt.EmitEntryUnsafePoint(s, pcdata, newprog)
|
||||||
return pcdata
|
return pcdata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,13 +195,13 @@ func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
|
||||||
return pcdata
|
return pcdata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similar to EmitEntryLiveness, but just emit register map.
|
// Similar to EmitEntryLiveness, but just emit unsafe point map.
|
||||||
func (ctxt *Link) EmitEntryRegMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
|
func (ctxt *Link) EmitEntryUnsafePoint(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
|
||||||
pcdata := Appendp(p, newprog)
|
pcdata := Appendp(p, newprog)
|
||||||
pcdata.Pos = s.Func().Text.Pos
|
pcdata.Pos = s.Func().Text.Pos
|
||||||
pcdata.As = APCDATA
|
pcdata.As = APCDATA
|
||||||
pcdata.From.Type = TYPE_CONST
|
pcdata.From.Type = TYPE_CONST
|
||||||
pcdata.From.Offset = objabi.PCDATA_RegMapIndex
|
pcdata.From.Offset = objabi.PCDATA_UnsafePoint
|
||||||
pcdata.To.Type = TYPE_CONST
|
pcdata.To.Type = TYPE_CONST
|
||||||
pcdata.To.Offset = -1
|
pcdata.To.Offset = -1
|
||||||
|
|
||||||
|
|
@ -216,9 +216,9 @@ func (ctxt *Link) StartUnsafePoint(p *Prog, newprog ProgAlloc) *Prog {
|
||||||
pcdata := Appendp(p, newprog)
|
pcdata := Appendp(p, newprog)
|
||||||
pcdata.As = APCDATA
|
pcdata.As = APCDATA
|
||||||
pcdata.From.Type = TYPE_CONST
|
pcdata.From.Type = TYPE_CONST
|
||||||
pcdata.From.Offset = objabi.PCDATA_RegMapIndex
|
pcdata.From.Offset = objabi.PCDATA_UnsafePoint
|
||||||
pcdata.To.Type = TYPE_CONST
|
pcdata.To.Type = TYPE_CONST
|
||||||
pcdata.To.Offset = objabi.PCDATA_RegMapUnsafe
|
pcdata.To.Offset = objabi.PCDATA_UnsafePointUnsafe
|
||||||
|
|
||||||
return pcdata
|
return pcdata
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +231,7 @@ func (ctxt *Link) EndUnsafePoint(p *Prog, newprog ProgAlloc, oldval int64) *Prog
|
||||||
pcdata := Appendp(p, newprog)
|
pcdata := Appendp(p, newprog)
|
||||||
pcdata.As = APCDATA
|
pcdata.As = APCDATA
|
||||||
pcdata.From.Type = TYPE_CONST
|
pcdata.From.Type = TYPE_CONST
|
||||||
pcdata.From.Offset = objabi.PCDATA_RegMapIndex
|
pcdata.From.Offset = objabi.PCDATA_UnsafePoint
|
||||||
pcdata.To.Type = TYPE_CONST
|
pcdata.To.Type = TYPE_CONST
|
||||||
pcdata.To.Offset = oldval
|
pcdata.To.Offset = oldval
|
||||||
|
|
||||||
|
|
@ -257,11 +257,11 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is
|
||||||
prevPcdata := int64(-1) // entry PC data value
|
prevPcdata := int64(-1) // entry PC data value
|
||||||
prevRestart := int64(0)
|
prevRestart := int64(0)
|
||||||
for p := prev.Link; p != nil; p, prev = p.Link, p {
|
for p := prev.Link; p != nil; p, prev = p.Link, p {
|
||||||
if p.As == APCDATA && p.From.Offset == objabi.PCDATA_RegMapIndex {
|
if p.As == APCDATA && p.From.Offset == objabi.PCDATA_UnsafePoint {
|
||||||
prevPcdata = p.To.Offset
|
prevPcdata = p.To.Offset
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if prevPcdata == objabi.PCDATA_RegMapUnsafe {
|
if prevPcdata == objabi.PCDATA_UnsafePointUnsafe {
|
||||||
continue // already unsafe
|
continue // already unsafe
|
||||||
}
|
}
|
||||||
if isUnsafePoint(p) {
|
if isUnsafePoint(p) {
|
||||||
|
|
@ -288,7 +288,7 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is
|
||||||
q := Appendp(prev, newprog)
|
q := Appendp(prev, newprog)
|
||||||
q.As = APCDATA
|
q.As = APCDATA
|
||||||
q.From.Type = TYPE_CONST
|
q.From.Type = TYPE_CONST
|
||||||
q.From.Offset = objabi.PCDATA_RegMapIndex
|
q.From.Offset = objabi.PCDATA_UnsafePoint
|
||||||
q.To.Type = TYPE_CONST
|
q.To.Type = TYPE_CONST
|
||||||
q.To.Offset = val
|
q.To.Offset = val
|
||||||
q.Pc = p.Pc
|
q.Pc = p.Pc
|
||||||
|
|
@ -305,7 +305,7 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is
|
||||||
p = Appendp(p, newprog)
|
p = Appendp(p, newprog)
|
||||||
p.As = APCDATA
|
p.As = APCDATA
|
||||||
p.From.Type = TYPE_CONST
|
p.From.Type = TYPE_CONST
|
||||||
p.From.Offset = objabi.PCDATA_RegMapIndex
|
p.From.Offset = objabi.PCDATA_UnsafePoint
|
||||||
p.To.Type = TYPE_CONST
|
p.To.Type = TYPE_CONST
|
||||||
p.To.Offset = prevPcdata
|
p.To.Offset = prevPcdata
|
||||||
p.Pc = p.Link.Pc
|
p.Pc = p.Link.Pc
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,12 @@ package objabi
|
||||||
// ../../../runtime/symtab.go.
|
// ../../../runtime/symtab.go.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PCDATA_RegMapIndex = 0 // if !go115ReduceLiveness
|
PCDATA_UnsafePoint = 0
|
||||||
PCDATA_UnsafePoint = 0 // if go115ReduceLiveness
|
|
||||||
PCDATA_StackMapIndex = 1
|
PCDATA_StackMapIndex = 1
|
||||||
PCDATA_InlTreeIndex = 2
|
PCDATA_InlTreeIndex = 2
|
||||||
|
|
||||||
FUNCDATA_ArgsPointerMaps = 0
|
FUNCDATA_ArgsPointerMaps = 0
|
||||||
FUNCDATA_LocalsPointerMaps = 1
|
FUNCDATA_LocalsPointerMaps = 1
|
||||||
FUNCDATA_RegPointerMaps = 2 // if !go115ReduceLiveness
|
|
||||||
FUNCDATA_StackObjects = 3
|
FUNCDATA_StackObjects = 3
|
||||||
FUNCDATA_InlTree = 4
|
FUNCDATA_InlTree = 4
|
||||||
FUNCDATA_OpenCodedDeferInfo = 5
|
FUNCDATA_OpenCodedDeferInfo = 5
|
||||||
|
|
@ -32,11 +30,6 @@ const (
|
||||||
|
|
||||||
// Special PCDATA values.
|
// Special PCDATA values.
|
||||||
const (
|
const (
|
||||||
// PCDATA_RegMapIndex values.
|
|
||||||
//
|
|
||||||
// Only if !go115ReduceLiveness.
|
|
||||||
PCDATA_RegMapUnsafe = PCDATA_UnsafePointUnsafe // Unsafe for async preemption
|
|
||||||
|
|
||||||
// PCDATA_UnsafePoint values.
|
// PCDATA_UnsafePoint values.
|
||||||
PCDATA_UnsafePointSafe = -1 // Safe for async preemption
|
PCDATA_UnsafePointSafe = -1 // Safe for async preemption
|
||||||
PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption
|
PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue