Commit 9fd1ec33 by Ian Lance Taylor

compiler: implement //go:nowritebarrierrec

    
    Reviewed-on: https://go-review.googlesource.com/134228

From-SVN: r264283
parent f0d89c77
baf07c40960dc4f8df9da97281870d80d4245b18 f68c03e509b26e7f483f2800eb70a5fbf3f74d0b
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -941,6 +941,9 @@ class Gogo ...@@ -941,6 +941,9 @@ class Gogo
std::vector<Bstatement*>&, std::vector<Bstatement*>&,
Bfunction* init_bfunction); Bfunction* init_bfunction);
void
propagate_writebarrierrec();
Named_object* Named_object*
write_barrier_variable(); write_barrier_variable();
......
...@@ -1922,9 +1922,15 @@ Lex::skip_cpp_comment() ...@@ -1922,9 +1922,15 @@ Lex::skip_cpp_comment()
// function that it calls, needs to use any write barriers, it // function that it calls, needs to use any write barriers, it
// should emit an error instead. // should emit an error instead.
// FIXME: Should only work when compiling the runtime package. // FIXME: Should only work when compiling the runtime package.
// FIXME: currently treated the same as go:nowritebarrier
this->pragmas_ |= GOPRAGMA_NOWRITEBARRIERREC; 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") else if (verb == "go:cgo_unsafe_args")
{ {
// Applies to the next function. Taking the address of any // Applies to the next function. Taking the address of any
......
...@@ -63,9 +63,11 @@ enum GoPragma ...@@ -63,9 +63,11 @@ enum GoPragma
GOPRAGMA_SYSTEMSTACK = 1 << 5, // Must run on system stack. GOPRAGMA_SYSTEMSTACK = 1 << 5, // Must run on system stack.
GOPRAGMA_NOWRITEBARRIER = 1 << 6, // No write barriers. GOPRAGMA_NOWRITEBARRIER = 1 << 6, // No write barriers.
GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees. GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees.
GOPRAGMA_CGOUNSAFEARGS = 1 << 8, // Pointer to arg is pointer to all. GOPRAGMA_YESWRITEBARRIERREC = 1 << 8, // Stops nowritebarrierrec.
GOPRAGMA_UINTPTRESCAPES = 1 << 9, // uintptr(p) escapes. GOPRAGMA_MARK = 1 << 9, // Marker for nowritebarrierrec.
GOPRAGMA_NOTINHEAP = 1 << 10 // type is not in heap. 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. // A token returned from the lexer.
......
...@@ -2360,7 +2360,10 @@ Parse::function_decl(unsigned int pragmas) ...@@ -2360,7 +2360,10 @@ Parse::function_decl(unsigned int pragmas)
{ GOPRAGMA_NOINLINE, "noinline", false, true, true }, { GOPRAGMA_NOINLINE, "noinline", false, true, true },
{ GOPRAGMA_SYSTEMSTACK, "systemstack", false, true, true }, { GOPRAGMA_SYSTEMSTACK, "systemstack", false, true, true },
{ GOPRAGMA_NOWRITEBARRIER, "nowritebarrier", 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_CGOUNSAFEARGS, "cgo_unsafe_args", false, true, true },
{ GOPRAGMA_UINTPTRESCAPES, "uintptrescapes", true, true, true }, { GOPRAGMA_UINTPTRESCAPES, "uintptrescapes", true, true, true },
}; };
......
...@@ -231,6 +231,133 @@ Check_escape::expression(Expression** pexpr) ...@@ -231,6 +231,133 @@ Check_escape::expression(Expression** pexpr)
return TRAVERSE_CONTINUE; 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<Named_object*>* worklist)
: Traverse(traverse_functions),
worklist_(worklist)
{ }
private:
int
function(Named_object*);
// The collected functions are put here.
std::vector<Named_object*>* 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<Named_object*>* worklist)
: Traverse(traverse_expressions),
worklist_(worklist)
{ }
private:
int
expression(Expression**);
// The collected callees are put here.
std::vector<Named_object*>* 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<Named_object*> 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 // Add write barriers to the IR. This are required by the concurrent
// garbage collector. A write barrier is needed for any write of a // garbage collector. A write barrier is needed for any write of a
// pointer into memory controlled by the garbage collector. Write // pointer into memory controlled by the garbage collector. Write
...@@ -492,6 +619,8 @@ Gogo::add_write_barriers() ...@@ -492,6 +619,8 @@ Gogo::add_write_barriers()
if (this->compiling_runtime() && this->package_name() == "runtime") if (this->compiling_runtime() && this->package_name() == "runtime")
{ {
this->propagate_writebarrierrec();
Check_escape chk(this); Check_escape chk(this);
this->traverse(&chk); this->traverse(&chk);
} }
...@@ -536,8 +665,8 @@ Gogo::assign_needs_write_barrier(Expression* lhs) ...@@ -536,8 +665,8 @@ Gogo::assign_needs_write_barrier(Expression* lhs)
if (!lhs->type()->has_pointer()) if (!lhs->type()->has_pointer())
return false; return false;
// An assignment to a field is handled like an assignment to the // An assignment to a field or an array index is handled like an
// struct. // assignment to the struct.
while (true) while (true)
{ {
// Nothing to do for a type that can not be in the heap, or a // 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) ...@@ -550,9 +679,22 @@ Gogo::assign_needs_write_barrier(Expression* lhs)
return false; return false;
Field_reference_expression* fre = lhs->field_reference_expression(); Field_reference_expression* fre = lhs->field_reference_expression();
if (fre == NULL) if (fre != NULL)
break; {
lhs = fre->expr(); 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. // Nothing to do for an assignment to a temporary.
...@@ -620,9 +762,7 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing, ...@@ -620,9 +762,7 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing,
Statement_inserter* inserter, Expression* lhs, Statement_inserter* inserter, Expression* lhs,
Expression* rhs, Location loc) Expression* rhs, Location loc)
{ {
if (function != NULL if (function != NULL && (function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0)
&& ((function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0
|| (function->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0))
go_error_at(loc, "write barrier prohibited"); go_error_at(loc, "write barrier prohibited");
Type* type = lhs->type(); Type* type = lhs->type();
......
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