aboutsummaryrefslogtreecommitdiff
path: root/gcc/go/gofrontend/expressions.cc
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2019-05-31 21:18:39 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2019-05-31 21:18:39 +0000
commit6303331c3330222fef94eff8d8d35c99ca1d3e88 (patch)
tree99ef20c56f1bba3d53736e534b7e49f270169863 /gcc/go/gofrontend/expressions.cc
parent2b5360d7477277e7e0f32a5bd5479afac819b5e1 (diff)
downloadgcc-6303331c3330222fef94eff8d8d35c99ca1d3e88.zip
gcc-6303331c3330222fef94eff8d8d35c99ca1d3e88.tar.gz
gcc-6303331c3330222fef94eff8d8d35c99ca1d3e88.tar.bz2
compiler: optimize append of make
The gc compiler recognizes append(s, make([]T, n)...), and generates code to directly zero the tail instead of allocating a new slice and copying. This CL lets the Go frontend do basically the same. The difficulty is that at the point we handle append, there may already be temporaries introduced (e.g. in order_evaluations), which makes it hard to find the append-of-make pattern. The compiler could "see through" the value of a temporary, but it is only safe to do if the temporary is not assigned multiple times. For this, we add tracking of assignments and uses for temporaries. This also helps in optimizing non-escape slice make. We already optimize non-escape slice make with constant len/cap to stack allocation. But it failed to handle things like f(make([]T, n)) (where the slice doesn't escape and n is constant), because of the temporary. With tracking of temporary assignments and uses, it can handle this now as well. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/179597 From-SVN: r271822
Diffstat (limited to 'gcc/go/gofrontend/expressions.cc')
-rw-r--r--gcc/go/gofrontend/expressions.cc236
1 files changed, 190 insertions, 46 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index f0c12f4..9d8c085 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -1052,6 +1052,7 @@ Temporary_reference_expression*
Expression::make_temporary_reference(Temporary_statement* statement,
Location location)
{
+ statement->add_use();
return new Temporary_reference_expression(statement, location);
}
@@ -8286,23 +8287,87 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
Temporary_statement* l2tmp = NULL;
Expression_list* add = NULL;
Expression* len2;
+ Call_expression* makecall = NULL;
if (this->is_varargs())
{
go_assert(args->size() == 2);
- // s2tmp := s2
- s2tmp = Statement::make_temporary(NULL, args->back(), loc);
- inserter->insert(s2tmp);
+ std::pair<Call_expression*, Temporary_statement*> p =
+ Expression::find_makeslice_call(args->back());
+ makecall = p.first;
+ if (makecall != NULL)
+ {
+ // We are handling
+ // append(s, make([]T, len[, cap])...))
+ // which has already been lowered to
+ // append(s, runtime.makeslice(T, len, cap)).
+ // We will optimize this to directly zeroing the tail,
+ // instead of allocating a new slice then copy.
+
+ // Retrieve the length. Cannot reference s2 as we will remove
+ // the makeslice call.
+ Expression* len_arg = makecall->args()->at(1);
+ len_arg = Expression::make_cast(int_type, len_arg, loc);
+ l2tmp = Statement::make_temporary(int_type, len_arg, loc);
+ inserter->insert(l2tmp);
+
+ Expression* cap_arg = makecall->args()->at(2);
+ cap_arg = Expression::make_cast(int_type, cap_arg, loc);
+ Temporary_statement* c2tmp =
+ Statement::make_temporary(int_type, cap_arg, loc);
+ inserter->insert(c2tmp);
+
+ // Check bad len/cap here.
+ // if len2 < 0 { panicmakeslicelen(); }
+ len2 = Expression::make_temporary_reference(l2tmp, loc);
+ Expression* zero = Expression::make_integer_ul(0, int_type, loc);
+ Expression* cond = Expression::make_binary(OPERATOR_LT, len2,
+ zero, loc);
+ Expression* arg =
+ Expression::make_integer_ul(RUNTIME_ERROR_MAKE_SLICE_LEN_OUT_OF_BOUNDS,
+ NULL, loc);
+ Expression* call = Runtime::make_call(Runtime::RUNTIME_ERROR,
+ loc, 1, arg);
+ cond = Expression::make_conditional(cond, call, zero->copy(), loc);
+ gogo->lower_expression(function, inserter, &cond);
+ gogo->flatten_expression(function, inserter, &cond);
+ Statement* s = Statement::make_statement(cond, false);
+ inserter->insert(s);
+
+ // if cap2 < 0 { panicmakeslicecap(); }
+ Expression* cap2 = Expression::make_temporary_reference(c2tmp, loc);
+ cond = Expression::make_binary(OPERATOR_LT, cap2,
+ zero->copy(), loc);
+ arg = Expression::make_integer_ul(RUNTIME_ERROR_MAKE_SLICE_CAP_OUT_OF_BOUNDS,
+ NULL, loc);
+ call = Runtime::make_call(Runtime::RUNTIME_ERROR, loc, 1, arg);
+ cond = Expression::make_conditional(cond, call, zero->copy(), loc);
+ gogo->lower_expression(function, inserter, &cond);
+ gogo->flatten_expression(function, inserter, &cond);
+ s = Statement::make_statement(cond, false);
+ inserter->insert(s);
+
+ // Remove the original makeslice call.
+ Temporary_statement* ts = p.second;
+ if (ts != NULL && ts->uses() == 1)
+ ts->set_init(Expression::make_nil(loc));
+ }
+ else
+ {
+ // s2tmp := s2
+ s2tmp = Statement::make_temporary(NULL, args->back(), loc);
+ inserter->insert(s2tmp);
- // l2tmp := len(s2tmp)
- lenref = Expression::make_func_reference(lenfn, NULL, loc);
- call_args = new Expression_list();
- call_args->push_back(Expression::make_temporary_reference(s2tmp, loc));
- len = Expression::make_call(lenref, call_args, false, loc);
- gogo->lower_expression(function, inserter, &len);
- gogo->flatten_expression(function, inserter, &len);
- l2tmp = Statement::make_temporary(int_type, len, loc);
- inserter->insert(l2tmp);
+ // l2tmp := len(s2tmp)
+ lenref = Expression::make_func_reference(lenfn, NULL, loc);
+ call_args = new Expression_list();
+ call_args->push_back(Expression::make_temporary_reference(s2tmp, loc));
+ len = Expression::make_call(lenref, call_args, false, loc);
+ gogo->lower_expression(function, inserter, &len);
+ gogo->flatten_expression(function, inserter, &len);
+ l2tmp = Statement::make_temporary(int_type, len, loc);
+ inserter->insert(l2tmp);
+ }
// len2 = l2tmp
len2 = Expression::make_temporary_reference(l2tmp, loc);
@@ -8422,52 +8487,92 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
inserter->insert(assign);
}
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
+
if (this->is_varargs())
{
- if (element_type->has_pointer())
+ if (makecall != NULL)
{
- // copy(s1tmp[l1tmp:], s2tmp)
- a1 = Expression::make_temporary_reference(s1tmp, loc);
- ref = Expression::make_temporary_reference(l1tmp, loc);
- Expression* nil = Expression::make_nil(loc);
- a1 = Expression::make_array_index(a1, ref, nil, NULL, loc);
- a1->array_index_expression()->set_needs_bounds_check(false);
-
- a2 = Expression::make_temporary_reference(s2tmp, loc);
-
- Named_object* copyfn = gogo->lookup_global("copy");
- Expression* copyref = Expression::make_func_reference(copyfn, NULL, loc);
- call_args = new Expression_list();
- call_args->push_back(a1);
- call_args->push_back(a2);
- call = Expression::make_call(copyref, call_args, false, loc);
- }
- else
- {
- // memmove(&s1tmp[l1tmp], s2tmp.ptr, l2tmp*sizeof(elem))
+ // memclr(&s1tmp[l1tmp], l2tmp*sizeof(elem))
a1 = Expression::make_temporary_reference(s1tmp, loc);
ref = Expression::make_temporary_reference(l1tmp, loc);
a1 = Expression::make_array_index(a1, ref, NULL, NULL, loc);
a1->array_index_expression()->set_needs_bounds_check(false);
a1 = Expression::make_unary(OPERATOR_AND, a1, loc);
- a2 = Expression::make_temporary_reference(s2tmp, loc);
- a2 = (a2->type()->is_string_type()
- ? Expression::make_string_info(a2,
- STRING_INFO_DATA,
- loc)
- : Expression::make_slice_info(a2,
- SLICE_INFO_VALUE_POINTER,
- loc));
-
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
ref = Expression::make_temporary_reference(l2tmp, loc);
ref = Expression::make_cast(uintptr_type, ref, loc);
- a3 = Expression::make_type_info(element_type, TYPE_INFO_SIZE);
- a3 = Expression::make_binary(OPERATOR_MULT, a3, ref, loc);
+ a2 = Expression::make_type_info(element_type, TYPE_INFO_SIZE);
+ a2 = Expression::make_binary(OPERATOR_MULT, a2, ref, loc);
- call = Runtime::make_call(Runtime::BUILTIN_MEMMOVE, loc, 3,
- a1, a2, a3);
+ Runtime::Function code = (element_type->has_pointer()
+ ? Runtime::MEMCLRHASPTR
+ : Runtime::MEMCLRNOPTR);
+ call = Runtime::make_call(code, loc, 2, a1, a2);
+
+ if (element_type->has_pointer())
+ {
+ // For a slice containing pointers, growslice already zeroed
+ // the memory. We only need to zero in non-growing case.
+ // Note: growslice does not zero the memory in non-pointer case.
+ 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);
+ Expression* zero = Expression::make_integer_ul(0, int_type, loc);
+ call = Expression::make_conditional(cond, call, zero, loc);
+ }
+ }
+ else
+ {
+ if (element_type->has_pointer())
+ {
+ // copy(s1tmp[l1tmp:], s2tmp)
+ a1 = Expression::make_temporary_reference(s1tmp, loc);
+ ref = Expression::make_temporary_reference(l1tmp, loc);
+ Expression* nil = Expression::make_nil(loc);
+ a1 = Expression::make_array_index(a1, ref, nil, NULL, loc);
+ a1->array_index_expression()->set_needs_bounds_check(false);
+
+ a2 = Expression::make_temporary_reference(s2tmp, loc);
+
+ Named_object* copyfn = gogo->lookup_global("copy");
+ Expression* copyref = Expression::make_func_reference(copyfn, NULL, loc);
+ call_args = new Expression_list();
+ call_args->push_back(a1);
+ call_args->push_back(a2);
+ call = Expression::make_call(copyref, call_args, false, loc);
+ }
+ else
+ {
+ // memmove(&s1tmp[l1tmp], s2tmp.ptr, l2tmp*sizeof(elem))
+ a1 = Expression::make_temporary_reference(s1tmp, loc);
+ ref = Expression::make_temporary_reference(l1tmp, loc);
+ a1 = Expression::make_array_index(a1, ref, NULL, NULL, loc);
+ a1->array_index_expression()->set_needs_bounds_check(false);
+ a1 = Expression::make_unary(OPERATOR_AND, a1, loc);
+
+ a2 = Expression::make_temporary_reference(s2tmp, loc);
+ a2 = (a2->type()->is_string_type()
+ ? Expression::make_string_info(a2,
+ STRING_INFO_DATA,
+ loc)
+ : Expression::make_slice_info(a2,
+ SLICE_INFO_VALUE_POINTER,
+ loc));
+
+ ref = Expression::make_temporary_reference(l2tmp, loc);
+ ref = Expression::make_cast(uintptr_type, ref, loc);
+ a3 = Expression::make_type_info(element_type, TYPE_INFO_SIZE);
+ a3 = Expression::make_binary(OPERATOR_MULT, a3, ref, loc);
+
+ call = Runtime::make_call(Runtime::BUILTIN_MEMMOVE, loc, 3,
+ a1, a2, a3);
+ }
}
gogo->lower_expression(function, inserter, &call);
gogo->flatten_expression(function, inserter, &call);
@@ -16540,6 +16645,45 @@ Expression::make_slice_value(Type* at, Expression* valmem, Expression* len,
return new Slice_value_expression(at, valmem, len, cap, location);
}
+// Look through the expression of a Slice_value_expression's valmem to
+// find an call to makeslice. If found, return the call expression and
+// the containing temporary statement (if any).
+
+std::pair<Call_expression*, Temporary_statement*>
+Expression::find_makeslice_call(Expression* expr)
+{
+ Unsafe_type_conversion_expression* utce =
+ expr->unsafe_conversion_expression();
+ if (utce != NULL)
+ expr = utce->expr();
+
+ Slice_value_expression* sve = expr->slice_value_expression();
+ if (sve == NULL)
+ return std::make_pair<Call_expression*, Temporary_statement*>(NULL, NULL);
+ expr = sve->valmem();
+
+ utce = expr->unsafe_conversion_expression();
+ if (utce != NULL)
+ expr = utce->expr();
+
+ Temporary_reference_expression* tre = expr->temporary_reference_expression();
+ Temporary_statement* ts = (tre != NULL ? tre->statement() : NULL);
+ if (ts != NULL && ts->init() != NULL && !ts->assigned()
+ && !ts->is_address_taken())
+ expr = ts->init();
+
+ Call_expression* call = expr->call_expression();
+ if (call == NULL)
+ return std::make_pair<Call_expression*, Temporary_statement*>(NULL, NULL);
+
+ Func_expression* fe = call->fn()->func_expression();
+ if (fe != NULL
+ && fe->runtime_code() == Runtime::MAKESLICE)
+ return std::make_pair(call, ts);
+
+ return std::make_pair<Call_expression*, Temporary_statement*>(NULL, NULL);
+}
+
// An expression that evaluates to some characteristic of a non-empty interface.
// This is used to access the method table or underlying object of an interface.