Commit ea5ac5a6 by Ian Lance Taylor

compiler,runtime: pass 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 code
    generation, pass the slice header's three fields as separate
    values, instead of a struct, to growslice.
    
    The drawback is that this makes the runtime implementation
    slightly diverges from the gc runtime.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/168277

From-SVN: r269811
parent e0748030
069afe85f38c099660c5d81950d65248ed4fc516 6e5ff227d4e77d340e86bd2c5e045d5532c2d7d7
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -7986,23 +7986,33 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, ...@@ -7986,23 +7986,33 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
// Using uint here means that if the computation of ntmp overflowed, // Using uint here means that if the computation of ntmp overflowed,
// we will call growslice which will panic. // 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"); Named_object* capfn = gogo->lookup_global("cap");
Expression* capref = Expression::make_func_reference(capfn, NULL, loc); Expression* capref = Expression::make_func_reference(capfn, NULL, loc);
call_args = new Expression_list(); call_args = new Expression_list();
call_args->push_back(Expression::make_temporary_reference(s1tmp, loc)); 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); right = Expression::make_cast(uint_type, right, loc);
Expression* cond = Expression::make_binary(OPERATOR_GT, left, 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* a1 = Expression::make_type_descriptor(element_type, loc);
Expression* a2 = Expression::make_temporary_reference(s1tmp, loc); Expression* a2 = Expression::make_temporary_reference(s1tmp, loc);
Expression* a3 = Expression::make_temporary_reference(ntmp, loc); a2 = slice_type->array_type()->get_value_pointer(gogo, a2, false);
Expression* call = Runtime::make_call(Runtime::GROWSLICE, loc, 3, a2 = Expression::make_cast(unsafe_ptr_type, a2, loc);
a1, a2, a3); 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); call = Expression::make_unsafe_cast(slice_type, call, loc);
ref = Expression::make_temporary_reference(s1tmp, loc); ref = Expression::make_temporary_reference(s1tmp, loc);
......
...@@ -202,7 +202,8 @@ DEF_GO_RUNTIME(TYPEDSLICECOPY, "runtime.typedslicecopy", ...@@ -202,7 +202,8 @@ DEF_GO_RUNTIME(TYPEDSLICECOPY, "runtime.typedslicecopy",
// Grow a slice for append. // 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. // Register roots (global variables) for the garbage collector.
......
...@@ -77,31 +77,31 @@ func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer { ...@@ -77,31 +77,31 @@ func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
// and it returns a new slice with at least that capacity, with the old data // and it returns a new slice with at least that capacity, with the old data
// copied into it. // copied into it.
// The new slice's length is set to the requested capacity. // 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 { if raceenabled {
callerpc := getcallerpc() 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 { 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")) panic(errorString("growslice: cap out of range"))
} }
if et.size == 0 { if et.size == 0 {
// append should not create a slice with nil pointer but non-zero len. // 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} return slice{unsafe.Pointer(&zerobase), cap, cap}
} }
newcap := old.cap newcap := oldcap
doublecap := newcap + newcap doublecap := newcap + newcap
if cap > doublecap { if cap > doublecap {
newcap = cap newcap = cap
} else { } else {
if old.len < 1024 { if oldlen < 1024 {
newcap = doublecap newcap = doublecap
} else { } else {
// Check 0 < newcap to detect overflow // Check 0 < newcap to detect overflow
...@@ -125,13 +125,13 @@ func growslice(et *_type, old slice, cap int) slice { ...@@ -125,13 +125,13 @@ func growslice(et *_type, old slice, cap int) slice {
// For powers of 2, use a variable shift. // For powers of 2, use a variable shift.
switch { switch {
case et.size == 1: case et.size == 1:
lenmem = uintptr(old.len) lenmem = uintptr(oldlen)
newlenmem = uintptr(cap) newlenmem = uintptr(cap)
capmem = roundupsize(uintptr(newcap)) capmem = roundupsize(uintptr(newcap))
overflow = uintptr(newcap) > maxAlloc overflow = uintptr(newcap) > maxAlloc
newcap = int(capmem) newcap = int(capmem)
case et.size == sys.PtrSize: case et.size == sys.PtrSize:
lenmem = uintptr(old.len) * sys.PtrSize lenmem = uintptr(oldlen) * sys.PtrSize
newlenmem = uintptr(cap) * sys.PtrSize newlenmem = uintptr(cap) * sys.PtrSize
capmem = roundupsize(uintptr(newcap) * sys.PtrSize) capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
overflow = uintptr(newcap) > maxAlloc/sys.PtrSize overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
...@@ -144,13 +144,13 @@ func growslice(et *_type, old slice, cap int) slice { ...@@ -144,13 +144,13 @@ func growslice(et *_type, old slice, cap int) slice {
} else { } else {
shift = uintptr(sys.Ctz32(uint32(et.size))) & 31 shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
} }
lenmem = uintptr(old.len) << shift lenmem = uintptr(oldlen) << shift
newlenmem = uintptr(cap) << shift newlenmem = uintptr(cap) << shift
capmem = roundupsize(uintptr(newcap) << shift) capmem = roundupsize(uintptr(newcap) << shift)
overflow = uintptr(newcap) > (maxAlloc >> shift) overflow = uintptr(newcap) > (maxAlloc >> shift)
newcap = int(capmem >> shift) newcap = int(capmem >> shift)
default: default:
lenmem = uintptr(old.len) * et.size lenmem = uintptr(oldlen) * et.size
newlenmem = uintptr(cap) * et.size newlenmem = uintptr(cap) * et.size
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
capmem = roundupsize(capmem) capmem = roundupsize(capmem)
...@@ -177,19 +177,19 @@ func growslice(et *_type, old slice, cap int) slice { ...@@ -177,19 +177,19 @@ func growslice(et *_type, old slice, cap int) slice {
var p unsafe.Pointer var p unsafe.Pointer
if et.kind&kindNoPointers != 0 { if et.kind&kindNoPointers != 0 {
p = mallocgc(capmem, nil, false) 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. // Only clear the part that will not be overwritten.
memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
} else { } else {
// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
p = mallocgc(capmem, et, true) p = mallocgc(capmem, et, true)
if writeBarrier.enabled { 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. // 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} return slice{p, cap, newcap}
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment