{ 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 },
};
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
if (this->compiling_runtime() && this->package_name() == "runtime")
{
+ this->propagate_writebarrierrec();
+
Check_escape chk(this);
this->traverse(&chk);
}
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
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.
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();