From 6fa886d2fa08e864f1e022c6fec70685e9b1a9e8 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 13 Sep 2018 17:49:07 +0000 Subject: [PATCH] compiler: implement //go:nowritebarrierrec Reviewed-on: https://go-review.googlesource.com/134228 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@264283 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/gogo.h | 3 + gcc/go/gofrontend/lex.cc | 8 ++- gcc/go/gofrontend/lex.h | 8 ++- gcc/go/gofrontend/parse.cc | 5 +- gcc/go/gofrontend/wb.cc | 156 ++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 168 insertions(+), 14 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 205ead9d553..3d640b2dc08 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -baf07c40960dc4f8df9da97281870d80d4245b18 +f68c03e509b26e7f483f2800eb70a5fbf3f74d0b 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/gogo.h b/gcc/go/gofrontend/gogo.h index 6511599f95a..0864ee1d72a 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -941,6 +941,9 @@ class Gogo std::vector&, Bfunction* init_bfunction); + void + propagate_writebarrierrec(); + Named_object* write_barrier_variable(); diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc index 560f5f9d34d..3965f30d808 100644 --- a/gcc/go/gofrontend/lex.cc +++ b/gcc/go/gofrontend/lex.cc @@ -1922,9 +1922,15 @@ Lex::skip_cpp_comment() // function that it calls, needs to use any write barriers, it // should emit an error instead. // FIXME: Should only work when compiling the runtime package. - // FIXME: currently treated the same as go:nowritebarrier this->pragmas_ |= GOPRAGMA_NOWRITEBARRIERREC; } + else if (verb == "go:yeswritebarrierrec") + { + // Applies to the next function. Disables go:nowritebarrierrec + // when looking at callees; write barriers are permitted here. + // FIXME: Should only work when compiling the runtime package. + this->pragmas_ |= GOPRAGMA_YESWRITEBARRIERREC; + } else if (verb == "go:cgo_unsafe_args") { // Applies to the next function. Taking the address of any diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h index a8b7091b584..70f752b7326 100644 --- a/gcc/go/gofrontend/lex.h +++ b/gcc/go/gofrontend/lex.h @@ -63,9 +63,11 @@ enum GoPragma GOPRAGMA_SYSTEMSTACK = 1 << 5, // Must run on system stack. GOPRAGMA_NOWRITEBARRIER = 1 << 6, // No write barriers. GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees. - GOPRAGMA_CGOUNSAFEARGS = 1 << 8, // Pointer to arg is pointer to all. - GOPRAGMA_UINTPTRESCAPES = 1 << 9, // uintptr(p) escapes. - GOPRAGMA_NOTINHEAP = 1 << 10 // type is not in heap. + GOPRAGMA_YESWRITEBARRIERREC = 1 << 8, // Stops nowritebarrierrec. + GOPRAGMA_MARK = 1 << 9, // Marker for nowritebarrierrec. + GOPRAGMA_CGOUNSAFEARGS = 1 << 10, // Pointer to arg is pointer to all. + GOPRAGMA_UINTPTRESCAPES = 1 << 11, // uintptr(p) escapes. + GOPRAGMA_NOTINHEAP = 1 << 12 // type is not in heap. }; // A token returned from the lexer. diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 4891e758f79..cc901dbfc84 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -2360,7 +2360,10 @@ Parse::function_decl(unsigned int pragmas) { GOPRAGMA_NOINLINE, "noinline", false, true, true }, { GOPRAGMA_SYSTEMSTACK, "systemstack", false, true, true }, { GOPRAGMA_NOWRITEBARRIER, "nowritebarrier", false, true, true }, - { GOPRAGMA_NOWRITEBARRIERREC, "nowritebarrierrec", false, true, true }, + { GOPRAGMA_NOWRITEBARRIERREC, "nowritebarrierrec", false, true, + true }, + { GOPRAGMA_YESWRITEBARRIERREC, "yeswritebarrierrec", false, true, + true }, { GOPRAGMA_CGOUNSAFEARGS, "cgo_unsafe_args", false, true, true }, { GOPRAGMA_UINTPTRESCAPES, "uintptrescapes", true, true, true }, }; diff --git a/gcc/go/gofrontend/wb.cc b/gcc/go/gofrontend/wb.cc index 58524cdb322..4944b68fccf 100644 --- a/gcc/go/gofrontend/wb.cc +++ b/gcc/go/gofrontend/wb.cc @@ -231,6 +231,133 @@ Check_escape::expression(Expression** pexpr) return TRAVERSE_CONTINUE; } +// Collect all writebarrierrec functions. This is used when compiling +// the runtime package, to propagate //go:nowritebarrierrec. + +class Collect_writebarrierrec_functions : public Traverse +{ + public: + Collect_writebarrierrec_functions(std::vector* worklist) + : Traverse(traverse_functions), + worklist_(worklist) + { } + + private: + int + function(Named_object*); + + // The collected functions are put here. + std::vector* worklist_; +}; + +int +Collect_writebarrierrec_functions::function(Named_object* no) +{ + if (no->is_function() + && no->func_value()->enclosing() == NULL + && (no->func_value()->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0) + { + go_assert((no->func_value()->pragmas() & GOPRAGMA_MARK) == 0); + this->worklist_->push_back(no); + } + return TRAVERSE_CONTINUE; +} + +// Collect all callees of this function. We only care about locally +// defined, known, functions. + +class Collect_callees : public Traverse +{ + public: + Collect_callees(std::vector* worklist) + : Traverse(traverse_expressions), + worklist_(worklist) + { } + + private: + int + expression(Expression**); + + // The collected callees are put here. + std::vector* worklist_; +}; + +int +Collect_callees::expression(Expression** pexpr) +{ + Call_expression* ce = (*pexpr)->call_expression(); + if (ce != NULL) + { + Func_expression* fe = ce->fn()->func_expression(); + if (fe != NULL) + { + Named_object* no = fe->named_object(); + if (no->package() == NULL && no->is_function()) + { + // The function runtime.systemstack is special, in that + // it is a common way to call a function in the runtime: + // mark its argument if we can. + if (Gogo::unpack_hidden_name(no->name()) != "systemstack") + this->worklist_->push_back(no); + else if (ce->args()->size() > 0) + { + fe = ce->args()->front()->func_expression(); + if (fe != NULL) + { + no = fe->named_object(); + if (no->package() == NULL && no->is_function()) + this->worklist_->push_back(no); + } + } + } + } + } + return TRAVERSE_CONTINUE; +} + +// When compiling the runtime package, propagate //go:nowritebarrierrec +// annotations. A function marked as //go:nowritebarrierrec does not +// permit write barriers, and also all the functions that it calls, +// recursively, do not permit write barriers. Except that a +// //go:yeswritebarrierrec annotation permits write barriers even if +// called by a //go:nowritebarrierrec function. Here we turn +// //go:nowritebarrierrec into //go:nowritebarrier, as appropriate. + +void +Gogo::propagate_writebarrierrec() +{ + std::vector worklist; + Collect_writebarrierrec_functions cwf(&worklist); + this->traverse(&cwf); + + Collect_callees cc(&worklist); + + while (!worklist.empty()) + { + Named_object* no = worklist.back(); + worklist.pop_back(); + + unsigned int pragmas = no->func_value()->pragmas(); + if ((pragmas & GOPRAGMA_MARK) != 0) + { + // We've already seen this function. + continue; + } + if ((pragmas & GOPRAGMA_YESWRITEBARRIERREC) != 0) + { + // We don't want to propagate //go:nowritebarrierrec into + // this function or it's callees. + continue; + } + + no->func_value()->set_pragmas(pragmas + | GOPRAGMA_NOWRITEBARRIER + | GOPRAGMA_MARK); + + no->func_value()->traverse(&cc); + } +} + // Add write barriers to the IR. This are required by the concurrent // garbage collector. A write barrier is needed for any write of a // pointer into memory controlled by the garbage collector. Write @@ -492,6 +619,8 @@ Gogo::add_write_barriers() if (this->compiling_runtime() && this->package_name() == "runtime") { + this->propagate_writebarrierrec(); + Check_escape chk(this); this->traverse(&chk); } @@ -536,8 +665,8 @@ Gogo::assign_needs_write_barrier(Expression* lhs) if (!lhs->type()->has_pointer()) return false; - // An assignment to a field is handled like an assignment to the - // struct. + // An assignment to a field or an array index is handled like an + // assignment to the struct. while (true) { // Nothing to do for a type that can not be in the heap, or a @@ -550,9 +679,22 @@ Gogo::assign_needs_write_barrier(Expression* lhs) return false; Field_reference_expression* fre = lhs->field_reference_expression(); - if (fre == NULL) - break; - lhs = fre->expr(); + if (fre != NULL) + { + lhs = fre->expr(); + continue; + } + + Array_index_expression* aie = lhs->array_index_expression(); + if (aie != NULL + && aie->end() == NULL + && !aie->array()->type()->is_slice_type()) + { + lhs = aie->array(); + continue; + } + + break; } // Nothing to do for an assignment to a temporary. @@ -620,9 +762,7 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing, Statement_inserter* inserter, Expression* lhs, Expression* rhs, Location loc) { - if (function != NULL - && ((function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0 - || (function->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0)) + if (function != NULL && (function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0) go_error_at(loc, "write barrier prohibited"); Type* type = lhs->type(); -- 2.11.4.GIT