Commit b1403b69 by Ian Lance Taylor

compiler: eliminate bound checks in append expression

    
    The compiler generates two array index expressions when lowering
    an append expression. Currently they generate bound checks.
    Bound checks are not necessary in this case, as we know the slice
    has, or will grow to, enough length and capacity. Eliminate them.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/166817

From-SVN: r269699
parent 928499cf
946aa5ab2e82d045a2a3b2f18ba2c5b00e957c4b
80a7f6dae0861a06407a44c501b6346a4abd119c
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -8008,8 +8008,8 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
ref = Expression::make_temporary_reference(s1tmp, loc);
Expression* zero = Expression::make_integer_ul(0, int_type, loc);
Expression* ref2 = Expression::make_temporary_reference(ntmp, loc);
// FIXME: Mark this index as not requiring bounds checks.
ref = Expression::make_index(ref, zero, ref2, NULL, loc);
ref = Expression::make_array_index(ref, zero, ref2, NULL, loc);
ref->array_index_expression()->set_needs_bounds_check(false);
if (assign_lhs == NULL)
{
......@@ -8058,8 +8058,8 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
a1 = Expression::make_temporary_reference(s1tmp, loc);
ref = Expression::make_temporary_reference(l1tmp, loc);
Expression* nil = Expression::make_nil(loc);
// FIXME: Mark this index as not requiring bounds checks.
a1 = Expression::make_index(a1, ref, nil, NULL, 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);
......@@ -8086,9 +8086,9 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
ref2 = Expression::make_temporary_reference(l1tmp, loc);
Expression* off = Expression::make_integer_ul(i, int_type, loc);
ref2 = Expression::make_binary(OPERATOR_PLUS, ref2, off, loc);
// FIXME: Mark this index as not requiring bounds checks.
Expression* lhs = Expression::make_index(ref, ref2, NULL, NULL,
loc);
Expression* lhs = Expression::make_array_index(ref, ref2, NULL,
NULL, loc);
lhs->array_index_expression()->set_needs_bounds_check(false);
gogo->lower_expression(function, inserter, &lhs);
gogo->flatten_expression(function, inserter, &lhs);
// The flatten pass runs after the write barrier pass, so we
......@@ -11328,15 +11328,6 @@ Array_index_expression::do_get_backend(Translate_context* context)
if (length == NULL)
length = cap_arg;
int code = (array_type->length() != NULL
? (this->end_ == NULL
? RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS
: RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS)
: (this->end_ == NULL
? RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS
: RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS));
Bexpression* crash = gogo->runtime_error(code, loc)->get_backend(context);
if (this->start_->type()->integer_type() == NULL
&& !Type::are_convertible(int_type, this->start_->type(), NULL))
{
......@@ -11344,11 +11335,22 @@ Array_index_expression::do_get_backend(Translate_context* context)
return context->backend()->error_expression();
}
Bexpression* bad_index =
Expression::check_bounds(this->start_, loc)->get_backend(context);
Bexpression* start = this->start_->get_backend(context);
start = gogo->backend()->convert_expression(int_btype, start, loc);
Bexpression* crash = NULL;
Bexpression* bad_index = NULL;
if (this->needs_bounds_check_)
{
int code = (array_type->length() != NULL
? (this->end_ == NULL
? RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS
: RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS)
: (this->end_ == NULL
? RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS
: RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS));
crash = gogo->runtime_error(code, loc)->get_backend(context);
bad_index = Expression::check_bounds(this->start_, loc)->get_backend(context);
Bexpression* start_too_large =
gogo->backend()->binary_expression((this->end_ == NULL
? OPERATOR_GE
......@@ -11358,14 +11360,18 @@ Array_index_expression::do_get_backend(Translate_context* context)
? length
: capacity),
loc);
bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, start_too_large,
bad_index = gogo->backend()->binary_expression(OPERATOR_OROR,
start_too_large,
bad_index, loc);
}
Bfunction* bfn = context->function()->func_value()->get_decl();
if (this->end_ == NULL)
{
// Simple array indexing. This has to return an l-value, so
// wrap the index check into START.
if (this->needs_bounds_check_)
start =
gogo->backend()->conditional_expression(bfn, int_btype, bad_index,
crash, start, loc);
......@@ -11396,12 +11402,15 @@ Array_index_expression::do_get_backend(Translate_context* context)
if (this->cap_ != NULL)
{
cap_arg = gogo->backend()->convert_expression(int_btype, cap_arg, loc);
if (this->needs_bounds_check_)
{
Bexpression* bounds_bcheck =
Expression::check_bounds(this->cap_, loc)->get_backend(context);
bad_index =
gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck,
bad_index, loc);
cap_arg = gogo->backend()->convert_expression(int_btype, cap_arg, loc);
Bexpression* cap_too_small =
gogo->backend()->binary_expression(OPERATOR_LT, cap_arg, start, loc);
......@@ -11413,21 +11422,23 @@ Array_index_expression::do_get_backend(Translate_context* context)
bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_cap,
bad_index, loc);
}
}
Bexpression* end;
if (this->end_->is_nil_expression())
end = length;
else
{
end = this->end_->get_backend(context);
end = gogo->backend()->convert_expression(int_btype, end, loc);
if (this->needs_bounds_check_)
{
Bexpression* bounds_bcheck =
Expression::check_bounds(this->end_, loc)->get_backend(context);
bad_index =
gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck,
bad_index, loc);
end = this->end_->get_backend(context);
end = gogo->backend()->convert_expression(int_btype, end, loc);
Bexpression* end_too_small =
gogo->backend()->binary_expression(OPERATOR_LT, end, start, loc);
Bexpression* end_too_large =
......@@ -11438,6 +11449,7 @@ Array_index_expression::do_get_backend(Translate_context* context)
bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_end,
bad_index, loc);
}
}
Bexpression* result_length =
gogo->backend()->binary_expression(OPERATOR_MINUS, end, start, loc);
......@@ -11468,10 +11480,12 @@ Array_index_expression::do_get_backend(Translate_context* context)
init.push_back(result_length);
init.push_back(result_capacity);
Bexpression* ctor =
Bexpression* ret =
gogo->backend()->constructor_expression(struct_btype, init, loc);
return gogo->backend()->conditional_expression(bfn, struct_btype, bad_index,
crash, ctor, loc);
if (this->needs_bounds_check_)
ret = gogo->backend()->conditional_expression(bfn, struct_btype, bad_index,
crash, ret, loc);
return ret;
}
// Dump ast representation for an array index expression.
......
......@@ -2854,7 +2854,7 @@ class Array_index_expression : public Expression
Expression* end, Expression* cap, Location location)
: Expression(EXPRESSION_ARRAY_INDEX, location),
array_(array), start_(start), end_(end), cap_(cap), type_(NULL),
is_lvalue_(false)
is_lvalue_(false), needs_bounds_check_(true)
{ }
// Return the array.
......@@ -2898,6 +2898,10 @@ class Array_index_expression : public Expression
set_is_lvalue()
{ this->is_lvalue_ = true; }
void
set_needs_bounds_check(bool b)
{ this->needs_bounds_check_ = b; }
protected:
int
do_traverse(Traverse*);
......@@ -2917,7 +2921,7 @@ class Array_index_expression : public Expression
Expression*
do_copy()
{
return Expression::make_array_index(this->array_->copy(),
Expression* ret = Expression::make_array_index(this->array_->copy(),
this->start_->copy(),
(this->end_ == NULL
? NULL
......@@ -2926,6 +2930,8 @@ class Array_index_expression : public Expression
? NULL
: this->cap_->copy()),
this->location());
ret->array_index_expression()->set_needs_bounds_check(this->needs_bounds_check_);
return ret;
}
bool
......@@ -2962,6 +2968,8 @@ class Array_index_expression : public Expression
Type* type_;
// Whether expr appears in an lvalue context.
bool is_lvalue_;
// Whether bounds check is needed.
bool needs_bounds_check_;
};
// A string index. This is used for both indexing and slicing.
......
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