compiler: move some escape check to Mark_address_taken
[official-gcc.git] / gcc / go / gofrontend / wb.cc
blob77a59ac4c7c260a76803521e0b4dba3485991ff3
1 // wb.cc -- Add write barriers as needed.
3 // Copyright 2017 The Go Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
7 #include "go-system.h"
9 #include "go-c.h"
10 #include "go-diagnostics.h"
11 #include "operator.h"
12 #include "lex.h"
13 #include "types.h"
14 #include "expressions.h"
15 #include "statements.h"
16 #include "runtime.h"
17 #include "gogo.h"
19 // Mark variables whose addresses are taken. This has to be done
20 // before the write barrier pass and after the escape analysis pass.
21 // It would be nice to do this elsewhere but there isn't an obvious
22 // place.
24 class Mark_address_taken : public Traverse
26 public:
27 Mark_address_taken(Gogo* gogo)
28 : Traverse(traverse_expressions),
29 gogo_(gogo)
30 { }
32 int
33 expression(Expression**);
35 private:
36 Gogo* gogo_;
39 // Mark variable addresses taken.
41 int
42 Mark_address_taken::expression(Expression** pexpr)
44 Expression* expr = *pexpr;
45 Unary_expression* ue = expr->unary_expression();
46 if (ue != NULL)
47 ue->check_operand_address_taken(this->gogo_);
49 Array_index_expression* aie = expr->array_index_expression();
50 if (aie != NULL
51 && aie->end() != NULL
52 && !aie->array()->type()->is_slice_type())
54 // Slice of an array. The escape analysis models this with
55 // a child Node representing the address of the array.
56 bool escapes = false;
57 if (!this->gogo_->compiling_runtime()
58 || this->gogo_->package_name() != "runtime")
60 Node* n = Node::make_node(expr);
61 if (n->child() == NULL
62 || (n->child()->encoding() & ESCAPE_MASK) != Node::ESCAPE_NONE)
63 escapes = true;
65 aie->array()->address_taken(escapes);
68 if (expr->allocation_expression() != NULL)
70 Node* n = Node::make_node(expr);
71 if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
72 expr->allocation_expression()->set_allocate_on_stack();
74 if (expr->heap_expression() != NULL)
76 Node* n = Node::make_node(expr);
77 if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
78 expr->heap_expression()->set_allocate_on_stack();
80 if (expr->slice_literal() != NULL)
82 Node* n = Node::make_node(expr);
83 if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
84 expr->slice_literal()->set_storage_does_not_escape();
87 // Rewrite non-escaping makeslice with constant size to stack allocation.
88 Unsafe_type_conversion_expression* uce =
89 expr->unsafe_conversion_expression();
90 if (uce != NULL
91 && uce->type()->is_slice_type()
92 && Node::make_node(uce->expr())->encoding() == Node::ESCAPE_NONE
93 && uce->expr()->call_expression() != NULL)
95 Call_expression* call = uce->expr()->call_expression();
96 if (call->fn()->func_expression() != NULL
97 && call->fn()->func_expression()->runtime_code() == Runtime::MAKESLICE)
99 Expression* len_arg = call->args()->at(1);
100 Expression* cap_arg = call->args()->at(2);
101 Numeric_constant nclen;
102 Numeric_constant nccap;
103 unsigned long vlen;
104 unsigned long vcap;
105 if (len_arg->numeric_constant_value(&nclen)
106 && cap_arg->numeric_constant_value(&nccap)
107 && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID
108 && nccap.to_unsigned_long(&vcap) == Numeric_constant::NC_UL_VALID)
110 // Turn it into a slice expression of an addressable array,
111 // which is allocated on stack.
112 Location loc = expr->location();
113 Type* elmt_type = expr->type()->array_type()->element_type();
114 Expression* len_expr =
115 Expression::make_integer_ul(vcap, cap_arg->type(), loc);
116 Type* array_type = Type::make_array_type(elmt_type, len_expr);
117 Expression* alloc = Expression::make_allocation(array_type, loc);
118 alloc->allocation_expression()->set_allocate_on_stack();
119 Expression* array = Expression::make_unary(OPERATOR_MULT, alloc, loc);
120 Expression* zero = Expression::make_integer_ul(0, len_arg->type(), loc);
121 Expression* slice =
122 Expression::make_array_index(array, zero, len_arg, cap_arg, loc);
123 *pexpr = slice;
127 return TRAVERSE_CONTINUE;
130 // Add write barriers to the IR. This are required by the concurrent
131 // garbage collector. A write barrier is needed for any write of a
132 // pointer into memory controlled by the garbage collector. Write
133 // barriers are not required for writes to local variables that live
134 // on the stack. Write barriers are only required when the runtime
135 // enables them, which can be checked using a run time check on
136 // runtime.writeBarrier.enabled.
138 // Essentially, for each assignment A = B, where A is or contains a
139 // pointer, and where A is not, or at any rate may not be, a stack
140 // variable, we rewrite it into
141 // if runtime.writeBarrier.enabled {
142 // typedmemmove(typeof(A), &A, &B)
143 // } else {
144 // A = B
145 // }
147 // The test of runtime.writeBarrier.Enabled is implemented by treating
148 // the variable as a *uint32, and testing *runtime.writeBarrier != 0.
149 // This is compatible with the definition in the runtime package.
151 // For types that are pointer shared (pointers, maps, chans, funcs),
152 // we replaced the call to typedmemmove with writebarrierptr(&A, B).
153 // As far as the GC is concerned, all pointers are the same, so it
154 // doesn't need the type descriptor.
156 // There are possible optimizations that are not implemented.
158 // runtime.writeBarrier can only change when the goroutine is
159 // preempted, which in practice means when a call is made into the
160 // runtime package, so we could optimize by only testing it once
161 // between function calls.
163 // A slice could be handled with a call to writebarrierptr plus two
164 // integer moves.
166 // Traverse the IR adding write barriers.
168 class Write_barriers : public Traverse
170 public:
171 Write_barriers(Gogo* gogo)
172 : Traverse(traverse_functions | traverse_variables | traverse_statements),
173 gogo_(gogo), function_(NULL)
177 function(Named_object*);
180 variable(Named_object*);
183 statement(Block*, size_t* pindex, Statement*);
185 private:
186 // General IR.
187 Gogo* gogo_;
188 // Current function.
189 Function* function_;
192 // Traverse a function. Just record it for later.
195 Write_barriers::function(Named_object* no)
197 go_assert(this->function_ == NULL);
198 this->function_ = no->func_value();
199 int t = this->function_->traverse(this);
200 this->function_ = NULL;
202 if (t == TRAVERSE_EXIT)
203 return t;
204 return TRAVERSE_SKIP_COMPONENTS;
207 // Insert write barriers for a global variable: ensure that variable
208 // initialization is handled correctly. This is rarely needed, since
209 // we currently don't enable background GC until after all global
210 // variables are initialized. But we do need this if an init function
211 // calls runtime.GC.
214 Write_barriers::variable(Named_object* no)
216 // We handle local variables in the variable declaration statement.
217 // We only have to handle global variables here.
218 if (!no->is_variable())
219 return TRAVERSE_CONTINUE;
220 Variable* var = no->var_value();
221 if (!var->is_global())
222 return TRAVERSE_CONTINUE;
224 // Nothing to do if there is no initializer.
225 Expression* init = var->init();
226 if (init == NULL)
227 return TRAVERSE_CONTINUE;
229 // Nothing to do for variables that do not contain any pointers.
230 if (!var->type()->has_pointer())
231 return TRAVERSE_CONTINUE;
233 // Nothing to do if the initializer is static.
234 init = Expression::make_cast(var->type(), init, var->location());
235 if (!var->has_pre_init() && init->is_static_initializer())
236 return TRAVERSE_CONTINUE;
238 // Nothing to do for a type that can not be in the heap, or a
239 // pointer to a type that can not be in the heap.
240 if (!var->type()->in_heap())
241 return TRAVERSE_CONTINUE;
242 if (var->type()->points_to() != NULL && !var->type()->points_to()->in_heap())
243 return TRAVERSE_CONTINUE;
245 // Otherwise change the initializer into a pre_init assignment
246 // statement with a write barrier.
248 // We can't check for a dependency of the variable on itself after
249 // we make this change, because the preinit statement will always
250 // depend on the variable (since it assigns to it). So check for a
251 // self-dependency now.
252 this->gogo_->check_self_dep(no);
254 // Replace the initializer.
255 Location loc = init->location();
256 Expression* ref = Expression::make_var_reference(no, loc);
258 Statement_inserter inserter(this->gogo_, var);
259 Statement* s = this->gogo_->assign_with_write_barrier(NULL, NULL, &inserter,
260 ref, init, loc);
262 var->add_preinit_statement(this->gogo_, s);
263 var->clear_init();
265 return TRAVERSE_CONTINUE;
268 // Insert write barriers for statements.
271 Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
273 switch (s->classification())
275 default:
276 break;
278 case Statement::STATEMENT_VARIABLE_DECLARATION:
280 Variable_declaration_statement* vds =
281 s->variable_declaration_statement();
282 Named_object* no = vds->var();
283 Variable* var = no->var_value();
285 // We may need to emit a write barrier for the initialization
286 // of the variable.
288 // Nothing to do for a variable with no initializer.
289 Expression* init = var->init();
290 if (init == NULL)
291 break;
293 // Nothing to do if the variable is not in the heap. Only
294 // local variables get declaration statements, and local
295 // variables on the stack do not require write barriers.
296 if (!var->is_in_heap())
297 break;
299 // Nothing to do if the variable does not contain any pointers.
300 if (!var->type()->has_pointer())
301 break;
303 // Nothing to do for a type that can not be in the heap, or a
304 // pointer to a type that can not be in the heap.
305 if (!var->type()->in_heap())
306 break;
307 if (var->type()->points_to() != NULL
308 && !var->type()->points_to()->in_heap())
309 break;
311 // Otherwise initialize the variable with a write barrier.
313 Function* function = this->function_;
314 Location loc = init->location();
315 Statement_inserter inserter(block, pindex);
317 // Insert the variable declaration statement with no
318 // initializer, so that the variable exists.
319 var->clear_init();
320 inserter.insert(s);
322 // Create a statement that initializes the variable with a
323 // write barrier.
324 Expression* ref = Expression::make_var_reference(no, loc);
325 Statement* assign = this->gogo_->assign_with_write_barrier(function,
326 block,
327 &inserter,
328 ref, init,
329 loc);
331 // Replace the old variable declaration statement with the new
332 // initialization.
333 block->replace_statement(*pindex, assign);
335 break;
337 case Statement::STATEMENT_ASSIGNMENT:
339 Assignment_statement* as = s->assignment_statement();
340 Expression* lhs = as->lhs();
341 Expression* rhs = as->rhs();
343 // We may need to emit a write barrier for the assignment.
345 if (!this->gogo_->assign_needs_write_barrier(lhs))
346 break;
348 // Change the assignment to use a write barrier.
349 Function* function = this->function_;
350 Location loc = as->location();
351 Statement_inserter inserter = Statement_inserter(block, pindex);
352 Statement* assign = this->gogo_->assign_with_write_barrier(function,
353 block,
354 &inserter,
355 lhs, rhs,
356 loc);
357 block->replace_statement(*pindex, assign);
359 break;
362 return TRAVERSE_CONTINUE;
365 // The write barrier pass.
367 void
368 Gogo::add_write_barriers()
370 Mark_address_taken mat(this);
371 this->traverse(&mat);
373 Write_barriers wb(this);
374 this->traverse(&wb);
377 // Return the runtime.writeBarrier variable.
379 Named_object*
380 Gogo::write_barrier_variable()
382 static Named_object* write_barrier_var;
383 if (write_barrier_var == NULL)
385 Location bloc = Linemap::predeclared_location();
387 // We pretend that writeBarrier is a uint32, so that we do a
388 // 32-bit load. That is what the gc toolchain does.
389 Type* uint32_type = Type::lookup_integer_type("uint32");
390 Variable* var = new Variable(uint32_type, NULL, true, false, false,
391 bloc);
393 bool add_to_globals;
394 Package* package = this->add_imported_package("runtime", "_", false,
395 "runtime", "runtime",
396 bloc, &add_to_globals);
397 write_barrier_var = Named_object::make_variable("writeBarrier",
398 package, var);
401 return write_barrier_var;
404 // Return whether an assignment that sets LHS needs a write barrier.
406 bool
407 Gogo::assign_needs_write_barrier(Expression* lhs)
409 // Nothing to do if the variable does not contain any pointers.
410 if (!lhs->type()->has_pointer())
411 return false;
413 // An assignment to a field is handled like an assignment to the
414 // struct.
415 while (true)
417 // Nothing to do for a type that can not be in the heap, or a
418 // pointer to a type that can not be in the heap. We check this
419 // at each level of a struct.
420 if (!lhs->type()->in_heap())
421 return false;
422 if (lhs->type()->points_to() != NULL
423 && !lhs->type()->points_to()->in_heap())
424 return false;
426 Field_reference_expression* fre = lhs->field_reference_expression();
427 if (fre == NULL)
428 break;
429 lhs = fre->expr();
432 // Nothing to do for an assignment to a temporary.
433 if (lhs->temporary_reference_expression() != NULL)
434 return false;
436 // Nothing to do for an assignment to a sink.
437 if (lhs->is_sink_expression())
438 return false;
440 // Nothing to do for an assignment to a local variable that is not
441 // on the heap.
442 Var_expression* ve = lhs->var_expression();
443 if (ve != NULL)
445 Named_object* no = ve->named_object();
446 if (no->is_variable())
448 Variable* var = no->var_value();
449 if (!var->is_global() && !var->is_in_heap())
450 return false;
452 else if (no->is_result_variable())
454 Result_variable* rvar = no->result_var_value();
455 if (!rvar->is_in_heap())
456 return false;
460 // For a struct assignment, we don't need a write barrier if all the
461 // pointer types can not be in the heap.
462 Struct_type* st = lhs->type()->struct_type();
463 if (st != NULL)
465 bool in_heap = false;
466 const Struct_field_list* fields = st->fields();
467 for (Struct_field_list::const_iterator p = fields->begin();
468 p != fields->end();
469 p++)
471 Type* ft = p->type();
472 if (!ft->has_pointer())
473 continue;
474 if (!ft->in_heap())
475 continue;
476 if (ft->points_to() != NULL && !ft->points_to()->in_heap())
477 continue;
478 in_heap = true;
479 break;
481 if (!in_heap)
482 return false;
485 // Write barrier needed in other cases.
486 return true;
489 // Return a statement that sets LHS to RHS using a write barrier.
490 // ENCLOSING is the enclosing block.
492 Statement*
493 Gogo::assign_with_write_barrier(Function* function, Block* enclosing,
494 Statement_inserter* inserter, Expression* lhs,
495 Expression* rhs, Location loc)
497 if (function != NULL
498 && ((function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0
499 || (function->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0))
500 go_error_at(loc, "write barrier prohibited");
502 Type* type = lhs->type();
503 go_assert(type->has_pointer());
505 Expression* addr;
506 if (lhs->unary_expression() != NULL
507 && lhs->unary_expression()->op() == OPERATOR_MULT)
508 addr = lhs->unary_expression()->operand();
509 else
511 addr = Expression::make_unary(OPERATOR_AND, lhs, loc);
512 addr->unary_expression()->set_does_not_escape();
514 Temporary_statement* lhs_temp = Statement::make_temporary(NULL, addr, loc);
515 inserter->insert(lhs_temp);
516 lhs = Expression::make_temporary_reference(lhs_temp, loc);
518 if (!Type::are_identical(type, rhs->type(), false, NULL)
519 && rhs->type()->interface_type() != NULL
520 && !rhs->is_variable())
522 // May need a temporary for interface conversion.
523 Temporary_statement* temp = Statement::make_temporary(NULL, rhs, loc);
524 inserter->insert(temp);
525 rhs = Expression::make_temporary_reference(temp, loc);
527 rhs = Expression::convert_for_assignment(this, type, rhs, loc);
528 Temporary_statement* rhs_temp = NULL;
529 if (!rhs->is_variable() && !rhs->is_constant())
531 rhs_temp = Statement::make_temporary(NULL, rhs, loc);
532 inserter->insert(rhs_temp);
533 rhs = Expression::make_temporary_reference(rhs_temp, loc);
536 Expression* indir =
537 Expression::make_dereference(lhs, Expression::NIL_CHECK_DEFAULT, loc);
538 Statement* assign = Statement::make_assignment(indir, rhs, loc);
540 lhs = Expression::make_temporary_reference(lhs_temp, loc);
541 if (rhs_temp != NULL)
542 rhs = Expression::make_temporary_reference(rhs_temp, loc);
544 Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type());
545 lhs = Expression::make_unsafe_cast(unsafe_ptr_type, lhs, loc);
547 Expression* call;
548 switch (type->base()->classification())
550 default:
551 go_unreachable();
553 case Type::TYPE_ERROR:
554 return assign;
556 case Type::TYPE_POINTER:
557 case Type::TYPE_FUNCTION:
558 case Type::TYPE_MAP:
559 case Type::TYPE_CHANNEL:
560 // These types are all represented by a single pointer.
561 call = Runtime::make_call(Runtime::WRITEBARRIERPTR, loc, 2, lhs, rhs);
562 break;
564 case Type::TYPE_STRING:
565 case Type::TYPE_STRUCT:
566 case Type::TYPE_ARRAY:
567 case Type::TYPE_INTERFACE:
569 rhs = Expression::make_unary(OPERATOR_AND, rhs, loc);
570 rhs->unary_expression()->set_does_not_escape();
571 call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3,
572 Expression::make_type_descriptor(type, loc),
573 lhs, rhs);
575 break;
578 return this->check_write_barrier(enclosing, assign,
579 Statement::make_statement(call, false));
582 // Return a statement that tests whether write barriers are enabled
583 // and executes either the efficient code or the write barrier
584 // function call, depending.
586 Statement*
587 Gogo::check_write_barrier(Block* enclosing, Statement* without,
588 Statement* with)
590 Location loc = without->location();
591 Named_object* wb = this->write_barrier_variable();
592 Expression* ref = Expression::make_var_reference(wb, loc);
593 Expression* zero = Expression::make_integer_ul(0, ref->type(), loc);
594 Expression* cond = Expression::make_binary(OPERATOR_EQEQ, ref, zero, loc);
596 Block* then_block = new Block(enclosing, loc);
597 then_block->add_statement(without);
599 Block* else_block = new Block(enclosing, loc);
600 else_block->add_statement(with);
602 return Statement::make_if_statement(cond, then_block, else_block, loc);