Skip to content

Commit

Permalink
cmd/compile: unify reflect, string and slice copy runtime functions
Browse files Browse the repository at this point in the history
Use a common runtime slicecopy function to copy strings or slices
into slices. This deduplicates similar code previously used in
reflect.slicecopy and runtime.stringslicecopy.

Change-Id: I09572ff0647a9e12bb5c6989689ce1c43f16b7f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/254658
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Martin Möhrmann <moehrmann@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
  • Loading branch information
martisch committed Sep 16, 2020
1 parent eaa97fb commit 790fa1c
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 303 deletions.
408 changes: 203 additions & 205 deletions src/cmd/compile/internal/gc/builtin.go

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions src/cmd/compile/internal/gc/builtin/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ func slicebytetostringtmp(ptr *byte, n int) string
func slicerunetostring(*[32]byte, []rune) string
func stringtoslicebyte(*[32]byte, string) []byte
func stringtoslicerune(*[32]rune, string) []rune
func slicecopy(toPtr *any, toLen int, frPtr *any, frLen int, wid uintptr) int
func slicestringcopy(toPtr *byte, toLen int, fr string) int
func slicecopy(toPtr *any, toLen int, fromPtr *any, fromLen int, wid uintptr) int

func decoderune(string, int) (retv rune, retk int)
func countrunes(string) int
Expand Down
12 changes: 8 additions & 4 deletions src/cmd/compile/internal/gc/subr.go
Original file line number Diff line number Diff line change
Expand Up @@ -928,16 +928,20 @@ func (o Op) IsSlice3() bool {
return false
}

// slicePtrLen extracts the pointer and length from a slice.
// backingArrayPtrLen extracts the pointer and length from a slice or string.
// This constructs two nodes referring to n, so n must be a cheapexpr.
func (n *Node) slicePtrLen() (ptr, len *Node) {
func (n *Node) backingArrayPtrLen() (ptr, len *Node) {
var init Nodes
c := cheapexpr(n, &init)
if c != n || init.Len() != 0 {
Fatalf("slicePtrLen not cheap: %v", n)
Fatalf("backingArrayPtrLen not cheap: %v", n)
}
ptr = nod(OSPTR, n, nil)
ptr.Type = n.Type.Elem().PtrTo()
if n.Type.IsString() {
ptr.Type = types.Types[TUINT8].PtrTo()
} else {
ptr.Type = n.Type.Elem().PtrTo()
}
len = nod(OLEN, n, nil)
len.Type = types.Types[TINT]
return ptr, len
Expand Down
60 changes: 23 additions & 37 deletions src/cmd/compile/internal/gc/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,7 @@ opswitch:
} else {
// slicebytetostring(*[32]byte, ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init)
ptr, len := n.Left.slicePtrLen()
ptr, len := n.Left.backingArrayPtrLen()
n = mkcall("slicebytetostring", n.Type, init, a, ptr, len)
}

Expand All @@ -1497,7 +1497,7 @@ opswitch:
}
// slicebytetostringtmp(ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init)
ptr, len := n.Left.slicePtrLen()
ptr, len := n.Left.backingArrayPtrLen()
n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len)

case OSTR2BYTES:
Expand Down Expand Up @@ -2764,36 +2764,25 @@ func appendslice(n *Node, init *Nodes) *Node {
// instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int
fn := syslook("typedslicecopy")
fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem())
ptr1, len1 := nptr1.slicePtrLen()
ptr2, len2 := nptr2.slicePtrLen()
ptr1, len1 := nptr1.backingArrayPtrLen()
ptr2, len2 := nptr2.backingArrayPtrLen()
ncopy = mkcall1(fn, types.Types[TINT], &nodes, typename(elemtype), ptr1, len1, ptr2, len2)

} else if instrumenting && !compiling_runtime {
// rely on runtime to instrument copy.
// copy(s[len(l1):], l2)
// rely on runtime to instrument:
// copy(s[len(l1):], l2)
// l2 can be a slice or string.
nptr1 := nod(OSLICE, s, nil)
nptr1.Type = s.Type
nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil)
nptr1 = cheapexpr(nptr1, &nodes)

nptr2 := l2

if l2.Type.IsString() {
// instantiate func slicestringcopy(toPtr *byte, toLen int, fr string) int
fn := syslook("slicestringcopy")
ptr, len := nptr1.slicePtrLen()
str := nod(OCONVNOP, nptr2, nil)
str.Type = types.Types[TSTRING]
ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr, len, str)
} else {
// instantiate func slicecopy(to any, fr any, wid uintptr) int
fn := syslook("slicecopy")
fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem())
ptr1, len1 := nptr1.slicePtrLen()
ptr2, len2 := nptr2.slicePtrLen()
ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width))
}
ptr1, len1 := nptr1.backingArrayPtrLen()
ptr2, len2 := nptr2.backingArrayPtrLen()

fn := syslook("slicecopy")
fn = substArgTypes(fn, ptr1.Type.Elem(), ptr2.Type.Elem())
ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width))
} else {
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
nptr1 := nod(OINDEX, s, nod(OLEN, l1, nil))
Expand Down Expand Up @@ -3092,28 +3081,25 @@ func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
Curfn.Func.setWBPos(n.Pos)
fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init)
ptrL, lenL := n.Left.slicePtrLen()
ptrL, lenL := n.Left.backingArrayPtrLen()
n.Right = cheapexpr(n.Right, init)
ptrR, lenR := n.Right.slicePtrLen()
ptrR, lenR := n.Right.backingArrayPtrLen()
return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR)
}

if runtimecall {
if n.Right.Type.IsString() {
fn := syslook("slicestringcopy")
n.Left = cheapexpr(n.Left, init)
ptr, len := n.Left.slicePtrLen()
str := nod(OCONVNOP, n.Right, nil)
str.Type = types.Types[TSTRING]
return mkcall1(fn, n.Type, init, ptr, len, str)
}
// rely on runtime to instrument:
// copy(n.Left, n.Right)
// n.Right can be a slice or string.

fn := syslook("slicecopy")
fn = substArgTypes(fn, n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init)
ptrL, lenL := n.Left.slicePtrLen()
ptrL, lenL := n.Left.backingArrayPtrLen()
n.Right = cheapexpr(n.Right, init)
ptrR, lenR := n.Right.slicePtrLen()
ptrR, lenR := n.Right.backingArrayPtrLen()

fn := syslook("slicecopy")
fn = substArgTypes(fn, ptrL.Type.Elem(), ptrR.Type.Elem())

return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width))
}

Expand Down
23 changes: 1 addition & 22 deletions src/runtime/mbarrier.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,28 +281,7 @@ func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe
//go:linkname reflect_typedslicecopy reflect.typedslicecopy
func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
if elemType.ptrdata == 0 {
n := dst.len
if n > src.len {
n = src.len
}
if n == 0 {
return 0
}

size := uintptr(n) * elemType.size
if raceenabled {
callerpc := getcallerpc()
pc := funcPC(reflect_typedslicecopy)
racewriterangepc(dst.array, size, callerpc, pc)
racereadrangepc(src.array, size, callerpc, pc)
}
if msanenabled {
msanwrite(dst.array, size)
msanread(src.array, size)
}

memmove(dst.array, src.array, size)
return n
return slicecopy(dst.array, dst.len, src.array, src.len, elemType.size)
}
return typedslicecopy(elemType, dst.array, dst.len, src.array, src.len)
}
Expand Down
44 changes: 11 additions & 33 deletions src/runtime/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,12 +243,13 @@ func isPowerOfTwo(x uintptr) bool {
return x&(x-1) == 0
}

func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int, width uintptr) int {
if fmLen == 0 || toLen == 0 {
// slicecopy is used to copy from a string or slice of pointerless elements into a slice.
func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int {
if fromLen == 0 || toLen == 0 {
return 0
}

n := fmLen
n := fromLen
if toLen < n {
n = toLen
}
Expand All @@ -257,46 +258,23 @@ func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int,
return n
}

size := uintptr(n) * width
if raceenabled {
callerpc := getcallerpc()
pc := funcPC(slicecopy)
racereadrangepc(fmPtr, uintptr(n*int(width)), callerpc, pc)
racewriterangepc(toPtr, uintptr(n*int(width)), callerpc, pc)
racereadrangepc(fromPtr, size, callerpc, pc)
racewriterangepc(toPtr, size, callerpc, pc)
}
if msanenabled {
msanread(fmPtr, uintptr(n*int(width)))
msanwrite(toPtr, uintptr(n*int(width)))
msanread(fromPtr, size)
msanwrite(toPtr, size)
}

size := uintptr(n) * width
if size == 1 { // common case worth about 2x to do here
// TODO: is this still worth it with new memmove impl?
*(*byte)(toPtr) = *(*byte)(fmPtr) // known to be a byte pointer
*(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer
} else {
memmove(toPtr, fmPtr, size)
}
return n
}

func slicestringcopy(toPtr *byte, toLen int, fm string) int {
if len(fm) == 0 || toLen == 0 {
return 0
}

n := len(fm)
if toLen < n {
n = toLen
memmove(toPtr, fromPtr, size)
}

if raceenabled {
callerpc := getcallerpc()
pc := funcPC(slicestringcopy)
racewriterangepc(unsafe.Pointer(toPtr), uintptr(n), callerpc, pc)
}
if msanenabled {
msanwrite(unsafe.Pointer(toPtr), uintptr(n))
}

memmove(unsafe.Pointer(toPtr), stringStructOf(&fm).str, uintptr(n))
return n
}

0 comments on commit 790fa1c

Please sign in to comment.