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
merge done from the gofrontend repository.
......@@ -941,6 +941,9 @@ class Gogo
std::vector<Bstatement*>&,
Bfunction* init_bfunction);
void
propagate_writebarrierrec();
Named_object*
write_barrier_variable();
......
......@@ -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
......
......@@ -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.
......
......@@ -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 },
};
......
......@@ -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<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
// 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();
......
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