diff mbox series

Go patch committed: Pass slice by elements to growslice

Message ID CAOyqgcUy55odyVSNMz89yPg6eqA3qUvBdOxJa7c5366u_OkKSA@mail.gmail.com
State New
Headers show
Series Go patch committed: Pass slice by elements to growslice | expand

Commit Message

Ian Lance Taylor March 19, 2019, 6:42 p.m. UTC
This patch by Cherry Zhang changes the Go frontend and libgo to pass
the old slice's ptr/len/cap by value to growslice.  In the C calling
convention, on AMD64, and probably a number of other architectures, a
3-word struct argument is passed on stack.  This is less efficient
than passing in three registers.  Further, this may affect the code
generation in other part of the program, even if the function is not
actually called.

Slices are common in Go and append is a common slice operation, which
calls growslice in the growing path.  To improve the codegeneration,
pass the slice header's three fields as separate values, instead of a
struct, to growslice.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
diff mbox series

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 269805)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-069afe85f38c099660c5d81950d65248ed4fc516
+6e5ff227d4e77d340e86bd2c5e045d5532c2d7d7
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 269805)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -7986,23 +7986,33 @@  Builtin_call_expression::flatten_append(
   // Using uint here means that if the computation of ntmp overflowed,
   // we will call growslice which will panic.
 
-  Expression* left = Expression::make_temporary_reference(ntmp, loc);
-  left = Expression::make_cast(uint_type, left, loc);
-
   Named_object* capfn = gogo->lookup_global("cap");
   Expression* capref = Expression::make_func_reference(capfn, NULL, loc);
   call_args = new Expression_list();
   call_args->push_back(Expression::make_temporary_reference(s1tmp, loc));
-  Expression* right = Expression::make_call(capref, call_args, false, loc);
+  Expression* cap = Expression::make_call(capref, call_args, false, loc);
+  gogo->lower_expression(function, inserter, &cap);
+  gogo->flatten_expression(function, inserter, &cap);
+  Temporary_statement* c1tmp = Statement::make_temporary(int_type, cap, loc);
+  inserter->insert(c1tmp);
+
+  Expression* left = Expression::make_temporary_reference(ntmp, loc);
+  left = Expression::make_cast(uint_type, left, loc);
+  Expression* right = Expression::make_temporary_reference(c1tmp, loc);
   right = Expression::make_cast(uint_type, right, loc);
 
   Expression* cond = Expression::make_binary(OPERATOR_GT, left, right, loc);
 
+  Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type());
   Expression* a1 = Expression::make_type_descriptor(element_type, loc);
   Expression* a2 = Expression::make_temporary_reference(s1tmp, loc);
-  Expression* a3 = Expression::make_temporary_reference(ntmp, loc);
-  Expression* call = Runtime::make_call(Runtime::GROWSLICE, loc, 3,
-					a1, a2, a3);
+  a2 = slice_type->array_type()->get_value_pointer(gogo, a2, false);
+  a2 = Expression::make_cast(unsafe_ptr_type, a2, loc);
+  Expression* a3 = Expression::make_temporary_reference(l1tmp, loc);
+  Expression* a4 = Expression::make_temporary_reference(c1tmp, loc);
+  Expression* a5 = Expression::make_temporary_reference(ntmp, loc);
+  Expression* call = Runtime::make_call(Runtime::GROWSLICE, loc, 5,
+					a1, a2, a3, a4, a5);
   call = Expression::make_unsafe_cast(slice_type, call, loc);
 
   ref = Expression::make_temporary_reference(s1tmp, loc);
Index: gcc/go/gofrontend/runtime.def
===================================================================
--- gcc/go/gofrontend/runtime.def	(revision 269805)
+++ gcc/go/gofrontend/runtime.def	(working copy)
@@ -202,7 +202,8 @@  DEF_GO_RUNTIME(TYPEDSLICECOPY, "runtime.
 
 
 // Grow a slice for append.
-DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice", P3(TYPE, SLICE, INT), R1(SLICE))
+DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice",
+               P5(TYPE, POINTER, INT, INT, INT), R1(SLICE))
 
 
 // Register roots (global variables) for the garbage collector.
Index: libgo/go/runtime/slice.go
===================================================================
--- libgo/go/runtime/slice.go	(revision 269805)
+++ libgo/go/runtime/slice.go	(working copy)
@@ -77,31 +77,31 @@  func makeslice64(et *_type, len64, cap64
 // and it returns a new slice with at least that capacity, with the old data
 // copied into it.
 // The new slice's length is set to the requested capacity.
-func growslice(et *_type, old slice, cap int) slice {
+func growslice(et *_type, oldarray unsafe.Pointer, oldlen, oldcap, cap int) slice {
 	if raceenabled {
 		callerpc := getcallerpc()
-		racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
+		racereadrangepc(oldarray, uintptr(oldlen*int(et.size)), callerpc, funcPC(growslice))
 	}
 	if msanenabled {
-		msanread(old.array, uintptr(old.len*int(et.size)))
+		msanread(oldarray, uintptr(oldlen*int(et.size)))
 	}
 
-	if cap < old.cap {
+	if cap < oldcap {
 		panic(errorString("growslice: cap out of range"))
 	}
 
 	if et.size == 0 {
 		// append should not create a slice with nil pointer but non-zero len.
-		// We assume that append doesn't need to preserve old.array in this case.
+		// We assume that append doesn't need to preserve oldarray in this case.
 		return slice{unsafe.Pointer(&zerobase), cap, cap}
 	}
 
-	newcap := old.cap
+	newcap := oldcap
 	doublecap := newcap + newcap
 	if cap > doublecap {
 		newcap = cap
 	} else {
-		if old.len < 1024 {
+		if oldlen < 1024 {
 			newcap = doublecap
 		} else {
 			// Check 0 < newcap to detect overflow
@@ -125,13 +125,13 @@  func growslice(et *_type, old slice, cap
 	// For powers of 2, use a variable shift.
 	switch {
 	case et.size == 1:
-		lenmem = uintptr(old.len)
+		lenmem = uintptr(oldlen)
 		newlenmem = uintptr(cap)
 		capmem = roundupsize(uintptr(newcap))
 		overflow = uintptr(newcap) > maxAlloc
 		newcap = int(capmem)
 	case et.size == sys.PtrSize:
-		lenmem = uintptr(old.len) * sys.PtrSize
+		lenmem = uintptr(oldlen) * sys.PtrSize
 		newlenmem = uintptr(cap) * sys.PtrSize
 		capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
 		overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
@@ -144,13 +144,13 @@  func growslice(et *_type, old slice, cap
 		} else {
 			shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
 		}
-		lenmem = uintptr(old.len) << shift
+		lenmem = uintptr(oldlen) << shift
 		newlenmem = uintptr(cap) << shift
 		capmem = roundupsize(uintptr(newcap) << shift)
 		overflow = uintptr(newcap) > (maxAlloc >> shift)
 		newcap = int(capmem >> shift)
 	default:
-		lenmem = uintptr(old.len) * et.size
+		lenmem = uintptr(oldlen) * et.size
 		newlenmem = uintptr(cap) * et.size
 		capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
 		capmem = roundupsize(capmem)
@@ -177,19 +177,19 @@  func growslice(et *_type, old slice, cap
 	var p unsafe.Pointer
 	if et.kind&kindNoPointers != 0 {
 		p = mallocgc(capmem, nil, false)
-		// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
+		// The append() that calls growslice is going to overwrite from oldlen to cap (which will be the new length).
 		// Only clear the part that will not be overwritten.
 		memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
 	} else {
 		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
 		p = mallocgc(capmem, et, true)
 		if writeBarrier.enabled {
-			// Only shade the pointers in old.array since we know the destination slice p
+			// Only shade the pointers in oldarray since we know the destination slice p
 			// only contains nil pointers because it has been cleared during alloc.
-			bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem)
+			bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(oldarray), lenmem)
 		}
 	}
-	memmove(p, old.array, lenmem)
+	memmove(p, oldarray, lenmem)
 
 	return slice{p, cap, newcap}
 }