[dev.ssa] cmd/compile/internal/gc: implement OAPPEND
Change-Id: I1fbce8c421c48074a964b4d9481c92fbc3524f80
Reviewed-on: https://go-review.googlesource.com/14525
Reviewed-by: Todd Neal <todd@tneal.org>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index e6a5627..5cd074b 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -181,7 +181,7 @@
hstr += fmt.Sprintf("%08b", b)
}
if strings.HasSuffix(hstr, os.Getenv("GOSSAHASH")) {
- fmt.Println("GOSSAHASH triggered %s\n", name)
+ fmt.Printf("GOSSAHASH triggered %s\n", name)
return s.f, true
}
return s.f, false
@@ -264,6 +264,7 @@
// dummy nodes for temporary variables
var ptrvar = Node{Op: ONAME, Sym: &Sym{Name: "ptr"}}
+var capvar = Node{Op: ONAME, Sym: &Sym{Name: "cap"}}
// startBlock sets the current block we're generating code in to b.
func (s *state) startBlock(b *ssa.Block) {
@@ -560,6 +561,16 @@
if n.Right != nil {
r = s.expr(n.Right)
}
+ if n.Right != nil && n.Right.Op == OAPPEND {
+ // Yuck! The frontend gets rid of the write barrier, but we need it!
+ // At least, we need it in the case where growslice is called.
+ // TODO: Do the write barrier on just the growslice branch.
+ // TODO: just add a ptr graying to the end of growslice?
+ // TODO: check whether we need to do this for ODOTTYPE and ORECV also.
+ // They get similar wb-removal treatment in walk.go:OAS.
+ s.assign(n.Left, r, true)
+ return
+ }
s.assign(n.Left, r, n.Op == OASWB)
case OIF:
@@ -1865,6 +1876,103 @@
case OGETG:
return s.newValue0(ssa.OpGetG, n.Type)
+ case OAPPEND:
+ // append(s, e1, e2, e3). Compile like:
+ // ptr,len,cap := s
+ // newlen := len + 3
+ // if newlen > s.cap {
+ // ptr,_,cap = growslice(s, newlen)
+ // }
+ // *(ptr+len) = e1
+ // *(ptr+len+1) = e2
+ // *(ptr+len+2) = e3
+ // makeslice(ptr,newlen,cap)
+
+ et := n.Type.Type
+ pt := Ptrto(et)
+
+ // Evaluate slice
+ slice := s.expr(n.List.N)
+
+ // Evaluate args
+ nargs := int64(count(n.List) - 1)
+ args := make([]*ssa.Value, 0, nargs)
+ for l := n.List.Next; l != nil; l = l.Next {
+ args = append(args, s.expr(l.N))
+ }
+
+ // Allocate new blocks
+ grow := s.f.NewBlock(ssa.BlockPlain)
+ growresult := s.f.NewBlock(ssa.BlockPlain)
+ assign := s.f.NewBlock(ssa.BlockPlain)
+
+ // Decide if we need to grow
+ p := s.newValue1(ssa.OpSlicePtr, pt, slice)
+ l := s.newValue1(ssa.OpSliceLen, Types[TINT], slice)
+ c := s.newValue1(ssa.OpSliceCap, Types[TINT], slice)
+ nl := s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], l, s.constInt(Types[TINT], nargs))
+ cmp := s.newValue2(s.ssaOp(OGT, Types[TINT]), Types[TBOOL], nl, c)
+ s.vars[&ptrvar] = p
+ s.vars[&capvar] = c
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.Likely = ssa.BranchUnlikely
+ b.Control = cmp
+ b.AddEdgeTo(grow)
+ b.AddEdgeTo(assign)
+
+ // Call growslice
+ s.startBlock(grow)
+ taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Types[TUINTPTR], typenamesym(n.Type)}, s.sb)
+
+ spplus1 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(Widthptr), s.sp)
+ spplus2 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(2*Widthptr), s.sp)
+ spplus3 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(3*Widthptr), s.sp)
+ spplus4 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(4*Widthptr), s.sp)
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), s.sp, taddr, s.mem())
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus1, p, s.mem())
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus2, l, s.mem())
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus3, c, s.mem())
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus4, nl, s.mem())
+ call := s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, syslook("growslice", 0).Sym, s.mem())
+ call.AuxInt = int64(8 * Widthptr)
+ s.vars[&memvar] = call
+ b = s.endBlock()
+ b.Kind = ssa.BlockCall
+ b.Control = call
+ b.AddEdgeTo(growresult)
+
+ // Read result of growslice
+ s.startBlock(growresult)
+ spplus5 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(5*Widthptr), s.sp)
+ // Note: we don't need to read the result's length.
+ spplus7 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(7*Widthptr), s.sp)
+ s.vars[&ptrvar] = s.newValue2(ssa.OpLoad, pt, spplus5, s.mem())
+ s.vars[&capvar] = s.newValue2(ssa.OpLoad, Types[TINT], spplus7, s.mem())
+ b = s.endBlock()
+ b.AddEdgeTo(assign)
+
+ // assign new elements to slots
+ s.startBlock(assign)
+ p = s.variable(&ptrvar, pt) // generates phi for ptr
+ c = s.variable(&capvar, Types[TINT]) // generates phi for cap
+ p2 := s.newValue2(ssa.OpPtrIndex, pt, p, l)
+ for i, arg := range args {
+ addr := s.newValue2(ssa.OpPtrIndex, pt, p2, s.constInt(Types[TUINTPTR], int64(i)))
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, et.Size(), addr, arg, s.mem())
+ if haspointers(et) {
+ // TODO: just one write barrier call for all of these writes?
+ // TODO: maybe just one writeBarrierEnabled check?
+ s.insertWB(et, addr)
+ }
+ }
+
+ // make result
+ r := s.newValue3(ssa.OpSliceMake, n.Type, p, nl, c)
+ delete(s.vars, &ptrvar)
+ delete(s.vars, &capvar)
+ return r
+
default:
s.Unimplementedf("unhandled expr %s", opnames[n.Op])
return nil
@@ -1902,39 +2010,7 @@
}
s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, t.Size(), addr, right, s.mem())
if wb {
- // if writeBarrierEnabled {
- // typedmemmove_nostore(t, &l)
- // }
- bThen := s.f.NewBlock(ssa.BlockPlain)
- bNext := s.f.NewBlock(ssa.BlockPlain)
-
- aux := &ssa.ExternSymbol{Types[TBOOL], syslook("writeBarrierEnabled", 0).Sym}
- flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TBOOL]), aux, s.sb)
- 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(bNext)
-
- s.startBlock(bThen)
- // NOTE: there must be no GC suspension points between the write above
- // (the OpStore) and this call to typedmemmove_nostore.
- // TODO: writebarrierptr_nostore if just one pointer word (or a few?)
- taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Types[TUINTPTR], typenamesym(left.Type)}, s.sb)
- s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), s.sp, taddr, s.mem())
- spplus8 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(Widthptr), s.sp)
- s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus8, addr, s.mem())
- call := s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, syslook("typedmemmove_nostore", 0).Sym, s.mem())
- call.AuxInt = int64(2 * Widthptr)
- s.vars[&memvar] = call
- c := s.endBlock()
- c.Kind = ssa.BlockCall
- c.Control = call
- c.AddEdgeTo(bNext)
-
- s.startBlock(bNext)
+ s.insertWB(left.Type, addr)
}
}
@@ -2228,6 +2304,44 @@
s.startBlock(bNext)
}
+// insertWB inserts a write barrier. A value of type t has already
+// been stored at location p. Tell the runtime about this write.
+// Note: there must be no GC suspension points between the write and
+// the call that this function inserts.
+func (s *state) insertWB(t *Type, p *ssa.Value) {
+ // if writeBarrierEnabled {
+ // typedmemmove_nostore(&t, p)
+ // }
+ bThen := s.f.NewBlock(ssa.BlockPlain)
+ bNext := s.f.NewBlock(ssa.BlockPlain)
+
+ aux := &ssa.ExternSymbol{Types[TBOOL], syslook("writeBarrierEnabled", 0).Sym}
+ flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TBOOL]), aux, s.sb)
+ 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(bNext)
+
+ s.startBlock(bThen)
+ // TODO: writebarrierptr_nostore if just one pointer word (or a few?)
+ taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Types[TUINTPTR], typenamesym(t)}, s.sb)
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), s.sp, taddr, s.mem())
+ spplus8 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(Widthptr), s.sp)
+ s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus8, p, s.mem())
+ call := s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, syslook("typedmemmove_nostore", 0).Sym, s.mem())
+ call.AuxInt = int64(2 * Widthptr)
+ s.vars[&memvar] = call
+ c := s.endBlock()
+ c.Kind = ssa.BlockCall
+ c.Control = call
+ c.AddEdgeTo(bNext)
+
+ s.startBlock(bNext)
+}
+
// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
// i,j,k may be nil, in which case they are set to their default value.
// t is a slice, ptr to array, or string type.