Commit 269f05ff by Ian Lance Taylor

compiler: make use of specialized fast map routines

    
    In the runtime there are specialized fast map routines for
    certain kep types. This CL lets the compiler make use of these
    functions, instead of always using the generic ones.
    
    As we now generate multiple versions of map delete calls, to make
    things easier we delay the expansion of the built-in delete
    function to flatten phase.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/180858

From-SVN: r271983
parent 91bd9240
2609f9b8420e2341fbbe40d7cf6af42b0fba7293
bc7374913367fba9b10dc284af87eb539fb6c5b2
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -1622,6 +1622,16 @@ Escape_analysis_assign::expression(Expression** pexpr)
}
break;
case Runtime::MAPASSIGN_FAST32PTR:
case Runtime::MAPASSIGN_FAST64PTR:
case Runtime::MAPASSIGN_FASTSTR:
{
// Map key escapes. The last argument is the key.
Node* key_node = Node::make_node(call->args()->back());
this->assign(this->context_->sink(), key_node);
}
break;
case Runtime::IFACEE2T2:
case Runtime::IFACEI2T2:
{
......
......@@ -109,6 +109,18 @@ DEF_GO_RUNTIME(CONSTRUCT_MAP, "__go_construct_map",
DEF_GO_RUNTIME(MAPACCESS1, "runtime.mapaccess1", P3(TYPE, MAP, POINTER),
R1(POINTER))
// Look up a uint32 key in a map.
DEF_GO_RUNTIME(MAPACCESS1_FAST32, "runtime.mapaccess1_fast32",
P3(TYPE, MAP, UINT32), R1(POINTER))
// Look up a uint64 key in a map.
DEF_GO_RUNTIME(MAPACCESS1_FAST64, "runtime.mapaccess1_fast64",
P3(TYPE, MAP, UINT64), R1(POINTER))
// Look up a string key in a map.
DEF_GO_RUNTIME(MAPACCESS1_FASTSTR, "runtime.mapaccess1_faststr",
P3(TYPE, MAP, STRING), R1(POINTER))
// 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))
......@@ -118,6 +130,21 @@ DEF_GO_RUNTIME(MAPACCESS1_FAT, "runtime.mapaccess1_fat",
DEF_GO_RUNTIME(MAPACCESS2, "runtime.mapaccess2", P3(TYPE, MAP, POINTER),
R2(POINTER, BOOL))
// Look up a uint32 key in a map returning the value and whether
// it is present.
DEF_GO_RUNTIME(MAPACCESS2_FAST32, "runtime.mapaccess2_fast32",
P3(TYPE, MAP, UINT32), R2(POINTER, BOOL))
// Look up a uint64 key in a map returning the value and whether
// it is present.
DEF_GO_RUNTIME(MAPACCESS2_FAST64, "runtime.mapaccess2_fast64",
P3(TYPE, MAP, UINT64), R2(POINTER, BOOL))
// Look up a string key in a map returning the value and whether
// it is present.
DEF_GO_RUNTIME(MAPACCESS2_FASTSTR, "runtime.mapaccess2_faststr",
P3(TYPE, MAP, STRING), R2(POINTER, BOOL))
// 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",
......@@ -127,9 +154,41 @@ DEF_GO_RUNTIME(MAPACCESS2_FAT, "runtime.mapaccess2_fat",
DEF_GO_RUNTIME(MAPASSIGN, "runtime.mapassign", P3(TYPE, MAP, POINTER),
R1(POINTER))
// Assignment to a uint32 key in a map.
DEF_GO_RUNTIME(MAPASSIGN_FAST32, "runtime.mapassign_fast32",
P3(TYPE, MAP, UINT32), R1(POINTER))
// Assignment to a uint64 key in a map.
DEF_GO_RUNTIME(MAPASSIGN_FAST64, "runtime.mapassign_fast64",
P3(TYPE, MAP, UINT64), R1(POINTER))
// Assignment to a 32-bit pointer key in a map.
DEF_GO_RUNTIME(MAPASSIGN_FAST32PTR, "runtime.mapassign_fast32ptr",
P3(TYPE, MAP, POINTER), R1(POINTER))
// Assignment to a 64-bit pointer key in a map.
DEF_GO_RUNTIME(MAPASSIGN_FAST64PTR, "runtime.mapassign_fast64ptr",
P3(TYPE, MAP, POINTER), R1(POINTER))
// Assignment to a string key in a map.
DEF_GO_RUNTIME(MAPASSIGN_FASTSTR, "runtime.mapassign_faststr",
P3(TYPE, MAP, STRING), R1(POINTER))
// Delete a key from a map.
DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P3(TYPE, MAP, POINTER), R0())
// Delete a uint32 key from a map.
DEF_GO_RUNTIME(MAPDELETE_FAST32, "runtime.mapdelete_fast32",
P3(TYPE, MAP, UINT32), R0())
// Delete a uint64 key from a map.
DEF_GO_RUNTIME(MAPDELETE_FAST64, "runtime.mapdelete_fast64",
P3(TYPE, MAP, UINT64), R0())
// Delete a string key from a map.
DEF_GO_RUNTIME(MAPDELETE_FASTSTR, "runtime.mapdelete_faststr",
P3(TYPE, MAP, STRING), R0())
// Begin a range over a map.
DEF_GO_RUNTIME(MAPITERINIT, "runtime.mapiterinit", P3(TYPE, MAP, POINTER),
R0())
......
......@@ -816,7 +816,7 @@ Assignment_statement::do_traverse_assignments(Traverse_assignments* tassign)
// call. Mark some slice assignments as not requiring a write barrier.
Statement*
Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
Assignment_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
Statement_inserter*)
{
Map_index_expression* mie = this->lhs_->map_index_expression();
......@@ -864,7 +864,59 @@ Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
Temporary_reference_expression* ref =
Expression::make_temporary_reference(key_temp, loc);
Expression* a3 = Expression::make_unary(OPERATOR_AND, ref, loc);
Expression* call = Runtime::make_call(Runtime::MAPASSIGN, loc, 3,
Runtime::Function code;
Map_type::Map_alg alg = mt->algorithm(gogo);
switch (alg)
{
case Map_type::MAP_ALG_FAST32:
{
code = Runtime::MAPASSIGN_FAST32;
Type* uint32_type = Type::lookup_integer_type("uint32");
Type* uint32_ptr_type = Type::make_pointer_type(uint32_type);
a3 = Expression::make_unsafe_cast(uint32_ptr_type, a3,
loc);
a3 = Expression::make_dereference(a3,
Expression::NIL_CHECK_NOT_NEEDED,
loc);
break;
}
case Map_type::MAP_ALG_FAST64:
{
code = Runtime::MAPASSIGN_FAST64;
Type* uint64_type = Type::lookup_integer_type("uint64");
Type* uint64_ptr_type = Type::make_pointer_type(uint64_type);
a3 = Expression::make_unsafe_cast(uint64_ptr_type, a3,
loc);
a3 = Expression::make_dereference(a3,
Expression::NIL_CHECK_NOT_NEEDED,
loc);
break;
}
case Map_type::MAP_ALG_FAST32PTR:
case Map_type::MAP_ALG_FAST64PTR:
{
code = (alg == Map_type::MAP_ALG_FAST32PTR
? Runtime::MAPASSIGN_FAST32PTR
: Runtime::MAPASSIGN_FAST64PTR);
Type* ptr_type =
Type::make_pointer_type(Type::make_void_type());
Type* ptr_ptr_type = Type::make_pointer_type(ptr_type);
a3 = Expression::make_unsafe_cast(ptr_ptr_type, a3,
loc);
a3 = Expression::make_dereference(a3,
Expression::NIL_CHECK_NOT_NEEDED,
loc);
break;
}
case Map_type::MAP_ALG_FASTSTR:
code = Runtime::MAPASSIGN_FASTSTR;
a3 = ref;
break;
default:
code = Runtime::MAPASSIGN;
break;
}
Expression* call = Runtime::make_call(code, loc, 3,
a1, a2, a3);
Type* ptrval_type = Type::make_pointer_type(mt->val_type());
call = Expression::make_cast(ptrval_type, call, loc);
......@@ -1451,7 +1503,47 @@ Tuple_map_assignment_statement::do_lower(Gogo* gogo, Named_object*,
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);
{
Runtime::Function code;
Map_type::Map_alg alg = map_type->algorithm(gogo);
switch (alg)
{
case Map_type::MAP_ALG_FAST32:
case Map_type::MAP_ALG_FAST32PTR:
{
code = Runtime::MAPACCESS2_FAST32;
Type* uint32_type = Type::lookup_integer_type("uint32");
Type* uint32_ptr_type = Type::make_pointer_type(uint32_type);
a3 = Expression::make_unsafe_cast(uint32_ptr_type, a3,
loc);
a3 = Expression::make_dereference(a3,
Expression::NIL_CHECK_NOT_NEEDED,
loc);
break;
}
case Map_type::MAP_ALG_FAST64:
case Map_type::MAP_ALG_FAST64PTR:
{
code = Runtime::MAPACCESS2_FAST64;
Type* uint64_type = Type::lookup_integer_type("uint64");
Type* uint64_ptr_type = Type::make_pointer_type(uint64_type);
a3 = Expression::make_unsafe_cast(uint64_ptr_type, a3,
loc);
a3 = Expression::make_dereference(a3,
Expression::NIL_CHECK_NOT_NEEDED,
loc);
break;
}
case Map_type::MAP_ALG_FASTSTR:
code = Runtime::MAPACCESS2_FASTSTR;
a3 = ref;
break;
default:
code = Runtime::MAPACCESS2;
break;
}
call = Runtime::make_call(code, 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);
......@@ -6325,47 +6417,20 @@ For_range_statement::lower_map_range_clear(Type* map_type,
if (enclosing->bindings()->lookup_local(index_no->name()) != index_no)
return NULL;
// Match the body. When lowering the builtin delete function, we have
// inserted temporaries, so we actually match for
//
// tmp1 = m
// tmp2 = k
// runtime.mapdelete(TYPE, tmp1, &tmp2)
// Match the body, a single call statement delete(m, k).
const std::vector<Statement*>* statements = this->statements_->statements();
if (statements->size() != 3)
return NULL;
Temporary_statement* ts1 = statements->at(0)->temporary_statement();
Temporary_statement* ts2 = statements->at(1)->temporary_statement();
Expression_statement* es3 = statements->at(2)->expression_statement();
if (ts1 == NULL || ts2 == NULL || es3 == NULL
|| !Expression::is_same_variable(orig_range_expr, ts1->init())
|| !Expression::is_same_variable(this->index_var_, ts2->init()))
return NULL;
Call_expression* call = es3->expr()->call_expression();
if (call == NULL)
return NULL;
Func_expression* fe = call->fn()->func_expression();
if (fe == NULL || !fe->is_runtime_function()
|| fe->runtime_code() != Runtime::MAPDELETE)
if (statements->size() != 1)
return NULL;
Expression* a1 = call->args()->at(1);
a1 = (a1->unsafe_conversion_expression() != NULL
? a1->unsafe_conversion_expression()->expr()
: a1);
Temporary_reference_expression* tre = a1->temporary_reference_expression();
if (tre == NULL || tre->statement() != ts1)
Expression_statement* es = statements->at(0)->expression_statement();
if (es == NULL)
return NULL;
Expression* a2 = call->args()->at(2);
a2 = (a2->conversion_expression() != NULL
? a2->conversion_expression()->expr()
: a2);
Unary_expression* ue = a2->unary_expression();
if (ue == NULL || ue->op() != OPERATOR_AND)
Call_expression* call = es->expr()->call_expression();
if (call == NULL || !call->is_builtin()
|| call->builtin_call_expression()->code()
!= Builtin_call_expression::BUILTIN_DELETE)
return NULL;
tre = ue->operand()->temporary_reference_expression();
if (tre == NULL || tre->statement() != ts2)
if (!Expression::is_same_variable(call->args()->at(0), orig_range_expr)
|| !Expression::is_same_variable(call->args()->at(1), this->index_var_))
return NULL;
// Everything matches. Rewrite to mapclear(TYPE, MAP).
......
......@@ -7890,7 +7890,7 @@ int64_t Map_type::zero_value_align;
// 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.
// must match maxZero in libgo/go/runtime/map.go.
Expression*
Map_type::fat_zero_value(Gogo* gogo)
......@@ -7938,6 +7938,43 @@ Map_type::fat_zero_value(Gogo* gogo)
return z;
}
// Map algorithm to use for this map type.
Map_type::Map_alg
Map_type::algorithm(Gogo* gogo)
{
int64_t size;
bool ok = this->val_type_->backend_type_size(gogo, &size);
if (!ok || size > Map_type::max_val_size)
return MAP_ALG_SLOW;
Type* key_type = this->key_type_;
if (key_type->is_string_type())
return MAP_ALG_FASTSTR;
if (!key_type->compare_is_identity(gogo))
return MAP_ALG_SLOW;
ok = key_type->backend_type_size(gogo, &size);
if (!ok)
return MAP_ALG_SLOW;
if (size == 4)
return (key_type->has_pointer()
? MAP_ALG_FAST32PTR
: MAP_ALG_FAST32);
if (size == 8)
{
if (!key_type->has_pointer())
return MAP_ALG_FAST64;
Type* ptr_type = Type::make_pointer_type(Type::make_void_type());
ok = ptr_type->backend_type_size(gogo, &size);
if (ok && size == 8)
return MAP_ALG_FAST64PTR;
// Key contains pointer but is not a single pointer.
// Use slow version.
}
return MAP_ALG_SLOW;
}
// Return whether VAR is the map zero value.
bool
......@@ -8027,7 +8064,7 @@ Map_type::do_hash_for_method(Gogo* gogo, int flags) const
// Get the backend representation for a map type. A map type is
// represented as a pointer to a struct. The struct is hmap in
// runtime/hashmap.go.
// runtime/map.go.
Btype*
Map_type::do_get_backend(Gogo* gogo)
......@@ -8233,7 +8270,7 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name)
}
// Return the bucket type to use for a map type. This must correspond
// to libgo/go/runtime/hashmap.go.
// to libgo/go/runtime/map.go.
Type*
Map_type::bucket_type(Gogo* gogo, int64_t keysize, int64_t valsize)
......@@ -8265,7 +8302,7 @@ Map_type::bucket_type(Gogo* gogo, int64_t keysize, int64_t valsize)
// 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.
// libgo/go/runtime/map.go.
Type* overflow_type;
if (!key_type->has_pointer() && !val_type->has_pointer())
overflow_type = Type::lookup_integer_type("uintptr");
......
......@@ -2912,6 +2912,27 @@ class Map_type : public Type
Expression*
fat_zero_value(Gogo*);
// Map algorithm to use for this map type. We may use specialized
// fast map routines for certain key types.
enum Map_alg
{
// 32-bit key.
MAP_ALG_FAST32,
// 32-bit pointer key.
MAP_ALG_FAST32PTR,
// 64-bit key.
MAP_ALG_FAST64,
// 64-bit pointer key.
MAP_ALG_FAST64PTR,
// String key.
MAP_ALG_FASTSTR,
// Anything else.
MAP_ALG_SLOW,
};
Map_alg
algorithm(Gogo*);
// Return whether VAR is the map zero value.
static bool
is_zero_value(Variable* var);
......@@ -2931,7 +2952,7 @@ class Map_type : public Type
static Type*
make_map_type_descriptor_type();
// This must be in sync with libgo/go/runtime/hashmap.go.
// This must be in sync with libgo/go/runtime/map.go.
static const int bucket_size = 8;
protected:
......@@ -2974,7 +2995,7 @@ class Map_type : public Type
do_export(Export*) const;
private:
// These must be in sync with libgo/go/runtime/hashmap.go.
// These must be in sync with libgo/go/runtime/map.go.
static const int max_key_size = 128;
static const int max_val_size = 128;
static const int max_zero_size = 1024;
......
......@@ -9,6 +9,15 @@ import (
"unsafe"
)
// For gccgo, use go:linkname to rename compiler-called functions to
// themselves, so that the compiler will export them.
//
//go:linkname mapaccess1_fast32 runtime.mapaccess1_fast32
//go:linkname mapaccess2_fast32 runtime.mapaccess2_fast32
//go:linkname mapassign_fast32 runtime.mapassign_fast32
//go:linkname mapassign_fast32ptr runtime.mapassign_fast32ptr
//go:linkname mapdelete_fast32 runtime.mapdelete_fast32
func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
if raceenabled && h != nil {
callerpc := getcallerpc()
......
......@@ -9,6 +9,15 @@ import (
"unsafe"
)
// For gccgo, use go:linkname to rename compiler-called functions to
// themselves, so that the compiler will export them.
//
//go:linkname mapaccess1_fast64 runtime.mapaccess1_fast64
//go:linkname mapaccess2_fast64 runtime.mapaccess2_fast64
//go:linkname mapassign_fast64 runtime.mapassign_fast64
//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr
//go:linkname mapdelete_fast64 runtime.mapdelete_fast64
func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
if raceenabled && h != nil {
callerpc := getcallerpc()
......
......@@ -9,6 +9,14 @@ import (
"unsafe"
)
// For gccgo, use go:linkname to rename compiler-called functions to
// themselves, so that the compiler will export them.
//
//go:linkname mapaccess1_faststr runtime.mapaccess1_faststr
//go:linkname mapaccess2_faststr runtime.mapaccess2_faststr
//go:linkname mapassign_faststr runtime.mapassign_faststr
//go:linkname mapdelete_faststr runtime.mapdelete_faststr
func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
if raceenabled && h != nil {
callerpc := getcallerpc()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment