ssa.go (go1.19.src) | : | ssa.go (go1.19.1.src) | ||
---|---|---|---|---|
skipping to change at line 108 | skipping to change at line 108 | |||
ir.Syms.AssertI2I2 = typecheck.LookupRuntimeFunc("assertI2I2") | ir.Syms.AssertI2I2 = typecheck.LookupRuntimeFunc("assertI2I2") | |||
ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignmen t") | ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignmen t") | |||
ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc") | ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc") | |||
ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack") | ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack") | |||
ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn") | ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn") | |||
ir.Syms.Duffcopy = typecheck.LookupRuntimeFunc("duffcopy") | ir.Syms.Duffcopy = typecheck.LookupRuntimeFunc("duffcopy") | |||
ir.Syms.Duffzero = typecheck.LookupRuntimeFunc("duffzero") | ir.Syms.Duffzero = typecheck.LookupRuntimeFunc("duffzero") | |||
ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier") | ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier") | |||
ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded") | ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded") | |||
ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice") | ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice") | |||
ir.Syms.Memmove = typecheck.LookupRuntimeFunc("memmove") | ||||
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread") | ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread") | |||
ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite") | ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite") | |||
ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove") | ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove") | |||
ir.Syms.Asanread = typecheck.LookupRuntimeFunc("asanread") | ir.Syms.Asanread = typecheck.LookupRuntimeFunc("asanread") | |||
ir.Syms.Asanwrite = typecheck.LookupRuntimeFunc("asanwrite") | ir.Syms.Asanwrite = typecheck.LookupRuntimeFunc("asanwrite") | |||
ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject") | ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject") | |||
ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc") | ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc") | |||
ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide") | ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide") | |||
ir.Syms.PanicdottypeE = typecheck.LookupRuntimeFunc("panicdottypeE") | ir.Syms.PanicdottypeE = typecheck.LookupRuntimeFunc("panicdottypeE") | |||
ir.Syms.PanicdottypeI = typecheck.LookupRuntimeFunc("panicdottypeI") | ir.Syms.PanicdottypeI = typecheck.LookupRuntimeFunc("panicdottypeI") | |||
skipping to change at line 1362 | skipping to change at line 1363 | |||
} | } | |||
func (s *state) zero(t *types.Type, dst *ssa.Value) { | func (s *state) zero(t *types.Type, dst *ssa.Value) { | |||
s.instrument(t, dst, instrumentWrite) | s.instrument(t, dst, instrumentWrite) | |||
store := s.newValue2I(ssa.OpZero, types.TypeMem, t.Size(), dst, s.mem()) | store := s.newValue2I(ssa.OpZero, types.TypeMem, t.Size(), dst, s.mem()) | |||
store.Aux = t | store.Aux = t | |||
s.vars[memVar] = store | s.vars[memVar] = store | |||
} | } | |||
func (s *state) move(t *types.Type, dst, src *ssa.Value) { | func (s *state) move(t *types.Type, dst, src *ssa.Value) { | |||
s.moveWhichMayOverlap(t, dst, src, false) | ||||
} | ||||
func (s *state) moveWhichMayOverlap(t *types.Type, dst, src *ssa.Value, mayOverl | ||||
ap bool) { | ||||
s.instrumentMove(t, dst, src) | s.instrumentMove(t, dst, src) | |||
if mayOverlap && t.IsArray() && t.NumElem() > 1 && !ssa.IsInlinableMemmov | ||||
e(dst, src, t.Size(), s.f.Config) { | ||||
// Normally, when moving Go values of type T from one location to | ||||
another, | ||||
// we don't need to worry about partial overlaps. The two Ts must | ||||
either be | ||||
// in disjoint (nonoverlapping) memory or in exactly the same loc | ||||
ation. | ||||
// There are 2 cases where this isn't true: | ||||
// 1) Using unsafe you can arrange partial overlaps. | ||||
// 2) Since Go 1.17, you can use a cast from a slice to a ptr-to | ||||
-array. | ||||
// https://go.dev/ref/spec#Conversions_from_slice_to_array_po | ||||
inter | ||||
// This feature can be used to construct partial overlaps of | ||||
array types. | ||||
// var a [3]int | ||||
// p := (*[2]int)(a[:]) | ||||
// q := (*[2]int)(a[1:]) | ||||
// *p = *q | ||||
// We don't care about solving 1. Or at least, we haven't histori | ||||
cally | ||||
// and no one has complained. | ||||
// For 2, we need to ensure that if there might be partial overla | ||||
p, | ||||
// then we can't use OpMove; we must use memmove instead. | ||||
// (memmove handles partial overlap by copying in the correct | ||||
// direction. OpMove does not.) | ||||
// | ||||
// Note that we have to be careful here not to introduce a call w | ||||
hen | ||||
// we're marshaling arguments to a call or unmarshaling results f | ||||
rom a call. | ||||
// Cases where this is happening must pass mayOverlap to false. | ||||
// (Currently this only happens when unmarshaling results of a ca | ||||
ll.) | ||||
if t.HasPointers() { | ||||
s.rtcall(ir.Syms.Typedmemmove, true, nil, s.reflectType(t | ||||
), dst, src) | ||||
// We would have otherwise implemented this move with str | ||||
aightline code, | ||||
// including a write barrier. Pretend we issue a write ba | ||||
rrier here, | ||||
// so that the write barrier tests work. (Otherwise they' | ||||
d need to know | ||||
// the details of IsInlineableMemmove.) | ||||
s.curfn.SetWBPos(s.peekPos()) | ||||
} else { | ||||
s.rtcall(ir.Syms.Memmove, true, nil, dst, src, s.constInt | ||||
(types.Types[types.TUINTPTR], t.Size())) | ||||
} | ||||
ssa.LogLargeCopy(s.f.Name, s.peekPos(), t.Size()) | ||||
return | ||||
} | ||||
store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.me m()) | store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.me m()) | |||
store.Aux = t | store.Aux = t | |||
s.vars[memVar] = store | s.vars[memVar] = store | |||
} | } | |||
// stmtList converts the statement list n to SSA and adds it to s. | // stmtList converts the statement list n to SSA and adds it to s. | |||
func (s *state) stmtList(l ir.Nodes) { | func (s *state) stmtList(l ir.Nodes) { | |||
for _, n := range l { | for _, n := range l { | |||
s.stmt(n) | s.stmt(n) | |||
} | } | |||
skipping to change at line 1544 | skipping to change at line 1585 | |||
// An x=x assignment. No point in doing anything | // An x=x assignment. No point in doing anything | |||
// here. In addition, skipping this assignment | // here. In addition, skipping this assignment | |||
// prevents generating: | // prevents generating: | |||
// VARDEF x | // VARDEF x | |||
// COPY x -> x | // COPY x -> x | |||
// which is bad because x is incorrectly considered | // which is bad because x is incorrectly considered | |||
// dead before the vardef. See issue #14904. | // dead before the vardef. See issue #14904. | |||
return | return | |||
} | } | |||
// mayOverlap keeps track of whether the LHS and RHS might | ||||
// refer to overlapping memory. | ||||
mayOverlap := true | ||||
if n.Y == nil { | ||||
// Not a move at all, mayOverlap is not relevant. | ||||
} else if n.Def { | ||||
// A variable being defined cannot overlap anything else. | ||||
mayOverlap = false | ||||
} else if n.X.Op() == ir.ONAME && n.Y.Op() == ir.ONAME { | ||||
// Two named things never overlap. | ||||
// (Or they are identical, which we treat as nonoverlappi | ||||
ng.) | ||||
mayOverlap = false | ||||
} else if n.Y.Op() == ir.ODEREF { | ||||
p := n.Y.(*ir.StarExpr).X | ||||
for p.Op() == ir.OCONVNOP { | ||||
p = p.(*ir.ConvExpr).X | ||||
} | ||||
if p.Op() == ir.OSPTR && p.(*ir.UnaryExpr).X.Type().IsStr | ||||
ing() { | ||||
// Pointer fields of strings point to unmodifiabl | ||||
e memory. | ||||
// That memory can't overlap with the memory bein | ||||
g written. | ||||
mayOverlap = false | ||||
} | ||||
} else if n.Y.Op() == ir.ORESULT || n.Y.Op() == ir.OCALLFUNC || n | ||||
.Y.Op() == ir.OCALLINTER { | ||||
// When copying values out of the return area of a call, | ||||
we know | ||||
// the source and destination don't overlap. Importantly, | ||||
we must | ||||
// set mayOverlap so we don't introduce a call to memmove | ||||
while | ||||
// we still have live data in the argument area. | ||||
mayOverlap = false | ||||
} | ||||
// Evaluate RHS. | // Evaluate RHS. | |||
rhs := n.Y | rhs := n.Y | |||
if rhs != nil { | if rhs != nil { | |||
switch rhs.Op() { | switch rhs.Op() { | |||
case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT: | case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT: | |||
// All literals with nonzero fields have already been | // All literals with nonzero fields have already been | |||
// rewritten during walk. Any that remain are jus t T{} | // rewritten during walk. Any that remain are jus t T{} | |||
// or equivalents. Use the zero value. | // or equivalents. Use the zero value. | |||
if !ir.IsZero(rhs) { | if !ir.IsZero(rhs) { | |||
s.Fatalf("literal with nonzero value in S SA: %v", rhs) | s.Fatalf("literal with nonzero value in S SA: %v", rhs) | |||
skipping to change at line 1644 | skipping to change at line 1715 | |||
skip |= skipPtr | skip |= skipPtr | |||
if j == nil { | if j == nil { | |||
skip |= skipLen | skip |= skipLen | |||
} | } | |||
if k == nil { | if k == nil { | |||
skip |= skipCap | skip |= skipCap | |||
} | } | |||
} | } | |||
} | } | |||
s.assign(n.X, r, deref, skip) | s.assignWhichMayOverlap(n.X, r, deref, skip, mayOverlap) | |||
case ir.OIF: | case ir.OIF: | |||
n := n.(*ir.IfStmt) | n := n.(*ir.IfStmt) | |||
if ir.IsConst(n.Cond, constant.Bool) { | if ir.IsConst(n.Cond, constant.Bool) { | |||
s.stmtList(n.Cond.Init()) | s.stmtList(n.Cond.Init()) | |||
if ir.BoolVal(n.Cond) { | if ir.BoolVal(n.Cond) { | |||
s.stmtList(n.Body) | s.stmtList(n.Body) | |||
} else { | } else { | |||
s.stmtList(n.Else) | s.stmtList(n.Else) | |||
} | } | |||
skipping to change at line 3567 | skipping to change at line 3638 | |||
skipPtr skipMask = 1 << iota | skipPtr skipMask = 1 << iota | |||
skipLen | skipLen | |||
skipCap | skipCap | |||
) | ) | |||
// assign does left = right. | // assign does left = right. | |||
// Right has already been evaluated to ssa, left has not. | // Right has already been evaluated to ssa, left has not. | |||
// If deref is true, then we do left = *right instead (and right has already bee n nil-checked). | // If deref is true, then we do left = *right instead (and right has already bee n nil-checked). | |||
// If deref is true and right == nil, just do left = 0. | // If deref is true and right == nil, just do left = 0. | |||
// skip indicates assignments (at the top level) that can be avoided. | // skip indicates assignments (at the top level) that can be avoided. | |||
// mayOverlap indicates whether left&right might partially overlap in memory. De fault is false. | ||||
func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask ) { | func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask ) { | |||
s.assignWhichMayOverlap(left, right, deref, skip, false) | ||||
} | ||||
func (s *state) assignWhichMayOverlap(left ir.Node, right *ssa.Value, deref bool | ||||
, skip skipMask, mayOverlap bool) { | ||||
if left.Op() == ir.ONAME && ir.IsBlank(left) { | if left.Op() == ir.ONAME && ir.IsBlank(left) { | |||
return | return | |||
} | } | |||
t := left.Type() | t := left.Type() | |||
types.CalcSize(t) | types.CalcSize(t) | |||
if s.canSSA(left) { | if s.canSSA(left) { | |||
if deref { | if deref { | |||
s.Fatalf("can SSA LHS %v but not RHS %s", left, right) | s.Fatalf("can SSA LHS %v but not RHS %s", left, right) | |||
} | } | |||
if left.Op() == ir.ODOT { | if left.Op() == ir.ODOT { | |||
skipping to change at line 3668 | skipping to change at line 3743 | |||
// is valid, even though they have type uintptr (#19168). | // is valid, even though they have type uintptr (#19168). | |||
// Mark it pointer type to signal the writebarrier pass to | // Mark it pointer type to signal the writebarrier pass to | |||
// insert a write barrier. | // insert a write barrier. | |||
t = types.Types[types.TUNSAFEPTR] | t = types.Types[types.TUNSAFEPTR] | |||
} | } | |||
if deref { | if deref { | |||
// Treat as a mem->mem move. | // Treat as a mem->mem move. | |||
if right == nil { | if right == nil { | |||
s.zero(t, addr) | s.zero(t, addr) | |||
} else { | } else { | |||
s.move(t, addr, right) | s.moveWhichMayOverlap(t, addr, right, mayOverlap) | |||
} | } | |||
return | return | |||
} | } | |||
// Treat as a store. | // Treat as a store. | |||
s.storeType(t, addr, right, skip, !ir.IsAutoTmp(left)) | s.storeType(t, addr, right, skip, !ir.IsAutoTmp(left)) | |||
} | } | |||
// zeroVal returns the zero value for type t. | // zeroVal returns the zero value for type t. | |||
func (s *state) zeroVal(t *types.Type) *ssa.Value { | func (s *state) zeroVal(t *types.Type) *ssa.Value { | |||
switch { | switch { | |||
skipping to change at line 7265 | skipping to change at line 7340 | |||
defframe(&s, e, f) | defframe(&s, e, f) | |||
f.HTMLWriter.Close() | f.HTMLWriter.Close() | |||
f.HTMLWriter = nil | f.HTMLWriter = nil | |||
} | } | |||
func defframe(s *State, e *ssafn, f *ssa.Func) { | func defframe(s *State, e *ssafn, f *ssa.Func) { | |||
pp := s.pp | pp := s.pp | |||
frame := types.Rnd(s.maxarg+e.stksize, int64(types.RegSize)) | s.maxarg = types.Rnd(s.maxarg, e.stkalign) | |||
frame := s.maxarg + e.stksize | ||||
if Arch.PadFrame != nil { | if Arch.PadFrame != nil { | |||
frame = Arch.PadFrame(frame) | frame = Arch.PadFrame(frame) | |||
} | } | |||
// Fill in argument and frame size. | // Fill in argument and frame size. | |||
pp.Text.To.Type = obj.TYPE_TEXTSIZE | pp.Text.To.Type = obj.TYPE_TEXTSIZE | |||
pp.Text.To.Val = int32(types.Rnd(f.OwnAux.ArgWidth(), int64(types.RegSize ))) | pp.Text.To.Val = int32(types.Rnd(f.OwnAux.ArgWidth(), int64(types.RegSize ))) | |||
pp.Text.To.Offset = frame | pp.Text.To.Offset = frame | |||
p := pp.Text | p := pp.Text | |||
skipping to change at line 7703 | skipping to change at line 7779 | |||
// so we don't have to recompute it each time we need it. | // so we don't have to recompute it each time we need it. | |||
} | } | |||
// ssafn holds frontend information about a function that the backend is process ing. | // ssafn holds frontend information about a function that the backend is process ing. | |||
// It also exports a bunch of compiler services for the ssa backend. | // It also exports a bunch of compiler services for the ssa backend. | |||
type ssafn struct { | type ssafn struct { | |||
curfn *ir.Func | curfn *ir.Func | |||
strings map[string]*obj.LSym // map from constant string to data symbo ls | strings map[string]*obj.LSym // map from constant string to data symbo ls | |||
stksize int64 // stack size for current frame | stksize int64 // stack size for current frame | |||
stkptrsize int64 // prefix of stack containing pointers | stkptrsize int64 // prefix of stack containing pointers | |||
log bool // print ssa debug to the stdout | ||||
// alignment for current frame. | ||||
// NOTE: when stkalign > PtrSize, currently this only ensures the offsets | ||||
of | ||||
// objects in the stack frame are aligned. The stack pointer is still ali | ||||
gned | ||||
// only PtrSize. | ||||
stkalign int64 | ||||
log bool // print ssa debug to the stdout | ||||
} | } | |||
// StringData returns a symbol which | // StringData returns a symbol which | |||
// is the data component of a global string constant containing s. | // is the data component of a global string constant containing s. | |||
func (e *ssafn) StringData(s string) *obj.LSym { | func (e *ssafn) StringData(s string) *obj.LSym { | |||
if aux, ok := e.strings[s]; ok { | if aux, ok := e.strings[s]; ok { | |||
return aux | return aux | |||
} | } | |||
if e.strings == nil { | if e.strings == nil { | |||
e.strings = make(map[string]*obj.LSym) | e.strings = make(map[string]*obj.LSym) | |||
End of changes. 10 change blocks. | ||||
4 lines changed or deleted | 116 lines changed or added |