Commit aebf6611 authored by Keith Randall's avatar Keith Randall

[dev.ssa] cmd/compile: reorg write barriers a bit

Use just a single write barrier flag test, even if there
are multiple pointer fields in a struct.

This helps move more of the wb-specific code (like the LEA
needed to materialize the write address) into the unlikely path.

Change-Id: Ic7a67145904369c4ff031e464d51267d71281c8f
Reviewed-on: https://go-review.googlesource.com/19085
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent a6fb514b
...@@ -2770,22 +2770,45 @@ func (s *state) insertWBstore(t *Type, left, right *ssa.Value, line int32) { ...@@ -2770,22 +2770,45 @@ func (s *state) insertWBstore(t *Type, left, right *ssa.Value, line int32) {
// store pointer fields // store pointer fields
// } // }
if t.IsStruct() { s.storeTypeScalars(t, left, right)
n := t.NumFields()
for i := int64(0); i < n; i++ { bThen := s.f.NewBlock(ssa.BlockPlain)
ft := t.FieldType(i) bElse := s.f.NewBlock(ssa.BlockPlain)
addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left) bEnd := s.f.NewBlock(ssa.BlockPlain)
val := s.newValue1I(ssa.OpStructSelect, ft, i, right)
if haspointers(ft.(*Type)) { aux := &ssa.ExternSymbol{Types[TBOOL], syslook("writeBarrier", 0).Sym}
s.insertWBstore(ft.(*Type), addr, val, line) flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TBOOL]), aux, s.sb)
} else { // TODO: select the .enabled field. It is currently first, so not needed for now.
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, ft.Size(), addr, val, s.mem()) flag := s.newValue2(ssa.OpLoad, Types[TBOOL], flagaddr, s.mem())
} b := s.endBlock()
} b.Kind = ssa.BlockIf
return b.Likely = ssa.BranchUnlikely
b.Control = flag
b.AddEdgeTo(bThen)
b.AddEdgeTo(bElse)
// Issue write barriers for pointer writes.
s.startBlock(bThen)
s.storeTypePtrsWB(t, left, right)
s.endBlock().AddEdgeTo(bEnd)
// Issue regular stores for pointer writes.
s.startBlock(bElse)
s.storeTypePtrs(t, left, right)
s.endBlock().AddEdgeTo(bEnd)
s.startBlock(bEnd)
if Debug_wb > 0 {
Warnl(int(line), "write barrier")
} }
}
// do *left = right for all scalar (non-pointer) parts of t.
func (s *state) storeTypeScalars(t *Type, left, right *ssa.Value) {
switch { switch {
case t.IsBoolean() || t.IsInteger() || t.IsFloat() || t.IsComplex():
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, t.Size(), left, right, s.mem())
case t.IsPtr() || t.IsMap() || t.IsChan(): case t.IsPtr() || t.IsMap() || t.IsChan():
// no scalar fields. // no scalar fields.
case t.IsString(): case t.IsString():
...@@ -2803,70 +2826,80 @@ func (s *state) insertWBstore(t *Type, left, right *ssa.Value, line int32) { ...@@ -2803,70 +2826,80 @@ func (s *state) insertWBstore(t *Type, left, right *ssa.Value, line int32) {
// itab field doesn't need a write barrier (even though it is a pointer). // itab field doesn't need a write barrier (even though it is a pointer).
itab := s.newValue1(ssa.OpITab, Ptrto(Types[TUINT8]), right) itab := s.newValue1(ssa.OpITab, Ptrto(Types[TUINT8]), right)
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, left, itab, s.mem()) s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, left, itab, s.mem())
case t.IsStruct():
n := t.NumFields()
for i := int64(0); i < n; i++ {
ft := t.FieldType(i)
addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
val := s.newValue1I(ssa.OpStructSelect, ft, i, right)
s.storeTypeScalars(ft.(*Type), addr, val)
}
default: default:
s.Fatalf("bad write barrier type %s", t) s.Fatalf("bad write barrier type %s", t)
} }
}
bThen := s.f.NewBlock(ssa.BlockPlain) // do *left = right for all pointer parts of t.
bElse := s.f.NewBlock(ssa.BlockPlain) func (s *state) storeTypePtrs(t *Type, left, right *ssa.Value) {
bEnd := s.f.NewBlock(ssa.BlockPlain)
aux := &ssa.ExternSymbol{Types[TBOOL], syslook("writeBarrier", 0).Sym}
flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TBOOL]), aux, s.sb)
// TODO: select the .enabled field. It is currently first, so not needed for now.
flag := s.newValue2(ssa.OpLoad, Types[TBOOL], flagaddr, s.mem())
b := s.endBlock()
b.Kind = ssa.BlockIf
b.Likely = ssa.BranchUnlikely
b.Control = flag
b.AddEdgeTo(bThen)
b.AddEdgeTo(bElse)
// Issue write barriers for pointer writes.
s.startBlock(bThen)
switch { switch {
case t.IsPtr() || t.IsMap() || t.IsChan(): case t.IsPtr() || t.IsMap() || t.IsChan():
s.rtcall(writebarrierptr, true, nil, left, right) s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, right, s.mem())
case t.IsString(): case t.IsString():
ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), right) ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), right)
s.rtcall(writebarrierptr, true, nil, left, ptr) s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem())
case t.IsSlice(): case t.IsSlice():
ptr := s.newValue1(ssa.OpSlicePtr, Ptrto(Types[TUINT8]), right) ptr := s.newValue1(ssa.OpSlicePtr, Ptrto(Types[TUINT8]), right)
s.rtcall(writebarrierptr, true, nil, left, ptr) s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem())
case t.IsInterface(): case t.IsInterface():
// itab field is treated as a scalar.
idata := s.newValue1(ssa.OpIData, Ptrto(Types[TUINT8]), right) idata := s.newValue1(ssa.OpIData, Ptrto(Types[TUINT8]), right)
idataAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TUINT8]), s.config.PtrSize, left) idataAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TUINT8]), s.config.PtrSize, left)
s.rtcall(writebarrierptr, true, nil, idataAddr, idata) s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, idataAddr, idata, s.mem())
case t.IsStruct():
n := t.NumFields()
for i := int64(0); i < n; i++ {
ft := t.FieldType(i)
if !haspointers(ft.(*Type)) {
continue
}
addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
val := s.newValue1I(ssa.OpStructSelect, ft, i, right)
s.storeTypePtrs(ft.(*Type), addr, val)
}
default: default:
s.Fatalf("bad write barrier type %s", t) s.Fatalf("bad write barrier type %s", t)
} }
s.endBlock().AddEdgeTo(bEnd) }
// Issue regular stores for pointer writes. // do *left = right with a write barrier for all pointer parts of t.
s.startBlock(bElse) func (s *state) storeTypePtrsWB(t *Type, left, right *ssa.Value) {
switch { switch {
case t.IsPtr() || t.IsMap() || t.IsChan(): case t.IsPtr() || t.IsMap() || t.IsChan():
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, right, s.mem()) s.rtcall(writebarrierptr, true, nil, left, right)
case t.IsString(): case t.IsString():
ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), right) ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), right)
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem()) s.rtcall(writebarrierptr, true, nil, left, ptr)
case t.IsSlice(): case t.IsSlice():
ptr := s.newValue1(ssa.OpSlicePtr, Ptrto(Types[TUINT8]), right) ptr := s.newValue1(ssa.OpSlicePtr, Ptrto(Types[TUINT8]), right)
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem()) s.rtcall(writebarrierptr, true, nil, left, ptr)
case t.IsInterface(): case t.IsInterface():
idata := s.newValue1(ssa.OpIData, Ptrto(Types[TUINT8]), right) idata := s.newValue1(ssa.OpIData, Ptrto(Types[TUINT8]), right)
idataAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TUINT8]), s.config.PtrSize, left) idataAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TUINT8]), s.config.PtrSize, left)
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, idataAddr, idata, s.mem()) s.rtcall(writebarrierptr, true, nil, idataAddr, idata)
case t.IsStruct():
n := t.NumFields()
for i := int64(0); i < n; i++ {
ft := t.FieldType(i)
if !haspointers(ft.(*Type)) {
continue
}
addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
val := s.newValue1I(ssa.OpStructSelect, ft, i, right)
s.storeTypePtrsWB(ft.(*Type), addr, val)
}
default: default:
s.Fatalf("bad write barrier type %s", t) s.Fatalf("bad write barrier type %s", t)
} }
s.endBlock().AddEdgeTo(bEnd)
s.startBlock(bEnd)
if Debug_wb > 0 {
Warnl(int(line), "write barrier")
}
} }
// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result. // slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
......
...@@ -7,7 +7,6 @@ Coverage ...@@ -7,7 +7,6 @@ Coverage
Correctness Correctness
----------- -----------
- Debugging info (check & fix as much as we can) - Debugging info (check & fix as much as we can)
- Fix write barriers so cgo tests work (misc/cgo/errors/ptr.go)
- Re-enable TestStackBarrierProfiling (src/runtime/pprof/pprof_test.go) - Re-enable TestStackBarrierProfiling (src/runtime/pprof/pprof_test.go)
- @ directive in rewrites might read overwritten data. Save @loc - @ directive in rewrites might read overwritten data. Save @loc
in variable before modifying v. in variable before modifying v.
...@@ -25,7 +24,6 @@ Optimizations (better compiled code) ...@@ -25,7 +24,6 @@ Optimizations (better compiled code)
SUBQ $8, AX SUBQ $8, AX
CMP AX, $0 CMP AX, $0
JEQ ... JEQ ...
- Use better write barrier calls
- If there are a lot of MOVQ $0, ..., then load - If there are a lot of MOVQ $0, ..., then load
0 into a register and use the register as the source instead. 0 into a register and use the register as the source instead.
- Allow arrays of length 1 (or longer, with all constant indexes?) to be SSAable. - Allow arrays of length 1 (or longer, with all constant indexes?) to be SSAable.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment