Mercurial > hg > CbC > CbC_gcc
diff gcc/go/gofrontend/expressions.cc @ 131:84e7813d76e9
gcc-8.2
author | mir3636 |
---|---|
date | Thu, 25 Oct 2018 07:37:49 +0900 |
parents | 04ced10e8804 |
children | 1830386684a0 |
line wrap: on
line diff
--- a/gcc/go/gofrontend/expressions.cc Fri Oct 27 22:46:09 2017 +0900 +++ b/gcc/go/gofrontend/expressions.cc Thu Oct 25 07:37:49 2018 +0900 @@ -131,6 +131,40 @@ this->do_determine_type(&context); } +// Return true if two expressions refer to the same variable or struct +// field. This can only be true when there are no side effects. + +bool +Expression::is_same_variable(Expression* a, Expression* b) +{ + if (a->classification() != b->classification()) + return false; + + Var_expression* av = a->var_expression(); + if (av != NULL) + return av->named_object() == b->var_expression()->named_object(); + + Field_reference_expression* af = a->field_reference_expression(); + if (af != NULL) + { + Field_reference_expression* bf = b->field_reference_expression(); + return (af->field_index() == bf->field_index() + && Expression::is_same_variable(af->expr(), bf->expr())); + } + + Unary_expression* au = a->unary_expression(); + if (au != NULL) + { + Unary_expression* bu = b->unary_expression(); + return (au->op() == OPERATOR_MULT + && bu->op() == OPERATOR_MULT + && Expression::is_same_variable(au->operand(), + bu->operand())); + } + + return false; +} + // Return an expression handling any conversions which must be done during // assignment. @@ -144,7 +178,10 @@ || rhs->is_error_expression()) return Expression::make_error(location); - bool are_identical = Type::are_identical(lhs_type, rhs_type, false, NULL); + bool are_identical = Type::are_identical(lhs_type, rhs_type, + (Type::COMPARE_ERRORS + | Type::COMPARE_TAGS), + NULL); if (!are_identical && lhs_type->interface_type() != NULL) { if (rhs_type->interface_type() == NULL) @@ -290,7 +327,7 @@ Expression::make_interface_info(rhs, INTERFACE_INFO_METHODS, location); Expression* descriptor = - Expression::make_unary(OPERATOR_MULT, mtable, location); + Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, location); descriptor = Expression::make_field_reference(descriptor, 0, location); Expression* nil = Expression::make_nil(location); @@ -307,7 +344,9 @@ bool for_type_guard, Location location) { - if (Type::are_identical(lhs_type, rhs->type(), false, NULL)) + if (Type::are_identical(lhs_type, rhs->type(), + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) return rhs; Interface_type* lhs_interface_type = lhs_type->interface_type(); @@ -393,7 +432,8 @@ { obj = Expression::make_unsafe_cast(Type::make_pointer_type(lhs_type), obj, location); - obj = Expression::make_unary(OPERATOR_MULT, obj, location); + obj = Expression::make_dereference(obj, NIL_CHECK_NOT_NEEDED, + location); } return Expression::make_compound(check_iface, obj, location); } @@ -771,7 +811,7 @@ go_unreachable(); Bexpression* ret = - context->backend()->var_expression(bvar, this->in_lvalue_pos_, loc); + context->backend()->var_expression(bvar, loc); if (is_in_heap) ret = context->backend()->indirect_expression(btype, ret, true, loc); return ret; @@ -782,7 +822,7 @@ void Var_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const { - ast_dump_context->ostream() << this->variable_->name() ; + ast_dump_context->ostream() << this->variable_->message_name() ; } // Make a reference to a variable in an expression. @@ -858,7 +898,7 @@ void Enclosed_var_expression::do_dump_expression(Ast_dump_context* adc) const { - adc->ostream() << this->variable_->name(); + adc->ostream() << this->variable_->message_name(); } // Make a reference to a variable within an enclosing function. @@ -898,10 +938,7 @@ { Gogo* gogo = context->gogo(); Bvariable* bvar = this->statement_->get_backend_variable(context); - Varexpr_context ve_ctxt = (this->is_lvalue_ ? VE_lvalue : VE_rvalue); - - Bexpression* ret = gogo->backend()->var_expression(bvar, ve_ctxt, - this->location()); + Bexpression* ret = gogo->backend()->var_expression(bvar, this->location()); // The backend can't always represent the same set of recursive types // that the Go frontend can. In some cases this means that a @@ -972,7 +1009,7 @@ Location loc = this->location(); Gogo* gogo = context->gogo(); Bvariable* bvar = this->statement_->get_backend_variable(context); - Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, VE_lvalue, loc); + Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, loc); Named_object* fn = context->function(); go_assert(fn != NULL); @@ -980,7 +1017,7 @@ Bexpression* bexpr = this->expr_->get_backend(context); Bstatement* set = gogo->backend()->assignment_statement(bfn, lvar_ref, bexpr, loc); - Bexpression* var_ref = gogo->backend()->var_expression(bvar, VE_rvalue, loc); + Bexpression* var_ref = gogo->backend()->var_expression(bvar, loc); Bexpression* ret = gogo->backend()->compound_expression(set, var_ref, loc); return ret; } @@ -1084,11 +1121,11 @@ gogo->backend()->temporary_variable(fn_ctx, context->bblock(), bt, NULL, false, loc, &decl); Bexpression* var_ref = - gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc); + gogo->backend()->var_expression(this->bvar_, loc); var_ref = gogo->backend()->compound_expression(decl, var_ref, loc); return var_ref; } - return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc); + return gogo->backend()->var_expression(this->bvar_, loc); } // Ast dump for sink expression. @@ -1302,7 +1339,7 @@ Named_object* no = this->fn_; Location loc = no->location(); if (this->dvar_ != NULL) - return context->backend()->var_expression(this->dvar_, VE_rvalue, loc); + return context->backend()->var_expression(this->dvar_, loc); Gogo* gogo = context->gogo(); std::string var_name(gogo->function_descriptor_name(no)); @@ -1312,6 +1349,16 @@ && Linemap::is_predeclared_location(no->location())) is_descriptor = true; + // The runtime package implements some functions defined in the + // syscall package. Let the syscall package define the descriptor + // in this case. + if (gogo->compiling_runtime() + && gogo->package_name() == "runtime" + && no->is_function() + && !no->func_value()->asm_name().empty() + && no->func_value()->asm_name().compare(0, 8, "syscall.") == 0) + is_descriptor = true; + Btype* btype = this->type()->get_backend(gogo); Bvariable* bvar; @@ -1322,9 +1369,24 @@ else { Location bloc = Linemap::predeclared_location(); + + // The runtime package has hash/equality functions that are + // referenced by type descriptors outside of the runtime, so the + // function descriptors must be visible even though they are not + // exported. + bool is_exported_runtime = false; + if (gogo->compiling_runtime() + && gogo->package_name() == "runtime" + && (no->name().find("hash") != std::string::npos + || no->name().find("equal") != std::string::npos)) + is_exported_runtime = true; + bool is_hidden = ((no->is_function() && no->func_value()->enclosing() != NULL) + || (Gogo::is_hidden_name(no->name()) + && !is_exported_runtime) || Gogo::is_thunk(no)); + bvar = context->backend()->immutable_struct(var_name, asm_name, is_hidden, false, btype, bloc); @@ -1340,7 +1402,7 @@ } this->dvar_ = bvar; - return gogo->backend()->var_expression(bvar, VE_rvalue, loc); + return gogo->backend()->var_expression(bvar, loc); } // Print a function descriptor expression. @@ -1920,10 +1982,16 @@ do_copy() { if (this->is_character_constant_) - return Expression::make_character(&this->val_, this->type_, + return Expression::make_character(&this->val_, + (this->type_ == NULL + ? NULL + : this->type_->copy_expressions()), this->location()); else - return Expression::make_integer_z(&this->val_, this->type_, + return Expression::make_integer_z(&this->val_, + (this->type_ == NULL + ? NULL + : this->type_->copy_expressions()), this->location()); } @@ -2315,7 +2383,10 @@ Expression* do_copy() - { return Expression::make_float(&this->val_, this->type_, + { return Expression::make_float(&this->val_, + (this->type_ == NULL + ? NULL + : this->type_->copy_expressions()), this->location()); } Bexpression* @@ -2506,7 +2577,10 @@ Expression* do_copy() { - return Expression::make_complex(&this->val_, this->type_, + return Expression::make_complex(&this->val_, + (this->type_ == NULL + ? NULL + : this->type_->copy_expressions()), this->location()); } @@ -2839,8 +2913,16 @@ if (this->seen_ || nc->lowering()) { - this->report_error(_("constant refers to itself")); + if (nc->type() == NULL || !nc->type()->is_error_type()) + { + Location loc = this->location(); + if (!this->seen_) + loc = nc->location(); + go_error_at(loc, "constant refers to itself"); + } + this->set_is_error(); this->type_ = Type::make_error_type(); + nc->set_type(this->type_); return this->type_; } @@ -2860,6 +2942,9 @@ this->seen_ = false; + if (ret->is_error_type()) + nc->set_type(ret); + return ret; } @@ -3309,7 +3394,9 @@ if (!this->expr_->is_static_initializer()) return false; - if (Type::are_identical(type, expr_type, false, NULL)) + if (Type::are_identical(type, expr_type, + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) return true; if (type->is_string_type() && expr_type->is_string_type()) @@ -3401,6 +3488,16 @@ this->set_is_error(); } +// Copy. + +Expression* +Type_conversion_expression::do_copy() +{ + return new Type_conversion_expression(this->type_->copy_expressions(), + this->expr_->copy(), + this->location()); +} + // Get the backend representation for a type conversion. Bexpression* @@ -3413,7 +3510,9 @@ Btype* btype = type->get_backend(gogo); Location loc = this->location(); - if (Type::are_identical(type, expr_type, false, NULL)) + if (Type::are_identical(type, expr_type, + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) { Bexpression* bexpr = this->expr_->get_backend(context); return gogo->backend()->convert_expression(btype, bexpr, loc); @@ -3602,6 +3701,16 @@ return false; } +// Copy. + +Expression* +Unsafe_type_conversion_expression::do_copy() +{ + return new Unsafe_type_conversion_expression(this->type_->copy_expressions(), + this->expr_->copy(), + this->location()); +} + // Convert to backend representation. Bexpression* @@ -3702,7 +3811,7 @@ // called after escape analysis but before inserting write barriers. void -Unary_expression::check_operand_address_taken(Gogo* gogo) +Unary_expression::check_operand_address_taken(Gogo*) { if (this->op_ != OPERATOR_AND) return; @@ -3716,13 +3825,6 @@ if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) this->escapes_ = false; - // When compiling the runtime, the address operator does not cause - // local variables to escape. When escape analysis becomes the - // default, this should be changed to make it an error if we have an - // address operator that escapes. - if (gogo->compiling_runtime() && gogo->package_name() == "runtime") - this->escapes_ = false; - Named_object* var = NULL; if (this->expr_->var_expression() != NULL) var = this->expr_->var_expression()->named_object(); @@ -3845,24 +3947,20 @@ && !this->expr_->is_variable()) { go_assert(this->expr_->type()->points_to() != NULL); - Type* ptype = this->expr_->type()->points_to(); - if (!ptype->is_void_type()) + switch (this->requires_nil_check(gogo)) { - int64_t s; - bool ok = ptype->backend_type_size(gogo, &s); - if (!ok) + case NIL_CHECK_ERROR_ENCOUNTERED: { go_assert(saw_errors()); return Expression::make_error(this->location()); } - if (s >= 4096 || this->issue_nil_check_) - { - Temporary_statement* temp = - Statement::make_temporary(NULL, this->expr_, location); - inserter->insert(temp); - this->expr_ = - Expression::make_temporary_reference(temp, location); - } + case NIL_CHECK_NOT_NEEDED: + break; + case NIL_CHECK_NEEDED: + this->create_temp_ = true; + break; + case NIL_CHECK_DEFAULT: + go_unreachable(); } } @@ -3963,6 +4061,41 @@ return false; } +// Return whether this dereference expression requires an explicit nil +// check. If we are dereferencing the pointer to a large struct +// (greater than the specified size threshold), we need to check for +// nil. We don't bother to check for small structs because we expect +// the system to crash on a nil pointer dereference. However, if we +// know the address of this expression is being taken, we must always +// check for nil. +Unary_expression::Nil_check_classification +Unary_expression::requires_nil_check(Gogo* gogo) +{ + go_assert(this->op_ == OPERATOR_MULT); + go_assert(this->expr_->type()->points_to() != NULL); + + if (this->issue_nil_check_ == NIL_CHECK_NEEDED) + return NIL_CHECK_NEEDED; + else if (this->issue_nil_check_ == NIL_CHECK_NOT_NEEDED) + return NIL_CHECK_NOT_NEEDED; + + Type* ptype = this->expr_->type()->points_to(); + int64_t type_size = -1; + if (!ptype->is_void_type()) + { + bool ok = ptype->backend_type_size(gogo, &type_size); + if (!ok) + return NIL_CHECK_ERROR_ENCOUNTERED; + } + + int64_t size_cutoff = gogo->nil_check_size_threshold(); + if (size_cutoff == -1 || (type_size != -1 && type_size >= size_cutoff)) + this->issue_nil_check_ = NIL_CHECK_NEEDED; + else + this->issue_nil_check_ = NIL_CHECK_NOT_NEEDED; + return this->issue_nil_check_; +} + // Apply unary opcode OP to UNC, setting NC. Return true if this // could be done, false if not. On overflow, issues an error and sets // *ISSUED_ERROR. @@ -4286,7 +4419,7 @@ Temporary_statement* temp = sut->temporary(); Bvariable* bvar = temp->get_backend_variable(context); Bexpression* bvar_expr = - gogo->backend()->var_expression(bvar, VE_lvalue, loc); + gogo->backend()->var_expression(bvar, loc); Bexpression* bval = sut->expression()->get_backend(context); Named_object* fn = context->function(); @@ -4363,7 +4496,8 @@ // initialize the value once, so we can use this directly // rather than copying it. In that case we can't make it // read-only, because the program is permitted to change it. - copy_to_heap = context->function() != NULL; + copy_to_heap = (context->function() != NULL + || context->is_const()); } std::string asm_name(go_selectively_encode_id(var_name)); Bvariable* implicit = @@ -4373,7 +4507,7 @@ gogo->backend()->implicit_variable_set_init(implicit, var_name, btype, true, copy_to_heap, false, bexpr); - bexpr = gogo->backend()->var_expression(implicit, VE_rvalue, loc); + bexpr = gogo->backend()->var_expression(implicit, loc); // If we are not copying a slice initializer to the heap, // then it can be changed by the program, so if it can @@ -4383,7 +4517,7 @@ && this->expr_->type()->has_pointer()) { Bexpression* root = - gogo->backend()->var_expression(implicit, VE_rvalue, loc); + gogo->backend()->var_expression(implicit, loc); root = gogo->backend()->address_expression(root, loc); Type* type = Type::make_pointer_type(this->expr_->type()); gogo->add_gc_root(Expression::make_backend(root, type, loc)); @@ -4400,7 +4534,7 @@ true, false, btype, loc); gogo->backend()->immutable_struct_set_init(decl, var_name, true, false, btype, loc, bexpr); - bexpr = gogo->backend()->var_expression(decl, VE_rvalue, loc); + bexpr = gogo->backend()->var_expression(decl, loc); } go_assert(!this->create_temp_ || this->expr_->is_variable()); @@ -4411,43 +4545,55 @@ { go_assert(this->expr_->type()->points_to() != NULL); - // If we are dereferencing the pointer to a large struct, we - // need to check for nil. We don't bother to check for small - // structs because we expect the system to crash on a nil - // pointer dereference. However, if we know the address of this - // expression is being taken, we must always check for nil. - + bool known_valid = false; Type* ptype = this->expr_->type()->points_to(); Btype* pbtype = ptype->get_backend(gogo); - if (!ptype->is_void_type()) - { - int64_t s; - bool ok = ptype->backend_type_size(gogo, &s); - if (!ok) + switch (this->requires_nil_check(gogo)) + { + case NIL_CHECK_NOT_NEEDED: + break; + case NIL_CHECK_ERROR_ENCOUNTERED: { go_assert(saw_errors()); return gogo->backend()->error_expression(); } - if (s >= 4096 || this->issue_nil_check_) - { + case NIL_CHECK_NEEDED: + { go_assert(this->expr_->is_variable()); + + // If we're nil-checking the result of a set-and-use-temporary + // expression, then pick out the target temp and use that + // for the final result of the conditional. + Bexpression* tbexpr = bexpr; + Bexpression* ubexpr = bexpr; + Set_and_use_temporary_expression* sut = + this->expr_->set_and_use_temporary_expression(); + if (sut != NULL) { + Temporary_statement* temp = sut->temporary(); + Bvariable* bvar = temp->get_backend_variable(context); + ubexpr = gogo->backend()->var_expression(bvar, loc); + } Bexpression* nil = - Expression::make_nil(loc)->get_backend(context); + Expression::make_nil(loc)->get_backend(context); Bexpression* compare = - gogo->backend()->binary_expression(OPERATOR_EQEQ, bexpr, + gogo->backend()->binary_expression(OPERATOR_EQEQ, tbexpr, nil, loc); Bexpression* crash = - gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, - loc)->get_backend(context); + gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, + loc)->get_backend(context); Bfunction* bfn = context->function()->func_value()->get_decl(); bexpr = gogo->backend()->conditional_expression(bfn, btype, compare, - crash, bexpr, + crash, ubexpr, loc); - - } - } - ret = gogo->backend()->indirect_expression(pbtype, bexpr, false, loc); + known_valid = true; + break; + } + case NIL_CHECK_DEFAULT: + go_unreachable(); + } + ret = gogo->backend()->indirect_expression(pbtype, bexpr, + known_valid, loc); } break; @@ -4532,6 +4678,19 @@ return new Unary_expression(op, expr, location); } +Expression* +Expression::make_dereference(Expression* ptr, + Nil_check_classification docheck, + Location location) +{ + Expression* deref = Expression::make_unary(OPERATOR_MULT, ptr, location); + if (docheck == NIL_CHECK_NEEDED) + deref->unary_expression()->set_requires_nil_check(true); + else if (docheck == NIL_CHECK_NOT_NEEDED) + deref->unary_expression()->set_requires_nil_check(false); + return deref; +} + // If this is an indirection through a pointer, return the expression // being pointed through. Otherwise return this. @@ -5283,7 +5442,10 @@ Struct_type* st2 = this->right_->type()->struct_type(); if (st2 == NULL) return this; - if (st != st2 && !Type::are_identical(st, st2, false, NULL)) + if (st != st2 + && !Type::are_identical(st, st2, + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) return this; if (!Type::are_compatible_for_comparison(true, this->left_->type(), this->right_->type(), NULL)) @@ -5362,7 +5524,10 @@ Array_type* at2 = this->right_->type()->array_type(); if (at2 == NULL) return this; - if (at != at2 && !Type::are_identical(at, at2, false, NULL)) + if (at != at2 + && !Type::are_identical(at, at2, + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) return this; if (!Type::are_compatible_for_comparison(true, this->left_->type(), this->right_->type(), NULL)) @@ -6752,7 +6917,7 @@ ? this->expr_type_ : this->expr_->type()); etype = etype->deref(); - if (!Type::are_identical(rtype, etype, true, NULL)) + if (!Type::are_identical(rtype, etype, Type::COMPARE_TAGS, NULL)) this->report_error(_("method type does not match object type")); } @@ -6798,7 +6963,8 @@ if (orig_fntype == NULL || !orig_fntype->is_method()) { - ins.first->second = Named_object::make_erroneous_name(Gogo::thunk_name()); + ins.first->second = + Named_object::make_erroneous_name(gogo->thunk_name()); return ins.first->second; } @@ -6806,8 +6972,8 @@ // The type here is wrong--it should be the C function type. But it // doesn't really matter. Type* vt = Type::make_pointer_type(Type::make_void_type()); - sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); - sfl->push_back(Struct_field(Typed_identifier("val.1", + sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val", orig_fntype->receiver()->type(), loc))); Struct_type* st = Type::make_struct_type(sfl, loc); @@ -6816,7 +6982,7 @@ Function_type* new_fntype = orig_fntype->copy_with_names(); - std::string thunk_name = Gogo::thunk_name(); + std::string thunk_name = gogo->thunk_name(); Named_object* new_no = gogo->start_function(thunk_name, new_fntype, false, loc); @@ -6832,7 +6998,7 @@ // Field 0 of the closure is the function code pointer, field 1 is // the value on which to invoke the method. Expression* arg = Expression::make_var_reference(cp, loc); - arg = Expression::make_unary(OPERATOR_MULT, arg, loc); + arg = Expression::make_dereference(arg, NIL_CHECK_NOT_NEEDED, loc); arg = Expression::make_field_reference(arg, 1, loc); Expression* bme = Expression::make_bound_method(arg, method, fn, loc); @@ -6896,7 +7062,8 @@ Expression::make_nil(loc), loc); cond = Expression::make_binary(OPERATOR_OROR, cond, n, loc); - *ref = Expression::make_unary(OPERATOR_MULT, *ref, loc); + *ref = Expression::make_dereference(*ref, Expression::NIL_CHECK_DEFAULT, + loc); go_assert((*ref)->type()->struct_type() == stype); } *ref = Expression::make_field_reference(*ref, field_indexes->field_index, @@ -6951,7 +7118,7 @@ Expression* val = expr; if (fntype->receiver()->type()->points_to() == NULL && val->type()->points_to() != NULL) - val = Expression::make_unary(OPERATOR_MULT, val, loc); + val = Expression::make_dereference(val, NIL_CHECK_DEFAULT, loc); // Note that we are ignoring this->expr_type_ here. The thunk will // expect a closure whose second field has type this->expr_type_ (if @@ -6961,10 +7128,10 @@ // away with this. Struct_field_list* fields = new Struct_field_list(); - fields->push_back(Struct_field(Typed_identifier("fn.0", + fields->push_back(Struct_field(Typed_identifier("fn", thunk->func_value()->type(), loc))); - fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc))); + fields->push_back(Struct_field(Typed_identifier("val", val->type(), loc))); Struct_type* st = Type::make_struct_type(fields, loc); st->set_is_struct_incomparable(); @@ -6973,21 +7140,14 @@ vals->push_back(val); Expression* ret = Expression::make_struct_composite_literal(st, vals, loc); - - if (!gogo->compiling_runtime() || gogo->package_name() != "runtime") - ret = Expression::make_heap_expression(ret, loc); - else - { - // When compiling the runtime, method closures do not escape. - // When escape analysis becomes the default, and applies to - // method closures, this should be changed to make it an error - // if a method closure escapes. - Temporary_statement* ctemp = Statement::make_temporary(st, ret, loc); - inserter->insert(ctemp); - ret = Expression::make_temporary_reference(ctemp, loc); - ret = Expression::make_unary(OPERATOR_AND, ret, loc); - ret->unary_expression()->set_does_not_escape(); - } + ret = Expression::make_heap_expression(ret, loc); + + Node* n = Node::make_node(this); + if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE) + ret->heap_expression()->set_allocate_on_stack(); + else if (gogo->compiling_runtime() && gogo->package_name() == "runtime") + go_error_at(loc, "%s escapes to heap, not allowed in runtime", + n->ast_format(gogo).c_str()); // If necessary, check whether the expression or any embedded // pointers are nil. @@ -7058,114 +7218,6 @@ // Class Builtin_call_expression. This is used for a call to a // builtin function. -class Builtin_call_expression : public Call_expression -{ - public: - Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args, - bool is_varargs, Location location); - - protected: - // This overrides Call_expression::do_lower. - Expression* - do_lower(Gogo*, Named_object*, Statement_inserter*, int); - - Expression* - do_flatten(Gogo*, Named_object*, Statement_inserter*); - - bool - do_is_constant() const; - - bool - do_numeric_constant_value(Numeric_constant*) const; - - bool - do_discarding_value(); - - Type* - do_type(); - - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - Expression* - do_copy(); - - Bexpression* - do_get_backend(Translate_context*); - - void - do_export(Export*) const; - - virtual bool - do_is_recover_call() const; - - virtual void - do_set_recover_arg(Expression*); - - private: - // The builtin functions. - enum Builtin_function_code - { - BUILTIN_INVALID, - - // Predeclared builtin functions. - BUILTIN_APPEND, - BUILTIN_CAP, - BUILTIN_CLOSE, - BUILTIN_COMPLEX, - BUILTIN_COPY, - BUILTIN_DELETE, - BUILTIN_IMAG, - BUILTIN_LEN, - BUILTIN_MAKE, - BUILTIN_NEW, - BUILTIN_PANIC, - BUILTIN_PRINT, - BUILTIN_PRINTLN, - BUILTIN_REAL, - BUILTIN_RECOVER, - - // Builtin functions from the unsafe package. - BUILTIN_ALIGNOF, - BUILTIN_OFFSETOF, - BUILTIN_SIZEOF - }; - - Expression* - one_arg() const; - - bool - check_one_arg(); - - static Type* - real_imag_type(Type*); - - static Type* - complex_type(Type*); - - Expression* - lower_make(Statement_inserter*); - - Expression* flatten_append(Gogo*, Named_object*, Statement_inserter*); - - bool - check_int_value(Expression*, bool is_length, bool* small); - - // A pointer back to the general IR structure. This avoids a global - // variable, or passing it around everywhere. - Gogo* gogo_; - // The builtin function being called. - Builtin_function_code code_; - // Used to stop endless loops when the length of an array uses len - // or cap of the array itself. - mutable bool seen_; - // Whether the argument is set for calls to BUILTIN_RECOVER. - bool recover_arg_is_set_; -}; - Builtin_call_expression::Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args, @@ -7372,7 +7424,32 @@ loc); Expression* e3 = Expression::make_temporary_reference(key_temp, loc); - e3 = Expression::make_unary(OPERATOR_AND, e3, loc); + + // If the call to delete is deferred, and is in a loop, + // then the loop will only have a single instance of the + // temporary variable. Passing the address of the + // temporary variable here means that the deferred call + // will see the last value in the loop, not the current + // value. So for this unusual case copy the value into + // the heap. + if (!this->is_deferred()) + e3 = Expression::make_unary(OPERATOR_AND, e3, loc); + else + { + Expression* a = Expression::make_allocation(mt->key_type(), + loc); + Temporary_statement* atemp = + Statement::make_temporary(NULL, a, loc); + inserter->insert(atemp); + + a = Expression::make_temporary_reference(atemp, loc); + a = Expression::make_dereference(a, NIL_CHECK_NOT_NEEDED, loc); + Statement* s = Statement::make_assignment(a, e3, loc); + inserter->insert(s); + + e3 = Expression::make_temporary_reference(atemp, loc); + } + return Runtime::make_call(Runtime::MAPDELETE, this->location(), 3, e1, e2, e3); } @@ -7418,7 +7495,7 @@ break; case BUILTIN_APPEND: - return this->flatten_append(gogo, function, inserter); + return this->flatten_append(gogo, function, inserter, NULL, NULL); case BUILTIN_COPY: { @@ -7536,11 +7613,17 @@ return Expression::make_error(this->location()); } len_arg = Expression::make_integer_ul(0, NULL, loc); + len_small = true; } else { len_arg = *parg; len_arg->determine_type(&int_context); + if (len_arg->type()->integer_type() == NULL) + { + go_error_at(len_arg->location(), "non-integer len argument in make"); + return Expression::make_error(this->location()); + } if (!this->check_int_value(len_arg, true, &len_small)) return Expression::make_error(this->location()); ++parg; @@ -7548,17 +7631,22 @@ Expression* cap_arg = NULL; bool cap_small = false; + Numeric_constant nclen; + Numeric_constant nccap; + unsigned long vlen; + unsigned long vcap; if (is_slice && parg != args->end()) { cap_arg = *parg; cap_arg->determine_type(&int_context); + if (cap_arg->type()->integer_type() == NULL) + { + go_error_at(cap_arg->location(), "non-integer cap argument in make"); + return Expression::make_error(this->location()); + } if (!this->check_int_value(cap_arg, false, &cap_small)) return Expression::make_error(this->location()); - Numeric_constant nclen; - Numeric_constant nccap; - unsigned long vlen; - unsigned long vcap; if (len_arg->numeric_constant_value(&nclen) && cap_arg->numeric_constant_value(&nccap) && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID @@ -7583,19 +7671,25 @@ Expression* call; if (is_slice) { + if (cap_arg == NULL) + { + cap_small = len_small; + if (len_arg->numeric_constant_value(&nclen) + && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID) + cap_arg = Expression::make_integer_ul(vlen, len_arg->type(), loc); + else + { + Temporary_statement* temp = Statement::make_temporary(NULL, + len_arg, + loc); + inserter->insert(temp); + len_arg = Expression::make_temporary_reference(temp, loc); + cap_arg = Expression::make_temporary_reference(temp, loc); + } + } + Type* et = type->array_type()->element_type(); Expression* type_arg = Expression::make_type_descriptor(et, type_loc); - if (cap_arg == NULL) - { - Temporary_statement* temp = Statement::make_temporary(NULL, - len_arg, - loc); - inserter->insert(temp); - len_arg = Expression::make_temporary_reference(temp, loc); - cap_arg = Expression::make_temporary_reference(temp, loc); - cap_small = len_small; - } - Runtime::Function code = Runtime::MAKESLICE; if (!len_small || !cap_small) code = Runtime::MAKESLICE64; @@ -7604,14 +7698,31 @@ else if (is_map) { Expression* type_arg = Expression::make_type_descriptor(type, type_loc); - call = Runtime::make_call(Runtime::MAKEMAP, loc, 4, type_arg, len_arg, - Expression::make_nil(loc), - Expression::make_nil(loc)); + if (!len_small) + call = Runtime::make_call(Runtime::MAKEMAP64, loc, 3, type_arg, + len_arg, + Expression::make_nil(loc)); + else + { + Numeric_constant nclen; + unsigned long vlen; + if (len_arg->numeric_constant_value(&nclen) + && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID + && vlen <= Map_type::bucket_size) + call = Runtime::make_call(Runtime::MAKEMAP_SMALL, loc, 0); + else + call = Runtime::make_call(Runtime::MAKEMAP, loc, 3, type_arg, + len_arg, + Expression::make_nil(loc)); + } } else if (is_chan) { Expression* type_arg = Expression::make_type_descriptor(type, type_loc); - call = Runtime::make_call(Runtime::MAKECHAN, loc, 2, type_arg, len_arg); + Runtime::Function code = Runtime::MAKECHAN; + if (!len_small) + code = Runtime::MAKECHAN64; + call = Runtime::make_call(code, loc, 2, type_arg, len_arg); } else go_unreachable(); @@ -7621,11 +7732,18 @@ // Flatten a call to the predeclared append function. We do this in // the flatten phase, not the lowering phase, so that we run after -// type checking and after order_evaluations. +// type checking and after order_evaluations. If ASSIGN_LHS is not +// NULL, this append is the right-hand-side of an assignment and +// ASSIGN_LHS is the left-hand-side; in that case, set LHS directly +// rather than returning a slice. This lets us omit a write barrier +// in common cases like a = append(a, ...) when the slice does not +// need to grow. ENCLOSING is not NULL iff ASSIGN_LHS is not NULL. Expression* Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, - Statement_inserter* inserter) + Statement_inserter* inserter, + Expression* assign_lhs, + Block* enclosing) { if (this->is_error_expression()) return this; @@ -7642,6 +7760,8 @@ if (args->size() == 1) { // append(s) evaluates to s. + if (assign_lhs != NULL) + return NULL; return args->front(); } @@ -7758,14 +7878,46 @@ // FIXME: Mark this index as not requiring bounds checks. ref = Expression::make_index(ref, zero, ref2, NULL, loc); - Expression* rhs = Expression::make_conditional(cond, call, ref, loc); - - gogo->lower_expression(function, inserter, &rhs); - gogo->flatten_expression(function, inserter, &rhs); - - Expression* lhs = Expression::make_temporary_reference(s1tmp, loc); - Statement* assign = Statement::make_assignment(lhs, rhs, loc); - inserter->insert(assign); + if (assign_lhs == NULL) + { + Expression* rhs = Expression::make_conditional(cond, call, ref, loc); + + gogo->lower_expression(function, inserter, &rhs); + gogo->flatten_expression(function, inserter, &rhs); + + ref = Expression::make_temporary_reference(s1tmp, loc); + Statement* assign = Statement::make_assignment(ref, rhs, loc); + inserter->insert(assign); + } + else + { + gogo->lower_expression(function, inserter, &cond); + gogo->flatten_expression(function, inserter, &cond); + gogo->lower_expression(function, inserter, &call); + gogo->flatten_expression(function, inserter, &call); + gogo->lower_expression(function, inserter, &ref); + gogo->flatten_expression(function, inserter, &ref); + + Block* then_block = new Block(enclosing, loc); + Assignment_statement* assign = + Statement::make_assignment(assign_lhs, call, loc); + then_block->add_statement(assign); + + Block* else_block = new Block(enclosing, loc); + assign = Statement::make_assignment(assign_lhs->copy(), ref, loc); + // This assignment will not change the pointer value, so it does + // not need a write barrier. + assign->set_omit_write_barrier(); + else_block->add_statement(assign); + + Statement* s = Statement::make_if_statement(cond, then_block, + else_block, loc); + inserter->insert(s); + + ref = Expression::make_temporary_reference(s1tmp, loc); + assign = Statement::make_assignment(ref, assign_lhs->copy(), loc); + inserter->insert(assign); + } if (this->is_varargs()) { @@ -7802,12 +7954,17 @@ 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. - lhs = Expression::make_index(ref, ref2, NULL, NULL, loc); + Expression* lhs = Expression::make_index(ref, ref2, NULL, NULL, + loc); gogo->lower_expression(function, inserter, &lhs); gogo->flatten_expression(function, inserter, &lhs); // The flatten pass runs after the write barrier pass, so we // need to insert a write barrier here if necessary. - if (!gogo->assign_needs_write_barrier(lhs)) + // However, if ASSIGN_LHS is not NULL, we have been called + // directly before the write barrier pass. + Statement* assign; + if (assign_lhs != NULL + || !gogo->assign_needs_write_barrier(lhs)) assign = Statement::make_assignment(lhs, *pa, loc); else { @@ -7819,6 +7976,9 @@ } } + if (assign_lhs != NULL) + return NULL; + return Expression::make_temporary_reference(s1tmp, loc); } @@ -7968,8 +8128,10 @@ int Find_call_expression::expression(Expression** pexpr) { - if ((*pexpr)->call_expression() != NULL - || (*pexpr)->receive_expression() != NULL) + Expression* expr = *pexpr; + if (!expr->is_constant() + && (expr->call_expression() != NULL + || expr->receive_expression() != NULL)) { this->found_ = true; return TRAVERSE_EXIT; @@ -7977,6 +8139,24 @@ return TRAVERSE_CONTINUE; } +// Return whether calling len or cap on EXPR, of array type, is a +// constant. The language spec says "the expressions len(s) and +// cap(s) are constants if the type of s is an array or pointer to an +// array and the expression s does not contain channel receives or +// (non-constant) function calls." + +bool +Builtin_call_expression::array_len_is_constant(Expression* expr) +{ + go_assert(expr->type()->deref()->array_type() != NULL + && !expr->type()->deref()->is_slice_type()); + if (expr->is_constant()) + return true; + Find_call_expression find_call; + Expression::traverse(&expr, &find_call); + return !find_call.found(); +} + // Return whether this is constant: len of a string constant, or len // or cap of an array, or unsafe.Sizeof, unsafe.Offsetof, // unsafe.Alignof. @@ -8004,20 +8184,14 @@ && !arg_type->points_to()->is_slice_type()) arg_type = arg_type->points_to(); - // The len and cap functions are only constant if there are no - // function calls or channel operations in the arguments. - // Otherwise we have to make the call. - if (!arg->is_constant()) - { - Find_call_expression find_call; - Expression::traverse(&arg, &find_call); - if (find_call.found()) - return false; - } - if (arg_type->array_type() != NULL && arg_type->array_type()->length() != NULL) - return true; + { + this->seen_ = true; + bool ret = Builtin_call_expression::array_len_is_constant(arg); + this->seen_ = false; + return ret; + } if (this->code_ == BUILTIN_LEN && arg_type->is_string_type()) { @@ -8178,6 +8352,11 @@ return false; if (st->named_type() != NULL) st->named_type()->convert(this->gogo_); + if (st->is_error_type()) + { + go_assert(saw_errors()); + return false; + } int64_t offset; this->seen_ = true; bool ok = st->struct_type()->backend_field_offset(this->gogo_, @@ -8240,7 +8419,9 @@ && !rnc.type()->is_abstract() && inc.type() != NULL && !inc.type()->is_abstract() - && !Type::are_identical(rnc.type(), inc.type(), false, NULL)) + && !Type::are_identical(rnc.type(), inc.type(), + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) return false; mpfr_t r; @@ -8327,12 +8508,19 @@ return Type::make_error_type(); case BUILTIN_NEW: + { + const Expression_list* args = this->args(); + if (args == NULL || args->empty()) + return Type::make_error_type(); + return Type::make_pointer_type(args->front()->type()); + } + case BUILTIN_MAKE: { const Expression_list* args = this->args(); if (args == NULL || args->empty()) return Type::make_error_type(); - return Type::make_pointer_type(args->front()->type()); + return args->front()->type(); } case BUILTIN_CAP: @@ -8714,7 +8902,7 @@ if (arg2_type->is_slice_type()) { Type* e2 = arg2_type->array_type()->element_type(); - if (!Type::are_identical(e1, e2, true, NULL)) + if (!Type::are_identical(e1, e2, Type::COMPARE_TAGS, NULL)) this->report_error(_("element types must be the same")); } else if (arg2_type->is_string_type()) @@ -8852,7 +9040,8 @@ || args->back()->type()->is_error()) this->set_is_error(); else if (!Type::are_identical(args->front()->type(), - args->back()->type(), true, NULL)) + args->back()->type(), + Type::COMPARE_TAGS, NULL)) this->report_error(_("complex arguments must have identical types")); else if (args->front()->type()->float_type() == NULL) this->report_error(_("complex arguments must have " @@ -8878,6 +9067,10 @@ if (this->varargs_are_lowered()) bce->set_varargs_are_lowered(); + if (this->is_deferred()) + bce->set_is_deferred(); + if (this->is_concurrent()) + bce->set_is_concurrent(); return bce; } @@ -8922,7 +9115,8 @@ arg_type = arg_type->points_to(); go_assert(arg_type->array_type() != NULL && !arg_type->is_slice_type()); - arg = Expression::make_unary(OPERATOR_MULT, arg, location); + arg = Expression::make_dereference(arg, NIL_CHECK_DEFAULT, + location); } Type* int_type = Type::lookup_integer_type("int"); @@ -8956,8 +9150,9 @@ arg, nil, location); Expression* zero = Expression::make_integer_ul(0, int_type, location); - Expression* indir = Expression::make_unary(OPERATOR_MULT, - arg, location); + Expression* indir = + Expression::make_dereference(arg, NIL_CHECK_NOT_NEEDED, + location); val = Expression::make_conditional(cmp, zero, indir, location); } else @@ -8998,8 +9193,9 @@ arg, nil, location); Expression* zero = Expression::make_integer_ul(0, int_type, location); - Expression* indir = Expression::make_unary(OPERATOR_MULT, - parg, location); + Expression* indir = + Expression::make_dereference(parg, NIL_CHECK_NOT_NEEDED, + location); val = Expression::make_conditional(cmp, zero, indir, location); } else @@ -9457,8 +9653,16 @@ // Recognize a call to a builtin function. if (fntype->is_builtin()) - return new Builtin_call_expression(gogo, this->fn_, this->args_, - this->is_varargs_, loc); + { + Builtin_call_expression* bce = + new Builtin_call_expression(gogo, this->fn_, this->args_, + this->is_varargs_, loc); + if (this->is_deferred_) + bce->set_is_deferred(); + if (this->is_concurrent_) + bce->set_is_concurrent(); + return bce; + } // If this call returns multiple results, create a temporary // variable to hold them. @@ -9553,14 +9757,8 @@ // could implement them in normal code, but then we would have to // explicitly unwind the stack. These functions are intended to be // efficient. Note that this technique obviously only works for - // direct calls, but that is the only way they are used. The actual - // argument to these functions is always the address of a parameter; - // we don't need that for the GCC builtin functions, so we just - // ignore it. - if (gogo->compiling_runtime() - && this->args_ != NULL - && this->args_->size() == 1 - && gogo->package_name() == "runtime") + // direct calls, but that is the only way they are used. + if (gogo->compiling_runtime() && gogo->package_name() == "runtime") { Func_expression* fe = this->fn_->func_expression(); if (fe != NULL @@ -9568,14 +9766,16 @@ && fe->named_object()->package() == NULL) { std::string n = Gogo::unpack_hidden_name(fe->named_object()->name()); - if (n == "getcallerpc") + if ((this->args_ == NULL || this->args_->size() == 0) + && n == "getcallerpc") { static Named_object* builtin_return_address; return this->lower_to_builtin(&builtin_return_address, "__builtin_return_address", 0); } - else if (n == "getcallersp") + else if ((this->args_ == NULL || this->args_->size() == 0) + && n == "getcallersp") { static Named_object* builtin_frame_address; return this->lower_to_builtin(&builtin_frame_address, @@ -9601,12 +9801,6 @@ Type* varargs_type, size_t param_count, Slice_storage_escape_disp escape_disp) { - // When compiling the runtime, varargs slices do not escape. When - // escape analysis becomes the default, this should be changed to - // make it an error if we have a varargs slice that escapes. - if (gogo->compiling_runtime() && gogo->package_name() == "runtime") - escape_disp = SLICE_STORAGE_DOES_NOT_ESCAPE; - if (this->varargs_are_lowered_) return; @@ -9759,7 +9953,8 @@ for (; pa != this->args_->end(); ++pa, ++pp) { go_assert(pp != fntype->parameters()->end()); - if (Type::are_identical(pp->type(), (*pa)->type(), true, NULL)) + if (Type::are_identical(pp->type(), (*pa)->type(), + Type::COMPARE_TAGS, NULL)) args->push_back(*pa); else { @@ -10077,7 +10272,7 @@ } const Typed_identifier_list* parameters = fntype->parameters(); - if (this->args_ == NULL) + if (this->args_ == NULL || this->args_->size() == 0) { if (parameters != NULL && !parameters->empty()) this->report_error(_("not enough arguments")); @@ -10136,6 +10331,10 @@ if (this->varargs_are_lowered_) call->set_varargs_are_lowered(); + if (this->is_deferred_) + call->set_is_deferred(); + if (this->is_concurrent_) + call->set_is_concurrent(); return call; } @@ -10277,7 +10476,7 @@ Type::make_pointer_type( Type::make_pointer_type(Type::make_void_type())); fn = Expression::make_unsafe_cast(pfntype, this->fn_, location); - fn = Expression::make_unary(OPERATOR_MULT, fn, location); + fn = Expression::make_dereference(fn, NIL_CHECK_NOT_NEEDED, location); } else { @@ -10535,8 +10734,8 @@ && type->points_to()->array_type() != NULL && !type->points_to()->is_slice_type()) { - Expression* deref = Expression::make_unary(OPERATOR_MULT, left, - location); + Expression* deref = + Expression::make_dereference(left, NIL_CHECK_DEFAULT, location); // For an ordinary index into the array, the pointer will be // dereferenced. For a slice it will not--the resulting slice @@ -10714,7 +10913,7 @@ // Check types of an array index. void -Array_index_expression::do_check_types(Gogo* gogo) +Array_index_expression::do_check_types(Gogo*) { Numeric_constant nc; unsigned long v; @@ -10833,19 +11032,24 @@ if (!this->array_->is_addressable()) this->report_error(_("slice of unaddressable value")); else - { - bool escapes = true; - - // When compiling the runtime, a slice operation does not - // cause local variables to escape. When escape analysis - // becomes the default, this should be changed to make it an - // error if we have a slice operation that escapes. - if (gogo->compiling_runtime() && gogo->package_name() == "runtime") - escapes = false; - - this->array_->address_taken(escapes); - } - } + // Set the array address taken but not escape. The escape + // analysis will make it escape to heap when needed. + this->array_->address_taken(false); + } +} + +// The subexpressions of an array index must be evaluated in order. +// If this is indexing into an array, rather than a slice, then only +// the index should be evaluated. Since this is called for values on +// the left hand side of an assigment, evaluating the array, meaning +// copying the array, will cause a different array to be modified. + +bool +Array_index_expression::do_must_eval_subexpressions_in_order( + int* skip) const +{ + *skip = this->array_->type()->is_slice_type() ? 0 : 1; + return true; } // Flatten array indexing by using temporary variables for slices and indexes. @@ -10921,6 +11125,14 @@ return this->array_->is_addressable(); } +void +Array_index_expression::do_address_taken(bool escapes) +{ + // In &x[0], if x is a slice, then x's address is not taken. + if (!this->array_->type()->is_slice_type()) + this->array_->address_taken(escapes); +} + // Get the backend representation for an array index. Bexpression* @@ -11300,7 +11512,8 @@ Location loc = this->location(); Expression* string_arg = this->string_; if (this->string_->type()->points_to() != NULL) - string_arg = Expression::make_unary(OPERATOR_MULT, this->string_, loc); + string_arg = Expression::make_dereference(this->string_, + NIL_CHECK_NOT_NEEDED, loc); Expression* bad_index = Expression::check_bounds(this->start_, loc); @@ -11436,7 +11649,9 @@ return Expression::make_error(loc); } - if (!Type::are_identical(mt->key_type(), this->index_->type(), false, NULL)) + if (!Type::are_identical(mt->key_type(), this->index_->type(), + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) { if (this->index_->type()->interface_type() != NULL && !this->index_->is_variable()) @@ -11534,8 +11749,9 @@ go_assert(this->value_pointer_ != NULL && this->value_pointer_->is_variable()); - Expression* val = Expression::make_unary(OPERATOR_MULT, this->value_pointer_, - this->location()); + Expression* val = Expression::make_dereference(this->value_pointer_, + NIL_CHECK_NOT_NEEDED, + this->location()); return val->get_backend(context); } @@ -11645,7 +11861,7 @@ Location loc = this->location(); std::string s = "fieldtrack \""; - Named_type* nt = this->expr_->type()->named_type(); + Named_type* nt = this->expr_->type()->unalias()->named_type(); if (nt == NULL || nt->named_object()->package() == NULL) s.append(gogo->pkgpath()); else @@ -11771,7 +11987,7 @@ Expression* ref = this->expr_; Location loc = this->location(); if (ref->type()->points_to() != NULL) - ref = Expression::make_unary(OPERATOR_MULT, ref, loc); + ref = Expression::make_dereference(ref, NIL_CHECK_DEFAULT, loc); Expression* mtable = Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc); @@ -11781,7 +11997,8 @@ 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); + + mtable = Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, loc); return Expression::make_field_reference(mtable, index, loc); } @@ -11793,7 +12010,8 @@ { Expression* expr = this->expr_; if (expr->type()->points_to() != NULL) - expr = Expression::make_unary(OPERATOR_MULT, expr, this->location()); + expr = Expression::make_dereference(expr, NIL_CHECK_DEFAULT, + this->location()); return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT, this->location()); } @@ -11824,10 +12042,9 @@ if (!this->expr_->is_variable()) { Temporary_statement* temp = - Statement::make_temporary(this->expr_->type(), NULL, this->location()); + Statement::make_temporary(NULL, this->expr_, this->location()); inserter->insert(temp); - this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_, - this->location()); + this->expr_ = Expression::make_temporary_reference(temp, this->location()); } return this; } @@ -11932,25 +12149,25 @@ const Typed_identifier* method_id = type->find_method(name); if (method_id == NULL) - return Named_object::make_erroneous_name(Gogo::thunk_name()); + return Named_object::make_erroneous_name(gogo->thunk_name()); Function_type* orig_fntype = method_id->type()->function_type(); if (orig_fntype == NULL) - return Named_object::make_erroneous_name(Gogo::thunk_name()); + return Named_object::make_erroneous_name(gogo->thunk_name()); Struct_field_list* sfl = new Struct_field_list(); // The type here is wrong--it should be the C function type. But it // doesn't really matter. Type* vt = Type::make_pointer_type(Type::make_void_type()); - sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); - sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc))); + sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val", type, loc))); Struct_type* st = Type::make_struct_type(sfl, loc); st->set_is_struct_incomparable(); Type* closure_type = Type::make_pointer_type(st); Function_type* new_fntype = orig_fntype->copy_with_names(); - std::string thunk_name = Gogo::thunk_name(); + std::string thunk_name = gogo->thunk_name(); Named_object* new_no = gogo->start_function(thunk_name, new_fntype, false, loc); @@ -11966,7 +12183,7 @@ // Field 0 of the closure is the function code pointer, field 1 is // the value on which to invoke the method. Expression* arg = Expression::make_var_reference(cp, loc); - arg = Expression::make_unary(OPERATOR_MULT, arg, loc); + arg = Expression::make_dereference(arg, NIL_CHECK_NOT_NEEDED, loc); arg = Expression::make_field_reference(arg, 1, loc); Expression *ifre = Expression::make_interface_field_reference(arg, name, @@ -12038,10 +12255,10 @@ Location loc = this->location(); Struct_field_list* fields = new Struct_field_list(); - fields->push_back(Struct_field(Typed_identifier("fn.0", + fields->push_back(Struct_field(Typed_identifier("fn", thunk->func_value()->type(), loc))); - fields->push_back(Struct_field(Typed_identifier("val.1", + fields->push_back(Struct_field(Typed_identifier("val", this->expr_->type(), loc))); Struct_type* st = Type::make_struct_type(fields, loc); @@ -12290,7 +12507,7 @@ return f; } - Named_object* no = gogo->start_function(Gogo::thunk_name(), fntype, false, + Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false, location); Named_object* vno = gogo->lookup(receiver_name, NULL); @@ -12395,7 +12612,8 @@ Allocation_expression::do_copy() { Allocation_expression* alloc = - new Allocation_expression(this->type_, this->location()); + new Allocation_expression(this->type_->copy_expressions(), + this->location()); if (this->allocate_on_stack_) alloc->set_allocate_on_stack(); return alloc; @@ -12408,10 +12626,9 @@ { Gogo* gogo = context->gogo(); Location loc = this->location(); - - Node* n = Node::make_node(this); - if (this->allocate_on_stack_ - || (n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) + Btype* btype = this->type_->get_backend(gogo); + + if (this->allocate_on_stack_) { int64_t size; bool ok = this->type_->backend_type_size(gogo, &size); @@ -12420,10 +12637,20 @@ go_assert(saw_errors()); return gogo->backend()->error_expression(); } - return gogo->backend()->stack_allocation_expression(size, loc); - } - - Btype* btype = this->type_->get_backend(gogo); + Bstatement* decl; + Named_object* fn = context->function(); + go_assert(fn != NULL); + Bfunction* fndecl = fn->func_value()->get_or_make_decl(gogo, fn); + Bexpression* zero = gogo->backend()->zero_expression(btype); + Bvariable* temp = + gogo->backend()->temporary_variable(fndecl, context->bblock(), btype, + zero, true, loc, &decl); + Bexpression* ret = gogo->backend()->var_expression(temp, loc); + ret = gogo->backend()->address_expression(ret, loc); + ret = gogo->backend()->compound_expression(decl, ret, loc); + return ret; + } + Bexpression* space = gogo->allocate_memory(this->type_, loc)->get_backend(context); Btype* pbtype = gogo->backend()->pointer_type(btype); @@ -12626,6 +12853,22 @@ go_assert(pv == this->vals()->end()); } +// Copy. + +Expression* +Struct_construction_expression::do_copy() +{ + Struct_construction_expression* ret = + new Struct_construction_expression(this->type_->copy_expressions(), + (this->vals() == NULL + ? NULL + : this->vals()->copy()), + this->location()); + if (this->traverse_order() != NULL) + ret->set_traverse_order(this->traverse_order()); + return ret; +} + // Flatten a struct construction expression. Store the values into // temporaries in case they need interface conversion. @@ -13017,6 +13260,20 @@ type, indexes, vals, location) { go_assert(type->array_type() != NULL && !type->is_slice_type()); } + +// Copy. + +Expression* +Fixed_array_construction_expression::do_copy() +{ + Type* t = this->type()->copy_expressions(); + return new Fixed_array_construction_expression(t, this->indexes(), + (this->vals() == NULL + ? NULL + : this->vals()->copy()), + this->location()); +} + // Return the backend representation for constructing a fixed array. Bexpression* @@ -13151,6 +13408,19 @@ ast_dump_context->dump_expression(this->slice_storage_); } +// Copy. + +Expression* +Slice_construction_expression::do_copy() +{ + return new Slice_construction_expression(this->type()->copy_expressions(), + this->indexes(), + (this->vals() == NULL + ? NULL + : this->vals()->copy()), + this->location()); +} + // Return the backend representation for constructing a slice. Bexpression* @@ -13187,13 +13457,8 @@ } else { + go_assert(this->storage_escapes_ || this->element_count() == 0); space = Expression::make_heap_expression(this->array_val_, loc); - Node* n = Node::make_node(this); - if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) - { - n = Node::make_node(space); - n->set_encoding(Node::ESCAPE_NONE); - } } // Build a constructor for the slice. @@ -13369,6 +13634,18 @@ } } +// Copy. + +Expression* +Map_construction_expression::do_copy() +{ + return new Map_construction_expression(this->type_->copy_expressions(), + (this->vals_ == NULL + ? NULL + : this->vals_->copy()), + this->location()); +} + // Return the backend representation for constructing a map. Bexpression* @@ -13531,6 +13808,7 @@ for (int depth = 0; depth < this->depth_; ++depth) { + type = type->deref(); if (type->array_type() != NULL) type = type->array_type()->element_type(); else if (type->map_type() != NULL) @@ -14048,6 +14326,23 @@ return new Map_construction_expression(type, this->vals_, location); } +// Copy. + +Expression* +Composite_literal_expression::do_copy() +{ + Composite_literal_expression* ret = + new Composite_literal_expression(this->type_->copy_expressions(), + this->depth_, this->has_keys_, + (this->vals_ == NULL + ? NULL + : this->vals_->copy()), + this->all_are_names_, + this->location()); + ret->key_path_ = this->key_path_; + return ret; +} + // Dump ast representation for a composite literal expression. void @@ -14224,6 +14519,16 @@ } } +// Copy. + +Expression* +Type_guard_expression::do_copy() +{ + return new Type_guard_expression(this->expr_->copy(), + this->type_->copy_expressions(), + this->location()); +} + // Return the backend representation for a type guard expression. Bexpression* @@ -14287,8 +14592,7 @@ Btype* btype = this->type()->get_backend(gogo); Expression* alloc = Expression::make_allocation(etype, loc); - Node* n = Node::make_node(this); - if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) + if (this->allocate_on_stack_) alloc->allocation_expression()->set_allocate_on_stack(); Bexpression* space = alloc->get_backend(context); @@ -14307,9 +14611,9 @@ // don't do this in the write barrier pass because in some cases // backend conversion can introduce new Heap_expression values. Bstatement* assn; - if (!etype->has_pointer()) - { - space = gogo->backend()->var_expression(space_temp, VE_lvalue, loc); + if (!etype->has_pointer() || this->allocate_on_stack_) + { + space = gogo->backend()->var_expression(space_temp, loc); Bexpression* ref = gogo->backend()->indirect_expression(expr_btype, space, true, loc); assn = gogo->backend()->assignment_statement(fndecl, ref, bexpr, loc); @@ -14322,12 +14626,12 @@ expr_btype, bexpr, true, loc, &edecl); Bexpression* btempref = gogo->backend()->var_expression(btemp, - VE_lvalue, loc); + loc); Bexpression* addr = gogo->backend()->address_expression(btempref, loc); Expression* td = Expression::make_type_descriptor(etype, loc); Type* etype_ptr = Type::make_pointer_type(etype); - space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc); + space = gogo->backend()->var_expression(space_temp, loc); Expression* elhs = Expression::make_backend(space, etype_ptr, loc); Expression* erhs = Expression::make_backend(addr, etype_ptr, loc); Expression* call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3, @@ -14337,7 +14641,7 @@ assn = gogo->backend()->compound_statement(edecl, s); } decl = gogo->backend()->compound_statement(decl, assn); - space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc); + space = gogo->backend()->var_expression(space_temp, loc); return gogo->backend()->compound_expression(decl, space, loc); } @@ -14661,7 +14965,7 @@ Bvariable* bvar = this->type_->gc_ptrmask_var(gogo, ptrsize, ptrdata); Location bloc = Linemap::predeclared_location(); - Bexpression* bref = gogo->backend()->var_expression(bvar, VE_rvalue, bloc); + Bexpression* bref = gogo->backend()->var_expression(bvar, bloc); Bexpression* baddr = gogo->backend()->address_expression(bref, bloc); Type* uint8_type = Type::lookup_integer_type("uint8"); @@ -14952,7 +15256,8 @@ Expression* do_copy() { - return new Slice_value_expression(this->type_, this->valptr_->copy(), + return new Slice_value_expression(this->type_->copy_expressions(), + this->valptr_->copy(), this->len_->copy(), this->cap_->copy(), this->location()); } @@ -15213,7 +15518,7 @@ Expression* do_copy() { - return new Interface_value_expression(this->type_, + return new Interface_value_expression(this->type_->copy_expressions(), this->first_field_->copy(), this->obj_->copy(), this->location()); } @@ -15308,7 +15613,9 @@ Expression* do_copy() { - return new Interface_mtable_expression(this->itype_, this->type_, + Interface_type* itype = this->itype_->copy_expressions()->interface_type(); + return new Interface_mtable_expression(itype, + this->type_->copy_expressions(), this->is_pointer_, this->location()); } @@ -15380,8 +15687,7 @@ Gogo* gogo = context->gogo(); Location loc = Linemap::predeclared_location(); if (this->bvar_ != NULL) - return gogo->backend()->var_expression(this->bvar_, VE_rvalue, - this->location()); + return gogo->backend()->var_expression(this->bvar_, this->location()); const Typed_identifier_list* interface_methods = this->itype_->methods(); go_assert(!interface_methods->empty()); @@ -15421,8 +15727,7 @@ this->bvar_ = gogo->backend()->immutable_struct_reference(mangled_name, asm_name, btype, loc); - return gogo->backend()->var_expression(this->bvar_, VE_rvalue, - this->location()); + return gogo->backend()->var_expression(this->bvar_, this->location()); } // The first element is the type descriptor. @@ -15487,7 +15792,7 @@ !is_public, btype, loc); gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false, !is_public, btype, loc, ctor); - return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc); + return gogo->backend()->var_expression(this->bvar_, loc); } void @@ -15664,7 +15969,8 @@ Conditional_expression::do_type() { Type* result_type = Type::make_void_type(); - if (Type::are_identical(this->then_->type(), this->else_->type(), false, + if (Type::are_identical(this->then_->type(), this->else_->type(), + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, NULL)) result_type = this->then_->type(); else if (this->then_->is_nil_expression() @@ -15798,6 +16104,13 @@ return TRAVERSE_CONTINUE; } +Expression* +Backend_expression::do_copy() +{ + return new Backend_expression(this->bexpr_, this->type_->copy_expressions(), + this->location()); +} + void Backend_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const { @@ -16026,10 +16339,16 @@ this->clear(); this->classification_ = NC_FLOAT; this->type_ = type; + // Numeric constants do not have negative zero values, so remove // them here. They also don't have infinity or NaN values, but we // should never see them here. - if (mpfr_zero_p(val)) + int bits = 0; + if (type != NULL + && type->float_type() != NULL + && !type->float_type()->is_abstract()) + bits = type->float_type()->bits(); + if (Numeric_constant::is_float_neg_zero(val, bits)) mpfr_init_set_ui(this->u_.float_val, 0, GMP_RNDN); else mpfr_init_set(this->u_.float_val, val, GMP_RNDN); @@ -16043,8 +16362,60 @@ this->clear(); this->classification_ = NC_COMPLEX; this->type_ = type; + + // Avoid negative zero as in set_float. + int bits = 0; + if (type != NULL + && type->complex_type() != NULL + && !type->complex_type()->is_abstract()) + bits = type->complex_type()->bits() / 2; + + mpfr_t real; + mpfr_init_set(real, mpc_realref(val), GMP_RNDN); + if (Numeric_constant::is_float_neg_zero(real, bits)) + mpfr_set_ui(real, 0, GMP_RNDN); + + mpfr_t imag; + mpfr_init_set(imag, mpc_imagref(val), GMP_RNDN); + if (Numeric_constant::is_float_neg_zero(imag, bits)) + mpfr_set_ui(imag, 0, GMP_RNDN); + mpc_init2(this->u_.complex_val, mpc_precision); - mpc_set(this->u_.complex_val, val, MPC_RNDNN); + mpc_set_fr_fr(this->u_.complex_val, real, imag, MPC_RNDNN); + + mpfr_clear(real); + mpfr_clear(imag); +} + +// Return whether VAL, at a precision of BITS, is a negative zero. +// BITS may be zero in which case it is ignored. + +bool +Numeric_constant::is_float_neg_zero(const mpfr_t val, int bits) +{ + if (!mpfr_signbit(val)) + return false; + if (mpfr_zero_p(val)) + return true; + mp_exp_t min_exp; + switch (bits) + { + case 0: + return false; + case 32: + // In a denormalized float32 the exponent is -126, and there are + // 24 bits of which at least the last must be 1, so the smallest + // representable non-zero exponent is -126 - (24 - 1) == -149. + min_exp = -149; + break; + case 64: + // Minimum exponent is -1022, there are 53 bits. + min_exp = -1074; + break; + default: + go_unreachable(); + } + return mpfr_get_exp(val) < min_exp; } // Get an int value.