compiler,runtime: pass only ptr and len to some runtime calls
This ports https://golang.org/cl/227163 to the Go frontend.
This is a step toward moving up to the go1.15rc1 release.
Original CL description:
cmd/compile,runtime: pass only ptr and len to some runtime calls
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 golang/go#38265 and golang/go#38266.
Wow, does that take a long time to run.
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%
For golang/go#36890
Change-Id: I1dc1424ccb092a9ad70472e560a743c35dd27bfc
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245099
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/go/expressions.cc b/go/expressions.cc
index 327f940..90f860b 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -4157,32 +4157,8 @@
go_assert(e->integer_type() != NULL);
go_assert(this->expr_->is_variable());
- Runtime::Function code;
- if (e->integer_type()->is_byte())
- {
- if (this->no_copy_)
- {
- if (gogo->debug_optimization())
- go_debug(loc, "no copy string([]byte)");
- Expression* ptr = Expression::make_slice_info(this->expr_,
- SLICE_INFO_VALUE_POINTER,
- loc);
- Expression* len = Expression::make_slice_info(this->expr_,
- SLICE_INFO_LENGTH,
- loc);
- Expression* str = Expression::make_string_value(ptr, len, loc);
- return str->get_backend(context);
- }
- code = Runtime::SLICEBYTETOSTRING;
- }
- else
- {
- go_assert(e->integer_type()->is_rune());
- code = Runtime::SLICERUNETOSTRING;
- }
-
Expression* buf;
- if (this->no_escape_)
+ if (this->no_escape_ && !this->no_copy_)
{
Type* byte_type = Type::lookup_integer_type("uint8");
Expression* buflen =
@@ -4194,8 +4170,30 @@
}
else
buf = Expression::make_nil(loc);
- return Runtime::make_call(code, loc, 2, buf,
- this->expr_)->get_backend(context);
+
+ if (e->integer_type()->is_byte())
+ {
+ Expression* ptr =
+ Expression::make_slice_info(this->expr_, SLICE_INFO_VALUE_POINTER,
+ loc);
+ Expression* len =
+ Expression::make_slice_info(this->expr_, SLICE_INFO_LENGTH, loc);
+ if (this->no_copy_)
+ {
+ if (gogo->debug_optimization())
+ go_debug(loc, "no copy string([]byte)");
+ Expression* str = Expression::make_string_value(ptr, len, loc);
+ return str->get_backend(context);
+ }
+ return Runtime::make_call(Runtime::SLICEBYTETOSTRING, loc, 3, buf,
+ ptr, len)->get_backend(context);
+ }
+ else
+ {
+ go_assert(e->integer_type()->is_rune());
+ return Runtime::make_call(Runtime::SLICERUNETOSTRING, loc, 2, buf,
+ this->expr_)->get_backend(context);
+ }
}
else if (type->is_slice_type() && expr_type->is_string_type())
{
@@ -8397,8 +8395,16 @@
if (et->has_pointer())
{
Expression* td = Expression::make_type_descriptor(et, loc);
+ Expression* pd =
+ Expression::make_slice_info(arg1, SLICE_INFO_VALUE_POINTER, loc);
+ Expression* ld =
+ Expression::make_slice_info(arg1, SLICE_INFO_LENGTH, loc);
+ Expression* ps =
+ Expression::make_slice_info(arg2, SLICE_INFO_VALUE_POINTER, loc);
+ Expression* ls =
+ Expression::make_slice_info(arg2, SLICE_INFO_LENGTH, loc);
ret = Runtime::make_call(Runtime::TYPEDSLICECOPY, loc,
- 3, td, arg1, arg2);
+ 5, td, pd, ld, ps, ls);
}
else
{
diff --git a/go/runtime.def b/go/runtime.def
index 2ef0f94..a950079 100644
--- a/go/runtime.def
+++ b/go/runtime.def
@@ -47,7 +47,7 @@
// Convert a []byte to a string.
DEF_GO_RUNTIME(SLICEBYTETOSTRING, "runtime.slicebytetostring",
- P2(POINTER, SLICE), R1(STRING))
+ P3(POINTER, POINTER, INT), R1(STRING))
// Convert a []rune to a string.
DEF_GO_RUNTIME(SLICERUNETOSTRING, "runtime.slicerunetostring",
@@ -249,17 +249,16 @@
// Copy.
-DEF_GO_RUNTIME(SLICECOPY, "runtime.slicecopy", P3(SLICE, SLICE, UINTPTR),
- R1(INT))
+DEF_GO_RUNTIME(SLICECOPY, "runtime.slicecopy",
+ P5(POINTER, INT, POINTER, INT, UINTPTR), R1(INT))
// Copy from string.
-DEF_GO_RUNTIME(SLICESTRINGCOPY, "runtime.slicestringcopy", P2(SLICE, STRING),
- R1(INT))
+DEF_GO_RUNTIME(SLICESTRINGCOPY, "runtime.slicestringcopy",
+ P3(POINTER, INT, STRING), R1(INT))
// Copy of value containing pointers.
DEF_GO_RUNTIME(TYPEDSLICECOPY, "runtime.typedslicecopy",
- P3(TYPE, SLICE, SLICE), R1(INT))
-
+ P5(TYPE, POINTER, INT, POINTER, INT), R1(INT))
// Grow a slice for append.
DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice",
diff --git a/libgo/go/runtime/cgocheck.go b/libgo/go/runtime/cgocheck.go
index c03bafe..42fdfe8 100644
--- a/libgo/go/runtime/cgocheck.go
+++ b/libgo/go/runtime/cgocheck.go
@@ -76,23 +76,24 @@
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)
diff --git a/libgo/go/runtime/mbarrier.go b/libgo/go/runtime/mbarrier.go
index e66b50d..a4f9b3c 100644
--- a/libgo/go/runtime/mbarrier.go
+++ b/libgo/go/runtime/mbarrier.go
@@ -219,16 +219,14 @@
}
//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
@@ -237,19 +235,19 @@
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
}
@@ -259,11 +257,11 @@
// before calling typedslicecopy.
size := uintptr(n) * typ.size
if writeBarrier.needed {
- bulkBarrierPreWrite(uintptr(dstp), uintptr(srcp), size)
+ bulkBarrierPreWrite(uintptr(dstPtr), uintptr(srcPtr), size)
}
// See typedmemmove for a discussion of the race between the
// barrier and memmove.
- memmove(dstp, srcp, size)
+ memmove(dstPtr, srcPtr, size)
return n
}
@@ -293,7 +291,7 @@
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
diff --git a/libgo/go/runtime/os_linux.go b/libgo/go/runtime/os_linux.go
index 1e86446..5d55064 100644
--- a/libgo/go/runtime/os_linux.go
+++ b/libgo/go/runtime/os_linux.go
@@ -207,13 +207,14 @@
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
}
diff --git a/libgo/go/runtime/slice.go b/libgo/go/runtime/slice.go
index b61c2b1..5197353 100644
--- a/libgo/go/runtime/slice.go
+++ b/libgo/go/runtime/slice.go
@@ -199,14 +199,14 @@
return x&(x-1) == 0
}
-func slicecopy(to, fm slice, width uintptr) int {
- if fm.len == 0 || to.len == 0 {
+func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int, width uintptr) int {
+ if fmLen == 0 || toLen == 0 {
return 0
}
- n := fm.len
- if to.len < n {
- n = to.len
+ n := fmLen
+ if toLen < n {
+ n = toLen
}
if width == 0 {
@@ -216,43 +216,43 @@
if raceenabled {
callerpc := getcallerpc()
pc := funcPC(slicecopy)
- racewriterangepc(to.array, uintptr(n*int(width)), callerpc, pc)
- racereadrangepc(fm.array, uintptr(n*int(width)), callerpc, pc)
+ racewriterangepc(toPtr, uintptr(n*int(width)), callerpc, pc)
+ racereadrangepc(fmPtr, uintptr(n*int(width)), callerpc, pc)
}
if msanenabled {
- msanwrite(to.array, uintptr(n*int(width)))
- msanread(fm.array, uintptr(n*int(width)))
+ msanwrite(toPtr, uintptr(n*int(width)))
+ msanread(fmPtr, uintptr(n*int(width)))
}
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)(to.array) = *(*byte)(fm.array) // known to be a byte pointer
+ *(*byte)(toPtr) = *(*byte)(fmPtr) // known to be a byte pointer
} else {
- memmove(to.array, fm.array, size)
+ memmove(toPtr, fmPtr, size)
}
return n
}
-func slicestringcopy(to []byte, fm string) int {
- if len(fm) == 0 || len(to) == 0 {
+func slicestringcopy(toPtr *byte, toLen int, fm string) int {
+ if len(fm) == 0 || toLen == 0 {
return 0
}
n := len(fm)
- if len(to) < n {
- n = len(to)
+ if toLen < n {
+ n = toLen
}
if raceenabled {
callerpc := getcallerpc()
pc := funcPC(slicestringcopy)
- racewriterangepc(unsafe.Pointer(&to[0]), uintptr(n), callerpc, pc)
+ racewriterangepc(unsafe.Pointer(toPtr), uintptr(n), callerpc, pc)
}
if msanenabled {
- msanwrite(unsafe.Pointer(&to[0]), uintptr(n))
+ msanwrite(unsafe.Pointer(toPtr), uintptr(n))
}
- memmove(unsafe.Pointer(&to[0]), stringStructOf(&fm).str, uintptr(n))
+ memmove(unsafe.Pointer(toPtr), stringStructOf(&fm).str, uintptr(n))
return n
}
diff --git a/libgo/go/runtime/string.go b/libgo/go/runtime/string.go
index df4cae7..7b66a1b 100644
--- a/libgo/go/runtime/string.go
+++ b/libgo/go/runtime/string.go
@@ -70,47 +70,50 @@
return s
}
+// slicebytetostring converts a byte slice to a string.
+// It is inserted by the compiler into generated code.
+// ptr is a pointer to the first element of the slice;
+// n is the length of the slice.
// Buf is a fixed-size buffer for the result,
// it is not nil if the result does not escape.
-func slicebytetostring(buf *tmpBuf, b []byte) (str string) {
- l := len(b)
- if l == 0 {
+func slicebytetostring(buf *tmpBuf, ptr *byte, n int) (str string) {
+ if n == 0 {
// Turns out to be a relatively common case.
// Consider that you want to parse out data between parens in "foo()bar",
// you find the indices and convert the subslice to string.
return ""
}
if raceenabled {
- racereadrangepc(unsafe.Pointer(&b[0]),
- uintptr(l),
+ racereadrangepc(unsafe.Pointer(ptr),
+ uintptr(n),
getcallerpc(),
funcPC(slicebytetostring))
}
if msanenabled {
- msanread(unsafe.Pointer(&b[0]), uintptr(l))
+ msanread(unsafe.Pointer(ptr), uintptr(n))
}
- if l == 1 {
- stringStructOf(&str).str = unsafe.Pointer(&staticbytes[b[0]])
+ if n == 1 {
+ stringStructOf(&str).str = unsafe.Pointer(&staticbytes[*ptr])
stringStructOf(&str).len = 1
return
}
var p unsafe.Pointer
- if buf != nil && len(b) <= len(buf) {
+ if buf != nil && n <= len(buf) {
p = unsafe.Pointer(buf)
} else {
- p = mallocgc(uintptr(len(b)), nil, false)
+ p = mallocgc(uintptr(n), nil, false)
}
stringStructOf(&str).str = p
- stringStructOf(&str).len = len(b)
- memmove(p, (*(*slice)(unsafe.Pointer(&b))).array, uintptr(len(b)))
+ stringStructOf(&str).len = n
+ memmove(p, unsafe.Pointer(ptr), uintptr(n))
return
}
func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {
if buf != nil && l <= len(buf) {
b = buf[:l]
- s = slicebytetostringtmp(b)
+ s = slicebytetostringtmp(&b[0], len(b))
} else {
s, b = rawstring(l)
}
@@ -131,17 +134,19 @@
// where k is []byte, T1 to Tn is a nesting of struct and array literals.
// - Used for "<"+string(b)+">" concatenation where b is []byte.
// - Used for string(b)=="foo" comparison where b is []byte.
-func slicebytetostringtmp(b []byte) string {
- if raceenabled && len(b) > 0 {
- racereadrangepc(unsafe.Pointer(&b[0]),
- uintptr(len(b)),
+func slicebytetostringtmp(ptr *byte, n int) (str string) {
+ if raceenabled && n > 0 {
+ racereadrangepc(unsafe.Pointer(ptr),
+ uintptr(n),
getcallerpc(),
funcPC(slicebytetostringtmp))
}
- if msanenabled && len(b) > 0 {
- msanread(unsafe.Pointer(&b[0]), uintptr(len(b)))
+ if msanenabled && n > 0 {
+ msanread(unsafe.Pointer(ptr), uintptr(n))
}
- return *(*string)(unsafe.Pointer(&b))
+ stringStructOf(&str).str = unsafe.Pointer(ptr)
+ stringStructOf(&str).len = n
+ return
}
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
@@ -232,7 +237,7 @@
var b []byte
if buf != nil {
b = buf[:]
- s = slicebytetostringtmp(b)
+ s = slicebytetostringtmp(&b[0], len(b))
} else {
s, b = rawstring(4)
}