summaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2016-09-21 20:58:51 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2016-09-21 20:58:51 +0000
commit0d5530d95cf09ef90848e0853794e53079bf954f (patch)
tree843fadb26050988a8c6037662e2d090533437044 /gcc/go
parentec46804971ac26e16f608e166ae69af0688b2ec8 (diff)
downloadgcc-0d5530d95cf09ef90848e0853794e53079bf954f.tar.gz
compiler, runtime: replace hashmap code with Go 1.7 hashmap
This change removes the gccgo-specific hashmap code and replaces it with the hashmap code from the Go 1.7 runtime. The Go 1.7 hashmap code is more efficient, does a better job on details like when to update a key, and provides some support against denial-of-service attacks. The compiler is changed to call the new hashmap functions instead of the old ones. The compiler now tracks which types are reflexive and which require updating when used as a map key, and records the information in map type descriptors. Map_index_expression is simplified. The special case for a map index on the right hand side of a tuple expression has been unnecessary for some time, and is removed. The support for specially marking a map index as an lvalue is removed, in favor of lowering an assignment to a map index into a function call. The long-obsolete support for a map index of a pointer to a map is removed. The __go_new_map_big function (known to the compiler as Runtime::MAKEMAPBIG) is no longer needed, as the new runtime.makemap function takes an int64 hint argument. The old map descriptor type and supporting expression is removed. The compiler was still supporting the long-obsolete syntax `m[k] = 0, false` to delete a value from a map. That is now removed, requiring a change to one of the gccgo-specific tests. The builtin len function applied to a map or channel p is now compiled as `p == nil ? 0 : *(*int)(p)`. The __go_chan_len function (known to the compiler as Runtime::CHAN_LEN) is removed. Support for a shared zero value for maps to large value types is introduced, along the lines of the gc compiler. The zero value is handled as a common variable. The hash function is changed to take a seed argument, changing the runtime hash functions and the compiler-generated hash functions. Unlike the gc compiler, both the hash and equal functions continue to take the type length. Types that can not be compared now store nil for the hash and equal functions, rather than pointing to functions that throw. Interface hash and comparison functions now check explicitly for nil. This matches the gc compiler and permits a simple implementation for ismapkey. The compiler is changed to permit marking struct and array types as incomparable, meaning that they have no hash or equal function. We use this for thunk types, removing the existing special code to avoid generating hash/equal functions for them. The C runtime code adds memclr, memequal, and memmove functions. The hashmap code uses go:linkname comments to make the functions visible, as otherwise the compiler would discard them. The hashmap code comments out the unused reference to the address of the first parameter in the race code, as otherwise the compiler thinks that the parameter escapes and copies it onto the heap. This is probably not needed when we enable escape analysis. Several runtime map tests that ere previously skipped for gccgo are now run. The Go runtime picks up type kind information and stubs. The type kind information causes the generated runtime header file to define some constants, including `empty`, and the C code is adjusted accordingly. A Go-callable version of runtime.throw, that takes a Go string, is added to be called from the hashmap code. Reviewed-on: https://go-review.googlesource.com/29447 * go.go-torture/execute/map-1.go: Replace old map deletion syntax with call to builtin delete function. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@240334 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/go')
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/escape.cc4
-rw-r--r--gcc/go/gofrontend/expressions.cc211
-rw-r--r--gcc/go/gofrontend/expressions.h52
-rw-r--r--gcc/go/gofrontend/gogo.cc5
-rw-r--r--gcc/go/gofrontend/parse.cc31
-rw-r--r--gcc/go/gofrontend/runtime.cc33
-rw-r--r--gcc/go/gofrontend/runtime.def47
-rw-r--r--gcc/go/gofrontend/runtime.h4
-rw-r--r--gcc/go/gofrontend/statements.cc514
-rw-r--r--gcc/go/gofrontend/statements.h21
-rw-r--r--gcc/go/gofrontend/types.cc691
-rw-r--r--gcc/go/gofrontend/types.h187
13 files changed, 1049 insertions, 753 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 1123e622c82..73645de171f 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-259b4fe81436a3836308f6c96e058edad07be338
+69668416034247ac6c7228c9dcbf6719af05b6ca
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc
index ee7fa093344..ea4978bb555 100644
--- a/gcc/go/gofrontend/escape.cc
+++ b/gcc/go/gofrontend/escape.cc
@@ -294,7 +294,6 @@ Node::op_format() const
case Runtime::MAKECHAN:
case Runtime::MAKECHANBIG:
case Runtime::MAKEMAP:
- case Runtime::MAKEMAPBIG:
case Runtime::MAKESLICE1:
case Runtime::MAKESLICE2:
case Runtime::MAKESLICE1BIG:
@@ -1231,7 +1230,6 @@ Escape_analysis_assign::expression(Expression** pexpr)
case Runtime::MAKECHAN:
case Runtime::MAKECHANBIG:
case Runtime::MAKEMAP:
- case Runtime::MAKEMAPBIG:
case Runtime::MAKESLICE1:
case Runtime::MAKESLICE2:
case Runtime::MAKESLICE1BIG:
@@ -1839,7 +1837,6 @@ Escape_analysis_assign::assign(Node* dst, Node* src)
case Runtime::MAKECHAN:
case Runtime::MAKECHANBIG:
case Runtime::MAKEMAP:
- case Runtime::MAKEMAPBIG:
case Runtime::MAKESLICE1:
case Runtime::MAKESLICE2:
case Runtime::MAKESLICE1BIG:
@@ -2582,7 +2579,6 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
case Runtime::MAKECHAN:
case Runtime::MAKECHANBIG:
case Runtime::MAKEMAP:
- case Runtime::MAKEMAPBIG:
case Runtime::MAKESLICE1:
case Runtime::MAKESLICE2:
case Runtime::MAKESLICE1BIG:
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index aabb35391ac..70853abcd78 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -6998,13 +6998,14 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function,
Statement::make_temporary(mt->key_type(), args->back(), loc);
inserter->insert(key_temp);
- Expression* e1 = Expression::make_temporary_reference(map_temp,
+ Expression* e1 = Expression::make_type_descriptor(mt, loc);
+ Expression* e2 = Expression::make_temporary_reference(map_temp,
loc);
- Expression* e2 = Expression::make_temporary_reference(key_temp,
+ Expression* e3 = Expression::make_temporary_reference(key_temp,
loc);
- e2 = Expression::make_unary(OPERATOR_AND, e2, loc);
+ e3 = Expression::make_unary(OPERATOR_AND, e3, loc);
return Runtime::make_call(Runtime::MAPDELETE, this->location(),
- 2, e1, e2);
+ 3, e1, e2, e3);
}
}
break;
@@ -7065,6 +7066,18 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*,
*pa = Expression::make_temporary_reference(temp, loc);
}
}
+
+ case BUILTIN_LEN:
+ Expression_list::iterator pa = this->args()->begin();
+ if (!(*pa)->is_variable()
+ && ((*pa)->type()->map_type() != NULL
+ || (*pa)->type()->channel_type() != NULL))
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, *pa, loc);
+ inserter->insert(temp);
+ *pa = Expression::make_temporary_reference(temp, loc);
+ }
}
return this;
@@ -7174,13 +7187,7 @@ Builtin_call_expression::lower_make()
}
Location type_loc = first_arg->location();
- Expression* type_arg;
- if (is_slice || is_chan)
- type_arg = Expression::make_type_descriptor(type, type_loc);
- else if (is_map)
- type_arg = Expression::make_map_descriptor(type->map_type(), type_loc);
- else
- go_unreachable();
+ Expression* type_arg = Expression::make_type_descriptor(type, type_loc);
Expression* call;
if (is_slice)
@@ -7197,10 +7204,9 @@ Builtin_call_expression::lower_make()
loc, 3, type_arg, len_arg, cap_arg);
}
else if (is_map)
- call = Runtime::make_call((have_big_args
- ? Runtime::MAKEMAPBIG
- : Runtime::MAKEMAP),
- loc, 2, type_arg, len_arg);
+ call = Runtime::make_call(Runtime::MAKEMAP, loc, 4, type_arg, len_arg,
+ Expression::make_nil(loc),
+ Expression::make_nil(loc));
else if (is_chan)
call = Runtime::make_call((have_big_args
? Runtime::MAKECHANBIG
@@ -8250,10 +8256,23 @@ Builtin_call_expression::do_get_backend(Translate_context* context)
val = arg_type->array_type()->get_length(gogo, arg);
this->seen_ = false;
}
- else if (arg_type->map_type() != NULL)
- val = Runtime::make_call(Runtime::MAP_LEN, location, 1, arg);
- else if (arg_type->channel_type() != NULL)
- val = Runtime::make_call(Runtime::CHAN_LEN, location, 1, arg);
+ else if (arg_type->map_type() != NULL
+ || arg_type->channel_type() != NULL)
+ {
+ // The first field is the length. If the pointer is
+ // nil, the length is zero.
+ Type* pint_type = Type::make_pointer_type(int_type);
+ arg = Expression::make_unsafe_cast(pint_type, arg, location);
+ Expression* nil = Expression::make_nil(location);
+ nil = Expression::make_cast(pint_type, nil, location);
+ Expression* cmp = Expression::make_binary(OPERATOR_EQEQ,
+ arg, nil, location);
+ Expression* zero = Expression::make_integer_ul(0, int_type,
+ location);
+ Expression* indir = Expression::make_unary(OPERATOR_MULT,
+ arg, location);
+ val = Expression::make_conditional(cmp, zero, indir, location);
+ }
else
go_unreachable();
}
@@ -9866,11 +9885,7 @@ Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
error_at(location, "invalid slice of map");
return Expression::make_error(location);
}
- Map_index_expression* ret = Expression::make_map_index(left, start,
- location);
- if (this->is_lvalue_)
- ret->set_is_lvalue();
- return ret;
+ return Expression::make_map_index(left, start, location);
}
else
{
@@ -10666,7 +10681,7 @@ Expression::make_string_index(Expression* string, Expression* start,
Map_type*
Map_index_expression::get_map_type() const
{
- Map_type* mt = this->map_->type()->deref()->map_type();
+ Map_type* mt = this->map_->type()->map_type();
if (mt == NULL)
go_assert(saw_errors());
return mt;
@@ -10724,7 +10739,7 @@ Map_index_expression::do_flatten(Gogo* gogo, Named_object*,
}
if (this->value_pointer_ == NULL)
- this->get_value_pointer(this->is_lvalue_);
+ this->get_value_pointer(gogo);
if (this->value_pointer_->is_error_expression()
|| this->value_pointer_->type()->is_error_type())
return Expression::make_error(loc);
@@ -10747,14 +10762,7 @@ Map_index_expression::do_type()
Map_type* mt = this->get_map_type();
if (mt == NULL)
return Type::make_error_type();
- Type* type = mt->val_type();
- // If this map index is in a tuple assignment, we actually return a
- // pointer to the value type. Tuple_map_assignment_statement is
- // responsible for handling this correctly. We need to get the type
- // right in case this gets assigned to a temporary variable.
- if (this->is_in_tuple_assignment_)
- type = Type::make_pointer_type(type);
- return type;
+ return mt->val_type();
}
// Fix the type of a map index.
@@ -10806,47 +10814,17 @@ Map_index_expression::do_get_backend(Translate_context* context)
go_assert(this->value_pointer_ != NULL
&& this->value_pointer_->is_variable());
- Bexpression* ret;
- if (this->is_lvalue_)
- {
- Expression* val =
- Expression::make_unary(OPERATOR_MULT, this->value_pointer_,
- this->location());
- ret = val->get_backend(context);
- }
- else if (this->is_in_tuple_assignment_)
- {
- // Tuple_map_assignment_statement is responsible for using this
- // appropriately.
- ret = this->value_pointer_->get_backend(context);
- }
- else
- {
- Location loc = this->location();
-
- Expression* nil_check =
- Expression::make_binary(OPERATOR_EQEQ, this->value_pointer_,
- Expression::make_nil(loc), loc);
- Bexpression* bnil_check = nil_check->get_backend(context);
- Expression* val =
- Expression::make_unary(OPERATOR_MULT, this->value_pointer_, loc);
- Bexpression* bval = val->get_backend(context);
-
- Gogo* gogo = context->gogo();
- Btype* val_btype = type->val_type()->get_backend(gogo);
- Bexpression* val_zero = gogo->backend()->zero_expression(val_btype);
- ret = gogo->backend()->conditional_expression(val_btype, bnil_check,
- val_zero, bval, loc);
- }
- return ret;
+ Expression* val = Expression::make_unary(OPERATOR_MULT, this->value_pointer_,
+ this->location());
+ return val->get_backend(context);
}
-// Get an expression for the map index. This returns an expression which
-// evaluates to a pointer to a value. The pointer will be NULL if the key is
-// not in the map.
+// Get an expression for the map index. This returns an expression
+// that evaluates to a pointer to a value. If the key is not in the
+// map, the pointer will point to a zero value.
Expression*
-Map_index_expression::get_value_pointer(bool insert)
+Map_index_expression::get_value_pointer(Gogo* gogo)
{
if (this->value_pointer_ == NULL)
{
@@ -10859,21 +10837,32 @@ Map_index_expression::get_value_pointer(bool insert)
Location loc = this->location();
Expression* map_ref = this->map_;
- if (this->map_->type()->points_to() != NULL)
- map_ref = Expression::make_unary(OPERATOR_MULT, map_ref, loc);
- Expression* index_ptr = Expression::make_unary(OPERATOR_AND, this->index_,
+ Expression* index_ptr = Expression::make_unary(OPERATOR_AND,
+ this->index_,
loc);
- Expression* map_index =
- Runtime::make_call(Runtime::MAP_INDEX, loc, 3,
- map_ref, index_ptr,
- Expression::make_boolean(insert, loc));
+
+ Expression* zero = type->fat_zero_value(gogo);
+
+ Expression* map_index;
+
+ if (zero == NULL)
+ map_index =
+ Runtime::make_call(Runtime::MAPACCESS1, loc, 3,
+ Expression::make_type_descriptor(type, loc),
+ map_ref, index_ptr);
+ else
+ map_index =
+ Runtime::make_call(Runtime::MAPACCESS1_FAT, loc, 4,
+ Expression::make_type_descriptor(type, loc),
+ map_ref, index_ptr, zero);
Type* val_type = type->val_type();
this->value_pointer_ =
Expression::make_unsafe_cast(Type::make_pointer_type(val_type),
map_index, this->location());
}
+
return this->value_pointer_;
}
@@ -12583,7 +12572,7 @@ Map_construction_expression::do_get_backend(Translate_context* context)
Type::make_builtin_struct_type(2,
"__key", mt->key_type(),
"__val", mt->val_type());
- Expression* descriptor = Expression::make_map_descriptor(mt, loc);
+ Expression* descriptor = Expression::make_type_descriptor(mt, loc);
Type* uintptr_t = Type::lookup_integer_type("uintptr");
Expression* count = Expression::make_integer_ul(i, uintptr_t, loc);
@@ -12596,12 +12585,10 @@ Map_construction_expression::do_get_backend(Translate_context* context)
this->element_type_->find_local_field("__val", &field_index);
Expression* val_offset =
Expression::make_struct_field_offset(this->element_type_, valfield);
- Expression* val_size =
- Expression::make_type_info(mt->val_type(), TYPE_INFO_SIZE);
Expression* map_ctor =
- Runtime::make_call(Runtime::CONSTRUCT_MAP, loc, 6, descriptor, count,
- entry_size, val_offset, val_size, ventries);
+ Runtime::make_call(Runtime::CONSTRUCT_MAP, loc, 5, descriptor, count,
+ entry_size, val_offset, ventries);
return map_ctor->get_backend(context);
}
@@ -14608,64 +14595,6 @@ Expression::make_struct_field_offset(Struct_type* type,
return new Struct_field_offset_expression(type, field);
}
-// An expression which evaluates to a pointer to the map descriptor of
-// a map type.
-
-class Map_descriptor_expression : public Expression
-{
- public:
- Map_descriptor_expression(Map_type* type, Location location)
- : Expression(EXPRESSION_MAP_DESCRIPTOR, location),
- type_(type)
- { }
-
- protected:
- Type*
- do_type()
- { return Type::make_pointer_type(Map_type::make_map_descriptor_type()); }
-
- void
- do_determine_type(const Type_context*)
- { }
-
- Expression*
- do_copy()
- { return this; }
-
- Bexpression*
- do_get_backend(Translate_context* context)
- {
- return this->type_->map_descriptor_pointer(context->gogo(),
- this->location());
- }
-
- void
- do_dump_expression(Ast_dump_context*) const;
-
- private:
- // The type for which this is the descriptor.
- Map_type* type_;
-};
-
-// Dump ast representation for a map descriptor expression.
-
-void
-Map_descriptor_expression::do_dump_expression(
- Ast_dump_context* ast_dump_context) const
-{
- ast_dump_context->ostream() << "map_descriptor(";
- ast_dump_context->dump_type(this->type_);
- ast_dump_context->ostream() << ")";
-}
-
-// Make a map descriptor expression.
-
-Expression*
-Expression::make_map_descriptor(Map_type* type, Location location)
-{
- return new Map_descriptor_expression(type, location);
-}
-
// An expression which evaluates to the address of an unnamed label.
class Label_addr_expression : public Expression
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index 339cb5d288b..8ecc11afd7f 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -133,7 +133,6 @@ class Expression
EXPRESSION_INTERFACE_VALUE,
EXPRESSION_INTERFACE_MTABLE,
EXPRESSION_STRUCT_FIELD_OFFSET,
- EXPRESSION_MAP_DESCRIPTOR,
EXPRESSION_LABEL_ADDR,
EXPRESSION_CONDITIONAL,
EXPRESSION_COMPOUND
@@ -467,11 +466,6 @@ class Expression
static Expression*
make_struct_field_offset(Struct_type*, const Struct_field*);
- // Make an expression which evaluates to the address of the map
- // descriptor for TYPE.
- static Expression*
- make_map_descriptor(Map_type* type, Location);
-
// Make an expression which evaluates to the address of an unnamed
// label.
static Expression*
@@ -2449,14 +2443,9 @@ class Index_expression : public Parser_expression
Index_expression(Expression* left, Expression* start, Expression* end,
Expression* cap, Location location)
: Parser_expression(EXPRESSION_INDEX, location),
- left_(left), start_(start), end_(end), cap_(cap), is_lvalue_(false)
+ left_(left), start_(start), end_(end), cap_(cap)
{ }
- // Record that this expression is an lvalue.
- void
- set_is_lvalue()
- { this->is_lvalue_ = true; }
-
// Dump an index expression, i.e. an expression of the form
// expr[expr], expr[expr:expr], or expr[expr:expr:expr] to a dump context.
static void
@@ -2509,9 +2498,6 @@ class Index_expression : public Parser_expression
// default capacity, non-NULL for indices and slices that specify the
// capacity.
Expression* cap_;
- // Whether this is being used as an l-value. We set this during the
- // parse because map index expressions need to know.
- bool is_lvalue_;
};
// An array index. This is used for both indexing and slicing.
@@ -2677,8 +2663,7 @@ class Map_index_expression : public Expression
Map_index_expression(Expression* map, Expression* index,
Location location)
: Expression(EXPRESSION_MAP_INDEX, location),
- map_(map), index_(index), is_lvalue_(false),
- is_in_tuple_assignment_(false), value_pointer_(NULL)
+ map_(map), index_(index), value_pointer_(NULL)
{ }
// Return the map.
@@ -2703,31 +2688,12 @@ class Map_index_expression : public Expression
Map_type*
get_map_type() const;
- // Record that this map expression is an lvalue. The difference is
- // that an lvalue always inserts the key.
- void
- set_is_lvalue()
- { this->is_lvalue_ = true; }
-
- // Return whether this map expression occurs in an assignment to a
- // pair of values.
- bool
- is_in_tuple_assignment() const
- { return this->is_in_tuple_assignment_; }
-
- // Record that this map expression occurs in an assignment to a pair
- // of values.
- void
- set_is_in_tuple_assignment()
- { this->is_in_tuple_assignment_ = true; }
-
- // Return an expression for the map index. This returns an expression which
- // evaluates to a pointer to a value in the map. If INSERT is true,
- // the key will be inserted if not present, and the value pointer
- // will be zero initialized. If INSERT is false, and the key is not
- // present in the map, the pointer will be NULL.
+ // Return an expression for the map index. This returns an
+ // expression that evaluates to a pointer to a value in the map. If
+ // the key is not present in the map, this will return a pointer to
+ // the zero value.
Expression*
- get_value_pointer(bool insert);
+ get_value_pointer(Gogo*);
protected:
int
@@ -2773,10 +2739,6 @@ class Map_index_expression : public Expression
Expression* map_;
// The index.
Expression* index_;
- // Whether this is an lvalue.
- bool is_lvalue_;
- // Whether this is in a tuple assignment to a pair of values.
- bool is_in_tuple_assignment_;
// A pointer to the value at this index.
Expression* value_pointer_;
};
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index 3b7ecd3491f..a3afdcb7b9a 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -4614,7 +4614,6 @@ Gogo::convert_named_types()
Array_type::make_array_type_descriptor_type();
Array_type::make_slice_type_descriptor_type();
Map_type::make_map_type_descriptor_type();
- Map_type::make_map_descriptor_type();
Channel_type::make_chan_type_descriptor_type();
Interface_type::make_interface_type_descriptor_type();
Expression::make_func_descriptor_type();
@@ -6547,7 +6546,9 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
Btype* btype = type->get_backend(gogo);
Bvariable* bvar;
- if (this->is_global_)
+ if (Map_type::is_zero_value(this))
+ bvar = Map_type::backend_zero_value(gogo);
+ else if (this->is_global_)
bvar = backend->global_variable((package == NULL
? gogo->package_name()
: package->package_name()),
diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc
index cb7f9664aaf..6b45ebf4fd0 100644
--- a/gcc/go/gofrontend/parse.cc
+++ b/gcc/go/gofrontend/parse.cc
@@ -4039,11 +4039,6 @@ void
Parse::inc_dec_stat(Expression* exp)
{
const Token* token = this->peek_token();
-
- // Lvalue maps require special handling.
- if (exp->index_expression() != NULL)
- exp->index_expression()->set_is_lvalue();
-
if (token->is_op(OPERATOR_PLUSPLUS))
this->gogo_->add_statement(Statement::make_inc_statement(exp));
else if (token->is_op(OPERATOR_MINUSMINUS))
@@ -4120,13 +4115,6 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit,
if (lhs == NULL)
return;
- // Map expressions act differently when they are lvalues.
- for (Expression_list::iterator plv = lhs->begin();
- plv != lhs->end();
- ++plv)
- if ((*plv)->index_expression() != NULL)
- (*plv)->index_expression()->set_is_lvalue();
-
if (p_range_clause != NULL && token->is_keyword(KEYWORD_RANGE))
{
if (op != OPERATOR_EQ)
@@ -4209,18 +4197,6 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit,
map_index, location);
this->gogo_->add_statement(s);
}
- else if (lhs->size() == 1
- && vals->size() == 2
- && (map_index = lhs->front()->index_expression()) != NULL)
- {
- if (op != OPERATOR_EQ)
- error_at(location, "assigning tuple to map index requires %<=%>");
- Expression* val = vals->front();
- Expression* should_set = vals->back();
- Statement* s = Statement::make_map_assignment(map_index, val, should_set,
- location);
- this->gogo_->add_statement(s);
- }
else if (lhs->size() == 2
&& vals->size() == 1
&& (receive = (*vals->begin())->receive_expression()) != NULL)
@@ -4952,13 +4928,6 @@ Parse::comm_clause(Select_clauses* clauses, bool* saw_default)
bool got_case = this->comm_case(&is_send, &channel, &val, &closed,
&varname, &closedname, &is_default);
- if (!is_send
- && varname.empty()
- && closedname.empty()
- && val != NULL
- && val->index_expression() != NULL)
- val->index_expression()->set_is_lvalue();
-
if (this->peek_token()->is_op(OPERATOR_COLON))
this->advance_token();
else
diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc
index 64920250e43..98678f4adea 100644
--- a/gcc/go/gofrontend/runtime.cc
+++ b/gcc/go/gofrontend/runtime.cc
@@ -54,8 +54,6 @@ enum Runtime_function_type
RFT_SLICE,
// Go type map[any]any, C type struct __go_map *.
RFT_MAP,
- // Pointer to map iteration type.
- RFT_MAPITER,
// Go type chan any, C type struct __go_channel *.
RFT_CHAN,
// Go type non-empty interface, C type struct __go_interface.
@@ -66,8 +64,6 @@ enum Runtime_function_type
RFT_FUNC_PTR,
// Pointer to Go type descriptor.
RFT_TYPE,
- // Pointer to map descriptor.
- RFT_MAPDESCRIPTOR,
NUMBER_OF_RUNTIME_FUNCTION_TYPES
};
@@ -153,10 +149,6 @@ runtime_function_type(Runtime_function_type bft)
t = Type::make_map_type(any, any, bloc);
break;
- case RFT_MAPITER:
- t = Type::make_pointer_type(Runtime::map_iteration_type());
- break;
-
case RFT_CHAN:
t = Type::make_channel_type(true, true, any);
break;
@@ -188,10 +180,6 @@ runtime_function_type(Runtime_function_type bft)
case RFT_TYPE:
t = Type::make_type_descriptor_ptr_type();
break;
-
- case RFT_MAPDESCRIPTOR:
- t = Type::make_pointer_type(Map_type::make_map_descriptor_type());
- break;
}
runtime_function_types[bft] = t;
@@ -225,7 +213,6 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
case RFT_COMPLEX128:
case RFT_STRING:
case RFT_POINTER:
- case RFT_MAPITER:
case RFT_FUNC_PTR:
{
Type* t = runtime_function_type(bft);
@@ -244,11 +231,6 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
case RFT_TYPE:
go_assert(e->type() == Type::make_type_descriptor_ptr_type());
return e;
-
- case RFT_MAPDESCRIPTOR:
- go_assert(e->type()->points_to()
- == Map_type::make_map_descriptor_type());
- return e;
}
}
@@ -389,21 +371,6 @@ Runtime::make_call(Runtime::Function code, Location loc,
return Expression::make_call(func, args, false, loc);
}
-// The type we use for a map iteration. This is really a struct which
-// is four pointers long. This must match the runtime struct
-// __go_hash_iter.
-
-Type*
-Runtime::map_iteration_type()
-{
- const unsigned long map_iteration_size = 4;
- Expression* iexpr =
- Expression::make_integer_ul(map_iteration_size, NULL,
- Linemap::predeclared_location());
- return Type::make_array_type(runtime_function_type(RFT_POINTER), iexpr);
-}
-
-
// Get the runtime code for a named builtin function. This is used as a helper
// when creating function references for call expressions. Every reference to
// a builtin runtime function should have the associated runtime code. If the
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index 2e79263a632..2be772bc950 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -85,54 +85,51 @@ DEF_GO_RUNTIME(MAKESLICE2BIG, "__go_make_slice2_big", P3(TYPE, UINT64, UINT64),
// Make a map.
-DEF_GO_RUNTIME(MAKEMAP, "__go_new_map", P2(MAPDESCRIPTOR, UINTPTR), R1(MAP))
-DEF_GO_RUNTIME(MAKEMAPBIG, "__go_new_map_big", P2(MAPDESCRIPTOR, UINT64),
+DEF_GO_RUNTIME(MAKEMAP, "runtime.makemap", P4(TYPE, INT64, POINTER, POINTER),
R1(MAP))
// Build a map from a composite literal.
DEF_GO_RUNTIME(CONSTRUCT_MAP, "__go_construct_map",
- P6(POINTER, UINTPTR, UINTPTR, UINTPTR, UINTPTR, POINTER),
+ P5(POINTER, UINTPTR, UINTPTR, UINTPTR, POINTER),
R1(MAP))
-// Get the length of a map (the number of entries).
-DEF_GO_RUNTIME(MAP_LEN, "__go_map_len", P1(MAP), R1(INT))
-
// Look up a key in a map.
-DEF_GO_RUNTIME(MAP_INDEX, "__go_map_index", P3(MAP, POINTER, BOOL),
+DEF_GO_RUNTIME(MAPACCESS1, "runtime.mapaccess1", P3(TYPE, MAP, POINTER),
R1(POINTER))
-// Look up a key in a map returning whether it is present.
-DEF_GO_RUNTIME(MAPACCESS2, "runtime.mapaccess2",
- P4(TYPE, MAP, POINTER, POINTER), R1(BOOL))
+// Look up a key in a map when the value is large.
+DEF_GO_RUNTIME(MAPACCESS1_FAT, "runtime.mapaccess1_fat",
+ P4(TYPE, MAP, POINTER, POINTER), R1(POINTER))
-// Tuple assignment to a map element.
-DEF_GO_RUNTIME(MAPASSIGN2, "runtime.mapassign2",
- P4(MAP, POINTER, POINTER, BOOL), R0())
+// Look up a key in a map returning the value and whether it is
+// present.
+DEF_GO_RUNTIME(MAPACCESS2, "runtime.mapaccess2", P3(TYPE, MAP, POINTER),
+ R2(POINTER, BOOL))
-// Delete a key from a map.
-DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P2(MAP, POINTER), R0())
+// Look up a key in a map, returning the value and whether it is
+// present, when the value is large.
+DEF_GO_RUNTIME(MAPACCESS2_FAT, "runtime.mapaccess2_fat",
+ P4(TYPE, MAP, POINTER, POINTER), R2(POINTER, BOOL))
-// Begin a range over a map.
-DEF_GO_RUNTIME(MAPITERINIT, "runtime.mapiterinit", P2(MAP, MAPITER), R0())
+// Assignment to a key in a map.
+DEF_GO_RUNTIME(MAPASSIGN, "runtime.mapassign1",
+ P4(TYPE, MAP, POINTER, POINTER), R0())
-// Range over a map, returning the next key.
-DEF_GO_RUNTIME(MAPITER1, "runtime.mapiter1", P2(MAPITER, POINTER), R0())
+// Delete a key from a map.
+DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P3(TYPE, MAP, POINTER), R0())
-// Range over a map, returning the next key and value.
-DEF_GO_RUNTIME(MAPITER2, "runtime.mapiter2", P3(MAPITER, POINTER, POINTER),
+// Begin a range over a map.
+DEF_GO_RUNTIME(MAPITERINIT, "runtime.mapiterinit", P3(TYPE, MAP, POINTER),
R0())
// Range over a map, moving to the next map entry.
-DEF_GO_RUNTIME(MAPITERNEXT, "runtime.mapiternext", P1(MAPITER), R0())
+DEF_GO_RUNTIME(MAPITERNEXT, "runtime.mapiternext", P1(POINTER), R0())
// Make a channel.
DEF_GO_RUNTIME(MAKECHAN, "__go_new_channel", P2(TYPE, UINTPTR), R1(CHAN))
DEF_GO_RUNTIME(MAKECHANBIG, "__go_new_channel_big", P2(TYPE, UINT64), R1(CHAN))
-// Get the length of a channel (the number of unread values).
-DEF_GO_RUNTIME(CHAN_LEN, "__go_chan_len", P1(CHAN), R1(INT))
-
// Get the capacity of a channel (the size of the buffer).
DEF_GO_RUNTIME(CHAN_CAP, "__go_chan_cap", P1(CHAN), R1(INT))
diff --git a/gcc/go/gofrontend/runtime.h b/gcc/go/gofrontend/runtime.h
index 636e1965006..e92510b33db 100644
--- a/gcc/go/gofrontend/runtime.h
+++ b/gcc/go/gofrontend/runtime.h
@@ -39,10 +39,6 @@ class Runtime
static void
convert_types(Gogo*);
- // Return the type used for iterations over maps.
- static Type*
- map_iteration_type();
-
// Return the runtime code for a named builtin function.
static Function
name_to_code(const std::string&);
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index 9066c016730..9e481741ad6 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -544,6 +544,106 @@ Statement::make_temporary(Type* type, Expression* init,
return new Temporary_statement(type, init, location);
}
+// The Move_subexpressions class is used to move all top-level
+// subexpressions of an expression. This is used for things like
+// index expressions in which we must evaluate the index value before
+// it can be changed by a multiple assignment.
+
+class Move_subexpressions : public Traverse
+{
+ public:
+ Move_subexpressions(int skip, Block* block)
+ : Traverse(traverse_expressions),
+ skip_(skip), block_(block)
+ { }
+
+ protected:
+ int
+ expression(Expression**);
+
+ private:
+ // The number of subexpressions to skip moving. This is used to
+ // avoid moving the array itself, as we only need to move the index.
+ int skip_;
+ // The block where new temporary variables should be added.
+ Block* block_;
+};
+
+int
+Move_subexpressions::expression(Expression** pexpr)
+{
+ if (this->skip_ > 0)
+ --this->skip_;
+ else if ((*pexpr)->temporary_reference_expression() == NULL
+ && !(*pexpr)->is_nil_expression()
+ && !(*pexpr)->is_constant())
+ {
+ Location loc = (*pexpr)->location();
+ Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc);
+ this->block_->add_statement(temp);
+ *pexpr = Expression::make_temporary_reference(temp, loc);
+ }
+ // We only need to move top-level subexpressions.
+ return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// The Move_ordered_evals class is used to find any subexpressions of
+// an expression that have an evaluation order dependency. It creates
+// temporary variables to hold them.
+
+class Move_ordered_evals : public Traverse
+{
+ public:
+ Move_ordered_evals(Block* block)
+ : Traverse(traverse_expressions),
+ block_(block)
+ { }
+
+ protected:
+ int
+ expression(Expression**);
+
+ private:
+ // The block where new temporary variables should be added.
+ Block* block_;
+};
+
+int
+Move_ordered_evals::expression(Expression** pexpr)
+{
+ // We have to look at subexpressions first.
+ if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+
+ int i;
+ if ((*pexpr)->must_eval_subexpressions_in_order(&i))
+ {
+ Move_subexpressions ms(i, this->block_);
+ if ((*pexpr)->traverse_subexpressions(&ms) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
+
+ if ((*pexpr)->must_eval_in_order())
+ {
+ Call_expression* call = (*pexpr)->call_expression();
+ if (call != NULL && call->is_multi_value_arg())
+ {
+ // A call expression which returns multiple results as an argument
+ // to another call must be handled specially. We can't create a
+ // temporary because there is no type to give it. Instead, group
+ // the caller and this multi-valued call argument and use a temporary
+ // variable to hold them.
+ return TRAVERSE_SKIP_COMPONENTS;
+ }
+
+ Location loc = (*pexpr)->location();
+ Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc);
+ this->block_->add_statement(temp);
+ *pexpr = Expression::make_temporary_reference(temp, loc);
+ }
+ return TRAVERSE_SKIP_COMPONENTS;
+}
+
// Class Assignment_statement.
// Traversal.
@@ -563,6 +663,66 @@ Assignment_statement::do_traverse_assignments(Traverse_assignments* tassign)
return true;
}
+// Lower an assignment to a map index expression to a runtime function
+// call.
+
+Statement*
+Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+ Statement_inserter*)
+{
+ Map_index_expression* mie = this->lhs_->map_index_expression();
+ if (mie != NULL)
+ {
+ Location loc = this->location();
+
+ Expression* map = mie->map();
+ Map_type* mt = map->type()->map_type();
+ if (mt == NULL)
+ {
+ go_assert(saw_errors());
+ return Statement::make_error_statement(loc);
+ }
+
+ Block* b = new Block(enclosing, loc);
+
+ // Move out any subexpressions on the left hand side to make
+ // sure that functions are called in the required order.
+ Move_ordered_evals moe(b);
+ mie->traverse_subexpressions(&moe);
+
+ // Copy key and value into temporaries so that we can take their
+ // address without pushing the value onto the heap.
+
+ // var key_temp KEY_TYPE = MAP_INDEX
+ Temporary_statement* key_temp = Statement::make_temporary(mt->key_type(),
+ mie->index(),
+ loc);
+ b->add_statement(key_temp);
+
+ // var val_temp VAL_TYPE = RHS
+ Temporary_statement* val_temp = Statement::make_temporary(mt->val_type(),
+ this->rhs_,
+ loc);
+ b->add_statement(val_temp);
+
+ // mapassign1(TYPE, MAP, &key_temp, &val_temp)
+ Expression* a1 = Expression::make_type_descriptor(mt, loc);
+ Expression* a2 = mie->map();
+ Temporary_reference_expression* ref =
+ Expression::make_temporary_reference(key_temp, loc);
+ Expression* a3 = Expression::make_unary(OPERATOR_AND, ref, loc);
+ ref = Expression::make_temporary_reference(val_temp, loc);
+ Expression* a4 = Expression::make_unary(OPERATOR_AND, ref, loc);
+ Expression* call = Runtime::make_call(Runtime::MAPASSIGN, loc, 4,
+ a1, a2, a3, a4);
+ b->add_statement(Statement::make_statement(call, false));
+
+ return Statement::make_block_statement(b, loc);
+ }
+
+ return this;
+}
+
// Set types for the assignment.
void
@@ -690,106 +850,6 @@ Statement::make_assignment(Expression* lhs, Expression* rhs,
return new Assignment_statement(lhs, rhs, location);
}
-// The Move_subexpressions class is used to move all top-level
-// subexpressions of an expression. This is used for things like
-// index expressions in which we must evaluate the index value before
-// it can be changed by a multiple assignment.
-
-class Move_subexpressions : public Traverse
-{
- public:
- Move_subexpressions(int skip, Block* block)
- : Traverse(traverse_expressions),
- skip_(skip), block_(block)
- { }
-
- protected:
- int
- expression(Expression**);
-
- private:
- // The number of subexpressions to skip moving. This is used to
- // avoid moving the array itself, as we only need to move the index.
- int skip_;
- // The block where new temporary variables should be added.
- Block* block_;
-};
-
-int
-Move_subexpressions::expression(Expression** pexpr)
-{
- if (this->skip_ > 0)
- --this->skip_;
- else if ((*pexpr)->temporary_reference_expression() == NULL
- && !(*pexpr)->is_nil_expression()
- && !(*pexpr)->is_constant())
- {
- Location loc = (*pexpr)->location();
- Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc);
- this->block_->add_statement(temp);
- *pexpr = Expression::make_temporary_reference(temp, loc);
- }
- // We only need to move top-level subexpressions.
- return TRAVERSE_SKIP_COMPONENTS;
-}
-
-// The Move_ordered_evals class is used to find any subexpressions of
-// an expression that have an evaluation order dependency. It creates
-// temporary variables to hold them.
-
-class Move_ordered_evals : public Traverse
-{
- public:
- Move_ordered_evals(Block* block)
- : Traverse(traverse_expressions),
- block_(block)
- { }
-
- protected:
- int
- expression(Expression**);
-
- private:
- // The block where new temporary variables should be added.
- Block* block_;
-};
-
-int
-Move_ordered_evals::expression(Expression** pexpr)
-{
- // We have to look at subexpressions first.
- if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
-
- int i;
- if ((*pexpr)->must_eval_subexpressions_in_order(&i))
- {
- Move_subexpressions ms(i, this->block_);
- if ((*pexpr)->traverse_subexpressions(&ms) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
-
- if ((*pexpr)->must_eval_in_order())
- {
- Call_expression* call = (*pexpr)->call_expression();
- if (call != NULL && call->is_multi_value_arg())
- {
- // A call expression which returns multiple results as an argument
- // to another call must be handled specially. We can't create a
- // temporary because there is no type to give it. Instead, group
- // the caller and this multi-valued call argument and use a temporary
- // variable to hold them.
- return TRAVERSE_SKIP_COMPONENTS;
- }
-
- Location loc = (*pexpr)->location();
- Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc);
- this->block_->add_statement(temp);
- *pexpr = Expression::make_temporary_reference(temp, loc);
- }
- return TRAVERSE_SKIP_COMPONENTS;
-}
-
// An assignment operation statement.
class Assignment_operation_statement : public Statement
@@ -1131,7 +1191,7 @@ Tuple_map_assignment_statement::do_traverse(Traverse* traverse)
// Lower a tuple map assignment.
Statement*
-Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
+Tuple_map_assignment_statement::do_lower(Gogo* gogo, Named_object*,
Block* enclosing, Statement_inserter*)
{
Location loc = this->location();
@@ -1162,10 +1222,11 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
Statement::make_temporary(map_type->key_type(), map_index->index(), loc);
b->add_statement(key_temp);
- // var val_temp VAL_TYPE
- Temporary_statement* val_temp =
- Statement::make_temporary(map_type->val_type(), NULL, loc);
- b->add_statement(val_temp);
+ // var val_ptr_temp *VAL_TYPE
+ Type* val_ptr_type = Type::make_pointer_type(map_type->val_type());
+ Temporary_statement* val_ptr_temp = Statement::make_temporary(val_ptr_type,
+ NULL, loc);
+ b->add_statement(val_ptr_temp);
// var present_temp bool
Temporary_statement* present_temp =
@@ -1175,24 +1236,34 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
NULL, loc);
b->add_statement(present_temp);
- // present_temp = mapaccess2(DESCRIPTOR, MAP, &key_temp, &val_temp)
+ // val_ptr_temp, present_temp = mapaccess2(DESCRIPTOR, MAP, &key_temp)
Expression* a1 = Expression::make_type_descriptor(map_type, loc);
Expression* a2 = map_index->map();
Temporary_reference_expression* ref =
Expression::make_temporary_reference(key_temp, loc);
Expression* a3 = Expression::make_unary(OPERATOR_AND, ref, loc);
- ref = Expression::make_temporary_reference(val_temp, loc);
- Expression* a4 = Expression::make_unary(OPERATOR_AND, ref, loc);
- Expression* call = Runtime::make_call(Runtime::MAPACCESS2, loc, 4,
- a1, a2, a3, a4);
+ Expression* a4 = map_type->fat_zero_value(gogo);
+ Call_expression* call;
+ if (a4 == NULL)
+ call = Runtime::make_call(Runtime::MAPACCESS2, loc, 3, a1, a2, a3);
+ else
+ call = Runtime::make_call(Runtime::MAPACCESS2_FAT, loc, 4, a1, a2, a3, a4);
+ ref = Expression::make_temporary_reference(val_ptr_temp, loc);
+ ref->set_is_lvalue();
+ Expression* res = Expression::make_call_result(call, 0);
+ res = Expression::make_unsafe_cast(val_ptr_type, res, loc);
+ Statement* s = Statement::make_assignment(ref, res, loc);
+ b->add_statement(s);
ref = Expression::make_temporary_reference(present_temp, loc);
ref->set_is_lvalue();
- Statement* s = Statement::make_assignment(ref, call, loc);
+ res = Expression::make_call_result(call, 1);
+ s = Statement::make_assignment(ref, res, loc);
b->add_statement(s);
- // val = val_temp
- ref = Expression::make_temporary_reference(val_temp, loc);
- s = Statement::make_assignment(this->val_, ref, loc);
+ // val = *val__ptr_temp
+ ref = Expression::make_temporary_reference(val_ptr_temp, loc);
+ Expression* ind = Expression::make_unary(OPERATOR_MULT, ref, loc);
+ s = Statement::make_assignment(this->val_, ind, loc);
b->add_statement(s);
// present = present_temp
@@ -1228,140 +1299,6 @@ Statement::make_tuple_map_assignment(Expression* val, Expression* present,
return new Tuple_map_assignment_statement(val, present, map_index, location);
}
-// Assign a pair of entries to a map.
-// m[k] = v, p
-
-class Map_assignment_statement : public Statement
-{
- public:
- Map_assignment_statement(Expression* map_index,
- Expression* val, Expression* should_set,
- Location location)
- : Statement(STATEMENT_MAP_ASSIGNMENT, location),
- map_index_(map_index), val_(val), should_set_(should_set)
- { }
-
- protected:
- int
- do_traverse(Traverse* traverse);
-
- bool
- do_traverse_assignments(Traverse_assignments*)
- { go_unreachable(); }
-
- Statement*
- do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
-
- Bstatement*
- do_get_backend(Translate_context*)
- { go_unreachable(); }
-
- void
- do_dump_statement(Ast_dump_context*) const;
-
- private:
- // A reference to the map index which should be set or deleted.
- Expression* map_index_;
- // The value to add to the map.
- Expression* val_;
- // Whether or not to add the value.
- Expression* should_set_;
-};
-
-// Traverse a map assignment.
-
-int
-Map_assignment_statement::do_traverse(Traverse* traverse)
-{
- if (this->traverse_expression(traverse, &this->map_index_) == TRAVERSE_EXIT
- || this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- return this->traverse_expression(traverse, &this->should_set_);
-}
-
-// Lower a map assignment to a function call.
-
-Statement*
-Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
- Statement_inserter*)
-{
- Location loc = this->location();
-
- Map_index_expression* map_index = this->map_index_->map_index_expression();
- if (map_index == NULL)
- {
- this->report_error(_("expected map index on left hand side"));
- return Statement::make_error_statement(loc);
- }
- Map_type* map_type = map_index->get_map_type();
- if (map_type == NULL)
- return Statement::make_error_statement(loc);
-
- Block* b = new Block(enclosing, loc);
-
- // Evaluate the map first to get order of evaluation right.
- // map_temp := m // we are evaluating m[k] = v, p
- Temporary_statement* map_temp = Statement::make_temporary(map_type,
- map_index->map(),
- loc);
- b->add_statement(map_temp);
-
- // var key_temp MAP_KEY_TYPE = k
- Temporary_statement* key_temp =
- Statement::make_temporary(map_type->key_type(), map_index->index(), loc);
- b->add_statement(key_temp);
-
- // var val_temp MAP_VAL_TYPE = v
- Temporary_statement* val_temp =
- Statement::make_temporary(map_type->val_type(), this->val_, loc);
- b->add_statement(val_temp);
-
- // var insert_temp bool = p
- Temporary_statement* insert_temp =
- Statement::make_temporary(Type::lookup_bool_type(), this->should_set_,
- loc);
- b->add_statement(insert_temp);
-
- // mapassign2(map_temp, &key_temp, &val_temp, p)
- Expression* p1 = Expression::make_temporary_reference(map_temp, loc);
- Expression* ref = Expression::make_temporary_reference(key_temp, loc);
- Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
- ref = Expression::make_temporary_reference(val_temp, loc);
- Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc);
- Expression* p4 = Expression::make_temporary_reference(insert_temp, loc);
- Expression* call = Runtime::make_call(Runtime::MAPASSIGN2, loc, 4,
- p1, p2, p3, p4);
- Statement* s = Statement::make_statement(call, true);
- b->add_statement(s);
-
- return Statement::make_block_statement(b, loc);
-}
-
-// Dump the AST representation for a map assignment statement.
-
-void
-Map_assignment_statement::do_dump_statement(
- Ast_dump_context* ast_dump_context) const
-{
- ast_dump_context->print_indent();
- ast_dump_context->dump_expression(this->map_index_);
- ast_dump_context->ostream() << " = ";
- ast_dump_context->dump_expression(this->val_);
- ast_dump_context->ostream() << ", ";
- ast_dump_context->dump_expression(this->should_set_);
- ast_dump_context->ostream() << std::endl;
-}
-
-// Make a statement which assigns a pair of entries to a map.
-
-Statement*
-Statement::make_map_assignment(Expression* map_index,
- Expression* val, Expression* should_set,
- Location location)
-{
- return new Map_assignment_statement(map_index, val, should_set, location);
-}
-
// A tuple assignment from a receive statement.
class Tuple_receive_assignment_statement : public Statement
@@ -1894,8 +1831,6 @@ Statement::make_dec_statement(Expression* expr)
// Class Thunk_statement. This is the base class for go and defer
// statements.
-Unordered_set(const Struct_type*) Thunk_statement::thunk_types;
-
// Constructor.
Thunk_statement::Thunk_statement(Statement_classification classification,
@@ -2278,21 +2213,10 @@ Thunk_statement::build_struct(Function_type* fntype)
}
Struct_type *st = Type::make_struct_type(fields, location);
-
- Thunk_statement::thunk_types.insert(st);
-
+ st->set_is_struct_incomparable();
return st;
}
-// Return whether ST is a type created to hold thunk parameters.
-
-bool
-Thunk_statement::is_thunk_struct(const Struct_type* st)
-{
- return (Thunk_statement::thunk_types.find(st)
- != Thunk_statement::thunk_types.end());
-}
-
// Build the thunk we are going to call. This is a brand new, albeit
// artificial, function.
@@ -5356,9 +5280,9 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
index_temp, value_temp, &init, &cond, &iter_init,
&post);
else if (range_type->map_type() != NULL)
- this->lower_range_map(gogo, temp_block, body, range_object, range_temp,
- index_temp, value_temp, &init, &cond, &iter_init,
- &post);
+ this->lower_range_map(gogo, range_type->map_type(), temp_block, body,
+ range_object, range_temp, index_temp, value_temp,
+ &init, &cond, &iter_init, &post);
else if (range_type->channel_type() != NULL)
this->lower_range_channel(gogo, temp_block, body, range_object, range_temp,
index_temp, value_temp, &init, &cond, &iter_init,
@@ -5753,7 +5677,8 @@ For_range_statement::lower_range_string(Gogo*,
// Lower a for range over a map.
void
-For_range_statement::lower_range_map(Gogo*,
+For_range_statement::lower_range_map(Gogo* gogo,
+ Map_type* map_type,
Block* enclosing,
Block* body_block,
Named_object* range_object,
@@ -5768,13 +5693,13 @@ For_range_statement::lower_range_map(Gogo*,
Location loc = this->location();
// The runtime uses a struct to handle ranges over a map. The
- // struct is four pointers long. The first pointer is NULL when we
- // have completed the iteration.
+ // struct is built by Map_type::hiter_type for a specific map type.
// The loop we generate:
// var hiter map_iteration_struct
- // for mapiterinit(range, &hiter); hiter[0] != nil; mapiternext(&hiter) {
- // mapiter2(hiter, &index_temp, &value_temp)
+ // for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
+ // index_temp = *hiter.key
+ // value_temp = *hiter.val
// index = index_temp
// value = value_temp
// original body
@@ -5782,54 +5707,57 @@ For_range_statement::lower_range_map(Gogo*,
// Set *PINIT to
// var hiter map_iteration_struct
- // runtime.mapiterinit(range, &hiter)
+ // runtime.mapiterinit(type, range, &hiter)
Block* init = new Block(enclosing, loc);
- Type* map_iteration_type = Runtime::map_iteration_type();
+ Type* map_iteration_type = map_type->hiter_type(gogo);
Temporary_statement* hiter = Statement::make_temporary(map_iteration_type,
NULL, loc);
init->add_statement(hiter);
- Expression* p1 = this->make_range_ref(range_object, range_temp, loc);
+ Expression* p1 = Expression::make_type_descriptor(map_type, loc);
+ Expression* p2 = this->make_range_ref(range_object, range_temp, loc);
Expression* ref = Expression::make_temporary_reference(hiter, loc);
- Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
- Expression* call = Runtime::make_call(Runtime::MAPITERINIT, loc, 2, p1, p2);
+ Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc);
+ Expression* call = Runtime::make_call(Runtime::MAPITERINIT, loc, 3,
+ p1, p2, p3);
init->add_statement(Statement::make_statement(call, true));
*pinit = init;
// Set *PCOND to
- // hiter[0] != nil
+ // hiter.key != nil
ref = Expression::make_temporary_reference(hiter, loc);
- Expression* zexpr = Expression::make_integer_ul(0, NULL, loc);
- Expression* index = Expression::make_index(ref, zexpr, NULL, NULL, loc);
- Expression* ne = Expression::make_binary(OPERATOR_NOTEQ, index,
+ ref = Expression::make_field_reference(ref, 0, loc);
+ Expression* ne = Expression::make_binary(OPERATOR_NOTEQ, ref,
Expression::make_nil(loc),
loc);
*pcond = ne;
// Set *PITER_INIT to
- // mapiter1(hiter, &index_temp)
- // or
- // mapiter2(hiter, &index_temp, &value_temp)
+ // index_temp = *hiter.key
+ // value_temp = *hiter.val
Block* iter_init = new Block(body_block, loc);
- ref = Expression::make_temporary_reference(hiter, loc);
- p1 = Expression::make_unary(OPERATOR_AND, ref, loc);
- ref = Expression::make_temporary_reference(index_temp, loc);
- p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
- if (value_temp == NULL)
- call = Runtime::make_call(Runtime::MAPITER1, loc, 2, p1, p2);
- else
+ 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);
+ Statement* set = Statement::make_assignment(lhs, rhs, loc);
+ iter_init->add_statement(set);
+
+ if (value_temp != NULL)
{
- ref = Expression::make_temporary_reference(value_temp, loc);
- Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc);
- call = Runtime::make_call(Runtime::MAPITER2, loc, 3, p1, p2, p3);
+ 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);
+ set = Statement::make_assignment(lhs, rhs, loc);
+ iter_init->add_statement(set);
}
- iter_init->add_statement(Statement::make_statement(call, true));
*piter_init = iter_init;
diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h
index c949dda599f..9ed8d7272ec 100644
--- a/gcc/go/gofrontend/statements.h
+++ b/gcc/go/gofrontend/statements.h
@@ -120,7 +120,6 @@ class Statement
STATEMENT_ASSIGNMENT_OPERATION,
STATEMENT_TUPLE_ASSIGNMENT,
STATEMENT_TUPLE_MAP_ASSIGNMENT,
- STATEMENT_MAP_ASSIGNMENT,
STATEMENT_TUPLE_RECEIVE_ASSIGNMENT,
STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT,
STATEMENT_INCDEC,
@@ -166,11 +165,6 @@ class Statement
make_tuple_map_assignment(Expression* val, Expression* present,
Expression*, Location);
- // Make a statement which assigns a pair of values to a map.
- static Statement*
- make_map_assignment(Expression*, Expression* val,
- Expression* should_set, Location);
-
// Make an assignment from a nonblocking receive to a pair of
// variables.
static Statement*
@@ -586,6 +580,9 @@ class Assignment_statement : public Statement
bool
do_traverse_assignments(Traverse_assignments*);
+ virtual Statement*
+ do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
void
do_determine_types();
@@ -1144,10 +1141,6 @@ class Thunk_statement : public Statement
bool
simplify_statement(Gogo*, Named_object*, Block*);
- // Return whether ST is a type created to hold thunk parameters.
- static bool
- is_thunk_struct(const Struct_type *st);
-
protected:
int
do_traverse(Traverse* traverse);
@@ -1186,9 +1179,6 @@ class Thunk_statement : public Statement
void
thunk_field_param(int n, char* buf, size_t buflen);
- // A list of all the struct types created for thunk statements.
- static Unordered_set(const Struct_type*) thunk_types;
-
// The function call to be executed in a separate thread (go) or
// later (defer).
Expression* call_;
@@ -1529,9 +1519,10 @@ class For_range_statement : public Statement
Block**, Expression**, Block**, Block**);
void
- lower_range_map(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
+ lower_range_map(Gogo*, Map_type*, Block*, Block*, Named_object*,
Temporary_statement*, Temporary_statement*,
- Block**, Expression**, Block**, Block**);
+ Temporary_statement*, Block**, Expression**, Block**,
+ Block**);
void
lower_range_channel(Gogo*, Block*, Block*, Named_object*,
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc
index d91180a0497..db5655ba06a 100644
--- a/gcc/go/gofrontend/types.cc
+++ b/gcc/go/gofrontend/types.cc
@@ -567,6 +567,12 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1,
return t2->named_type()->named_type_is_comparable(reason);
else if (t1->struct_type() != NULL)
{
+ if (t1->struct_type()->is_struct_incomparable())
+ {
+ if (reason != NULL)
+ *reason = _("invalid comparison of generated struct");
+ return false;
+ }
const Struct_field_list* fields = t1->struct_type()->fields();
for (Struct_field_list::const_iterator p = fields->begin();
p != fields->end();
@@ -582,6 +588,12 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1,
}
else if (t1->array_type() != NULL)
{
+ if (t1->array_type()->is_array_incomparable())
+ {
+ if (reason != NULL)
+ *reason = _("invalid comparison of generated array");
+ return false;
+ }
if (t1->array_type()->length()->is_nil_expression()
|| !t1->array_type()->element_type()->is_comparable())
{
@@ -1478,6 +1490,7 @@ Type::make_type_descriptor_type()
Typed_identifier_list *params = new Typed_identifier_list();
params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc));
+ params->push_back(Typed_identifier("seed", uintptr_type, bloc));
params->push_back(Typed_identifier("key_size", uintptr_type, bloc));
Typed_identifier_list* results = new Typed_identifier_list();
@@ -1579,6 +1592,13 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
Function_type* equal_fntype, Named_object** hash_fn,
Named_object** equal_fn)
{
+ if (!this->is_comparable())
+ {
+ *hash_fn = NULL;
+ *equal_fn = NULL;
+ return;
+ }
+
if (hash_fntype == NULL || equal_fntype == NULL)
{
Location bloc = Linemap::predeclared_location();
@@ -1592,6 +1612,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
Typed_identifier_list* params = new Typed_identifier_list();
params->push_back(Typed_identifier("key", unsafe_pointer_type,
bloc));
+ params->push_back(Typed_identifier("seed", uintptr_type, bloc));
params->push_back(Typed_identifier("key_size", uintptr_type, bloc));
Typed_identifier_list* results = new Typed_identifier_list();
@@ -1623,13 +1644,6 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
hash_fnname = "__go_type_hash_identity";
equal_fnname = "__go_type_equal_identity";
}
- else if (!this->is_comparable() ||
- (this->struct_type() != NULL
- && Thunk_statement::is_thunk_struct(this->struct_type())))
- {
- hash_fnname = "__go_type_hash_error";
- equal_fnname = "__go_type_equal_error";
- }
else
{
switch (this->base()->classification())
@@ -1844,6 +1858,8 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name,
return;
}
+ go_assert(this->is_comparable());
+
Named_object* hash_fn = gogo->start_function(hash_name, hash_fntype, false,
bloc);
hash_fn->func_value()->set_is_type_specific_function();
@@ -1909,6 +1925,10 @@ Type::write_named_hash(Gogo* gogo, Named_type* name,
Named_object* key_arg = gogo->lookup("key", NULL);
go_assert(key_arg != NULL);
+ // The seed argument to the hash function.
+ Named_object* seed_arg = gogo->lookup("seed", NULL);
+ go_assert(seed_arg != NULL);
+
// The size of the type we are going to hash.
Named_object* keysz_arg = gogo->lookup("key_size", NULL);
go_assert(keysz_arg != NULL);
@@ -1920,9 +1940,11 @@ Type::write_named_hash(Gogo* gogo, Named_type* name,
// Call the hash function for the base type.
Expression* key_ref = Expression::make_var_reference(key_arg, bloc);
+ Expression* seed_ref = Expression::make_var_reference(seed_arg, bloc);
Expression* keysz_ref = Expression::make_var_reference(keysz_arg, bloc);
Expression_list* args = new Expression_list();
args->push_back(key_ref);
+ args->push_back(seed_ref);
args->push_back(keysz_ref);
Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc);
Expression* call = Expression::make_call(func, args, false, bloc);
@@ -2044,8 +2066,18 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind,
Named_object* equal_fn;
this->type_functions(gogo, name, hash_fntype, equal_fntype, &hash_fn,
&equal_fn);
- vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc));
- vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc));
+ if (hash_fn == NULL)
+ vals->push_back(Expression::make_cast(hash_fntype,
+ Expression::make_nil(bloc),
+ bloc));
+ else
+ vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc));
+ if (equal_fn == NULL)
+ vals->push_back(Expression::make_cast(equal_fntype,
+ Expression::make_nil(bloc),
+ bloc));
+ else
+ vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc));
++p;
go_assert(p->is_field_name("gc"));
@@ -4842,6 +4874,44 @@ Struct_type::do_compare_is_identity(Gogo* gogo)
return true;
}
+// Return whether this struct type is reflexive--whether a value of
+// this type is always equal to itself.
+
+bool
+Struct_type::do_is_reflexive()
+{
+ const Struct_field_list* fields = this->fields_;
+ if (fields == NULL)
+ return true;
+ for (Struct_field_list::const_iterator pf = fields->begin();
+ pf != fields->end();
+ ++pf)
+ {
+ if (!pf->type()->is_reflexive())
+ return false;
+ }
+ return true;
+}
+
+// Return whether this struct type needs a key update when used as a
+// map key.
+
+bool
+Struct_type::do_needs_key_update()
+{
+ const Struct_field_list* fields = this->fields_;
+ if (fields == NULL)
+ return false;
+ for (Struct_field_list::const_iterator pf = fields->begin();
+ pf != fields->end();
+ ++pf)
+ {
+ if (pf->type()->needs_key_update())
+ return true;
+ }
+ return false;
+}
+
// Build identity and hash functions for this struct.
// Hash code.
@@ -5310,18 +5380,20 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*,
go_assert(key_arg != NULL);
Type* key_arg_type = key_arg->var_value()->type();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ // The seed argument to the hash function.
+ Named_object* seed_arg = gogo->lookup("seed", NULL);
+ go_assert(seed_arg != NULL);
- // Get a 0.
- Expression* zero = Expression::make_integer_ul(0, uintptr_type, bloc);
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
- // Make a temporary to hold the return value, initialized to 0.
- Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero,
+ // Make a temporary to hold the return value, initialized to the seed.
+ Expression* ref = Expression::make_var_reference(seed_arg, bloc);
+ Temporary_statement* retval = Statement::make_temporary(uintptr_type, ref,
bloc);
gogo->add_statement(retval);
// Make a temporary to hold the key as a uintptr.
- Expression* ref = Expression::make_var_reference(key_arg, bloc);
+ ref = Expression::make_var_reference(key_arg, bloc);
ref = Expression::make_cast(uintptr_type, ref, bloc);
Temporary_statement* key = Statement::make_temporary(uintptr_type, ref,
bloc);
@@ -5367,19 +5439,20 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*,
pf->type()->type_functions(gogo, pf->type()->named_type(), hash_fntype,
equal_fntype, &hash_fn, &equal_fn);
- // Call the hash function for the field.
+ // Call the hash function for the field, passing retval as the seed.
+ ref = Expression::make_temporary_reference(retval, bloc);
Expression_list* args = new Expression_list();
args->push_back(subkey);
+ args->push_back(ref);
args->push_back(size);
Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc);
Expression* call = Expression::make_call(func, args, false, bloc);
- // Add the field's hash value to retval.
+ // Set retval to the result.
Temporary_reference_expression* tref =
Expression::make_temporary_reference(retval, bloc);
tref->set_is_lvalue();
- Statement* s = Statement::make_assignment_operation(OPERATOR_PLUSEQ,
- tref, call, bloc);
+ Statement* s = Statement::make_assignment(tref, call, bloc);
gogo->add_statement(s);
}
@@ -5733,6 +5806,11 @@ Struct_type::can_write_type_to_c_header(
return true;
case TYPE_POINTER:
+ // Don't try to handle a pointer to an array.
+ if (t->points_to()->array_type() != NULL
+ && !t->points_to()->is_slice_type())
+ return false;
+
if (t->points_to()->named_type() != NULL
&& t->points_to()->struct_type() != NULL)
declare->push_back(t->points_to()->named_type()->named_object());
@@ -6157,18 +6235,20 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name,
go_assert(key_arg != NULL);
Type* key_arg_type = key_arg->var_value()->type();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ // The seed argument to the hash function.
+ Named_object* seed_arg = gogo->lookup("seed", NULL);
+ go_assert(seed_arg != NULL);
- // Get a 0.
- Expression* zero = Expression::make_integer_ul(0, uintptr_type, bloc);
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
- // Make a temporary to hold the return value, initialized to 0.
- Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero,
+ // Make a temporary to hold the return value, initialized to the seed.
+ Expression* ref = Expression::make_var_reference(seed_arg, bloc);
+ Temporary_statement* retval = Statement::make_temporary(uintptr_type, ref,
bloc);
gogo->add_statement(retval);
// Make a temporary to hold the key as a uintptr.
- Expression* ref = Expression::make_var_reference(key_arg, bloc);
+ ref = Expression::make_var_reference(key_arg, bloc);
ref = Expression::make_cast(uintptr_type, ref, bloc);
Temporary_statement* key = Statement::make_temporary(uintptr_type, ref,
bloc);
@@ -6216,18 +6296,20 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name,
Expression* ele_size = Expression::make_type_info(this->element_type_,
Expression::TYPE_INFO_SIZE);
- // Get the hash of this element.
+ // Get the hash of this element, passing retval as the seed.
+ ref = Expression::make_temporary_reference(retval, bloc);
Expression_list* args = new Expression_list();
args->push_back(subkey);
+ args->push_back(ref);
args->push_back(ele_size);
Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc);
Expression* call = Expression::make_call(func, args, false, bloc);
- // Add the element's hash value to retval.
+ // Set retval to the result.
Temporary_reference_expression* tref =
Expression::make_temporary_reference(retval, bloc);
tref->set_is_lvalue();
- s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, tref, call, bloc);
+ s = Statement::make_assignment(tref, call, bloc);
gogo->add_statement(s);
// Increase the element pointer.
@@ -6846,6 +6928,100 @@ Type::make_array_type(Type* element_type, Expression* length)
// Class Map_type.
+Named_object* Map_type::zero_value;
+int64_t Map_type::zero_value_size;
+int64_t Map_type::zero_value_align;
+
+// If this map requires the "fat" functions, return the pointer to
+// pass as the zero value to those functions. Otherwise, in the
+// normal case, return NULL. The map requires the "fat" functions if
+// the value size is larger than max_zero_size bytes. max_zero_size
+// must match maxZero in libgo/go/runtime/hashmap.go.
+
+Expression*
+Map_type::fat_zero_value(Gogo* gogo)
+{
+ int64_t valsize;
+ if (!this->val_type_->backend_type_size(gogo, &valsize))
+ {
+ go_assert(saw_errors());
+ return NULL;
+ }
+ if (valsize <= Map_type::max_zero_size)
+ return NULL;
+
+ if (Map_type::zero_value_size < valsize)
+ Map_type::zero_value_size = valsize;
+
+ int64_t valalign;
+ if (!this->val_type_->backend_type_align(gogo, &valalign))
+ {
+ go_assert(saw_errors());
+ return NULL;
+ }
+
+ if (Map_type::zero_value_align < valalign)
+ Map_type::zero_value_align = valalign;
+
+ Location bloc = Linemap::predeclared_location();
+
+ if (Map_type::zero_value == NULL)
+ {
+ // The final type will be set in backend_zero_value.
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Expression* size = Expression::make_integer_ul(0, NULL, bloc);
+ Type* array_type = Type::make_array_type(uint8_type, size);
+ Variable* var = new Variable(array_type, NULL, true, false, false, bloc);
+ Map_type::zero_value = Named_object::make_variable("go$zerovalue", NULL,
+ var);
+ }
+
+ Expression* z = Expression::make_var_reference(Map_type::zero_value, bloc);
+ z = Expression::make_unary(OPERATOR_AND, z, bloc);
+ Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type());
+ z = Expression::make_cast(unsafe_ptr_type, z, bloc);
+ return z;
+}
+
+// Return whether VAR is the map zero value.
+
+bool
+Map_type::is_zero_value(Variable* var)
+{
+ return (Map_type::zero_value != NULL
+ && Map_type::zero_value->var_value() == var);
+}
+
+// Return the backend representation for the zero value.
+
+Bvariable*
+Map_type::backend_zero_value(Gogo* gogo)
+{
+ Location bloc = Linemap::predeclared_location();
+
+ go_assert(Map_type::zero_value != NULL);
+
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Btype* buint8_type = uint8_type->get_backend(gogo);
+
+ Type* int_type = Type::lookup_integer_type("int");
+
+ Expression* e = Expression::make_integer_int64(Map_type::zero_value_size,
+ int_type, bloc);
+ Translate_context context(gogo, NULL, NULL, NULL);
+ Bexpression* blength = e->get_backend(&context);
+
+ Btype* barray_type = gogo->backend()->array_type(buint8_type, blength);
+
+ std::string zname = Map_type::zero_value->name();
+ Bvariable* zvar =
+ gogo->backend()->implicit_variable(zname, barray_type, false, true, true,
+ Map_type::zero_value_align);
+ gogo->backend()->implicit_variable_set_init(zvar, zname, barray_type,
+ false, true, true, NULL);
+ return zvar;
+}
+
// Traversal.
int
@@ -6890,8 +7066,8 @@ Map_type::do_hash_for_method(Gogo* gogo) const
}
// Get the backend representation for a map type. A map type is
-// represented as a pointer to a struct. The struct is __go_map in
-// libgo/map.h.
+// represented as a pointer to a struct. The struct is hmap in
+// runtime/hashmap.go.
Btype*
Map_type::do_get_backend(Gogo* gogo)
@@ -6899,33 +7075,50 @@ Map_type::do_get_backend(Gogo* gogo)
static Btype* backend_map_type;
if (backend_map_type == NULL)
{
- std::vector<Backend::Btyped_identifier> bfields(4);
+ std::vector<Backend::Btyped_identifier> bfields(8);
Location bloc = Linemap::predeclared_location();
- Type* pdt = Type::make_type_descriptor_ptr_type();
- bfields[0].name = "__descriptor";
- bfields[0].btype = pdt->get_backend(gogo);
+ Type* int_type = Type::lookup_integer_type("int");
+ bfields[0].name = "count";
+ bfields[0].btype = int_type->get_backend(gogo);
bfields[0].location = bloc;
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
- bfields[1].name = "__element_count";
- bfields[1].btype = uintptr_type->get_backend(gogo);
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ bfields[1].name = "flags";
+ bfields[1].btype = uint8_type->get_backend(gogo);
bfields[1].location = bloc;
- bfields[2].name = "__bucket_count";
+ bfields[2].name = "B";
bfields[2].btype = bfields[1].btype;
bfields[2].location = bloc;
+ Type* uint32_type = Type::lookup_integer_type("uint32");
+ bfields[3].name = "hash0";
+ bfields[3].btype = uint32_type->get_backend(gogo);
+ bfields[3].location = bloc;
+
Btype* bvt = gogo->backend()->void_type();
Btype* bpvt = gogo->backend()->pointer_type(bvt);
- Btype* bppvt = gogo->backend()->pointer_type(bpvt);
- bfields[3].name = "__buckets";
- bfields[3].btype = bppvt;
- bfields[3].location = bloc;
+ bfields[4].name = "buckets";
+ bfields[4].btype = bpvt;
+ bfields[4].location = bloc;
+
+ bfields[5].name = "oldbuckets";
+ bfields[5].btype = bpvt;
+ bfields[5].location = bloc;
+
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ bfields[6].name = "nevacuate";
+ bfields[6].btype = uintptr_type->get_backend(gogo);
+ bfields[6].location = bloc;
+
+ bfields[7].name = "overflow";
+ bfields[7].btype = bpvt;
+ bfields[7].location = bloc;
Btype *bt = gogo->backend()->struct_type(bfields);
- bt = gogo->backend()->named_type("__go_map", bt, bloc);
+ bt = gogo->backend()->named_type("runtime.hmap", bt, bloc);
backend_map_type = gogo->backend()->pointer_type(bt);
}
return backend_map_type;
@@ -6941,12 +7134,24 @@ Map_type::make_map_type_descriptor_type()
{
Type* tdt = Type::make_type_descriptor_type();
Type* ptdt = Type::make_type_descriptor_ptr_type();
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* uint16_type = Type::lookup_integer_type("uint16");
+ Type* bool_type = Type::lookup_bool_type();
Struct_type* sf =
- Type::make_builtin_struct_type(3,
+ Type::make_builtin_struct_type(12,
"", tdt,
"key", ptdt,
- "elem", ptdt);
+ "elem", ptdt,
+ "bucket", ptdt,
+ "hmap", ptdt,
+ "keysize", uint8_type,
+ "indirectkey", bool_type,
+ "valuesize", uint8_type,
+ "indirectvalue", bool_type,
+ "bucketsize", uint16_type,
+ "reflexivekey", bool_type,
+ "needkeyupdate", bool_type);
ret = Type::make_builtin_named_type("MapType", sf);
}
@@ -6962,11 +7167,48 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name)
Location bloc = Linemap::predeclared_location();
Type* mtdt = Map_type::make_map_type_descriptor_type();
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* uint16_type = Type::lookup_integer_type("uint16");
+
+ int64_t keysize;
+ if (!this->key_type_->backend_type_size(gogo, &keysize))
+ {
+ error_at(this->location_, "error determining map key type size");
+ return Expression::make_error(this->location_);
+ }
+
+ int64_t valsize;
+ if (!this->val_type_->backend_type_size(gogo, &valsize))
+ {
+ error_at(this->location_, "error determining map value type size");
+ return Expression::make_error(this->location_);
+ }
+
+ int64_t ptrsize;
+ if (!Type::make_pointer_type(uint8_type)->backend_type_size(gogo, &ptrsize))
+ {
+ go_assert(saw_errors());
+ return Expression::make_error(this->location_);
+ }
+
+ Type* bucket_type = this->bucket_type(gogo, keysize, valsize);
+ if (bucket_type == NULL)
+ {
+ go_assert(saw_errors());
+ return Expression::make_error(this->location_);
+ }
+
+ int64_t bucketsize;
+ if (!bucket_type->backend_type_size(gogo, &bucketsize))
+ {
+ go_assert(saw_errors());
+ return Expression::make_error(this->location_);
+ }
const Struct_field_list* fields = mtdt->struct_type()->fields();
Expression_list* vals = new Expression_list();
- vals->reserve(3);
+ vals->reserve(12);
Struct_field_list::const_iterator p = fields->begin();
go_assert(p->is_field_name("commonType"));
@@ -6983,130 +7225,270 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name)
vals->push_back(Expression::make_type_descriptor(this->val_type_, bloc));
++p;
- go_assert(p == fields->end());
-
- return Expression::make_struct_composite_literal(mtdt, vals, bloc);
-}
-
-// A mapping from map types to map descriptors.
-
-Map_type::Map_descriptors Map_type::map_descriptors;
-
-// Build a map descriptor for this type. Return a pointer to it.
+ go_assert(p->is_field_name("bucket"));
+ vals->push_back(Expression::make_type_descriptor(bucket_type, bloc));
-Bexpression*
-Map_type::map_descriptor_pointer(Gogo* gogo, Location location)
-{
- Bvariable* bvar = this->map_descriptor(gogo);
- Bexpression* var_expr = gogo->backend()->var_expression(bvar, location);
- return gogo->backend()->address_expression(var_expr, location);
-}
+ ++p;
+ go_assert(p->is_field_name("hmap"));
+ Type* hmap_type = this->hmap_type(bucket_type);
+ vals->push_back(Expression::make_type_descriptor(hmap_type, bloc));
-// Build a map descriptor for this type.
+ ++p;
+ go_assert(p->is_field_name("keysize"));
+ if (keysize > Map_type::max_key_size)
+ vals->push_back(Expression::make_integer_int64(ptrsize, uint8_type, bloc));
+ else
+ vals->push_back(Expression::make_integer_int64(keysize, uint8_type, bloc));
-Bvariable*
-Map_type::map_descriptor(Gogo* gogo)
-{
- std::pair<Map_type*, Bvariable*> val(this, NULL);
- std::pair<Map_type::Map_descriptors::iterator, bool> ins =
- Map_type::map_descriptors.insert(val);
- if (!ins.second)
- return ins.first->second;
+ ++p;
+ go_assert(p->is_field_name("indirectkey"));
+ vals->push_back(Expression::make_boolean(keysize > Map_type::max_key_size,
+ bloc));
- Type* key_type = this->key_type_;
- Type* val_type = this->val_type_;
+ ++p;
+ go_assert(p->is_field_name("valuesize"));
+ if (valsize > Map_type::max_val_size)
+ vals->push_back(Expression::make_integer_int64(ptrsize, uint8_type, bloc));
+ else
+ vals->push_back(Expression::make_integer_int64(valsize, uint8_type, bloc));
- // The map entry type is a struct with three fields. Build that
- // struct so that we can get the offsets of the key and value within
- // a map entry. The first field should technically be a pointer to
- // this type itself, but since we only care about field offsets we
- // just use pointer to bool.
- Type* pbool = Type::make_pointer_type(Type::make_boolean_type());
- Struct_type* map_entry_type =
- Type::make_builtin_struct_type(3,
- "__next", pbool,
- "__key", key_type,
- "__val", val_type);
+ ++p;
+ go_assert(p->is_field_name("indirectvalue"));
+ vals->push_back(Expression::make_boolean(valsize > Map_type::max_val_size,
+ bloc));
- Type* map_descriptor_type = Map_type::make_map_descriptor_type();
+ ++p;
+ go_assert(p->is_field_name("bucketsize"));
+ vals->push_back(Expression::make_integer_int64(bucketsize, uint16_type,
+ bloc));
- const Struct_field_list* fields =
- map_descriptor_type->struct_type()->fields();
+ ++p;
+ go_assert(p->is_field_name("reflexivekey"));
+ vals->push_back(Expression::make_boolean(this->key_type_->is_reflexive(),
+ bloc));
- Expression_list* vals = new Expression_list();
- vals->reserve(4);
+ ++p;
+ go_assert(p->is_field_name("needkeyupdate"));
+ vals->push_back(Expression::make_boolean(this->key_type_->needs_key_update(),
+ bloc));
- Location bloc = Linemap::predeclared_location();
+ ++p;
+ go_assert(p == fields->end());
- Struct_field_list::const_iterator p = fields->begin();
+ return Expression::make_struct_composite_literal(mtdt, vals, bloc);
+}
- go_assert(p->is_field_name("__map_descriptor"));
- vals->push_back(Expression::make_type_descriptor(this, bloc));
+// Return the bucket type to use for a map type. This must correspond
+// to libgo/go/runtime/hashmap.go.
- ++p;
- go_assert(p->is_field_name("__entry_size"));
- Expression::Type_info type_info = Expression::TYPE_INFO_SIZE;
- vals->push_back(Expression::make_type_info(map_entry_type, type_info));
+Type*
+Map_type::bucket_type(Gogo* gogo, int64_t keysize, int64_t valsize)
+{
+ if (this->bucket_type_ != NULL)
+ return this->bucket_type_;
- Struct_field_list::const_iterator pf = map_entry_type->fields()->begin();
- ++pf;
- go_assert(pf->is_field_name("__key"));
+ Type* key_type = this->key_type_;
+ if (keysize > Map_type::max_key_size)
+ key_type = Type::make_pointer_type(key_type);
- ++p;
- go_assert(p->is_field_name("__key_offset"));
- vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf));
+ Type* val_type = this->val_type_;
+ if (valsize > Map_type::max_val_size)
+ val_type = Type::make_pointer_type(val_type);
+
+ Expression* bucket_size = Expression::make_integer_ul(Map_type::bucket_size,
+ NULL, this->location_);
+
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Array_type* topbits_type = Type::make_array_type(uint8_type, bucket_size);
+ topbits_type->set_is_array_incomparable();
+ Array_type* keys_type = Type::make_array_type(key_type, bucket_size);
+ keys_type->set_is_array_incomparable();
+ Array_type* values_type = Type::make_array_type(val_type, bucket_size);
+ values_type->set_is_array_incomparable();
+
+ // If keys and values have no pointers, the map implementation can
+ // keep a list of overflow pointers on the side so that buckets can
+ // be marked as having no pointers. Arrange for the bucket to have
+ // no pointers by changing the type of the overflow field to uintptr
+ // in this case. See comment on the hmap.overflow field in
+ // libgo/go/runtime/hashmap.go.
+ Type* overflow_type;
+ if (!key_type->has_pointer() && !val_type->has_pointer())
+ overflow_type = Type::lookup_integer_type("uintptr");
+ else
+ {
+ // This should really be a pointer to the bucket type itself,
+ // but that would require us to construct a Named_type for it to
+ // give it a way to refer to itself. Since nothing really cares
+ // (except perhaps for someone using a debugger) just use an
+ // unsafe pointer.
+ overflow_type = Type::make_pointer_type(Type::make_void_type());
+ }
+
+ // Make sure the overflow pointer is the last memory in the struct,
+ // because the runtime assumes it can use size-ptrSize as the offset
+ // of the overflow pointer. We double-check that property below
+ // once the offsets and size are computed.
+
+ int64_t topbits_field_size, topbits_field_align;
+ int64_t keys_field_size, keys_field_align;
+ int64_t values_field_size, values_field_align;
+ int64_t overflow_field_size, overflow_field_align;
+ if (!topbits_type->backend_type_size(gogo, &topbits_field_size)
+ || !topbits_type->backend_type_field_align(gogo, &topbits_field_align)
+ || !keys_type->backend_type_size(gogo, &keys_field_size)
+ || !keys_type->backend_type_field_align(gogo, &keys_field_align)
+ || !values_type->backend_type_size(gogo, &values_field_size)
+ || !values_type->backend_type_field_align(gogo, &values_field_align)
+ || !overflow_type->backend_type_size(gogo, &overflow_field_size)
+ || !overflow_type->backend_type_field_align(gogo, &overflow_field_align))
+ {
+ go_assert(saw_errors());
+ return NULL;
+ }
- ++pf;
- go_assert(pf->is_field_name("__val"));
+ Struct_type* ret;
+ int64_t max_align = std::max(std::max(topbits_field_align, keys_field_align),
+ values_field_align);
+ if (max_align <= overflow_field_align)
+ ret = make_builtin_struct_type(4,
+ "topbits", topbits_type,
+ "keys", keys_type,
+ "values", values_type,
+ "overflow", overflow_type);
+ else
+ {
+ size_t off = topbits_field_size;
+ off = ((off + keys_field_align - 1)
+ &~ static_cast<size_t>(keys_field_align - 1));
+ off += keys_field_size;
+ off = ((off + values_field_align - 1)
+ &~ static_cast<size_t>(values_field_align - 1));
+ off += values_field_size;
+
+ int64_t padded_overflow_field_size =
+ ((overflow_field_size + max_align - 1)
+ &~ static_cast<size_t>(max_align - 1));
+
+ size_t ovoff = off;
+ ovoff = ((ovoff + max_align - 1)
+ &~ static_cast<size_t>(max_align - 1));
+ size_t pad = (ovoff - off
+ + padded_overflow_field_size - overflow_field_size);
+
+ Expression* pad_expr = Expression::make_integer_ul(pad, NULL,
+ this->location_);
+ Type* pad_type = Type::make_array_type(uint8_type, pad_expr);
+
+ ret = make_builtin_struct_type(5,
+ "topbits", topbits_type,
+ "keys", keys_type,
+ "values", values_type,
+ "pad", pad_type,
+ "overflow", overflow_type);
+ }
+
+ // Verify that the overflow field is just before the end of the
+ // bucket type.
+
+ Btype* btype = ret->get_backend(gogo);
+ int64_t offset = gogo->backend()->type_field_offset(btype,
+ ret->field_count() - 1);
+ int64_t size;
+ if (!ret->backend_type_size(gogo, &size))
+ {
+ go_assert(saw_errors());
+ return NULL;
+ }
- ++p;
- go_assert(p->is_field_name("__val_offset"));
- vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf));
+ int64_t ptr_size;
+ if (!Type::make_pointer_type(uint8_type)->backend_type_size(gogo, &ptr_size))
+ {
+ go_assert(saw_errors());
+ return NULL;
+ }
- ++p;
- go_assert(p == fields->end());
+ go_assert(offset + ptr_size == size);
- Expression* initializer =
- Expression::make_struct_composite_literal(map_descriptor_type, vals, bloc);
+ ret->set_is_struct_incomparable();
- std::string mangled_name = "__go_map_" + this->mangled_name(gogo);
- Btype* map_descriptor_btype = map_descriptor_type->get_backend(gogo);
- Bvariable* bvar = gogo->backend()->immutable_struct(mangled_name, false,
- true,
- map_descriptor_btype,
- bloc);
+ this->bucket_type_ = ret;
+ return ret;
+}
- Translate_context context(gogo, NULL, NULL, NULL);
- context.set_is_const();
- Bexpression* binitializer = initializer->get_backend(&context);
+// Return the hashmap type for a map type.
- gogo->backend()->immutable_struct_set_init(bvar, mangled_name, false, true,
- map_descriptor_btype, bloc,
- binitializer);
+Type*
+Map_type::hmap_type(Type* bucket_type)
+{
+ if (this->hmap_type_ != NULL)
+ return this->hmap_type_;
- ins.first->second = bvar;
- return bvar;
+ Type* int_type = Type::lookup_integer_type("int");
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* uint32_type = Type::lookup_integer_type("uint32");
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ Type* void_ptr_type = Type::make_pointer_type(Type::make_void_type());
+
+ Type* ptr_bucket_type = Type::make_pointer_type(bucket_type);
+
+ Struct_type* ret = make_builtin_struct_type(8,
+ "count", int_type,
+ "flags", uint8_type,
+ "B", uint8_type,
+ "hash0", uint32_type,
+ "buckets", ptr_bucket_type,
+ "oldbuckets", ptr_bucket_type,
+ "nevacuate", uintptr_type,
+ "overflow", void_ptr_type);
+ ret->set_is_struct_incomparable();
+ this->hmap_type_ = ret;
+ return ret;
}
-// Build the type of a map descriptor. This must match the struct
-// __go_map_descriptor in libgo/runtime/map.h.
+// Return the iterator type for a map type. This is the type of the
+// value used when doing a range over a map.
Type*
-Map_type::make_map_descriptor_type()
+Map_type::hiter_type(Gogo* gogo)
{
- static Type* ret;
- if (ret == NULL)
+ if (this->hiter_type_ != NULL)
+ return this->hiter_type_;
+
+ int64_t keysize, valsize;
+ if (!this->key_type_->backend_type_size(gogo, &keysize)
+ || !this->val_type_->backend_type_size(gogo, &valsize))
{
- Type* ptdt = Type::make_type_descriptor_ptr_type();
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
- Struct_type* sf =
- Type::make_builtin_struct_type(4,
- "__map_descriptor", ptdt,
- "__entry_size", uintptr_type,
- "__key_offset", uintptr_type,
- "__val_offset", uintptr_type);
- ret = Type::make_builtin_named_type("__go_map_descriptor", sf);
+ go_assert(saw_errors());
+ return NULL;
}
+
+ Type* key_ptr_type = Type::make_pointer_type(this->key_type_);
+ Type* val_ptr_type = Type::make_pointer_type(this->val_type_);
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* uint8_ptr_type = Type::make_pointer_type(uint8_type);
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ Type* bucket_type = this->bucket_type(gogo, keysize, valsize);
+ Type* bucket_ptr_type = Type::make_pointer_type(bucket_type);
+ Type* hmap_type = this->hmap_type(bucket_type);
+ Type* hmap_ptr_type = Type::make_pointer_type(hmap_type);
+ Type* void_ptr_type = Type::make_pointer_type(Type::make_void_type());
+
+ Struct_type* ret = make_builtin_struct_type(12,
+ "key", key_ptr_type,
+ "val", val_ptr_type,
+ "t", uint8_ptr_type,
+ "h", hmap_ptr_type,
+ "buckets", bucket_ptr_type,
+ "bptr", bucket_ptr_type,
+ "overflow0", void_ptr_type,
+ "overflow1", void_ptr_type,
+ "startBucket", uintptr_type,
+ "stuff", uintptr_type,
+ "bucket", uintptr_type,
+ "checkBucket", uintptr_type);
+ ret->set_is_struct_incomparable();
+ this->hiter_type_ = ret;
return ret;
}
@@ -9016,6 +9398,33 @@ Named_type::do_compare_is_identity(Gogo* gogo)
return ret;
}
+// Return whether this type is reflexive--whether it is always equal
+// to itself.
+
+bool
+Named_type::do_is_reflexive()
+{
+ if (this->seen_in_compare_is_identity_)
+ return false;
+ this->seen_in_compare_is_identity_ = true;
+ bool ret = this->type_->is_reflexive();
+ this->seen_in_compare_is_identity_ = false;
+ return ret;
+}
+
+// Return whether this type needs a key update when used as a map key.
+
+bool
+Named_type::do_needs_key_update()
+{
+ if (this->seen_in_compare_is_identity_)
+ return true;
+ this->seen_in_compare_is_identity_ = true;
+ bool ret = this->type_->needs_key_update();
+ this->seen_in_compare_is_identity_ = false;
+ return ret;
+}
+
// Return a hash code. This is used for method lookup. We simply
// hash on the name itself.
diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h
index 5de49ae3534..3d9a3c47fae 100644
--- a/gcc/go/gofrontend/types.h
+++ b/gcc/go/gofrontend/types.h
@@ -14,6 +14,7 @@
class Gogo;
class Package;
+class Variable;
class Traverse;
class Typed_identifier;
class Typed_identifier_list;
@@ -629,6 +630,18 @@ class Type
compare_is_identity(Gogo* gogo)
{ return this->do_compare_is_identity(gogo); }
+ // Return whether values of this type are reflexive: if a comparison
+ // of a value with itself always returns true.
+ bool
+ is_reflexive()
+ { return this->do_is_reflexive(); }
+
+ // Return whether values of this, when used as a key in map,
+ // requires the key to be updated when an assignment is made.
+ bool
+ needs_key_update()
+ { return this->do_needs_key_update(); }
+
// Return a hash code for this type for the method hash table.
// Types which are equivalent according to are_identical will have
// the same hash code.
@@ -1006,6 +1019,14 @@ class Type
virtual bool
do_compare_is_identity(Gogo*) = 0;
+ virtual bool
+ do_is_reflexive()
+ { return true; }
+
+ virtual bool
+ do_needs_key_update()
+ { return false; }
+
virtual unsigned int
do_hash_for_method(Gogo*) const;
@@ -1639,6 +1660,15 @@ class Float_type : public Type
do_compare_is_identity(Gogo*)
{ return false; }
+ bool
+ do_is_reflexive()
+ { return false; }
+
+ // Distinction between +0 and -0 requires a key update.
+ bool
+ do_needs_key_update()
+ { return true; }
+
unsigned int
do_hash_for_method(Gogo*) const;
@@ -1712,6 +1742,15 @@ class Complex_type : public Type
do_compare_is_identity(Gogo*)
{ return false; }
+ bool
+ do_is_reflexive()
+ { return false; }
+
+ // Distinction between +0 and -0 requires a key update.
+ bool
+ do_needs_key_update()
+ { return true; }
+
unsigned int
do_hash_for_method(Gogo*) const;
@@ -1768,6 +1807,11 @@ class String_type : public Type
do_compare_is_identity(Gogo*)
{ return false; }
+ // New string might have a smaller backing store.
+ bool
+ do_needs_key_update()
+ { return true; }
+
Btype*
do_get_backend(Gogo*);
@@ -2218,7 +2262,8 @@ class Struct_type : public Type
public:
Struct_type(Struct_field_list* fields, Location location)
: Type(TYPE_STRUCT),
- fields_(fields), location_(location), all_methods_(NULL)
+ fields_(fields), location_(location), all_methods_(NULL),
+ is_struct_incomparable_(false)
{ }
// Return the field NAME. This only looks at local fields, not at
@@ -2323,6 +2368,16 @@ class Struct_type : public Type
static Type*
make_struct_type_descriptor_type();
+ // Return whether this is a generated struct that is not comparable.
+ bool
+ is_struct_incomparable() const
+ { return this->is_struct_incomparable_; }
+
+ // Record that this is a generated struct that is not comparable.
+ void
+ set_is_struct_incomparable()
+ { this->is_struct_incomparable_ = true; }
+
// Write the hash function for this type.
void
write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*);
@@ -2354,6 +2409,12 @@ class Struct_type : public Type
bool
do_compare_is_identity(Gogo*);
+ bool
+ do_is_reflexive();
+
+ bool
+ do_needs_key_update();
+
unsigned int
do_hash_for_method(Gogo*) const;
@@ -2418,6 +2479,9 @@ class Struct_type : public Type
Location location_;
// If this struct is unnamed, a list of methods.
Methods* all_methods_;
+ // True if this is a generated struct that is not considered to be
+ // comparable.
+ bool is_struct_incomparable_;
};
// The type of an array.
@@ -2428,7 +2492,7 @@ class Array_type : public Type
Array_type(Type* element_type, Expression* length)
: Type(TYPE_ARRAY),
element_type_(element_type), length_(length), blength_(NULL),
- issued_length_error_(false)
+ issued_length_error_(false), is_array_incomparable_(false)
{ }
// Return the element type.
@@ -2479,6 +2543,16 @@ class Array_type : public Type
static Type*
make_slice_type_descriptor_type();
+ // Return whether this is a generated array that is not comparable.
+ bool
+ is_array_incomparable() const
+ { return this->is_array_incomparable_; }
+
+ // Record that this is a generated array that is not comparable.
+ void
+ set_is_array_incomparable()
+ { this->is_array_incomparable_ = true; }
+
// Write the hash function for this type.
void
write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*);
@@ -2503,6 +2577,16 @@ class Array_type : public Type
bool
do_compare_is_identity(Gogo*);
+ bool
+ do_is_reflexive()
+ {
+ return this->length_ != NULL && this->element_type_->is_reflexive();
+ }
+
+ bool
+ do_needs_key_update()
+ { return this->element_type_->needs_key_update(); }
+
unsigned int
do_hash_for_method(Gogo*) const;
@@ -2550,6 +2634,9 @@ class Array_type : public Type
// Whether or not an invalid length error has been issued for this type,
// to avoid knock-on errors.
mutable bool issued_length_error_;
+ // True if this is a generated array that is not considered to be
+ // comparable.
+ bool is_array_incomparable_;
};
// The type of a map.
@@ -2559,7 +2646,8 @@ class Map_type : public Type
public:
Map_type(Type* key_type, Type* val_type, Location location)
: Type(TYPE_MAP),
- key_type_(key_type), val_type_(val_type), location_(location)
+ key_type_(key_type), val_type_(val_type), hmap_type_(NULL),
+ bucket_type_(NULL), hiter_type_(NULL), location_(location)
{ }
// Return the key type.
@@ -2572,6 +2660,24 @@ class Map_type : public Type
val_type() const
{ return this->val_type_; }
+ // Return the type used for an iteration over this map.
+ Type*
+ hiter_type(Gogo*);
+
+ // If this map requires the "fat" functions, returns the pointer to
+ // pass as the zero value to those functions. Otherwise, in the
+ // normal case, returns NULL.
+ Expression*
+ fat_zero_value(Gogo*);
+
+ // Return whether VAR is the map zero value.
+ static bool
+ is_zero_value(Variable* var);
+
+ // Return the backend representation of the map zero value.
+ static Bvariable*
+ backend_zero_value(Gogo*);
+
// Whether this type is identical with T.
bool
is_identical(const Map_type* t, bool errors_are_identical) const;
@@ -2583,15 +2689,6 @@ class Map_type : public Type
static Type*
make_map_type_descriptor_type();
- static Type*
- make_map_descriptor_type();
-
- // Build a map descriptor for this type. Return a pointer to it.
- // The location is the location which causes us to need the
- // descriptor.
- Bexpression*
- map_descriptor_pointer(Gogo* gogo, Location);
-
protected:
int
do_traverse(Traverse*);
@@ -2607,6 +2704,12 @@ class Map_type : public Type
do_compare_is_identity(Gogo*)
{ return false; }
+ bool
+ do_is_reflexive()
+ {
+ return this->key_type_->is_reflexive() && this->val_type_->is_reflexive();
+ }
+
unsigned int
do_hash_for_method(Gogo*) const;
@@ -2629,18 +2732,41 @@ class Map_type : public Type
do_export(Export*) const;
private:
- // Mapping from map types to map descriptors.
- typedef Unordered_map_hash(const Map_type*, Bvariable*, Type_hash_identical,
- Type_identical) Map_descriptors;
- static Map_descriptors map_descriptors;
+ // These must be in sync with libgo/go/runtime/hashmap.go.
+ static const int bucket_size = 8;
+ static const int max_key_size = 128;
+ static const int max_val_size = 128;
+ static const int max_zero_size = 1024;
+
+ // Maps with value types larger than max_zero_size require passing a
+ // zero value pointer to the map functions.
+
+ // The zero value variable.
+ static Named_object* zero_value;
- Bvariable*
- map_descriptor(Gogo*);
+ // The current size of the zero value.
+ static int64_t zero_value_size;
+
+ // The current alignment of the zero value.
+ static int64_t zero_value_align;
+
+ Type*
+ bucket_type(Gogo*, int64_t, int64_t);
+
+ Type*
+ hmap_type(Type*);
// The key type.
Type* key_type_;
// The value type.
Type* val_type_;
+ // The hashmap type. At run time a map is represented as a pointer
+ // to this type.
+ Type* hmap_type_;
+ // The bucket type, the type used to hold keys and values at run time.
+ Type* bucket_type_;
+ // The iterator type.
+ Type* hiter_type_;
// Where the type was defined.
Location location_;
};
@@ -2832,6 +2958,17 @@ class Interface_type : public Type
do_compare_is_identity(Gogo*)
{ return false; }
+ // Not reflexive if it contains a float.
+ bool
+ do_is_reflexive()
+ { return false; }
+
+ // Distinction between +0 and -0 requires a key update if it
+ // contains a float.
+ bool
+ do_needs_key_update()
+ { return true; }
+
unsigned int
do_hash_for_method(Gogo*) const;
@@ -3121,6 +3258,12 @@ class Named_type : public Type
bool
do_compare_is_identity(Gogo*);
+ bool
+ do_is_reflexive();
+
+ bool
+ do_needs_key_update();
+
unsigned int
do_hash_for_method(Gogo*) const;
@@ -3268,6 +3411,14 @@ class Forward_declaration_type : public Type
do_compare_is_identity(Gogo* gogo)
{ return this->real_type()->compare_is_identity(gogo); }
+ bool
+ do_is_reflexive()
+ { return this->real_type()->is_reflexive(); }
+
+ bool
+ do_needs_key_update()
+ { return this->real_type()->needs_key_update(); }
+
unsigned int
do_hash_for_method(Gogo* gogo) const
{ return this->real_type()->hash_for_method(gogo); }