mirror of
https://github.com/golang/go
synced 2024-10-05 18:21:21 -06:00
[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>
This commit is contained in:
parent
6793cb755c
commit
9d22c101f5
@ -181,7 +181,7 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
|
||||
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 @@ var memvar = Node{Op: ONAME, Sym: &Sym{Name: "mem"}}
|
||||
|
||||
// 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 @@ func (s *state) stmt(n *Node) {
|
||||
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 @@ func (s *state) expr(n *Node) *ssa.Value {
|
||||
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 @@ func (s *state) assign(left *Node, right *ssa.Value, wb bool) {
|
||||
}
|
||||
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 @@ func (s *state) check(cmp *ssa.Value, panicOp ssa.Op) {
|
||||
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.
|
||||
|
@ -82,3 +82,5 @@ func TestDeferNoReturn(t *testing.T) { buildTest(t, "deferNoReturn_ssa.go") }
|
||||
func TestClosure(t *testing.T) { runTest(t, "closure_ssa.go") }
|
||||
|
||||
func TestArray(t *testing.T) { runTest(t, "array_ssa.go") }
|
||||
|
||||
func TestAppend(t *testing.T) { runTest(t, "append_ssa.go") }
|
||||
|
71
src/cmd/compile/internal/gc/testdata/append_ssa.go
vendored
Normal file
71
src/cmd/compile/internal/gc/testdata/append_ssa.go
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// append_ssa.go tests append operations.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var failed = false
|
||||
|
||||
func appendOne_ssa(a []int, x int) []int {
|
||||
switch { // prevent inlining
|
||||
}
|
||||
return append(a, x)
|
||||
}
|
||||
func appendThree_ssa(a []int, x, y, z int) []int {
|
||||
switch { // prevent inlining
|
||||
}
|
||||
return append(a, x, y, z)
|
||||
}
|
||||
|
||||
func eq(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func expect(got, want []int) {
|
||||
if eq(got, want) {
|
||||
return
|
||||
}
|
||||
fmt.Printf("expected %v, got %v\n", want, got)
|
||||
failed = true
|
||||
}
|
||||
|
||||
func testAppend() {
|
||||
var store [7]int
|
||||
a := store[:0]
|
||||
|
||||
a = appendOne_ssa(a, 1)
|
||||
expect(a, []int{1})
|
||||
a = appendThree_ssa(a, 2, 3, 4)
|
||||
expect(a, []int{1, 2, 3, 4})
|
||||
a = appendThree_ssa(a, 5, 6, 7)
|
||||
expect(a, []int{1, 2, 3, 4, 5, 6, 7})
|
||||
if &a[0] != &store[0] {
|
||||
fmt.Println("unnecessary grow")
|
||||
failed = true
|
||||
}
|
||||
a = appendOne_ssa(a, 8)
|
||||
expect(a, []int{1, 2, 3, 4, 5, 6, 7, 8})
|
||||
if &a[0] == &store[0] {
|
||||
fmt.Println("didn't grow")
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
testAppend()
|
||||
|
||||
if failed {
|
||||
panic("failed")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user