From 1afab7a878555956f4842dcbed19bfe3c1c28a6e Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 7 Jun 2019 21:16:11 +0000 Subject: [PATCH] compiler: improve write barrier generation For string, slice, interface values, do assignments field by field instead of using typedmemmove. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/181297 From-SVN: r272055 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.h | 6 +- gcc/go/gofrontend/wb.cc | 115 +++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 7 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index e107d529eb6..91b85c0fecd 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -9df825b5f142ac2b6f48a8dac94fcff740acd411 +b79e9e79fddc9040ab58c7c518eb08454f308def 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/expressions.h b/gcc/go/gofrontend/expressions.h index 6ba7fe1ee7c..22dd2fc4c1d 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -237,7 +237,7 @@ class Expression // 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 @@ class Expression // 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 @@ class Expression // 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/gcc/go/gofrontend/wb.cc b/gcc/go/gofrontend/wb.cc index 2eae08f7bdc..47cffee8db8 100644 --- a/gcc/go/gofrontend/wb.cc +++ b/gcc/go/gofrontend/wb.cc @@ -822,6 +822,7 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing, 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 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing, 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, -- 2.30.2