diff options
author | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2014-01-23 11:48:32 +0000 |
---|---|---|
committer | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2014-01-23 11:48:32 +0000 |
commit | f6fc17d03f28a94e35a53d0562595ea179ae2629 (patch) | |
tree | e32f9a0a6bdc05a424906b384d8c31c4fa78eb35 /gcc/go/gofrontend | |
parent | 8e6ce8a843f5da1c63f8e21c58188e7b7f1a471f (diff) | |
download | gcc-f6fc17d03f28a94e35a53d0562595ea179ae2629.tar.gz |
2014-01-23 Basile Starynkevitch <basile@starynkevitch.net>
{{merge using svnmerge.py with trunk GCC 4.9 svn rev.206958. All is
well compiled.}}
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@206959 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/go/gofrontend')
-rw-r--r-- | gcc/go/gofrontend/backend.h | 22 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 1090 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 89 | ||||
-rw-r--r-- | gcc/go/gofrontend/go.cc | 5 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 215 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 18 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 11 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.h | 19 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 92 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 42 |
10 files changed, 1084 insertions, 519 deletions
diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h index 55805941da6..e3025167fd4 100644 --- a/gcc/go/gofrontend/backend.h +++ b/gcc/go/gofrontend/backend.h @@ -10,6 +10,8 @@ #include <gmp.h> #include <mpfr.h> +#include "operator.h" + // Pointers to these types are created by the backend, passed to the // frontend, and passed back to the backend. The types must be // defined by the backend using these names. @@ -284,6 +286,26 @@ class Backend virtual Bexpression* struct_field_expression(Bexpression* bstruct, size_t index, Location) = 0; + // Create an expression that executes BSTAT before BEXPR. + virtual Bexpression* + compound_expression(Bstatement* bstat, Bexpression* bexpr, Location) = 0; + + // Return an expression that executes THEN_EXPR if CONDITION is true, or + // ELSE_EXPR otherwise and returns the result as type BTYPE. ELSE_EXPR + // may be NULL. BTYPE may be NULL. + virtual Bexpression* + conditional_expression(Btype* btype, Bexpression* condition, + Bexpression* then_expr, Bexpression* else_expr, + Location) = 0; + + // Return an expression for the binary operation LEFT OP RIGHT. + // Supported values of OP are (from operators.h): + // EQEQ, NOTEQ, LT, LE, GT, GE, PLUS, MINUS, OR, XOR, MULT, DIV, MOD, + // LSHIFT, RSHIFT, AND, NOT. + virtual Bexpression* + binary_expression(Operator op, Bexpression* left, Bexpression* right, + Location) = 0; + // Statements. // Create an error statement. This is used for cases which should diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 2f1c026c983..544996ea6cb 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3060,6 +3060,9 @@ class Type_conversion_expression : public Expression Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + bool do_is_constant() const; @@ -3203,6 +3206,25 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*, return this; } +// Flatten a type conversion by using a temporary variable for the slice +// in slice to string conversions. + +Expression* +Type_conversion_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + if (this->type()->is_string_type() + && this->expr_->type()->is_slice_type() + && !this->expr_->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->expr_, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_temporary_reference(temp, this->location()); + } + return this; +} + // Return whether a type conversion is a constant. bool @@ -3361,47 +3383,24 @@ Type_conversion_expression::do_get_tree(Translate_context* context) } else if (type->is_string_type() && expr_type->is_slice_type()) { - if (!DECL_P(expr_tree)) - expr_tree = save_expr(expr_tree); - - Type* int_type = Type::lookup_integer_type("int"); - tree int_type_tree = type_to_tree(int_type->get_backend(gogo)); - + Location location = this->location(); Array_type* a = expr_type->array_type(); Type* e = a->element_type()->forwarded(); go_assert(e->integer_type() != NULL); - tree valptr = fold_convert(const_ptr_type_node, - a->value_pointer_tree(gogo, expr_tree)); - tree len = a->length_tree(gogo, expr_tree); - len = fold_convert_loc(this->location().gcc_location(), int_type_tree, - len); + go_assert(this->expr_->is_variable()); + + Runtime::Function code; if (e->integer_type()->is_byte()) - { - static tree byte_array_to_string_fndecl; - ret = Gogo::call_builtin(&byte_array_to_string_fndecl, - this->location(), - "__go_byte_array_to_string", - 2, - type_tree, - const_ptr_type_node, - valptr, - int_type_tree, - len); - } + code = Runtime::BYTE_ARRAY_TO_STRING; else - { - go_assert(e->integer_type()->is_rune()); - static tree int_array_to_string_fndecl; - ret = Gogo::call_builtin(&int_array_to_string_fndecl, - this->location(), - "__go_int_array_to_string", - 2, - type_tree, - const_ptr_type_node, - valptr, - int_type_tree, - len); - } + { + go_assert(e->integer_type()->is_rune()); + code = Runtime::INT_ARRAY_TO_STRING; + } + Expression* valptr = a->get_value_pointer(gogo, this->expr_); + Expression* len = a->get_length(gogo, this->expr_); + Expression* a2s_expr = Runtime::make_call(code, location, 2, valptr, len); + ret = a2s_expr->get_tree(context); } else if (type->is_slice_type() && expr_type->is_string_type()) { @@ -5537,6 +5536,61 @@ Binary_expression::lower_compare_to_memcmp(Gogo*, Statement_inserter* inserter) return Expression::make_binary(this->op_, call, zero, loc); } +Expression* +Binary_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + Location loc = this->location(); + Temporary_statement* temp; + if (this->left_->type()->is_string_type() + && this->op_ == OPERATOR_PLUS) + { + if (!this->left_->is_variable()) + { + temp = Statement::make_temporary(NULL, this->left_, loc); + inserter->insert(temp); + this->left_ = Expression::make_temporary_reference(temp, loc); + } + if (!this->right_->is_variable()) + { + temp = + Statement::make_temporary(this->left_->type(), this->right_, loc); + this->right_ = Expression::make_temporary_reference(temp, loc); + inserter->insert(temp); + } + } + + Type* left_type = this->left_->type(); + bool is_shift_op = (this->op_ == OPERATOR_LSHIFT + || this->op_ == OPERATOR_RSHIFT); + bool is_idiv_op = ((this->op_ == OPERATOR_DIV && + left_type->integer_type() != NULL) + || this->op_ == OPERATOR_MOD); + + // FIXME: go_check_divide_zero and go_check_divide_overflow are globals + // defined in gcc/go/lang.opt. These should be defined in go_create_gogo + // and accessed from the Gogo* passed to do_flatten. + if (is_shift_op + || (is_idiv_op && (go_check_divide_zero || go_check_divide_overflow))) + { + if (!this->left_->is_variable()) + { + temp = Statement::make_temporary(NULL, this->left_, loc); + inserter->insert(temp); + this->left_ = Expression::make_temporary_reference(temp, loc); + } + if (!this->right_->is_variable()) + { + temp = + Statement::make_temporary(NULL, this->right_, loc); + this->right_ = Expression::make_temporary_reference(temp, loc); + inserter->insert(temp); + } + } + return this; +} + + // Return the address of EXPR, cast to unsafe.Pointer. Expression* @@ -5957,14 +6011,10 @@ tree Binary_expression::do_get_tree(Translate_context* context) { Gogo* gogo = context->gogo(); + Location loc = this->location(); + Type* left_type = this->left_->type(); + Type* right_type = this->right_->type(); - tree left = this->left_->get_tree(context); - tree right = this->right_->get_tree(context); - - if (left == error_mark_node || right == error_mark_node) - return error_mark_node; - - enum tree_code code; bool use_left_type = true; bool is_shift_op = false; bool is_idiv_op = false; @@ -5976,198 +6026,126 @@ Binary_expression::do_get_tree(Translate_context* context) case OPERATOR_LE: case OPERATOR_GT: case OPERATOR_GE: - return Expression::comparison_tree(context, this->type_, this->op_, - this->left_, this->right_, - this->location()); + { + Bexpression* ret = + Expression::comparison(context, this->type_, this->op_, + this->left_, this->right_, loc); + return expr_to_tree(ret); + } case OPERATOR_OROR: - code = TRUTH_ORIF_EXPR; - use_left_type = false; - break; case OPERATOR_ANDAND: - code = TRUTH_ANDIF_EXPR; use_left_type = false; break; case OPERATOR_PLUS: - code = PLUS_EXPR; - break; case OPERATOR_MINUS: - code = MINUS_EXPR; - break; case OPERATOR_OR: - code = BIT_IOR_EXPR; - break; case OPERATOR_XOR: - code = BIT_XOR_EXPR; - break; case OPERATOR_MULT: - code = MULT_EXPR; break; case OPERATOR_DIV: - { - Type *t = this->left_->type(); - if (t->float_type() != NULL || t->complex_type() != NULL) - code = RDIV_EXPR; - else - { - code = TRUNC_DIV_EXPR; - is_idiv_op = true; - } - } - break; + if (left_type->float_type() != NULL || left_type->complex_type() != NULL) + break; case OPERATOR_MOD: - code = TRUNC_MOD_EXPR; is_idiv_op = true; break; case OPERATOR_LSHIFT: - code = LSHIFT_EXPR; - is_shift_op = true; - break; case OPERATOR_RSHIFT: - code = RSHIFT_EXPR; is_shift_op = true; break; - case OPERATOR_AND: - code = BIT_AND_EXPR; - break; case OPERATOR_BITCLEAR: - right = fold_build1(BIT_NOT_EXPR, TREE_TYPE(right), right); - code = BIT_AND_EXPR; + this->right_ = Expression::make_unary(OPERATOR_XOR, this->right_, loc); + case OPERATOR_AND: break; default: go_unreachable(); } - location_t gccloc = this->location().gcc_location(); - tree type = use_left_type ? TREE_TYPE(left) : TREE_TYPE(right); - - if (this->left_->type()->is_string_type()) + if (left_type->is_string_type()) { go_assert(this->op_ == OPERATOR_PLUS); - Type* st = Type::make_string_type(); - tree string_type = type_to_tree(st->get_backend(gogo)); - static tree string_plus_decl; - return Gogo::call_builtin(&string_plus_decl, - this->location(), - "__go_string_plus", - 2, - string_type, - string_type, - left, - string_type, - right); - } - - // For complex division Go wants slightly different results than the - // GCC library provides, so we have our own runtime routine. + Expression* string_plus = + Runtime::make_call(Runtime::STRING_PLUS, loc, 2, + this->left_, this->right_); + return string_plus->get_tree(context); + } + + // For complex division Go might want slightly different results than the + // backend implementation provides, so we have our own runtime routine. if (this->op_ == OPERATOR_DIV && this->left_->type()->complex_type() != NULL) { - const char *name; - tree *pdecl; - Type* ctype; - static tree complex64_div_decl; - static tree complex128_div_decl; + Runtime::Function complex_code; switch (this->left_->type()->complex_type()->bits()) { case 64: - name = "__go_complex64_div"; - pdecl = &complex64_div_decl; - ctype = Type::lookup_complex_type("complex64"); + complex_code = Runtime::COMPLEX64_DIV; break; case 128: - name = "__go_complex128_div"; - pdecl = &complex128_div_decl; - ctype = Type::lookup_complex_type("complex128"); + complex_code = Runtime::COMPLEX128_DIV; break; default: go_unreachable(); } - Btype* cbtype = ctype->get_backend(gogo); - tree ctype_tree = type_to_tree(cbtype); - return Gogo::call_builtin(pdecl, - this->location(), - name, - 2, - ctype_tree, - ctype_tree, - fold_convert_loc(gccloc, ctype_tree, left), - type, - fold_convert_loc(gccloc, ctype_tree, right)); + Expression* complex_div = + Runtime::make_call(complex_code, loc, 2, this->left_, this->right_); + return complex_div->get_tree(context); } - tree compute_type = excess_precision_type(type); - if (compute_type != NULL_TREE) - { - left = ::convert(compute_type, left); - right = ::convert(compute_type, right); - } + Bexpression* left = tree_to_expr(this->left_->get_tree(context)); + Bexpression* right = tree_to_expr(this->right_->get_tree(context)); - tree eval_saved = NULL_TREE; - if (is_shift_op - || (is_idiv_op && (go_check_divide_zero || go_check_divide_overflow))) - { - // Make sure the values are evaluated. - if (!DECL_P(left)) - { - left = save_expr(left); - eval_saved = left; - } - if (!DECL_P(right)) - { - right = save_expr(right); - if (eval_saved == NULL_TREE) - eval_saved = right; - else - eval_saved = fold_build2_loc(gccloc, COMPOUND_EXPR, - void_type_node, eval_saved, right); - } - } + Type* type = use_left_type ? left_type : right_type; + Btype* btype = type->get_backend(gogo); + + Bexpression* ret = + gogo->backend()->binary_expression(this->op_, left, right, loc); + ret = gogo->backend()->convert_expression(btype, ret, loc); - tree ret = fold_build2_loc(gccloc, code, - compute_type != NULL_TREE ? compute_type : type, - left, right); + // Initialize overflow constants. + Bexpression* overflow; + mpz_t zero; + mpz_init_set_ui(zero, 0UL); + mpz_t one; + mpz_init_set_ui(one, 1UL); + mpz_t neg_one; + mpz_init_set_si(neg_one, -1); - if (compute_type != NULL_TREE) - ret = ::convert(type, ret); + Btype* left_btype = left_type->get_backend(gogo); + Btype* right_btype = right_type->get_backend(gogo); // In Go, a shift larger than the size of the type is well-defined. - // This is not true in GENERIC, so we need to insert a conditional. + // This is not true in C, so we need to insert a conditional. if (is_shift_op) { - go_assert(INTEGRAL_TYPE_P(TREE_TYPE(left))); - go_assert(this->left_->type()->integer_type() != NULL); - int bits = TYPE_PRECISION(TREE_TYPE(left)); + go_assert(left_type->integer_type() != NULL); - tree compare = fold_build2(LT_EXPR, boolean_type_node, right, - build_int_cst_type(TREE_TYPE(right), bits)); + mpz_t bitsval; + int bits = left_type->integer_type()->bits(); + mpz_init_set_ui(bitsval, bits); + Bexpression* bits_expr = + gogo->backend()->integer_constant_expression(right_btype, bitsval); + Bexpression* compare = + gogo->backend()->binary_expression(OPERATOR_LT, + right, bits_expr, loc); - tree overflow_result = fold_convert_loc(gccloc, TREE_TYPE(left), - integer_zero_node); + Bexpression* zero_expr = + gogo->backend()->integer_constant_expression(left_btype, zero); + overflow = zero_expr; if (this->op_ == OPERATOR_RSHIFT - && !this->left_->type()->integer_type()->is_unsigned()) + && !left_type->integer_type()->is_unsigned()) { - tree neg = - fold_build2_loc(gccloc, LT_EXPR, boolean_type_node, - left, - fold_convert_loc(gccloc, TREE_TYPE(left), - integer_zero_node)); - tree neg_one = - fold_build2_loc(gccloc, MINUS_EXPR, TREE_TYPE(left), - fold_convert_loc(gccloc, TREE_TYPE(left), - integer_zero_node), - fold_convert_loc(gccloc, TREE_TYPE(left), - integer_one_node)); - overflow_result = - fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(left), - neg, neg_one, overflow_result); + Bexpression* neg_expr = + gogo->backend()->binary_expression(OPERATOR_LT, left, + zero_expr, loc); + Bexpression* neg_one_expr = + gogo->backend()->integer_constant_expression(left_btype, neg_one); + overflow = gogo->backend()->conditional_expression(btype, neg_expr, + neg_one_expr, + zero_expr, loc); } - - ret = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(left), - compare, ret, overflow_result); - - if (eval_saved != NULL_TREE) - ret = fold_build2_loc(gccloc, COMPOUND_EXPR, TREE_TYPE(ret), - eval_saved, ret); + ret = gogo->backend()->conditional_expression(btype, compare, ret, + overflow, loc); + mpz_clear(bitsval); } // Add checks for division by zero and division overflow as needed. @@ -6176,23 +6154,20 @@ Binary_expression::do_get_tree(Translate_context* context) if (go_check_divide_zero) { // right == 0 - tree check = fold_build2_loc(gccloc, EQ_EXPR, boolean_type_node, - right, - fold_convert_loc(gccloc, - TREE_TYPE(right), - integer_zero_node)); + Bexpression* zero_expr = + gogo->backend()->integer_constant_expression(right_btype, zero); + Bexpression* check = + gogo->backend()->binary_expression(OPERATOR_EQEQ, + right, zero_expr, loc); - // __go_runtime_error(RUNTIME_ERROR_DIVISION_BY_ZERO), 0 + // __go_runtime_error(RUNTIME_ERROR_DIVISION_BY_ZERO) int errcode = RUNTIME_ERROR_DIVISION_BY_ZERO; - Expression* crash = gogo->runtime_error(errcode, this->location()); - tree panic = fold_build2_loc(gccloc, COMPOUND_EXPR, TREE_TYPE(ret), - crash->get_tree(context), - fold_convert_loc(gccloc, TREE_TYPE(ret), - integer_zero_node)); + Expression* crash = gogo->runtime_error(errcode, loc); + Bexpression* crash_expr = tree_to_expr(crash->get_tree(context)); // right == 0 ? (__go_runtime_error(...), 0) : ret - ret = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret), - check, panic, ret); + ret = gogo->backend()->conditional_expression(btype, check, + crash_expr, ret, loc); } if (go_check_divide_overflow) @@ -6200,60 +6175,62 @@ Binary_expression::do_get_tree(Translate_context* context) // right == -1 // FIXME: It would be nice to say that this test is expected // to return false. - tree m1 = integer_minus_one_node; - tree check = fold_build2_loc(gccloc, EQ_EXPR, boolean_type_node, - right, - fold_convert_loc(gccloc, - TREE_TYPE(right), - m1)); - - tree overflow; - if (TYPE_UNSIGNED(TREE_TYPE(ret))) + + Bexpression* neg_one_expr = + gogo->backend()->integer_constant_expression(right_btype, neg_one); + Bexpression* check = + gogo->backend()->binary_expression(OPERATOR_EQEQ, + right, neg_one_expr, loc); + + Bexpression* zero_expr = + gogo->backend()->integer_constant_expression(btype, zero); + Bexpression* one_expr = + gogo->backend()->integer_constant_expression(btype, one); + + if (type->integer_type()->is_unsigned()) { // An unsigned -1 is the largest possible number, so // dividing is always 1 or 0. - tree cmp = fold_build2_loc(gccloc, EQ_EXPR, boolean_type_node, - left, right); + + Bexpression* cmp = + gogo->backend()->binary_expression(OPERATOR_EQEQ, + left, right, loc); if (this->op_ == OPERATOR_DIV) - overflow = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret), - cmp, - fold_convert_loc(gccloc, - TREE_TYPE(ret), - integer_one_node), - fold_convert_loc(gccloc, - TREE_TYPE(ret), - integer_zero_node)); + overflow = + gogo->backend()->conditional_expression(btype, cmp, + one_expr, zero_expr, + loc); else - overflow = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret), - cmp, - fold_convert_loc(gccloc, - TREE_TYPE(ret), - integer_zero_node), - left); + overflow = + gogo->backend()->conditional_expression(btype, cmp, + zero_expr, left, + loc); } else { // Computing left / -1 is the same as computing - left, // which does not overflow since Go sets -fwrapv. if (this->op_ == OPERATOR_DIV) - overflow = fold_build1_loc(gccloc, NEGATE_EXPR, TREE_TYPE(left), - left); + { + Expression* negate_expr = + Expression::make_unary(OPERATOR_MINUS, this->left_, loc); + overflow = tree_to_expr(negate_expr->get_tree(context)); + } else - overflow = integer_zero_node; + overflow = zero_expr; } - overflow = fold_convert_loc(gccloc, TREE_TYPE(ret), overflow); + overflow = gogo->backend()->convert_expression(btype, overflow, loc); // right == -1 ? - left : ret - ret = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret), - check, overflow, ret); + ret = gogo->backend()->conditional_expression(btype, check, overflow, + ret, loc); } - - if (eval_saved != NULL_TREE) - ret = fold_build2_loc(gccloc, COMPOUND_EXPR, TREE_TYPE(ret), - eval_saved, ret); } - return ret; + mpz_clear(zero); + mpz_clear(one); + mpz_clear(neg_one); + return expr_to_tree(ret); } // Export a binary expression. @@ -6472,55 +6449,24 @@ Expression::make_binary(Operator op, Expression* left, Expression* right, // Implement a comparison. -tree -Expression::comparison_tree(Translate_context* context, Type* result_type, - Operator op, Expression* left_expr, - Expression* right_expr, Location location) +Bexpression* +Expression::comparison(Translate_context* context, Type* result_type, + Operator op, Expression* left, Expression* right, + Location location) { - Type* left_type = left_expr->type(); - Type* right_type = right_expr->type(); + Type* left_type = left->type(); + Type* right_type = right->type(); mpz_t zval; mpz_init_set_ui(zval, 0UL); Expression* zexpr = Expression::make_integer(&zval, NULL, location); mpz_clear(zval); - enum tree_code code; - switch (op) - { - case OPERATOR_EQEQ: - code = EQ_EXPR; - break; - case OPERATOR_NOTEQ: - code = NE_EXPR; - break; - case OPERATOR_LT: - code = LT_EXPR; - break; - case OPERATOR_LE: - code = LE_EXPR; - break; - case OPERATOR_GT: - code = GT_EXPR; - break; - case OPERATOR_GE: - code = GE_EXPR; - break; - default: - go_unreachable(); - } - - // FIXME: Computing the tree here means it will be computed multiple times, - // which is wasteful. This is a temporary modification until all tree code - // here can be replaced with frontend expressions. - tree left_tree = left_expr->get_tree(context); - tree right_tree = right_expr->get_tree(context); if (left_type->is_string_type() && right_type->is_string_type()) { - Expression* strcmp_call = Runtime::make_call(Runtime::STRCMP, location, 2, - left_expr, right_expr); - left_tree = strcmp_call->get_tree(context); - right_tree = zexpr->get_tree(context); + left = Runtime::make_call(Runtime::STRCMP, location, 2, + left, right); + right = zexpr; } else if ((left_type->interface_type() != NULL && right_type->interface_type() == NULL @@ -6533,31 +6479,30 @@ Expression::comparison_tree(Translate_context* context, Type* result_type, if (left_type->interface_type() == NULL) { std::swap(left_type, right_type); - std::swap(left_expr, right_expr); + std::swap(left, right); } // The right operand is not an interface. We need to take its // address if it is not a pointer. Expression* pointer_arg = NULL; if (right_type->points_to() != NULL) - pointer_arg = right_expr; + pointer_arg = right; else { - go_assert(right_expr->is_addressable()); - pointer_arg = Expression::make_unary(OPERATOR_AND, right_expr, + go_assert(right->is_addressable()); + pointer_arg = Expression::make_unary(OPERATOR_AND, right, location); } - Expression* descriptor_expr = Expression::make_type_descriptor(right_type, - location); - Call_expression* iface_valcmp = + Expression* descriptor = + Expression::make_type_descriptor(right_type, location); + left = Runtime::make_call((left_type->interface_type()->is_empty() ? Runtime::EMPTY_INTERFACE_VALUE_COMPARE : Runtime::INTERFACE_VALUE_COMPARE), - location, 3, left_expr, descriptor_expr, + location, 3, left, descriptor, pointer_arg); - left_tree = iface_valcmp->get_tree(context); - right_tree = zexpr->get_tree(context); + right = zexpr; } else if (left_type->interface_type() != NULL && right_type->interface_type() != NULL) @@ -6575,66 +6520,49 @@ Expression::comparison_tree(Translate_context* context, Type* result_type, { go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ); std::swap(left_type, right_type); - std::swap(left_expr, right_expr); + std::swap(left, right); } go_assert(!left_type->interface_type()->is_empty()); go_assert(right_type->interface_type()->is_empty()); compare_function = Runtime::INTERFACE_EMPTY_COMPARE; } - Call_expression* ifacecmp_call = - Runtime::make_call(compare_function, location, 2, - left_expr, right_expr); - - left_tree = ifacecmp_call->get_tree(context); - right_tree = zexpr->get_tree(context); + left = Runtime::make_call(compare_function, location, 2, left, right); + right = zexpr; } if (left_type->is_nil_type() && (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ)) { std::swap(left_type, right_type); - std::swap(left_tree, right_tree); + std::swap(left, right); } if (right_type->is_nil_type()) { + right = Expression::make_nil(location); if (left_type->array_type() != NULL && left_type->array_type()->length() == NULL) { Array_type* at = left_type->array_type(); - left_tree = at->value_pointer_tree(context->gogo(), left_tree); - right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node); + left = at->get_value_pointer(context->gogo(), left); } else if (left_type->interface_type() != NULL) { // An interface is nil if the first field is nil. - tree left_type_tree = TREE_TYPE(left_tree); - go_assert(TREE_CODE(left_type_tree) == RECORD_TYPE); - tree field = TYPE_FIELDS(left_type_tree); - left_tree = build3(COMPONENT_REF, TREE_TYPE(field), left_tree, - field, NULL_TREE); - right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node); - } - else - { - go_assert(POINTER_TYPE_P(TREE_TYPE(left_tree))); - right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node); + left = Expression::make_field_reference(left, 0, location); } } - if (left_tree == error_mark_node || right_tree == error_mark_node) - return error_mark_node; + Bexpression* left_bexpr = tree_to_expr(left->get_tree(context)); + Bexpression* right_bexpr = tree_to_expr(right->get_tree(context)); - tree result_type_tree; - if (result_type == NULL) - result_type_tree = boolean_type_node; - else - result_type_tree = type_to_tree(result_type->get_backend(context->gogo())); - - tree ret = fold_build2(code, result_type_tree, left_tree, right_tree); - if (CAN_HAVE_LOCATION_P(ret)) - SET_EXPR_LOCATION(ret, location.gcc_location()); + Gogo* gogo = context->gogo(); + Bexpression* ret = gogo->backend()->binary_expression(op, left_bexpr, + right_bexpr, location); + if (result_type != NULL) + ret = gogo->backend()->convert_expression(result_type->get_backend(gogo), + ret, location); return ret; } @@ -6850,6 +6778,7 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, Block* b = gogo->finish_block(loc); gogo->add_block(b, loc); gogo->lower_block(new_no, b); + gogo->flatten_block(new_no, b); gogo->finish_function(loc); ins.first->second = new_no; @@ -7037,6 +6966,9 @@ class Builtin_call_expression : public Call_expression Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + bool do_is_constant() const; @@ -7367,6 +7299,36 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, return this; } +// Flatten a builtin call expression. This turns the arguments of copy and +// append into temporary expressions. + +Expression* +Builtin_call_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + if (this->code_ == BUILTIN_APPEND + || this->code_ == BUILTIN_COPY) + { + Location loc = this->location(); + Type* at = this->args()->front()->type(); + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) + { + if ((*pa)->is_nil_expression()) + *pa = Expression::make_slice_composite_literal(at, NULL, loc); + if (!(*pa)->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + } + return this; +} + // Lower a make expression. Expression* @@ -8503,7 +8465,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) return error_mark_node; } this->seen_ = true; - val_tree = arg_type->array_type()->length_tree(gogo, arg_tree); + Expression* len = arg_type->array_type()->get_length(gogo, arg); + val_tree = len->get_tree(context); this->seen_ = false; } else if (arg_type->map_type() != NULL) @@ -8543,8 +8506,9 @@ Builtin_call_expression::do_get_tree(Translate_context* context) return error_mark_node; } this->seen_ = true; - val_tree = arg_type->array_type()->capacity_tree(gogo, - arg_tree); + Expression* cap = + arg_type->array_type()->get_capacity(gogo, arg); + val_tree = cap->get_tree(context); this->seen_ = false; } else if (arg_type->channel_type() != NULL) @@ -8848,9 +8812,11 @@ Builtin_call_expression::do_get_tree(Translate_context* context) Type* arg1_type = arg1->type(); Array_type* at = arg1_type->array_type(); - arg1_tree = save_expr(arg1_tree); - tree arg1_val = at->value_pointer_tree(gogo, arg1_tree); - tree arg1_len = at->length_tree(gogo, arg1_tree); + go_assert(arg1->is_variable()); + Expression* arg1_valptr = at->get_value_pointer(gogo, arg1); + Expression* arg1_len_expr = at->get_length(gogo, arg1); + tree arg1_val = arg1_valptr->get_tree(context); + tree arg1_len = arg1_len_expr->get_tree(context); if (arg1_val == error_mark_node || arg1_len == error_mark_node) return error_mark_node; @@ -8860,9 +8826,11 @@ Builtin_call_expression::do_get_tree(Translate_context* context) if (arg2_type->is_slice_type()) { at = arg2_type->array_type(); - arg2_tree = save_expr(arg2_tree); - arg2_val = at->value_pointer_tree(gogo, arg2_tree); - arg2_len = at->length_tree(gogo, arg2_tree); + go_assert(arg2->is_variable()); + Expression* arg2_valptr = at->get_value_pointer(gogo, arg2); + Expression* arg2_len_expr = at->get_length(gogo, arg2); + arg2_val = arg2_valptr->get_tree(context); + arg2_len = arg2_len_expr->get_tree(context); } else { @@ -8950,23 +8918,15 @@ Builtin_call_expression::do_get_tree(Translate_context* context) } else { - arg2_tree = Expression::convert_for_assignment(context, at, - arg2->type(), - arg2_tree, - location); - if (arg2_tree == error_mark_node) + go_assert(arg2->is_variable()); + arg2_val = + at->get_value_pointer(gogo, arg2)->get_tree(context); + arg2_len = at->get_length(gogo, arg2)->get_tree(context); + Btype* element_btype = element_type->get_backend(gogo); + tree element_type_tree = type_to_tree(element_btype); + if (element_type_tree == error_mark_node) return error_mark_node; - - arg2_tree = save_expr(arg2_tree); - - arg2_val = at->value_pointer_tree(gogo, arg2_tree); - arg2_len = at->length_tree(gogo, arg2_tree); - - Btype* element_btype = element_type->get_backend(gogo); - tree element_type_tree = type_to_tree(element_btype); - if (element_type_tree == error_mark_node) - return error_mark_node; - element_size = TYPE_SIZE_UNIT(element_type_tree); + element_size = TYPE_SIZE_UNIT(element_type_tree); } arg2_val = fold_convert_loc(location.gcc_location(), ptr_type_node, @@ -9713,21 +9673,13 @@ Call_expression::do_must_eval_in_order() const // Get the function and the first argument to use when calling an // interface method. -tree +Expression* Call_expression::interface_method_function( - Translate_context* context, Interface_field_reference_expression* interface_method, - tree* first_arg_ptr) + Expression** first_arg_ptr) { - tree expr = interface_method->expr()->get_tree(context); - if (expr == error_mark_node) - return error_mark_node; - expr = save_expr(expr); - tree first_arg = interface_method->get_underlying_object_tree(context, expr); - if (first_arg == error_mark_node) - return error_mark_node; - *first_arg_ptr = first_arg; - return interface_method->get_function_tree(context, expr); + *first_arg_ptr = interface_method->get_underlying_object(); + return interface_method->get_function(); } // Build the call expression. @@ -9857,8 +9809,12 @@ Call_expression::do_get_tree(Translate_context* context) } else { - fn = this->interface_method_function(context, interface_method, - &args[0]); + Expression* first_arg; + Expression* fn_expr = + this->interface_method_function(interface_method, &first_arg); + args[0] = first_arg->get_tree(context); + fn = fn_expr->get_tree(context); + if (fn == error_mark_node) return error_mark_node; closure_tree = NULL_TREE; @@ -10371,6 +10327,9 @@ class Array_index_expression : public Expression do_check_types(Gogo*); Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Expression* do_copy() { return Expression::make_array_index(this->array_->copy(), @@ -10611,6 +10570,22 @@ Array_index_expression::do_check_types(Gogo*) } } +// Flatten array indexing by using a temporary variable for slices. + +Expression* +Array_index_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + Location loc = this->location(); + if (this->array_->type()->is_slice_type() && !this->array_->is_variable()) + { + Temporary_statement* temp = Statement::make_temporary(NULL, this->array_, loc); + inserter->insert(temp); + this->array_ = Expression::make_temporary_reference(temp, loc); + } + return this; +} + // Return whether this expression is addressable. bool @@ -10643,22 +10618,17 @@ Array_index_expression::do_get_tree(Translate_context* context) go_assert(this->array_->type()->is_error()); return error_mark_node; } + go_assert(!array_type->is_slice_type() || this->array_->is_variable()); tree type_tree = type_to_tree(array_type->get_backend(gogo)); if (type_tree == error_mark_node) return error_mark_node; - tree array_tree = this->array_->get_tree(context); - if (array_tree == error_mark_node) - return error_mark_node; - - if (array_type->length() == NULL && !DECL_P(array_tree)) - array_tree = save_expr(array_tree); - tree length_tree = NULL_TREE; if (this->end_ == NULL || this->end_->is_nil_expression()) { - length_tree = array_type->length_tree(gogo, array_tree); + Expression* len = array_type->get_length(gogo, this->array_); + length_tree = len->get_tree(context); if (length_tree == error_mark_node) return error_mark_node; length_tree = save_expr(length_tree); @@ -10667,7 +10637,8 @@ Array_index_expression::do_get_tree(Translate_context* context) tree capacity_tree = NULL_TREE; if (this->end_ != NULL) { - capacity_tree = array_type->capacity_tree(gogo, array_tree); + Expression* cap = array_type->get_capacity(gogo, this->array_); + capacity_tree = cap->get_tree(context); if (capacity_tree == error_mark_node) return error_mark_node; capacity_tree = save_expr(capacity_tree); @@ -10732,13 +10703,18 @@ Array_index_expression::do_get_tree(Translate_context* context) if (array_type->length() != NULL) { // Fixed array. + tree array_tree = this->array_->get_tree(context); + if (array_tree == error_mark_node) + return error_mark_node; return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree, start_tree, NULL_TREE, NULL_TREE); } else { // Open array. - tree values = array_type->value_pointer_tree(gogo, array_tree); + Expression* valptr = + array_type->get_value_pointer(gogo, this->array_); + tree values = valptr->get_tree(context); Type* element_type = array_type->element_type(); Btype* belement_type = element_type->get_backend(gogo); tree element_type_tree = type_to_tree(belement_type); @@ -10820,7 +10796,8 @@ Array_index_expression::do_get_tree(Translate_context* context) start_tree), element_size); - tree value_pointer = array_type->value_pointer_tree(gogo, array_tree); + Expression* valptr = array_type->get_value_pointer(gogo, this->array_); + tree value_pointer = valptr->get_tree(context); if (value_pointer == error_mark_node) return error_mark_node; @@ -11570,58 +11547,39 @@ Expression::make_field_reference(Expression* expr, unsigned int field_index, // Class Interface_field_reference_expression. -// Return a tree for the pointer to the function to call. +// Return an expression for the pointer to the function to call. -tree -Interface_field_reference_expression::get_function_tree(Translate_context*, - tree expr) +Expression* +Interface_field_reference_expression::get_function() { - if (this->expr_->type()->points_to() != NULL) - expr = build_fold_indirect_ref(expr); - - tree expr_type = TREE_TYPE(expr); - go_assert(TREE_CODE(expr_type) == RECORD_TYPE); - - tree field = TYPE_FIELDS(expr_type); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") == 0); - - tree table = build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE); - go_assert(POINTER_TYPE_P(TREE_TYPE(table))); + Expression* ref = this->expr_; + Location loc = this->location(); + if (ref->type()->points_to() != NULL) + ref = Expression::make_unary(OPERATOR_MULT, ref, loc); - table = build_fold_indirect_ref(table); - go_assert(TREE_CODE(TREE_TYPE(table)) == RECORD_TYPE); + Expression* mtable = + Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc); + Struct_type* mtable_type = mtable->type()->points_to()->struct_type(); std::string name = Gogo::unpack_hidden_name(this->name_); - for (field = DECL_CHAIN(TYPE_FIELDS(TREE_TYPE(table))); - field != NULL_TREE; - field = DECL_CHAIN(field)) - { - if (name == IDENTIFIER_POINTER(DECL_NAME(field))) - break; - } - go_assert(field != NULL_TREE); - - return build3(COMPONENT_REF, TREE_TYPE(field), table, field, NULL_TREE); + unsigned int index; + const Struct_field* field = mtable_type->find_local_field(name, &index); + go_assert(field != NULL); + mtable = Expression::make_unary(OPERATOR_MULT, mtable, loc); + return Expression::make_field_reference(mtable, index, loc); } -// Return a tree for the first argument to pass to the interface +// Return an expression for the first argument to pass to the interface // function. -tree -Interface_field_reference_expression::get_underlying_object_tree( - Translate_context*, - tree expr) +Expression* +Interface_field_reference_expression::get_underlying_object() { - if (this->expr_->type()->points_to() != NULL) - expr = build_fold_indirect_ref(expr); - - tree expr_type = TREE_TYPE(expr); - go_assert(TREE_CODE(expr_type) == RECORD_TYPE); - - tree field = DECL_CHAIN(TYPE_FIELDS(expr_type)); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0); - - return build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE); + Expression* expr = this->expr_; + if (expr->type()->points_to() != NULL) + expr = Expression::make_unary(OPERATOR_MULT, expr, this->location()); + return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT, + this->location()); } // Traversal. @@ -11641,9 +11599,7 @@ Interface_field_reference_expression::do_lower(Gogo*, Named_object*, Statement_inserter* inserter, int) { - if (this->expr_->var_expression() == NULL - && this->expr_->temporary_reference_expression() == NULL - && this->expr_->set_and_use_temporary_expression() == NULL) + if (!this->expr_->is_variable()) { Temporary_statement* temp = Statement::make_temporary(this->expr_->type(), NULL, this->location()); @@ -11820,6 +11776,7 @@ Interface_field_reference_expression::create_thunk(Gogo* gogo, Block* b = gogo->finish_block(loc); gogo->add_block(b, loc); gogo->lower_block(new_no, b); + gogo->flatten_block(new_no, b); gogo->finish_function(loc); ins.first->second->push_back(std::make_pair(name, new_no)); @@ -11870,30 +11827,22 @@ Interface_field_reference_expression::do_get_tree(Translate_context* context) Expression* expr = Expression::make_struct_composite_literal(st, vals, loc); expr = Expression::make_heap_composite(expr, loc); - tree closure_tree = expr->get_tree(context); + Bexpression* bclosure = tree_to_expr(expr->get_tree(context)); + Expression* nil_check = + Expression::make_binary(OPERATOR_EQEQ, this->expr_, + Expression::make_nil(loc), loc); + Bexpression* bnil_check = tree_to_expr(nil_check->get_tree(context)); - // Note that we are evaluating this->expr_ twice, but that is OK - // because in the lowering pass we forced it into a temporary - // variable. - tree nil_check_tree = Expression::comparison_tree(context, - Type::lookup_bool_type(), - OPERATOR_EQEQ, - this->expr_, - Expression::make_nil(loc), - loc); - Expression* crash_expr = - context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); - tree crash = crash_expr->get_tree(context); - if (closure_tree == error_mark_node - || nil_check_tree == error_mark_node - || crash == error_mark_node) - return error_mark_node; - return fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, - TREE_TYPE(closure_tree), - build3_loc(loc.gcc_location(), COND_EXPR, - void_type_node, nil_check_tree, crash, - NULL_TREE), - closure_tree); + Gogo* gogo = context->gogo(); + Expression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); + Bexpression* bcrash = tree_to_expr(crash->get_tree(context)); + + Bexpression* bcond = + gogo->backend()->conditional_expression(NULL, bnil_check, bcrash, NULL, loc); + Bstatement* cond_statement = gogo->backend()->expression_statement(bcond); + Bexpression* ret = + gogo->backend()->compound_expression(cond_statement, bclosure, loc); + return expr_to_tree(ret); } // Dump ast representation for an interface field reference. @@ -12158,6 +12107,7 @@ Selector_expression::lower_method_expression(Gogo* gogo) // Lower the call in case there are multiple results. gogo->lower_block(no, b); + gogo->flatten_block(no, b); gogo->finish_function(location); @@ -14133,6 +14083,22 @@ Expression::is_nonconstant_composite_literal() const } } +// Return true if this is a variable or temporary_variable. + +bool +Expression::is_variable() const +{ + switch (this->classification_) + { + case EXPRESSION_VAR_REFERENCE: + case EXPRESSION_TEMPORARY_REFERENCE: + case EXPRESSION_SET_AND_USE_TEMPORARY: + return true; + default: + return false; + } +} + // Return true if this is a reference to a local variable. bool @@ -14574,6 +14540,266 @@ Expression::make_type_info(Type* type, Type_info type_info) return new Type_info_expression(type, type_info); } +// An expression that evaluates to some characteristic of a slice. +// This is used when indexing, bound-checking, or nil checking a slice. + +class Slice_info_expression : public Expression +{ + public: + Slice_info_expression(Expression* slice, Slice_info slice_info, + Location location) + : Expression(EXPRESSION_SLICE_INFO, location), + slice_(slice), slice_info_(slice_info) + { } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Slice_info_expression(this->slice_->copy(), this->slice_info_, + this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->slice_->issue_nil_check(); } + + private: + // The slice for which we are getting information. + Expression* slice_; + // What information we want. + Slice_info slice_info_; +}; + +// Return the type of the slice info. + +Type* +Slice_info_expression::do_type() +{ + switch (this->slice_info_) + { + case SLICE_INFO_VALUE_POINTER: + return Type::make_pointer_type( + this->slice_->type()->array_type()->element_type()); + case SLICE_INFO_LENGTH: + case SLICE_INFO_CAPACITY: + return Type::lookup_integer_type("int"); + default: + go_unreachable(); + } +} + +// Return slice information in GENERIC. + +tree +Slice_info_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + + Bexpression* bslice = tree_to_expr(this->slice_->get_tree(context)); + Bexpression* ret; + switch (this->slice_info_) + { + case SLICE_INFO_VALUE_POINTER: + case SLICE_INFO_LENGTH: + case SLICE_INFO_CAPACITY: + ret = gogo->backend()->struct_field_expression(bslice, this->slice_info_, + this->location()); + break; + default: + go_unreachable(); + } + return expr_to_tree(ret); +} + +// Dump ast representation for a type info expression. + +void +Slice_info_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "sliceinfo("; + this->slice_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << ","; + ast_dump_context->ostream() << + (this->slice_info_ == SLICE_INFO_VALUE_POINTER ? "values" + : this->slice_info_ == SLICE_INFO_LENGTH ? "length" + : this->slice_info_ == SLICE_INFO_CAPACITY ? "capacity " + : "unknown"); + ast_dump_context->ostream() << ")"; +} + +// Make a slice info expression. + +Expression* +Expression::make_slice_info(Expression* slice, Slice_info slice_info, + Location location) +{ + return new Slice_info_expression(slice, slice_info, location); +} + + +// 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. + +class Interface_info_expression : public Expression +{ + public: + Interface_info_expression(Expression* iface, Interface_info iface_info, + Location location) + : Expression(EXPRESSION_INTERFACE_INFO, location), + iface_(iface), iface_info_(iface_info) + { } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Interface_info_expression(this->iface_->copy(), + this->iface_info_, this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->iface_->issue_nil_check(); } + + private: + // The interface for which we are getting information. + Expression* iface_; + // What information we want. + Interface_info iface_info_; +}; + +// Return the type of the interface info. + +Type* +Interface_info_expression::do_type() +{ + switch (this->iface_info_) + { + case INTERFACE_INFO_METHODS: + { + Location loc = this->location(); + Struct_field_list* sfl = new Struct_field_list(); + Type* pdt = Type::make_type_descriptor_ptr_type(); + sfl->push_back( + Struct_field(Typed_identifier("__type_descriptor", pdt, loc))); + + Interface_type* itype = this->iface_->type()->interface_type(); + for (Typed_identifier_list::const_iterator p = itype->methods()->begin(); + p != itype->methods()->end(); + ++p) + { + Function_type* ft = p->type()->function_type(); + go_assert(ft->receiver() == NULL); + + const Typed_identifier_list* params = ft->parameters(); + Typed_identifier_list* mparams = new Typed_identifier_list(); + if (params != NULL) + mparams->reserve(params->size() + 1); + Type* vt = Type::make_pointer_type(Type::make_void_type()); + mparams->push_back(Typed_identifier("", vt, ft->location())); + if (params != NULL) + { + for (Typed_identifier_list::const_iterator pp = params->begin(); + pp != params->end(); + ++pp) + mparams->push_back(*pp); + } + + Typed_identifier_list* mresults = (ft->results() == NULL + ? NULL + : ft->results()->copy()); + Backend_function_type* mft = + Type::make_backend_function_type(NULL, mparams, mresults, + ft->location()); + + std::string fname = Gogo::unpack_hidden_name(p->name()); + sfl->push_back(Struct_field(Typed_identifier(fname, mft, loc))); + } + + return Type::make_pointer_type(Type::make_struct_type(sfl, loc)); + } + case INTERFACE_INFO_OBJECT: + return Type::make_pointer_type(Type::make_void_type()); + default: + go_unreachable(); + } +} + +// Return interface information in GENERIC. + +tree +Interface_info_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + + Bexpression* biface = tree_to_expr(this->iface_->get_tree(context)); + Bexpression* ret; + switch (this->iface_info_) + { + case INTERFACE_INFO_METHODS: + case INTERFACE_INFO_OBJECT: + ret = gogo->backend()->struct_field_expression(biface, this->iface_info_, + this->location()); + break; + default: + go_unreachable(); + } + return expr_to_tree(ret); +} + +// Dump ast representation for an interface info expression. + +void +Interface_info_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "interfaceinfo("; + this->iface_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << ","; + ast_dump_context->ostream() << + (this->iface_info_ == INTERFACE_INFO_METHODS ? "methods" + : this->iface_info_ == INTERFACE_INFO_OBJECT ? "object" + : "unknown"); + ast_dump_context->ostream() << ")"; +} + +// Make an interface info expression. + +Expression* +Expression::make_interface_info(Expression* iface, Interface_info iface_info, + Location location) +{ + return new Interface_info_expression(iface, iface_info, location); +} + // An expression which evaluates to the offset of a field within a // struct. This, like Type_info_expression, q.v., is only used to // initialize fields of a type descriptor. diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index e447418b949..9ddd17122f2 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -102,6 +102,8 @@ class Expression EXPRESSION_RECEIVE, EXPRESSION_TYPE_DESCRIPTOR, EXPRESSION_TYPE_INFO, + EXPRESSION_SLICE_INFO, + EXPRESSION_INTERFACE_INFO, EXPRESSION_STRUCT_FIELD_OFFSET, EXPRESSION_MAP_DESCRIPTOR, EXPRESSION_LABEL_ADDR @@ -339,6 +341,37 @@ class Expression static Expression* make_type_info(Type* type, Type_info); + // Make an expression that evaluates to some characteristic of a + // slice. For simplicity, the enum values must match the field indexes + // in the underlying struct. + enum Slice_info + { + // The underlying data of the slice. + SLICE_INFO_VALUE_POINTER, + // The length of the slice. + SLICE_INFO_LENGTH, + // The capacity of the slice. + SLICE_INFO_CAPACITY + }; + + static Expression* + make_slice_info(Expression* slice, Slice_info, Location); + + + // Make an expression that evaluates to some characteristic of a + // interface. For simplicity, the enum values must match the field indexes + // of a non-empty interface in the underlying struct. + enum Interface_info + { + // The methods of an interface. + INTERFACE_INFO_METHODS, + // The first argument to pass to an interface method. + INTERFACE_INFO_OBJECT + }; + + static Expression* + make_interface_info(Expression* iface, Interface_info, Location); + // Make an expression which evaluates to the offset of a field in a // struct. This is only used for type descriptors, so there is no // location parameter. @@ -544,6 +577,10 @@ class Expression bool is_nonconstant_composite_literal() const; + // Return true if this is a variable or temporary variable. + bool + is_variable() const; + // Return true if this is a reference to a local variable. bool is_local_variable() const; @@ -575,6 +612,18 @@ class Expression int iota_value) { return this->do_lower(gogo, function, inserter, iota_value); } + // Flatten an expression. This is called after order_evaluation. + // FUNCTION is the function we are in; it will be NULL for an + // expression initializing a global variable. INSERTER may be used + // to insert statements before the statement or initializer + // containing this expression; it is normally used to create + // temporary variables. This function must resolve expressions + // which could not be fully parsed into their final form. It + // returns the same Expression or a new one. + Expression* + flatten(Gogo* gogo, Named_object* function, Statement_inserter* inserter) + { return this->do_flatten(gogo, function, inserter); } + // Determine the real type of an expression with abstract integer, // floating point, or complex type. TYPE_CONTEXT describes the // expected type. @@ -655,11 +704,11 @@ class Expression Type* rhs_type, tree rhs_tree, bool for_type_guard, Location); - // Return a tree implementing the comparison LHS_EXPR OP RHS_EXPR. + // Return a backend expression implementing the comparison LEFT OP RIGHT. // TYPE is the type of both sides. - static tree - comparison_tree(Translate_context*, Type* result_type, Operator op, - Expression* left_expr, Expression* right_expr, Location); + static Bexpression* + comparison(Translate_context*, Type* result_type, Operator op, + Expression* left, Expression* right, Location); // Return the backend expression for the numeric constant VAL. static Bexpression* @@ -698,6 +747,12 @@ class Expression do_lower(Gogo*, Named_object*, Statement_inserter*, int) { return this; } + // Return a flattened expression. + virtual Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*) + { return this; } + + // Return whether this is a constant expression. virtual bool do_is_constant() const @@ -1234,6 +1289,9 @@ class Binary_expression : public Expression Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + bool do_is_constant() const { return this->left_->is_constant() && this->right_->is_constant(); } @@ -1469,10 +1527,9 @@ class Call_expression : public Expression bool check_argument_type(int, const Type*, const Type*, Location, bool); - tree - interface_method_function(Translate_context*, - Interface_field_reference_expression*, - tree*); + Expression* + interface_method_function(Interface_field_reference_expression*, + Expression**); tree set_results(Translate_context*, tree); @@ -2076,16 +2133,14 @@ class Interface_field_reference_expression : public Expression static Named_object* create_thunk(Gogo*, Interface_type* type, const std::string& name); - // Return a tree for the pointer to the function to call, given a - // tree for the expression. - tree - get_function_tree(Translate_context*, tree); + // Return an expression for the pointer to the function to call. + Expression* + get_function(); - // Return a tree for the first argument to pass to the interface - // function, given a tree for the expression. This is the real - // object associated with the interface object. - tree - get_underlying_object_tree(Translate_context*, tree); + // Return an expression for the first argument to pass to the interface + // function. This is the real object associated with the interface object. + Expression* + get_underlying_object(); protected: int diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index 55b4dca8579..26e83a1db64 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -119,12 +119,15 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, // Use temporary variables to force order of evaluation. ::gogo->order_evaluations(); + // Flatten the parse tree. + ::gogo->flatten(); + // Build thunks for functions which call recover. ::gogo->build_recover_thunks(); // Convert complicated go and defer statements into simpler ones. ::gogo->simplify_thunk_statements(); - + // Dump ast, use filename[0] as the base name ::gogo->dump_ast(filenames[0]); } diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index e46bf9c4193..6ecc6cd0f0f 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -2703,6 +2703,178 @@ Gogo::order_evaluations() this->traverse(&order_eval); } +// Traversal to flatten parse tree after order of evaluation rules are applied. + +class Flatten : public Traverse +{ + public: + Flatten(Gogo* gogo, Named_object* function) + : Traverse(traverse_variables + | traverse_functions + | traverse_statements + | traverse_expressions), + gogo_(gogo), function_(function), inserter_() + { } + + void + set_inserter(const Statement_inserter* inserter) + { this->inserter_ = *inserter; } + + int + variable(Named_object*); + + int + function(Named_object*); + + int + statement(Block*, size_t* pindex, Statement*); + + int + expression(Expression**); + + private: + // General IR. + Gogo* gogo_; + // The function we are traversing. + Named_object* function_; + // Current statement inserter for use by expressions. + Statement_inserter inserter_; +}; + +// Flatten variables. + +int +Flatten::variable(Named_object* no) +{ + if (!no->is_variable()) + return TRAVERSE_CONTINUE; + + if (no->is_variable() && no->var_value()->is_global()) + { + // Global variables can have loops in their initialization + // expressions. This is handled in flatten_init_expression. + no->var_value()->flatten_init_expression(this->gogo_, this->function_, + &this->inserter_); + return TRAVERSE_CONTINUE; + } + + go_assert(!no->var_value()->has_pre_init()); + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten the body of a function. Record the function while flattening it, +// so that we can pass it down when flattening an expression. + +int +Flatten::function(Named_object* no) +{ + go_assert(this->function_ == NULL); + this->function_ = no; + int t = no->func_value()->traverse(this); + this->function_ = NULL; + + if (t == TRAVERSE_EXIT) + return t; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten statement parse trees. + +int +Flatten::statement(Block* block, size_t* pindex, Statement* sorig) +{ + // Because we explicitly traverse the statement's contents + // ourselves, we want to skip block statements here. There is + // nothing to flatten in a block statement. + if (sorig->is_block_statement()) + return TRAVERSE_CONTINUE; + + Statement_inserter hold_inserter(this->inserter_); + this->inserter_ = Statement_inserter(block, pindex); + + // Flatten the expressions first. + int t = sorig->traverse_contents(this); + if (t == TRAVERSE_EXIT) + { + this->inserter_ = hold_inserter; + return t; + } + + // Keep flattening until nothing changes. + Statement* s = sorig; + while (true) + { + Statement* snew = s->flatten(this->gogo_, this->function_, block, + &this->inserter_); + if (snew == s) + break; + s = snew; + t = s->traverse_contents(this); + if (t == TRAVERSE_EXIT) + { + this->inserter_ = hold_inserter; + return t; + } + } + + if (s != sorig) + block->replace_statement(*pindex, s); + + this->inserter_ = hold_inserter; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten expression parse trees. + +int +Flatten::expression(Expression** pexpr) +{ + // Keep flattening until nothing changes. + while (true) + { + Expression* e = *pexpr; + if (e->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + + Expression* enew = e->flatten(this->gogo_, this->function_, + &this->inserter_); + if (enew == e) + break; + *pexpr = enew; + } + return TRAVERSE_SKIP_COMPONENTS; +} + +// Flatten a block. + +void +Gogo::flatten_block(Named_object* function, Block* block) +{ + Flatten flatten(this, function); + block->traverse(&flatten); +} + +// Flatten an expression. INSERTER may be NULL, in which case the +// expression had better not need to create any temporaries. + +void +Gogo::flatten_expression(Named_object* function, Statement_inserter* inserter, + Expression** pexpr) +{ + Flatten flatten(this, function); + if (inserter != NULL) + flatten.set_inserter(inserter); + flatten.expression(pexpr); +} + +void +Gogo::flatten() +{ + Flatten flatten(this, NULL); + this->traverse(&flatten); +} + // Traversal to convert calls to the predeclared recover function to // pass in an argument indicating whether it can recover from a panic // or not. @@ -4286,10 +4458,11 @@ Variable::Variable(Type* type, Expression* init, bool is_global, backend_(NULL), is_global_(is_global), is_parameter_(is_parameter), is_receiver_(is_receiver), is_varargs_parameter_(false), is_used_(false), is_address_taken_(false), is_non_escaping_address_taken_(false), - seen_(false), init_is_lowered_(false), type_from_init_tuple_(false), - type_from_range_index_(false), type_from_range_value_(false), - type_from_chan_element_(false), is_type_switch_var_(false), - determined_type_(false), in_unique_section_(false) + seen_(false), init_is_lowered_(false), init_is_flattened_(false), + type_from_init_tuple_(false), type_from_range_index_(false), + type_from_range_value_(false), type_from_chan_element_(false), + is_type_switch_var_(false), determined_type_(false), + in_unique_section_(false) { go_assert(type != NULL || init != NULL); go_assert(!is_parameter || init == NULL); @@ -4351,6 +4524,40 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function, } } +// Flatten the initialization expression after ordering evaluations. + +void +Variable::flatten_init_expression(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) +{ + Named_object* dep = gogo->var_depends_on(this); + if (dep != NULL && dep->is_variable()) + dep->var_value()->flatten_init_expression(gogo, function, inserter); + + if (this->init_ != NULL && !this->init_is_flattened_) + { + if (this->seen_) + { + // We will give an error elsewhere, this is just to prevent + // an infinite loop. + return; + } + this->seen_ = true; + + Statement_inserter global_inserter; + if (this->is_global_) + { + global_inserter = Statement_inserter(gogo, this); + inserter = &global_inserter; + } + + gogo->flatten_expression(function, inserter, &this->init_); + + this->seen_ = false; + this->init_is_flattened_ = true; + } +} + // Get the preinit block. Block* diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index a9a56815c17..3f2808781b7 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -487,6 +487,14 @@ class Gogo void lower_constant(Named_object*); + // Flatten all the statements in a block. + void + flatten_block(Named_object* function, Block*); + + // Flatten an expression. + void + flatten_expression(Named_object* function, Statement_inserter*, Expression**); + // Create all necessary function descriptors. void create_function_descriptors(); @@ -531,6 +539,10 @@ class Gogo void order_evaluations(); + // Flatten parse tree. + void + flatten(); + // Build thunks for functions which call recover. void build_recover_thunks(); @@ -1447,6 +1459,10 @@ class Variable void lower_init_expression(Gogo*, Named_object*, Statement_inserter*); + // Flatten the initialization expression after ordering evaluations. + void + flatten_init_expression(Gogo*, Named_object*, Statement_inserter*); + // A special case: the init value is used only to determine the // type. This is used if the variable is defined using := with the // comma-ok form of a map index or a receive expression. The init @@ -1580,6 +1596,8 @@ class Variable bool seen_ : 1; // True if we have lowered the initialization expression. bool init_is_lowered_ : 1; + // True if we have flattened the initialization expression. + bool init_is_flattened_ : 1; // True if init is a tuple used to set the type. bool type_from_init_tuple_ : 1; // True if init is a range clause and the type is the index type. diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 3a0bc3b9739..d195ab9845a 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -246,6 +246,16 @@ Variable_declaration_statement::do_lower(Gogo* gogo, Named_object* function, return this; } +// Flatten the variable's initialization expression. + +Statement* +Variable_declaration_statement::do_flatten(Gogo* gogo, Named_object* function, + Block*, Statement_inserter* inserter) +{ + this->var_->var_value()->flatten_init_expression(gogo, function, inserter); + return this; +} + // Convert a variable declaration to the backend representation. Bstatement* @@ -2461,6 +2471,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) gogo->add_block(b, location); gogo->lower_block(function, b); + gogo->flatten_block(function, b); // We already ran the determine_types pass, so we need to run it // just for the call statement now. The other types are known. diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index b128fa0a8eb..7d9bcfde8b7 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -306,6 +306,16 @@ class Statement Statement_inserter* inserter) { return this->do_lower(gogo, function, block, inserter); } + // Flatten a statement. This is called immediately after the order of + // evaluation rules are applied to statements. It returns the same + // Statement or a new one. FUNCTION is the function containing this + // statement. BLOCK is the block containing this statement. + // INSERTER can be used to insert new statements before this one. + Statement* + flatten(Gogo* gogo, Named_object* function, Block* block, + Statement_inserter* inserter) + { return this->do_flatten(gogo, function, block, inserter); } + // Set type information for unnamed constants. void determine_types(); @@ -412,6 +422,12 @@ class Statement do_lower(Gogo*, Named_object*, Block*, Statement_inserter*) { return this; } + // Implemented by the child class: lower this statement to a simpler + // one. + virtual Statement* + do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*) + { return this; } + // Implemented by child class: set type information for unnamed // constants. Any statement which includes an expression needs to // implement this. @@ -583,6 +599,9 @@ class Variable_declaration_statement : public Statement Statement* do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + Statement* + do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); + Bstatement* do_get_backend(Translate_context*); diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index d079565d18b..2148a1a43e6 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -4066,6 +4066,17 @@ Type::make_function_type(Typed_identifier* receiver, return new Function_type(receiver, parameters, results, location); } +// Make a backend function type. + +Backend_function_type* +Type::make_backend_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, + Location location) +{ + return new Backend_function_type(receiver, parameters, results, location); +} + // Class Pointer_type. // Traversal. @@ -6000,84 +6011,53 @@ Array_type::finish_backend_element(Gogo* gogo) } } -// Return a tree for a pointer to the values in ARRAY. +// Return an expression for a pointer to the values in ARRAY. -tree -Array_type::value_pointer_tree(Gogo*, tree array) const +Expression* +Array_type::get_value_pointer(Gogo*, Expression* array) const { - tree ret; if (this->length() != NULL) { // Fixed array. - ret = fold_convert(build_pointer_type(TREE_TYPE(TREE_TYPE(array))), - build_fold_addr_expr(array)); + go_assert(array->type()->array_type() != NULL); + Type* etype = array->type()->array_type()->element_type(); + array = Expression::make_unary(OPERATOR_AND, array, array->location()); + return Expression::make_cast(Type::make_pointer_type(etype), array, + array->location()); } - else - { - // Open array. - tree field = TYPE_FIELDS(TREE_TYPE(array)); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), - "__values") == 0); - ret = fold_build3(COMPONENT_REF, TREE_TYPE(field), array, field, - NULL_TREE); - } - if (TREE_CONSTANT(array)) - TREE_CONSTANT(ret) = 1; - return ret; + + // Open array. + return Expression::make_slice_info(array, + Expression::SLICE_INFO_VALUE_POINTER, + array->location()); } -// Return a tree for the length of the array ARRAY which has this +// Return an expression for the length of the array ARRAY which has this // type. -tree -Array_type::length_tree(Gogo* gogo, tree array) +Expression* +Array_type::get_length(Gogo*, Expression* array) const { if (this->length_ != NULL) - { - if (TREE_CODE(array) == SAVE_EXPR) - return this->get_length_tree(gogo); - else - { - tree len = this->get_length_tree(gogo); - return omit_one_operand(TREE_TYPE(len), len, array); - } - } + return this->length_; // This is an open array. We need to read the length field. - - tree type = TREE_TYPE(array); - go_assert(TREE_CODE(type) == RECORD_TYPE); - - tree field = DECL_CHAIN(TYPE_FIELDS(type)); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0); - - tree ret = build3(COMPONENT_REF, TREE_TYPE(field), array, field, NULL_TREE); - if (TREE_CONSTANT(array)) - TREE_CONSTANT(ret) = 1; - return ret; + return Expression::make_slice_info(array, Expression::SLICE_INFO_LENGTH, + array->location()); } -// Return a tree for the capacity of the array ARRAY which has this +// Return an expression for the capacity of the array ARRAY which has this // type. -tree -Array_type::capacity_tree(Gogo* gogo, tree array) +Expression* +Array_type::get_capacity(Gogo*, Expression* array) const { if (this->length_ != NULL) - { - tree len = this->get_length_tree(gogo); - return omit_one_operand(TREE_TYPE(len), len, array); - } + return this->length_; // This is an open array. We need to read the capacity field. - - tree type = TREE_TYPE(array); - go_assert(TREE_CODE(type) == RECORD_TYPE); - - tree field = DECL_CHAIN(DECL_CHAIN(TYPE_FIELDS(type))); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0); - - return build3(COMPONENT_REF, TREE_TYPE(field), array, field, NULL_TREE); + return Expression::make_slice_info(array, Expression::SLICE_INFO_CAPACITY, + array->location()); } // Export. diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 9f965916131..5fda4e7285e 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -19,6 +19,7 @@ class Float_type; class Complex_type; class String_type; class Function_type; +class Backend_function_type; class Struct_field; class Struct_field_list; class Struct_type; @@ -484,6 +485,12 @@ class Type Typed_identifier_list* results, Location); + static Backend_function_type* + make_backend_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, + Location); + static Pointer_type* make_pointer_type(Type*); @@ -1896,6 +1903,23 @@ class Function_type : public Type Btype* fnbtype_; }; +// The type of a function's backend representation. + +class Backend_function_type : public Function_type +{ + public: + Backend_function_type(Typed_identifier* receiver, + Typed_identifier_list* parameters, + Typed_identifier_list* results, Location location) + : Function_type(receiver, parameters, results, location) + { } + + protected: + Btype* + do_get_backend(Gogo* gogo) + { return this->get_backend_fntype(gogo); } +}; + // The type of a pointer. class Pointer_type : public Type @@ -2312,17 +2336,17 @@ class Array_type : public Type array_has_hidden_fields(const Named_type* within, std::string* reason) const { return this->element_type_->has_hidden_fields(within, reason); } - // Return a tree for the pointer to the values in an array. - tree - value_pointer_tree(Gogo*, tree array) const; + // Return an expression for the pointer to the values in an array. + Expression* + get_value_pointer(Gogo*, Expression* array) const; - // Return a tree for the length of an array with this type. - tree - length_tree(Gogo*, tree array); + // Return an expression for the length of an array with this type. + Expression* + get_length(Gogo*, Expression* array) const; - // Return a tree for the capacity of an array with this type. - tree - capacity_tree(Gogo*, tree array); + // Return an expression for the capacity of an array with this type. + Expression* + get_capacity(Gogo*, Expression* array) const; // Import an array type. static Array_type* |