Skip to content

Commit

Permalink
cmd/compile,runtime: pass only ptr and len to some runtime calls
Browse files Browse the repository at this point in the history
Some runtime calls accept a slice, but only use ptr and len.
This change modifies most such routines to accept only ptr and len.

After this change, the only runtime calls that accept an unnecessary
cap arg are concatstrings and slicerunetostring.
Neither is particularly common, and both are complicated to modify.

Negligible compiler performance impact. Shrinks binaries a little.
There are only a few regressions; the one I investigated was
due to register allocation fluctuation.

Passes 'go test -race std cmd', modulo #38265 and #38266.
Wow, does that take a long time to run.

Updates #36890

file      before    after     Δ       %       
compile   19655024  19655152  +128    +0.001% 
cover     5244840   5236648   -8192   -0.156% 
dist      3662376   3658280   -4096   -0.112% 
link      6680056   6675960   -4096   -0.061% 
pprof     14789844  14777556  -12288  -0.083% 
test2json 2824744   2820648   -4096   -0.145% 
trace     11647876  11639684  -8192   -0.070% 
vet       8260472   8256376   -4096   -0.050% 
total     115163736 115118808 -44928  -0.039% 

Change-Id: Idb29fa6a81d6a82bfd3b65740b98cf3275ca0a78
Reviewed-on: https://go-review.googlesource.com/c/go/+/227163
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
  • Loading branch information
josharian committed Apr 8, 2020
1 parent ade0811 commit b6feb03
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 109 deletions.
32 changes: 16 additions & 16 deletions src/cmd/compile/internal/gc/builtin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions src/cmd/compile/internal/gc/builtin/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ func concatstrings(*[32]byte, []string) string

func cmpstring(string, string) int
func intstring(*[4]byte, int64) string
func slicebytetostring(*[32]byte, []byte) string
func slicebytetostringtmp([]byte) string
func slicebytetostring(buf *[32]byte, ptr *byte, n int) string
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(to any, fr any, wid uintptr) int
func slicestringcopy(to any, fr any) int
func slicecopy(toPtr *any, toLen int, frPtr *any, frLen int, wid uintptr) int
func slicestringcopy(toPtr *byte, toLen int, fr string) int

func decoderune(string, int) (retv rune, retk int)
func countrunes(string) int
Expand Down Expand Up @@ -162,7 +162,7 @@ var writeBarrier struct {
// *byte is really *runtime.Type
func typedmemmove(typ *byte, dst *any, src *any)
func typedmemclr(typ *byte, dst *any)
func typedslicecopy(typ *byte, dst any, src any) int
func typedslicecopy(typ *byte, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int

func selectnbsend(hchan chan<- any, elem *any) bool
func selectnbrecv(elem *any, hchan <-chan any) bool
Expand Down
5 changes: 1 addition & 4 deletions src/cmd/compile/internal/gc/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -3277,10 +3277,7 @@ func init() {
// Compiler frontend optimizations emit OBYTES2STRTMP nodes
// for the backend instead of slicebytetostringtmp calls
// when not instrumenting.
slice := args[0]
ptr := s.newValue1(ssa.OpSlicePtr, s.f.Config.Types.BytePtr, slice)
len := s.newValue1(ssa.OpSliceLen, types.Types[TINT], slice)
return s.newValue2(ssa.OpStringMake, n.Type, ptr, len)
return s.newValue2(ssa.OpStringMake, n.Type, args[0], args[1])
},
all...)
}
Expand Down
15 changes: 15 additions & 0 deletions src/cmd/compile/internal/gc/subr.go
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,21 @@ func (o Op) IsSlice3() bool {
return false
}

// slicePtrLen extracts the pointer and length from a slice.
// This constructs two nodes referring to n, so n must be a cheapexpr.
func (n *Node) slicePtrLen() (ptr, len *Node) {
var init Nodes
c := cheapexpr(n, &init)
if c != n || init.Len() != 0 {
Fatalf("slicePtrLen not cheap: %v", n)
}
ptr = nod(OSPTR, n, nil)
ptr.Type = n.Type.Elem().PtrTo()
len = nod(OLEN, n, nil)
len.Type = types.Types[TINT]
return ptr, len
}

// labeledControl returns the control flow Node (for, switch, select)
// associated with the label n, if any.
func (n *Node) labeledControl() *Node {
Expand Down
69 changes: 48 additions & 21 deletions src/cmd/compile/internal/gc/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -1414,13 +1414,15 @@ opswitch:
t := types.NewArray(types.Types[TUINT8], tmpstringbufsize)
a = nod(OADDR, temp(t), nil)
}
fn := "slicebytetostring"
if n.Op == ORUNES2STR {
fn = "slicerunetostring"
// slicerunetostring(*[32]byte, []rune) string
n = mkcall("slicerunetostring", n.Type, init, a, n.Left)
} else {
// slicebytetostring(*[32]byte, ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init)
ptr, len := n.Left.slicePtrLen()
n = mkcall("slicebytetostring", n.Type, init, a, ptr, len)
}
// slicebytetostring(*[32]byte, []byte) string
// slicerunetostring(*[32]byte, []rune) string
n = mkcall(fn, n.Type, init, a, n.Left)

case OBYTES2STRTMP:
n.Left = walkexpr(n.Left, init)
Expand All @@ -1429,8 +1431,10 @@ opswitch:
// to avoid a function call to slicebytetostringtmp.
break
}
// slicebytetostringtmp([]byte) string
n = mkcall("slicebytetostringtmp", n.Type, init, n.Left)
// slicebytetostringtmp(ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init)
ptr, len := n.Left.slicePtrLen()
n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len)

case OSTR2BYTES:
s := n.Left
Expand Down Expand Up @@ -2645,6 +2649,8 @@ func appendslice(n *Node, init *Nodes) *Node {

l1 := n.List.First()
l2 := n.List.Second()
l2 = cheapexpr(l2, init)
n.List.SetSecond(l2)

var nodes Nodes

Expand Down Expand Up @@ -2682,35 +2688,45 @@ func appendslice(n *Node, init *Nodes) *Node {
if elemtype.HasHeapPointer() {
// copy(s[len(l1):], l2)
nptr1 := nod(OSLICE, s, nil)
nptr1.Type = s.Type
nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil)
nptr1 = cheapexpr(nptr1, &nodes)

nptr2 := l2

Curfn.Func.setWBPos(n.Pos)

// instantiate typedslicecopy(typ *type, dst any, src any) int
// instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int
fn := syslook("typedslicecopy")
fn = substArgTypes(fn, l1.Type, l2.Type)
ncopy = mkcall1(fn, types.Types[TINT], &nodes, typename(elemtype), nptr1, nptr2)
fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem())
ptr1, len1 := nptr1.slicePtrLen()
ptr2, len2 := nptr2.slicePtrLen()
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)
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(to any, fr any) int
// instantiate func slicestringcopy(toPtr *byte, toLen int, fr string) int
fn := syslook("slicestringcopy")
fn = substArgTypes(fn, l1.Type, l2.Type)
ncopy = mkcall1(fn, types.Types[TINT], &nodes, nptr1, nptr2)
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, l2.Type)
ncopy = mkcall1(fn, types.Types[TINT], &nodes, nptr1, nptr2, nodintconst(elemtype.Width))
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))
}

} else {
Expand Down Expand Up @@ -3009,20 +3025,31 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
if n.Left.Type.Elem().HasHeapPointer() {
Curfn.Func.setWBPos(n.Pos)
fn := writebarrierfn("typedslicecopy", n.Left.Type, n.Right.Type)
return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), n.Left, n.Right)
fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init)
ptrL, lenL := n.Left.slicePtrLen()
n.Right = cheapexpr(n.Right, init)
ptrR, lenR := n.Right.slicePtrLen()
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")
fn = substArgTypes(fn, n.Left.Type, n.Right.Type)
return mkcall1(fn, n.Type, init, n.Left, n.Right)
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)
}

fn := syslook("slicecopy")
fn = substArgTypes(fn, n.Left.Type, n.Right.Type)
return mkcall1(fn, n.Type, init, n.Left, n.Right, nodintconst(n.Left.Type.Elem().Width))
fn = substArgTypes(fn, n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init)
ptrL, lenL := n.Left.slicePtrLen()
n.Right = cheapexpr(n.Right, init)
ptrR, lenR := n.Right.slicePtrLen()
return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width))
}

n.Left = walkexpr(n.Left, init)
Expand Down
13 changes: 7 additions & 6 deletions src/runtime/cgocheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,24 @@ func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
cgoCheckTypedBlock(typ, src, off, size)
}

// cgoCheckSliceCopy is called when copying n elements of a slice from
// src to dst. typ is the element type of the slice.
// cgoCheckSliceCopy is called when copying n elements of a slice.
// src and dst are pointers to the first element of the slice.
// typ is the element type of the slice.
// It throws if the program is copying slice elements that contain Go pointers
// into non-Go memory.
//go:nosplit
//go:nowritebarrier
func cgoCheckSliceCopy(typ *_type, dst, src slice, n int) {
func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) {
if typ.ptrdata == 0 {
return
}
if !cgoIsGoPointer(src.array) {
if !cgoIsGoPointer(src) {
return
}
if cgoIsGoPointer(dst.array) {
if cgoIsGoPointer(dst) {
return
}
p := src.array
p := src
for i := 0; i < n; i++ {
cgoCheckTypedBlock(typ, p, 0, typ.size)
p = add(p, typ.size)
Expand Down
28 changes: 13 additions & 15 deletions src/runtime/mbarrier.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,16 +231,14 @@ func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr) {
}

//go:nosplit
func typedslicecopy(typ *_type, dst, src slice) int {
n := dst.len
if n > src.len {
n = src.len
func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe.Pointer, srcLen int) int {
n := dstLen
if n > srcLen {
n = srcLen
}
if n == 0 {
return 0
}
dstp := dst.array
srcp := src.array

// The compiler emits calls to typedslicecopy before
// instrumentation runs, so unlike the other copying and
Expand All @@ -249,19 +247,19 @@ func typedslicecopy(typ *_type, dst, src slice) int {
if raceenabled {
callerpc := getcallerpc()
pc := funcPC(slicecopy)
racewriterangepc(dstp, uintptr(n)*typ.size, callerpc, pc)
racereadrangepc(srcp, uintptr(n)*typ.size, callerpc, pc)
racewriterangepc(dstPtr, uintptr(n)*typ.size, callerpc, pc)
racereadrangepc(srcPtr, uintptr(n)*typ.size, callerpc, pc)
}
if msanenabled {
msanwrite(dstp, uintptr(n)*typ.size)
msanread(srcp, uintptr(n)*typ.size)
msanwrite(dstPtr, uintptr(n)*typ.size)
msanread(srcPtr, uintptr(n)*typ.size)
}

if writeBarrier.cgo {
cgoCheckSliceCopy(typ, dst, src, n)
cgoCheckSliceCopy(typ, dstPtr, srcPtr, n)
}

if dstp == srcp {
if dstPtr == srcPtr {
return n
}

Expand All @@ -272,11 +270,11 @@ func typedslicecopy(typ *_type, dst, src slice) int {
size := uintptr(n) * typ.size
if writeBarrier.needed {
pwsize := size - typ.size + typ.ptrdata
bulkBarrierPreWrite(uintptr(dstp), uintptr(srcp), pwsize)
bulkBarrierPreWrite(uintptr(dstPtr), uintptr(srcPtr), pwsize)
}
// See typedmemmove for a discussion of the race between the
// barrier and memmove.
memmove(dstp, srcp, size)
memmove(dstPtr, srcPtr, size)
return n
}

Expand Down Expand Up @@ -306,7 +304,7 @@ func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
memmove(dst.array, src.array, size)
return n
}
return typedslicecopy(elemType, dst, src)
return typedslicecopy(elemType, dst.array, dst.len, src.array, src.len)
}

// typedmemclr clears the typed memory at ptr with type typ. The
Expand Down
7 changes: 4 additions & 3 deletions src/runtime/os_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,13 +277,14 @@ func getHugePageSize() uintptr {
if fd < 0 {
return 0
}
n := read(fd, noescape(unsafe.Pointer(&numbuf[0])), int32(len(numbuf)))
ptr := noescape(unsafe.Pointer(&numbuf[0]))
n := read(fd, ptr, int32(len(numbuf)))
closefd(fd)
if n <= 0 {
return 0
}
l := n - 1 // remove trailing newline
v, ok := atoi(slicebytetostringtmp(numbuf[:l]))
n-- // remove trailing newline
v, ok := atoi(slicebytetostringtmp((*byte)(ptr), int(n)))
if !ok || v < 0 {
v = 0
}
Expand Down
Loading

0 comments on commit b6feb03

Please sign in to comment.