This patch to the Go frontend by Than McIntosh adds a new control variable to the Gogo class that stores the size threshold for nil checks. This value can be used to control the policy for deciding when a given deference operation needs a check and when it does not. A size threshold of -1 means that every potentially faulting dereference needs an explicit check (and branch to error call). A size threshold of K (where K > 0) means that if the size of the object being dereferenced is >= K, then we need a check. Currently for gccgo we keep the same policy: do a nil check for an offset more than 4096. Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu. Committed to mainline.
Ian 2017-12-01 Than McIntosh <th...@google.com> * go-c.h (go_create_gogo_args): Add nil_check_size_threshold field. * go-lang.c (go_langhook_init): Set nil_check_size_threshold.
Index: gcc/go/go-c.h =================================================================== --- gcc/go/go-c.h (revision 254090) +++ gcc/go/go-c.h (working copy) @@ -47,6 +47,7 @@ struct go_create_gogo_args bool check_divide_overflow; bool compiling_runtime; int debug_escape_level; + int64_t nil_check_size_threshold; }; extern void go_create_gogo (const struct go_create_gogo_args*); Index: gcc/go/go-lang.c =================================================================== --- gcc/go/go-lang.c (revision 254090) +++ gcc/go/go-lang.c (working copy) @@ -112,6 +112,7 @@ go_langhook_init (void) args.check_divide_overflow = go_check_divide_overflow; args.compiling_runtime = go_compiling_runtime; args.debug_escape_level = go_debug_escape_level; + args.nil_check_size_threshold = 4096; args.linemap = go_get_linemap(); args.backend = go_get_backend(); go_create_gogo (&args); Index: gcc/go/gofrontend/MERGE =================================================================== --- gcc/go/gofrontend/MERGE (revision 255266) +++ gcc/go/gofrontend/MERGE (working copy) @@ -1,4 +1,4 @@ -0d6b3abcbfe04949db947081651a503ceb12fe6e +8cd42a3e9e0e618bb09e67be73f7d2f2477a0faa The first line of this file holds the git revision number of the last merge done from the gofrontend repository. Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 254748) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -290,7 +290,7 @@ Expression::get_interface_type_descripto 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); @@ -393,7 +393,8 @@ Expression::convert_interface_to_type(Ty { 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); } @@ -3842,24 +3843,20 @@ Unary_expression::do_flatten(Gogo* gogo, && !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(); } } @@ -3960,6 +3957,41 @@ Unary_expression::base_is_static_initial 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. @@ -4408,43 +4440,42 @@ Unary_expression::do_get_backend(Transla { 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()); 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, 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, 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; @@ -4529,6 +4560,19 @@ Expression::make_unary(Operator op, Expr 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. @@ -6829,7 +6873,7 @@ Bound_method_expression::create_thunk(Go // 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); @@ -6893,7 +6937,8 @@ bme_check_nil(const Method::Field_indexe 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, @@ -6948,7 +6993,7 @@ Bound_method_expression::do_flatten(Gogo 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 @@ -8919,7 +8964,8 @@ Builtin_call_expression::do_get_backend( 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"); @@ -8953,8 +8999,9 @@ Builtin_call_expression::do_get_backend( 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 @@ -8995,8 +9042,9 @@ Builtin_call_expression::do_get_backend( 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 @@ -10274,7 +10322,7 @@ Call_expression::do_get_backend(Translat 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 { @@ -10532,8 +10580,8 @@ Index_expression::do_lower(Gogo*, Named_ && 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 @@ -11297,7 +11345,8 @@ String_index_expression::do_get_backend( 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); @@ -11531,8 +11580,9 @@ Map_index_expression::do_get_backend(Tra 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); } @@ -11768,7 +11818,7 @@ Interface_field_reference_expression::ge 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); @@ -11778,7 +11828,8 @@ Interface_field_reference_expression::ge 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); } @@ -11790,7 +11841,8 @@ Interface_field_reference_expression::ge { 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()); } @@ -11963,7 +12015,7 @@ Interface_field_reference_expression::cr // 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, Index: gcc/go/gofrontend/expressions.h =================================================================== --- gcc/go/gofrontend/expressions.h (revision 254748) +++ gcc/go/gofrontend/expressions.h (working copy) @@ -506,6 +506,23 @@ class Expression static Expression* make_backend(Bexpression*, Type*, Location); + enum Nil_check_classification + { + // Use the default policy for deciding if this deref needs a check. + NIL_CHECK_DEFAULT, + // An explicit check is required for this dereference operation. + NIL_CHECK_NEEDED, + // No check needed for this dereference operation. + NIL_CHECK_NOT_NEEDED, + // A type error or error construct was encountered when determining + // whether this deref needs an explicit check. + NIL_CHECK_ERROR_ENCOUNTERED + }; + + // Make a dereference expression. + static Expression* + make_dereference(Expression*, Nil_check_classification, Location); + // Return the expression classification. Expression_classification classification() const @@ -1730,7 +1747,8 @@ class Unary_expression : public Expressi Unary_expression(Operator op, Expression* expr, Location location) : Expression(EXPRESSION_UNARY, location), op_(op), escapes_(true), create_temp_(false), is_gc_root_(false), - is_slice_init_(false), expr_(expr), issue_nil_check_(false) + is_slice_init_(false), expr_(expr), + issue_nil_check_(NIL_CHECK_DEFAULT) { } // Return the operator. @@ -1792,6 +1810,17 @@ class Unary_expression : public Expressi static Expression* do_import(Import*); + // Declare that this deref does or does not require an explicit nil check. + void + set_requires_nil_check(bool needed) + { + go_assert(this->op_ == OPERATOR_MULT); + if (needed) + this->issue_nil_check_ = NIL_CHECK_NEEDED; + else + this->issue_nil_check_ = NIL_CHECK_NOT_NEEDED; + } + protected: int do_traverse(Traverse* traverse) @@ -1847,12 +1876,20 @@ class Unary_expression : public Expressi void do_issue_nil_check() - { this->issue_nil_check_ = (this->op_ == OPERATOR_MULT); } + { + if (this->op_ == OPERATOR_MULT) + this->set_requires_nil_check(true); + } private: static bool base_is_static_initializer(Expression*); + // Return a determination as to whether this dereference expression + // requires a nil check. + Nil_check_classification + requires_nil_check(Gogo*); + // The unary operator to apply. Operator op_; // Normally true. False if this is an address expression which does @@ -1874,7 +1911,7 @@ class Unary_expression : public Expressi Expression* expr_; // Whether or not to issue a nil check for this expression if its address // is being taken. - bool issue_nil_check_; + Nil_check_classification issue_nil_check_; }; // A binary expression. Index: gcc/go/gofrontend/go.cc =================================================================== --- gcc/go/gofrontend/go.cc (revision 254090) +++ gcc/go/gofrontend/go.cc (working copy) @@ -41,6 +41,7 @@ go_create_gogo(const struct go_create_go if (args->c_header != NULL) ::gogo->set_c_header(args->c_header); ::gogo->set_debug_escape_level(args->debug_escape_level); + ::gogo->set_nil_check_size_threshold(args->nil_check_size_threshold); } // Parse the input files. Index: gcc/go/gofrontend/gogo.cc =================================================================== --- gcc/go/gofrontend/gogo.cc (revision 254748) +++ gcc/go/gofrontend/gogo.cc (working copy) @@ -55,6 +55,7 @@ Gogo::Gogo(Backend* backend, Linemap* li check_divide_overflow_(true), compiling_runtime_(false), debug_escape_level_(0), + nil_check_size_threshold_(4096), verify_types_(), interface_types_(), specific_type_functions_(), @@ -5567,7 +5568,10 @@ Function::build(Gogo* gogo, Named_object vars.push_back(bvar); Expression* parm_ref = Expression::make_var_reference(parm_no, loc); - parm_ref = Expression::make_unary(OPERATOR_MULT, parm_ref, loc); + parm_ref = + Expression::make_dereference(parm_ref, + Expression::NIL_CHECK_DEFAULT, + loc); if ((*p)->var_value()->is_in_heap()) parm_ref = Expression::make_heap_expression(parm_ref, loc); var_inits.push_back(parm_ref->get_backend(&context)); Index: gcc/go/gofrontend/gogo.h =================================================================== --- gcc/go/gofrontend/gogo.h (revision 255062) +++ gcc/go/gofrontend/gogo.h (working copy) @@ -318,6 +318,20 @@ class Gogo set_debug_escape_level(int level) { this->debug_escape_level_ = level; } + // Return the size threshold used to determine whether to issue + // a nil-check for a given pointer dereference. A threshold of -1 + // implies that all potentially faulting dereference ops should + // be nil-checked. A positive threshold of N implies that a deref + // of *P where P has size less than N doesn't need a nil check. + int64_t + nil_check_size_threshold() const + { return this->nil_check_size_threshold_; } + + // Set the nil-check size threshold, as described above. + void + set_nil_check_size_threshold(int64_t bytes) + { this->nil_check_size_threshold_ = bytes; } + // Import a package. FILENAME is the file name argument, LOCAL_NAME // is the local name to give to the package. If LOCAL_NAME is empty // the declarations are added to the global scope. @@ -1025,6 +1039,8 @@ class Gogo // The level of escape analysis debug information to emit, from the // -fgo-debug-escape option. int debug_escape_level_; + // Nil-check size threshhold. + int64_t nil_check_size_threshold_; // A list of types to verify. std::vector<Type*> verify_types_; // A list of interface types defined while parsing. Index: gcc/go/gofrontend/parse.cc =================================================================== --- gcc/go/gofrontend/parse.cc (revision 255062) +++ gcc/go/gofrontend/parse.cc (working copy) @@ -2745,14 +2745,17 @@ Parse::enclosing_var_reference(Named_obj Expression* closure_ref = Expression::make_var_reference(closure, location); - closure_ref = Expression::make_unary(OPERATOR_MULT, closure_ref, location); + closure_ref = + Expression::make_dereference(closure_ref, + Expression::NIL_CHECK_DEFAULT, + location); // The closure structure holds pointers to the variables, so we need // to introduce an indirection. Expression* e = Expression::make_field_reference(closure_ref, ins.first->index(), location); - e = Expression::make_unary(OPERATOR_MULT, e, location); + e = Expression::make_dereference(e, Expression::NIL_CHECK_DEFAULT, location); return Expression::make_enclosing_var_reference(e, var, location); } Index: gcc/go/gofrontend/statements.cc =================================================================== --- gcc/go/gofrontend/statements.cc (revision 254983) +++ gcc/go/gofrontend/statements.cc (working copy) @@ -315,7 +315,8 @@ Variable_declaration_statement::do_get_b if (binit != NULL) { Expression* e = Expression::make_temporary_reference(temp, loc); - e = Expression::make_unary(OPERATOR_MULT, e, loc); + e = Expression::make_dereference(e, Expression::NIL_CHECK_NOT_NEEDED, + loc); Bexpression* be = e->get_backend(context); set = context->backend()->assignment_statement(bfunction, be, binit, loc); } @@ -740,7 +741,9 @@ Assignment_statement::do_lower(Gogo*, Na a1, a2, a3); Type* ptrval_type = Type::make_pointer_type(mt->val_type()); call = Expression::make_cast(ptrval_type, call, loc); - Expression* indir = Expression::make_unary(OPERATOR_MULT, call, loc); + Expression* indir = + Expression::make_dereference(call, Expression::NIL_CHECK_NOT_NEEDED, + loc); ref = Expression::make_temporary_reference(val_temp, loc); b->add_statement(Statement::make_assignment(indir, ref, loc)); @@ -1292,7 +1295,8 @@ Tuple_map_assignment_statement::do_lower // val = *val__ptr_temp ref = Expression::make_temporary_reference(val_ptr_temp, loc); - Expression* ind = Expression::make_unary(OPERATOR_MULT, ref, loc); + Expression* ind = + Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED, loc); s = Statement::make_assignment(this->val_, ind, loc); b->add_statement(s); @@ -2367,8 +2371,10 @@ Thunk_statement::build_thunk(Gogo* gogo, // ones used in build_struct. Expression* thunk_parameter = Expression::make_var_reference(named_parameter, location); - thunk_parameter = Expression::make_unary(OPERATOR_MULT, thunk_parameter, - location); + thunk_parameter = + Expression::make_dereference(thunk_parameter, + Expression::NIL_CHECK_NOT_NEEDED, + location); Interface_field_reference_expression* interface_method = ce->fn()->interface_field_reference_expression(); @@ -2421,8 +2427,10 @@ Thunk_statement::build_thunk(Gogo* gogo, { Expression* thunk_param = Expression::make_var_reference(named_parameter, location); - thunk_param = - Expression::make_unary(OPERATOR_MULT, thunk_param, location); + thunk_param = + Expression::make_dereference(thunk_param, + Expression::NIL_CHECK_NOT_NEEDED, + location); param = Expression::make_field_reference(thunk_param, next_index, location); @@ -5837,7 +5845,8 @@ For_range_statement::lower_range_map(Gog Expression* lhs = Expression::make_temporary_reference(index_temp, loc); Expression* rhs = Expression::make_temporary_reference(hiter, loc); rhs = Expression::make_field_reference(ref, 0, loc); - rhs = Expression::make_unary(OPERATOR_MULT, ref, loc); + rhs = Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED, + loc); Statement* set = Statement::make_assignment(lhs, rhs, loc); iter_init->add_statement(set); @@ -5846,7 +5855,8 @@ For_range_statement::lower_range_map(Gog lhs = Expression::make_temporary_reference(value_temp, loc); rhs = Expression::make_temporary_reference(hiter, loc); rhs = Expression::make_field_reference(rhs, 1, loc); - rhs = Expression::make_unary(OPERATOR_MULT, rhs, loc); + rhs = Expression::make_dereference(rhs, Expression::NIL_CHECK_NOT_NEEDED, + loc); set = Statement::make_assignment(lhs, rhs, loc); iter_init->add_statement(set); } Index: gcc/go/gofrontend/types.cc =================================================================== --- gcc/go/gofrontend/types.cc (revision 255266) +++ gcc/go/gofrontend/types.cc (working copy) @@ -2215,10 +2215,10 @@ Type::write_named_equal(Gogo* gogo, Name // Compare the values for equality. Expression* t1 = Expression::make_temporary_reference(p1, bloc); - t1 = Expression::make_unary(OPERATOR_MULT, t1, bloc); + t1 = Expression::make_dereference(t1, Expression::NIL_CHECK_NOT_NEEDED, bloc); Expression* t2 = Expression::make_temporary_reference(p2, bloc); - t2 = Expression::make_unary(OPERATOR_MULT, t2, bloc); + t2 = Expression::make_dereference(t2, Expression::NIL_CHECK_NOT_NEEDED, bloc); Expression* cond = Expression::make_binary(OPERATOR_EQEQ, t1, t2, bloc); @@ -5911,7 +5911,9 @@ Struct_type::field_reference_depth(Expre Expression* here = Expression::make_field_reference(struct_expr, i, location); if (pf->type()->points_to() != NULL) - here = Expression::make_unary(OPERATOR_MULT, here, location); + here = Expression::make_dereference(here, + Expression::NIL_CHECK_DEFAULT, + location); while (sub->expr() != NULL) { sub = sub->expr()->deref()->field_reference_expression(); @@ -6342,11 +6344,13 @@ Struct_type::write_equal_function(Gogo* // Compare one field in both P1 and P2. Expression* f1 = Expression::make_temporary_reference(p1, bloc); - f1 = Expression::make_unary(OPERATOR_MULT, f1, bloc); + f1 = Expression::make_dereference(f1, Expression::NIL_CHECK_DEFAULT, + bloc); f1 = Expression::make_field_reference(f1, field_index, bloc); Expression* f2 = Expression::make_temporary_reference(p2, bloc); - f2 = Expression::make_unary(OPERATOR_MULT, f2, bloc); + f2 = Expression::make_dereference(f2, Expression::NIL_CHECK_DEFAULT, + bloc); f2 = Expression::make_field_reference(f2, field_index, bloc); Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, f1, f2, bloc); @@ -7193,12 +7197,12 @@ Array_type::write_equal_function(Gogo* g // Compare element in P1 and P2. Expression* e1 = Expression::make_temporary_reference(p1, bloc); - e1 = Expression::make_unary(OPERATOR_MULT, e1, bloc); + e1 = Expression::make_dereference(e1, Expression::NIL_CHECK_DEFAULT, bloc); ref = Expression::make_temporary_reference(index, bloc); e1 = Expression::make_array_index(e1, ref, NULL, NULL, bloc); Expression* e2 = Expression::make_temporary_reference(p2, bloc); - e2 = Expression::make_unary(OPERATOR_MULT, e2, bloc); + e2 = Expression::make_dereference(e2, Expression::NIL_CHECK_DEFAULT, bloc); ref = Expression::make_temporary_reference(index, bloc); e2 = Expression::make_array_index(e2, ref, NULL, NULL, bloc); @@ -11219,7 +11223,8 @@ Type::apply_field_indexes(Expression* ex if (expr->type()->struct_type() == NULL) { go_assert(expr->type()->points_to() != NULL); - expr = Expression::make_unary(OPERATOR_MULT, expr, location); + expr = Expression::make_dereference(expr, Expression::NIL_CHECK_DEFAULT, + location); go_assert(expr->type()->struct_type() == stype); } return Expression::make_field_reference(expr, field_indexes->field_index, @@ -11323,7 +11328,8 @@ Type::bind_field_or_method(Gogo* gogo, c && type->points_to() != NULL && type->points_to()->points_to() != NULL) { - expr = Expression::make_unary(OPERATOR_MULT, expr, location); + expr = Expression::make_dereference(expr, Expression::NIL_CHECK_DEFAULT, + location); type = type->points_to(); if (type->deref()->is_error_type()) return Expression::make_error(location); @@ -11356,8 +11362,9 @@ Type::bind_field_or_method(Gogo* gogo, c return Expression::make_error(location); } go_assert(type->points_to() != NULL); - expr = Expression::make_unary(OPERATOR_MULT, expr, - location); + expr = Expression::make_dereference(expr, + Expression::NIL_CHECK_DEFAULT, + location); go_assert(expr->type()->struct_type() == st); } ret = st->field_reference(expr, name, location); Index: gcc/go/gofrontend/wb.cc =================================================================== --- gcc/go/gofrontend/wb.cc (revision 254748) +++ gcc/go/gofrontend/wb.cc (working copy) @@ -417,7 +417,8 @@ Gogo::assign_with_write_barrier(Function rhs = Expression::make_temporary_reference(rhs_temp, loc); } - Expression* indir = Expression::make_unary(OPERATOR_MULT, lhs, loc); + Expression* indir = + Expression::make_dereference(lhs, Expression::NIL_CHECK_DEFAULT, loc); Statement* assign = Statement::make_assignment(indir, rhs, loc); lhs = Expression::make_temporary_reference(lhs_temp, loc);