compiler: improve write barrier generation
For string, slice, interface values, do assignments field by
field instead of using typedmemmove.
Change-Id: I431ee71c26e7047e8fad495a2994336f617c1705
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/181297
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.h b/go/expressions.h
index 6ba7fe1..22dd2fc 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -237,7 +237,7 @@
// Make an expression that evaluates to some characteristic of an string.
// For simplicity, the enum values must match the field indexes in the
- // underlying struct.
+ // underlying struct. This returns an lvalue.
enum String_info
{
// The underlying data in the string.
@@ -448,7 +448,7 @@
// Make an expression that evaluates to some characteristic of a
// slice. For simplicity, the enum values must match the field indexes
- // in the underlying struct.
+ // in the underlying struct. This returns an lvalue.
enum Slice_info
{
// The underlying data of the slice.
@@ -469,7 +469,7 @@
// Make an expression that evaluates to some characteristic of an
// interface. For simplicity, the enum values must match the field indexes
- // in the underlying struct.
+ // in the underlying struct. This returns an lvalue.
enum Interface_info
{
// The type descriptor of an empty interface.
diff --git a/go/wb.cc b/go/wb.cc
index 2eae08f..47cffee 100644
--- a/go/wb.cc
+++ b/go/wb.cc
@@ -822,6 +822,7 @@
Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type());
lhs = Expression::make_unsafe_cast(unsafe_ptr_type, lhs, loc);
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
Expression* call;
switch (type->base()->classification())
{
@@ -837,17 +838,125 @@
case Type::TYPE_CHANNEL:
{
// These types are all represented by a single pointer.
- Type* uintptr_type = Type::lookup_integer_type("uintptr");
rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
}
break;
case Type::TYPE_STRING:
- case Type::TYPE_STRUCT:
- case Type::TYPE_ARRAY:
+ {
+ // Assign the length field directly.
+ Expression* llen =
+ Expression::make_string_info(indir->copy(),
+ Expression::STRING_INFO_LENGTH,
+ loc);
+ Expression* rlen =
+ Expression::make_string_info(rhs,
+ Expression::STRING_INFO_LENGTH,
+ loc);
+ Statement* as = Statement::make_assignment(llen, rlen, loc);
+ inserter->insert(as);
+
+ // Assign the data field with a write barrier.
+ lhs =
+ Expression::make_string_info(indir->copy(),
+ Expression::STRING_INFO_DATA,
+ loc);
+ rhs =
+ Expression::make_string_info(rhs,
+ Expression::STRING_INFO_DATA,
+ loc);
+ assign = Statement::make_assignment(lhs, rhs, loc);
+ lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
+ rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
+ call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
+ }
+ break;
+
case Type::TYPE_INTERFACE:
{
+ // Assign the first field directly.
+ // The first field is either a type descriptor or a method table.
+ // Type descriptors are either statically created, or created by
+ // the reflect package. For the latter the reflect package keeps
+ // all references.
+ // Method tables are either statically created or persistently
+ // allocated.
+ // In all cases they don't need a write barrier.
+ Expression* ltab =
+ Expression::make_interface_info(indir->copy(),
+ Expression::INTERFACE_INFO_METHODS,
+ loc);
+ Expression* rtab =
+ Expression::make_interface_info(rhs,
+ Expression::INTERFACE_INFO_METHODS,
+ loc);
+ Statement* as = Statement::make_assignment(ltab, rtab, loc);
+ inserter->insert(as);
+
+ // Assign the data field with a write barrier.
+ lhs =
+ Expression::make_interface_info(indir->copy(),
+ Expression::INTERFACE_INFO_OBJECT,
+ loc);
+ rhs =
+ Expression::make_interface_info(rhs,
+ Expression::INTERFACE_INFO_OBJECT,
+ loc);
+ assign = Statement::make_assignment(lhs, rhs, loc);
+ lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
+ rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
+ call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
+ }
+ break;
+
+ case Type::TYPE_ARRAY:
+ if (type->is_slice_type())
+ {
+ // Assign the lenth fields directly.
+ Expression* llen =
+ Expression::make_slice_info(indir->copy(),
+ Expression::SLICE_INFO_LENGTH,
+ loc);
+ Expression* rlen =
+ Expression::make_slice_info(rhs,
+ Expression::SLICE_INFO_LENGTH,
+ loc);
+ Statement* as = Statement::make_assignment(llen, rlen, loc);
+ inserter->insert(as);
+
+ // Assign the capacity fields directly.
+ Expression* lcap =
+ Expression::make_slice_info(indir->copy(),
+ Expression::SLICE_INFO_CAPACITY,
+ loc);
+ Expression* rcap =
+ Expression::make_slice_info(rhs,
+ Expression::SLICE_INFO_CAPACITY,
+ loc);
+ as = Statement::make_assignment(lcap, rcap, loc);
+ inserter->insert(as);
+
+ // Assign the data field with a write barrier.
+ lhs =
+ Expression::make_slice_info(indir->copy(),
+ Expression::SLICE_INFO_VALUE_POINTER,
+ loc);
+ rhs =
+ Expression::make_slice_info(rhs,
+ Expression::SLICE_INFO_VALUE_POINTER,
+ loc);
+ assign = Statement::make_assignment(lhs, rhs, loc);
+ lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
+ rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
+ call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
+ break;
+ }
+ // fallthrough
+
+ case Type::TYPE_STRUCT:
+ {
+ // TODO: split assignments for small struct/array?
rhs = Expression::make_unary(OPERATOR_AND, rhs, loc);
rhs->unary_expression()->set_does_not_escape();
call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3,