111
|
1 // wb.cc -- Add write barriers as needed.
|
|
2
|
|
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.
|
|
6
|
|
7 #include "go-system.h"
|
|
8
|
|
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"
|
|
18
|
131
|
19 // Mark variables whose addresses are taken and do some other
|
|
20 // cleanups. This has to be done before the write barrier pass and
|
|
21 // after the escape analysis pass. It would be nice to do this
|
|
22 // elsewhere but there isn't an obvious place.
|
111
|
23
|
|
24 class Mark_address_taken : public Traverse
|
|
25 {
|
|
26 public:
|
|
27 Mark_address_taken(Gogo* gogo)
|
131
|
28 : Traverse(traverse_functions
|
|
29 | traverse_statements
|
|
30 | traverse_expressions),
|
|
31 gogo_(gogo), function_(NULL)
|
111
|
32 { }
|
|
33
|
|
34 int
|
131
|
35 function(Named_object*);
|
|
36
|
|
37 int
|
|
38 statement(Block*, size_t*, Statement*);
|
|
39
|
|
40 int
|
111
|
41 expression(Expression**);
|
|
42
|
|
43 private:
|
131
|
44 // General IR.
|
111
|
45 Gogo* gogo_;
|
131
|
46 // The function we are traversing.
|
|
47 Named_object* function_;
|
111
|
48 };
|
|
49
|
131
|
50 // Record a function.
|
|
51
|
|
52 int
|
|
53 Mark_address_taken::function(Named_object* no)
|
|
54 {
|
|
55 go_assert(this->function_ == NULL);
|
|
56 this->function_ = no;
|
|
57 int t = no->func_value()->traverse(this);
|
|
58 this->function_ = NULL;
|
|
59
|
|
60 if (t == TRAVERSE_EXIT)
|
|
61 return t;
|
|
62 return TRAVERSE_SKIP_COMPONENTS;
|
|
63 }
|
|
64
|
|
65 // Traverse a statement.
|
|
66
|
|
67 int
|
|
68 Mark_address_taken::statement(Block* block, size_t* pindex, Statement* s)
|
|
69 {
|
|
70 // If this is an assignment of the form s = append(s, ...), expand
|
|
71 // it now, so that we can assign it to the left hand side in the
|
|
72 // middle of the expansion and possibly skip a write barrier.
|
|
73 Assignment_statement* as = s->assignment_statement();
|
|
74 if (as != NULL && !as->lhs()->is_sink_expression())
|
|
75 {
|
|
76 Call_expression* rce = as->rhs()->call_expression();
|
|
77 if (rce != NULL
|
|
78 && rce->builtin_call_expression() != NULL
|
|
79 && (rce->builtin_call_expression()->code()
|
|
80 == Builtin_call_expression::BUILTIN_APPEND)
|
|
81 && Expression::is_same_variable(as->lhs(), rce->args()->front()))
|
|
82 {
|
|
83 Statement_inserter inserter = Statement_inserter(block, pindex);
|
|
84 Expression* a =
|
|
85 rce->builtin_call_expression()->flatten_append(this->gogo_,
|
|
86 this->function_,
|
|
87 &inserter,
|
|
88 as->lhs(),
|
|
89 block);
|
|
90 go_assert(a == NULL);
|
|
91 // That does the assignment, so remove this statement.
|
|
92 Expression* e = Expression::make_boolean(true, s->location());
|
|
93 Statement* dummy = Statement::make_statement(e, true);
|
|
94 block->replace_statement(*pindex, dummy);
|
|
95 }
|
|
96 }
|
|
97 return TRAVERSE_CONTINUE;
|
|
98 }
|
|
99
|
111
|
100 // Mark variable addresses taken.
|
|
101
|
|
102 int
|
|
103 Mark_address_taken::expression(Expression** pexpr)
|
|
104 {
|
|
105 Expression* expr = *pexpr;
|
|
106 Unary_expression* ue = expr->unary_expression();
|
|
107 if (ue != NULL)
|
|
108 ue->check_operand_address_taken(this->gogo_);
|
131
|
109
|
|
110 Array_index_expression* aie = expr->array_index_expression();
|
|
111 if (aie != NULL
|
|
112 && aie->end() != NULL
|
|
113 && !aie->array()->type()->is_slice_type())
|
|
114 {
|
|
115 // Slice of an array. The escape analysis models this with
|
|
116 // a child Node representing the address of the array.
|
|
117 bool escapes = false;
|
|
118 Node* n = Node::make_node(expr);
|
|
119 if (n->child() == NULL
|
|
120 || (n->child()->encoding() & ESCAPE_MASK) != Node::ESCAPE_NONE)
|
|
121 escapes = true;
|
|
122 aie->array()->address_taken(escapes);
|
|
123 }
|
|
124
|
|
125 if (expr->allocation_expression() != NULL)
|
|
126 {
|
|
127 Node* n = Node::make_node(expr);
|
|
128 if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
|
|
129 expr->allocation_expression()->set_allocate_on_stack();
|
|
130 }
|
|
131 if (expr->heap_expression() != NULL)
|
|
132 {
|
|
133 Node* n = Node::make_node(expr);
|
|
134 if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
|
|
135 expr->heap_expression()->set_allocate_on_stack();
|
|
136 }
|
|
137 if (expr->slice_literal() != NULL)
|
|
138 {
|
|
139 Node* n = Node::make_node(expr);
|
|
140 if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
|
|
141 expr->slice_literal()->set_storage_does_not_escape();
|
|
142 }
|
|
143
|
|
144 // Rewrite non-escaping makeslice with constant size to stack allocation.
|
145
|
145 Slice_value_expression* sve = expr->slice_value_expression();
|
|
146 if (sve != NULL)
|
131
|
147 {
|
145
|
148 std::pair<Call_expression*, Temporary_statement*> p =
|
|
149 Expression::find_makeslice_call(sve);
|
|
150 Call_expression* call = p.first;
|
|
151 Temporary_statement* ts = p.second;
|
|
152 if (call != NULL
|
|
153 && Node::make_node(call)->encoding() == Node::ESCAPE_NONE)
|
131
|
154 {
|
|
155 Expression* len_arg = call->args()->at(1);
|
|
156 Expression* cap_arg = call->args()->at(2);
|
|
157 Numeric_constant nclen;
|
|
158 Numeric_constant nccap;
|
|
159 unsigned long vlen;
|
|
160 unsigned long vcap;
|
|
161 if (len_arg->numeric_constant_value(&nclen)
|
|
162 && cap_arg->numeric_constant_value(&nccap)
|
|
163 && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID
|
|
164 && nccap.to_unsigned_long(&vcap) == Numeric_constant::NC_UL_VALID)
|
|
165 {
|
145
|
166 // Stack allocate an array and make a slice value from it.
|
131
|
167 Location loc = expr->location();
|
|
168 Type* elmt_type = expr->type()->array_type()->element_type();
|
|
169 Expression* len_expr =
|
|
170 Expression::make_integer_ul(vcap, cap_arg->type(), loc);
|
|
171 Type* array_type = Type::make_array_type(elmt_type, len_expr);
|
|
172 Expression* alloc = Expression::make_allocation(array_type, loc);
|
|
173 alloc->allocation_expression()->set_allocate_on_stack();
|
145
|
174 Type* ptr_type = Type::make_pointer_type(elmt_type);
|
|
175 Expression* ptr = Expression::make_unsafe_cast(ptr_type, alloc,
|
|
176 loc);
|
|
177 Expression* slice =
|
|
178 Expression::make_slice_value(expr->type(), ptr, len_arg,
|
|
179 cap_arg, loc);
|
131
|
180 *pexpr = slice;
|
145
|
181 if (ts != NULL && ts->uses() == 1)
|
|
182 ts->set_init(Expression::make_nil(loc));
|
131
|
183 }
|
|
184 }
|
|
185 }
|
111
|
186 return TRAVERSE_CONTINUE;
|
|
187 }
|
|
188
|
131
|
189 // Check variables and closures do not escape when compiling runtime.
|
|
190
|
|
191 class Check_escape : public Traverse
|
|
192 {
|
|
193 public:
|
|
194 Check_escape(Gogo* gogo)
|
|
195 : Traverse(traverse_expressions | traverse_variables),
|
|
196 gogo_(gogo)
|
|
197 { }
|
|
198
|
|
199 int
|
|
200 expression(Expression**);
|
|
201
|
|
202 int
|
|
203 variable(Named_object*);
|
|
204
|
|
205 private:
|
|
206 Gogo* gogo_;
|
|
207 };
|
|
208
|
|
209 int
|
|
210 Check_escape::variable(Named_object* no)
|
|
211 {
|
|
212 if ((no->is_variable() && no->var_value()->is_in_heap())
|
|
213 || (no->is_result_variable()
|
|
214 && no->result_var_value()->is_in_heap()))
|
|
215 go_error_at(no->location(),
|
|
216 "%s escapes to heap, not allowed in runtime",
|
|
217 no->message_name().c_str());
|
|
218 return TRAVERSE_CONTINUE;
|
|
219 }
|
|
220
|
|
221 int
|
|
222 Check_escape::expression(Expression** pexpr)
|
|
223 {
|
|
224 Expression* expr = *pexpr;
|
|
225 Func_expression* fe = expr->func_expression();
|
|
226 if (fe != NULL && fe->closure() != NULL)
|
|
227 {
|
|
228 Node* n = Node::make_node(expr);
|
|
229 if (n->encoding() == Node::ESCAPE_HEAP)
|
|
230 go_error_at(expr->location(),
|
|
231 "heap-allocated closure, not allowed in runtime");
|
|
232 }
|
|
233 return TRAVERSE_CONTINUE;
|
|
234 }
|
|
235
|
|
236 // Collect all writebarrierrec functions. This is used when compiling
|
|
237 // the runtime package, to propagate //go:nowritebarrierrec.
|
|
238
|
|
239 class Collect_writebarrierrec_functions : public Traverse
|
|
240 {
|
|
241 public:
|
|
242 Collect_writebarrierrec_functions(std::vector<Named_object*>* worklist)
|
|
243 : Traverse(traverse_functions),
|
|
244 worklist_(worklist)
|
|
245 { }
|
|
246
|
|
247 private:
|
|
248 int
|
|
249 function(Named_object*);
|
|
250
|
|
251 // The collected functions are put here.
|
|
252 std::vector<Named_object*>* worklist_;
|
|
253 };
|
|
254
|
|
255 int
|
|
256 Collect_writebarrierrec_functions::function(Named_object* no)
|
|
257 {
|
|
258 if (no->is_function()
|
|
259 && no->func_value()->enclosing() == NULL
|
|
260 && (no->func_value()->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0)
|
|
261 {
|
|
262 go_assert((no->func_value()->pragmas() & GOPRAGMA_MARK) == 0);
|
|
263 this->worklist_->push_back(no);
|
|
264 }
|
|
265 return TRAVERSE_CONTINUE;
|
|
266 }
|
|
267
|
|
268 // Collect all callees of this function. We only care about locally
|
|
269 // defined, known, functions.
|
|
270
|
|
271 class Collect_callees : public Traverse
|
|
272 {
|
|
273 public:
|
|
274 Collect_callees(std::vector<Named_object*>* worklist)
|
|
275 : Traverse(traverse_expressions),
|
|
276 worklist_(worklist)
|
|
277 { }
|
|
278
|
|
279 private:
|
|
280 int
|
|
281 expression(Expression**);
|
|
282
|
|
283 // The collected callees are put here.
|
|
284 std::vector<Named_object*>* worklist_;
|
|
285 };
|
|
286
|
|
287 int
|
|
288 Collect_callees::expression(Expression** pexpr)
|
|
289 {
|
|
290 Call_expression* ce = (*pexpr)->call_expression();
|
|
291 if (ce != NULL)
|
|
292 {
|
|
293 Func_expression* fe = ce->fn()->func_expression();
|
|
294 if (fe != NULL)
|
|
295 {
|
|
296 Named_object* no = fe->named_object();
|
|
297 if (no->package() == NULL && no->is_function())
|
|
298 {
|
|
299 // The function runtime.systemstack is special, in that
|
|
300 // it is a common way to call a function in the runtime:
|
|
301 // mark its argument if we can.
|
|
302 if (Gogo::unpack_hidden_name(no->name()) != "systemstack")
|
|
303 this->worklist_->push_back(no);
|
|
304 else if (ce->args()->size() > 0)
|
|
305 {
|
|
306 fe = ce->args()->front()->func_expression();
|
|
307 if (fe != NULL)
|
|
308 {
|
|
309 no = fe->named_object();
|
|
310 if (no->package() == NULL && no->is_function())
|
|
311 this->worklist_->push_back(no);
|
|
312 }
|
|
313 }
|
|
314 }
|
|
315 }
|
|
316 }
|
|
317 return TRAVERSE_CONTINUE;
|
|
318 }
|
|
319
|
|
320 // When compiling the runtime package, propagate //go:nowritebarrierrec
|
|
321 // annotations. A function marked as //go:nowritebarrierrec does not
|
|
322 // permit write barriers, and also all the functions that it calls,
|
|
323 // recursively, do not permit write barriers. Except that a
|
|
324 // //go:yeswritebarrierrec annotation permits write barriers even if
|
|
325 // called by a //go:nowritebarrierrec function. Here we turn
|
|
326 // //go:nowritebarrierrec into //go:nowritebarrier, as appropriate.
|
|
327
|
|
328 void
|
|
329 Gogo::propagate_writebarrierrec()
|
|
330 {
|
|
331 std::vector<Named_object*> worklist;
|
|
332 Collect_writebarrierrec_functions cwf(&worklist);
|
|
333 this->traverse(&cwf);
|
|
334
|
|
335 Collect_callees cc(&worklist);
|
|
336
|
|
337 while (!worklist.empty())
|
|
338 {
|
|
339 Named_object* no = worklist.back();
|
|
340 worklist.pop_back();
|
|
341
|
|
342 unsigned int pragmas = no->func_value()->pragmas();
|
|
343 if ((pragmas & GOPRAGMA_MARK) != 0)
|
|
344 {
|
|
345 // We've already seen this function.
|
|
346 continue;
|
|
347 }
|
|
348 if ((pragmas & GOPRAGMA_YESWRITEBARRIERREC) != 0)
|
|
349 {
|
|
350 // We don't want to propagate //go:nowritebarrierrec into
|
|
351 // this function or it's callees.
|
|
352 continue;
|
|
353 }
|
|
354
|
|
355 no->func_value()->set_pragmas(pragmas
|
|
356 | GOPRAGMA_NOWRITEBARRIER
|
|
357 | GOPRAGMA_MARK);
|
|
358
|
|
359 no->func_value()->traverse(&cc);
|
|
360 }
|
|
361 }
|
|
362
|
111
|
363 // Add write barriers to the IR. This are required by the concurrent
|
|
364 // garbage collector. A write barrier is needed for any write of a
|
|
365 // pointer into memory controlled by the garbage collector. Write
|
|
366 // barriers are not required for writes to local variables that live
|
|
367 // on the stack. Write barriers are only required when the runtime
|
|
368 // enables them, which can be checked using a run time check on
|
|
369 // runtime.writeBarrier.enabled.
|
|
370 //
|
|
371 // Essentially, for each assignment A = B, where A is or contains a
|
|
372 // pointer, and where A is not, or at any rate may not be, a stack
|
|
373 // variable, we rewrite it into
|
|
374 // if runtime.writeBarrier.enabled {
|
|
375 // typedmemmove(typeof(A), &A, &B)
|
|
376 // } else {
|
|
377 // A = B
|
|
378 // }
|
|
379 //
|
|
380 // The test of runtime.writeBarrier.Enabled is implemented by treating
|
|
381 // the variable as a *uint32, and testing *runtime.writeBarrier != 0.
|
|
382 // This is compatible with the definition in the runtime package.
|
|
383 //
|
|
384 // For types that are pointer shared (pointers, maps, chans, funcs),
|
131
|
385 // we replaced the call to typedmemmove with gcWriteBarrier(&A, B).
|
111
|
386 // As far as the GC is concerned, all pointers are the same, so it
|
|
387 // doesn't need the type descriptor.
|
|
388 //
|
|
389 // There are possible optimizations that are not implemented.
|
|
390 //
|
|
391 // runtime.writeBarrier can only change when the goroutine is
|
|
392 // preempted, which in practice means when a call is made into the
|
|
393 // runtime package, so we could optimize by only testing it once
|
|
394 // between function calls.
|
|
395 //
|
131
|
396 // A slice could be handled with a call to gcWriteBarrier plus two
|
111
|
397 // integer moves.
|
|
398
|
|
399 // Traverse the IR adding write barriers.
|
|
400
|
|
401 class Write_barriers : public Traverse
|
|
402 {
|
|
403 public:
|
|
404 Write_barriers(Gogo* gogo)
|
145
|
405 : Traverse(traverse_functions
|
|
406 | traverse_blocks
|
|
407 | traverse_variables
|
|
408 | traverse_statements),
|
|
409 gogo_(gogo), function_(NULL), statements_added_(),
|
|
410 nonwb_pointers_()
|
111
|
411 { }
|
|
412
|
|
413 int
|
|
414 function(Named_object*);
|
|
415
|
|
416 int
|
145
|
417 block(Block*);
|
|
418
|
|
419 int
|
111
|
420 variable(Named_object*);
|
|
421
|
|
422 int
|
|
423 statement(Block*, size_t* pindex, Statement*);
|
|
424
|
|
425 private:
|
|
426 // General IR.
|
|
427 Gogo* gogo_;
|
|
428 // Current function.
|
|
429 Function* function_;
|
131
|
430 // Statements introduced.
|
|
431 Statement_inserter::Statements statements_added_;
|
145
|
432 // Within a single block, pointer variables that point to values
|
|
433 // that do not need write barriers.
|
|
434 Unordered_set(const Named_object*) nonwb_pointers_;
|
111
|
435 };
|
|
436
|
|
437 // Traverse a function. Just record it for later.
|
|
438
|
|
439 int
|
|
440 Write_barriers::function(Named_object* no)
|
|
441 {
|
|
442 go_assert(this->function_ == NULL);
|
|
443 this->function_ = no->func_value();
|
|
444 int t = this->function_->traverse(this);
|
|
445 this->function_ = NULL;
|
|
446
|
|
447 if (t == TRAVERSE_EXIT)
|
|
448 return t;
|
|
449 return TRAVERSE_SKIP_COMPONENTS;
|
|
450 }
|
|
451
|
145
|
452 // Traverse a block. Clear anything we know about local pointer
|
|
453 // variables.
|
|
454
|
|
455 int
|
|
456 Write_barriers::block(Block*)
|
|
457 {
|
|
458 this->nonwb_pointers_.clear();
|
|
459 return TRAVERSE_CONTINUE;
|
|
460 }
|
|
461
|
111
|
462 // Insert write barriers for a global variable: ensure that variable
|
|
463 // initialization is handled correctly. This is rarely needed, since
|
|
464 // we currently don't enable background GC until after all global
|
|
465 // variables are initialized. But we do need this if an init function
|
|
466 // calls runtime.GC.
|
|
467
|
|
468 int
|
|
469 Write_barriers::variable(Named_object* no)
|
|
470 {
|
|
471 // We handle local variables in the variable declaration statement.
|
|
472 // We only have to handle global variables here.
|
|
473 if (!no->is_variable())
|
|
474 return TRAVERSE_CONTINUE;
|
|
475 Variable* var = no->var_value();
|
|
476 if (!var->is_global())
|
|
477 return TRAVERSE_CONTINUE;
|
|
478
|
|
479 // Nothing to do if there is no initializer.
|
|
480 Expression* init = var->init();
|
|
481 if (init == NULL)
|
|
482 return TRAVERSE_CONTINUE;
|
|
483
|
|
484 // Nothing to do for variables that do not contain any pointers.
|
|
485 if (!var->type()->has_pointer())
|
|
486 return TRAVERSE_CONTINUE;
|
|
487
|
|
488 // Nothing to do if the initializer is static.
|
|
489 init = Expression::make_cast(var->type(), init, var->location());
|
|
490 if (!var->has_pre_init() && init->is_static_initializer())
|
|
491 return TRAVERSE_CONTINUE;
|
|
492
|
|
493 // Nothing to do for a type that can not be in the heap, or a
|
|
494 // pointer to a type that can not be in the heap.
|
|
495 if (!var->type()->in_heap())
|
|
496 return TRAVERSE_CONTINUE;
|
|
497 if (var->type()->points_to() != NULL && !var->type()->points_to()->in_heap())
|
|
498 return TRAVERSE_CONTINUE;
|
|
499
|
|
500 // Otherwise change the initializer into a pre_init assignment
|
|
501 // statement with a write barrier.
|
|
502
|
|
503 // We can't check for a dependency of the variable on itself after
|
|
504 // we make this change, because the preinit statement will always
|
|
505 // depend on the variable (since it assigns to it). So check for a
|
|
506 // self-dependency now.
|
|
507 this->gogo_->check_self_dep(no);
|
|
508
|
|
509 // Replace the initializer.
|
|
510 Location loc = init->location();
|
|
511 Expression* ref = Expression::make_var_reference(no, loc);
|
|
512
|
131
|
513 Statement_inserter inserter(this->gogo_, var, &this->statements_added_);
|
111
|
514 Statement* s = this->gogo_->assign_with_write_barrier(NULL, NULL, &inserter,
|
|
515 ref, init, loc);
|
131
|
516 this->statements_added_.insert(s);
|
111
|
517
|
|
518 var->add_preinit_statement(this->gogo_, s);
|
|
519 var->clear_init();
|
|
520
|
|
521 return TRAVERSE_CONTINUE;
|
|
522 }
|
|
523
|
|
524 // Insert write barriers for statements.
|
|
525
|
|
526 int
|
|
527 Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
|
|
528 {
|
131
|
529 if (this->statements_added_.find(s) != this->statements_added_.end())
|
|
530 return TRAVERSE_SKIP_COMPONENTS;
|
|
531
|
111
|
532 switch (s->classification())
|
|
533 {
|
|
534 default:
|
|
535 break;
|
|
536
|
|
537 case Statement::STATEMENT_VARIABLE_DECLARATION:
|
|
538 {
|
|
539 Variable_declaration_statement* vds =
|
|
540 s->variable_declaration_statement();
|
|
541 Named_object* no = vds->var();
|
|
542 Variable* var = no->var_value();
|
|
543
|
|
544 // We may need to emit a write barrier for the initialization
|
|
545 // of the variable.
|
|
546
|
|
547 // Nothing to do for a variable with no initializer.
|
|
548 Expression* init = var->init();
|
|
549 if (init == NULL)
|
|
550 break;
|
|
551
|
|
552 // Nothing to do if the variable is not in the heap. Only
|
|
553 // local variables get declaration statements, and local
|
|
554 // variables on the stack do not require write barriers.
|
|
555 if (!var->is_in_heap())
|
145
|
556 {
|
|
557 // If this is a pointer variable, and assigning through
|
|
558 // the initializer does not require a write barrier,
|
|
559 // record that fact.
|
|
560 if (var->type()->points_to() != NULL
|
|
561 && this->gogo_->is_nonwb_pointer(init, &this->nonwb_pointers_))
|
|
562 this->nonwb_pointers_.insert(no);
|
|
563
|
|
564 break;
|
|
565 }
|
111
|
566
|
|
567 // Nothing to do if the variable does not contain any pointers.
|
|
568 if (!var->type()->has_pointer())
|
|
569 break;
|
|
570
|
|
571 // Nothing to do for a type that can not be in the heap, or a
|
|
572 // pointer to a type that can not be in the heap.
|
|
573 if (!var->type()->in_heap())
|
|
574 break;
|
|
575 if (var->type()->points_to() != NULL
|
|
576 && !var->type()->points_to()->in_heap())
|
|
577 break;
|
|
578
|
|
579 // Otherwise initialize the variable with a write barrier.
|
|
580
|
|
581 Function* function = this->function_;
|
|
582 Location loc = init->location();
|
131
|
583 Statement_inserter inserter(block, pindex, &this->statements_added_);
|
111
|
584
|
|
585 // Insert the variable declaration statement with no
|
|
586 // initializer, so that the variable exists.
|
|
587 var->clear_init();
|
|
588 inserter.insert(s);
|
|
589
|
|
590 // Create a statement that initializes the variable with a
|
|
591 // write barrier.
|
|
592 Expression* ref = Expression::make_var_reference(no, loc);
|
|
593 Statement* assign = this->gogo_->assign_with_write_barrier(function,
|
|
594 block,
|
|
595 &inserter,
|
|
596 ref, init,
|
|
597 loc);
|
131
|
598 this->statements_added_.insert(assign);
|
111
|
599
|
|
600 // Replace the old variable declaration statement with the new
|
|
601 // initialization.
|
|
602 block->replace_statement(*pindex, assign);
|
|
603 }
|
|
604 break;
|
|
605
|
|
606 case Statement::STATEMENT_ASSIGNMENT:
|
|
607 {
|
|
608 Assignment_statement* as = s->assignment_statement();
|
131
|
609
|
145
|
610 Expression* lhs = as->lhs();
|
|
611 Expression* rhs = as->rhs();
|
|
612
|
|
613 // Keep track of variables whose values do not escape.
|
|
614 Var_expression* lhsve = lhs->var_expression();
|
|
615 if (lhsve != NULL && lhsve->type()->points_to() != NULL)
|
|
616 {
|
|
617 Named_object* no = lhsve->named_object();
|
|
618 if (this->gogo_->is_nonwb_pointer(rhs, &this->nonwb_pointers_))
|
|
619 this->nonwb_pointers_.insert(no);
|
|
620 else
|
|
621 this->nonwb_pointers_.erase(no);
|
|
622 }
|
|
623
|
131
|
624 if (as->omit_write_barrier())
|
|
625 break;
|
|
626
|
111
|
627 // We may need to emit a write barrier for the assignment.
|
|
628
|
145
|
629 if (!this->gogo_->assign_needs_write_barrier(lhs,
|
|
630 &this->nonwb_pointers_))
|
111
|
631 break;
|
|
632
|
|
633 // Change the assignment to use a write barrier.
|
|
634 Function* function = this->function_;
|
|
635 Location loc = as->location();
|
131
|
636 Statement_inserter inserter =
|
|
637 Statement_inserter(block, pindex, &this->statements_added_);
|
111
|
638 Statement* assign = this->gogo_->assign_with_write_barrier(function,
|
|
639 block,
|
|
640 &inserter,
|
|
641 lhs, rhs,
|
|
642 loc);
|
131
|
643 this->statements_added_.insert(assign);
|
111
|
644 block->replace_statement(*pindex, assign);
|
|
645 }
|
|
646 break;
|
|
647 }
|
|
648
|
|
649 return TRAVERSE_CONTINUE;
|
|
650 }
|
|
651
|
|
652 // The write barrier pass.
|
|
653
|
|
654 void
|
|
655 Gogo::add_write_barriers()
|
|
656 {
|
131
|
657 if (saw_errors())
|
|
658 return;
|
|
659
|
111
|
660 Mark_address_taken mat(this);
|
|
661 this->traverse(&mat);
|
|
662
|
131
|
663 if (this->compiling_runtime() && this->package_name() == "runtime")
|
|
664 {
|
|
665 this->propagate_writebarrierrec();
|
|
666
|
|
667 Check_escape chk(this);
|
|
668 this->traverse(&chk);
|
|
669 }
|
|
670
|
111
|
671 Write_barriers wb(this);
|
|
672 this->traverse(&wb);
|
|
673 }
|
|
674
|
|
675 // Return the runtime.writeBarrier variable.
|
|
676
|
|
677 Named_object*
|
|
678 Gogo::write_barrier_variable()
|
|
679 {
|
|
680 static Named_object* write_barrier_var;
|
|
681 if (write_barrier_var == NULL)
|
|
682 {
|
|
683 Location bloc = Linemap::predeclared_location();
|
|
684
|
145
|
685 Type* bool_type = Type::lookup_bool_type();
|
|
686 Array_type* pad_type = Type::make_array_type(this->lookup_global("byte")->type_value(),
|
|
687 Expression::make_integer_ul(3, NULL, bloc));
|
|
688 Type* uint64_type = Type::lookup_integer_type("uint64");
|
|
689 Type* wb_type = Type::make_builtin_struct_type(5,
|
|
690 "enabled", bool_type,
|
|
691 "pad", pad_type,
|
|
692 "needed", bool_type,
|
|
693 "cgo", bool_type,
|
|
694 "alignme", uint64_type);
|
|
695
|
|
696 Variable* var = new Variable(wb_type, NULL,
|
|
697 true, false, false, bloc);
|
111
|
698
|
|
699 bool add_to_globals;
|
|
700 Package* package = this->add_imported_package("runtime", "_", false,
|
|
701 "runtime", "runtime",
|
|
702 bloc, &add_to_globals);
|
|
703 write_barrier_var = Named_object::make_variable("writeBarrier",
|
|
704 package, var);
|
|
705 }
|
|
706
|
|
707 return write_barrier_var;
|
|
708 }
|
|
709
|
|
710 // Return whether an assignment that sets LHS needs a write barrier.
|
145
|
711 // NONWB_POINTERS is a set of variables that point to values that do
|
|
712 // not need write barriers.
|
111
|
713
|
|
714 bool
|
145
|
715 Gogo::assign_needs_write_barrier(
|
|
716 Expression* lhs,
|
|
717 Unordered_set(const Named_object*)* nonwb_pointers)
|
111
|
718 {
|
|
719 // Nothing to do if the variable does not contain any pointers.
|
|
720 if (!lhs->type()->has_pointer())
|
|
721 return false;
|
|
722
|
131
|
723 // An assignment to a field or an array index is handled like an
|
|
724 // assignment to the struct.
|
|
725 while (true)
|
|
726 {
|
|
727 // Nothing to do for a type that can not be in the heap, or a
|
|
728 // pointer to a type that can not be in the heap. We check this
|
|
729 // at each level of a struct.
|
|
730 if (!lhs->type()->in_heap())
|
|
731 return false;
|
|
732 if (lhs->type()->points_to() != NULL
|
|
733 && !lhs->type()->points_to()->in_heap())
|
|
734 return false;
|
|
735
|
145
|
736 // For a struct assignment, we don't need a write barrier if all
|
|
737 // the field types can not be in the heap.
|
|
738 Struct_type* st = lhs->type()->struct_type();
|
|
739 if (st != NULL)
|
|
740 {
|
|
741 bool in_heap = false;
|
|
742 const Struct_field_list* fields = st->fields();
|
|
743 for (Struct_field_list::const_iterator p = fields->begin();
|
|
744 p != fields->end();
|
|
745 p++)
|
|
746 {
|
|
747 Type* ft = p->type();
|
|
748 if (!ft->has_pointer())
|
|
749 continue;
|
|
750 if (!ft->in_heap())
|
|
751 continue;
|
|
752 if (ft->points_to() != NULL && !ft->points_to()->in_heap())
|
|
753 continue;
|
|
754 in_heap = true;
|
|
755 break;
|
|
756 }
|
|
757 if (!in_heap)
|
|
758 return false;
|
|
759 }
|
|
760
|
131
|
761 Field_reference_expression* fre = lhs->field_reference_expression();
|
|
762 if (fre != NULL)
|
|
763 {
|
|
764 lhs = fre->expr();
|
|
765 continue;
|
|
766 }
|
|
767
|
|
768 Array_index_expression* aie = lhs->array_index_expression();
|
|
769 if (aie != NULL
|
|
770 && aie->end() == NULL
|
|
771 && !aie->array()->type()->is_slice_type())
|
|
772 {
|
|
773 lhs = aie->array();
|
|
774 continue;
|
|
775 }
|
|
776
|
|
777 break;
|
|
778 }
|
|
779
|
111
|
780 // Nothing to do for an assignment to a temporary.
|
|
781 if (lhs->temporary_reference_expression() != NULL)
|
|
782 return false;
|
|
783
|
|
784 // Nothing to do for an assignment to a sink.
|
|
785 if (lhs->is_sink_expression())
|
|
786 return false;
|
|
787
|
|
788 // Nothing to do for an assignment to a local variable that is not
|
|
789 // on the heap.
|
|
790 Var_expression* ve = lhs->var_expression();
|
|
791 if (ve != NULL)
|
|
792 {
|
|
793 Named_object* no = ve->named_object();
|
|
794 if (no->is_variable())
|
|
795 {
|
|
796 Variable* var = no->var_value();
|
|
797 if (!var->is_global() && !var->is_in_heap())
|
|
798 return false;
|
|
799 }
|
|
800 else if (no->is_result_variable())
|
|
801 {
|
|
802 Result_variable* rvar = no->result_var_value();
|
|
803 if (!rvar->is_in_heap())
|
|
804 return false;
|
|
805 }
|
|
806 }
|
|
807
|
145
|
808 // Nothing to do for an assignment to *(convert(&x)) where
|
|
809 // x is local variable or a temporary variable.
|
|
810 Unary_expression* ue = lhs->unary_expression();
|
|
811 if (ue != NULL
|
|
812 && ue->op() == OPERATOR_MULT
|
|
813 && this->is_nonwb_pointer(ue->operand(), nonwb_pointers))
|
|
814 return false;
|
|
815
|
|
816 // Write barrier needed in other cases.
|
|
817 return true;
|
|
818 }
|
|
819
|
|
820 // Return whether EXPR is the address of a variable that can be set
|
|
821 // without a write barrier. That is, if this returns true, then an
|
|
822 // assignment to *EXPR does not require a write barrier.
|
|
823 // NONWB_POINTERS is a set of variables that point to values that do
|
|
824 // not need write barriers.
|
|
825
|
|
826 bool
|
|
827 Gogo::is_nonwb_pointer(Expression* expr,
|
|
828 Unordered_set(const Named_object*)* nonwb_pointers)
|
|
829 {
|
|
830 while (true)
|
131
|
831 {
|
145
|
832 if (expr->conversion_expression() != NULL)
|
|
833 expr = expr->conversion_expression()->expr();
|
|
834 else if (expr->unsafe_conversion_expression() != NULL)
|
|
835 expr = expr->unsafe_conversion_expression()->expr();
|
|
836 else
|
|
837 break;
|
131
|
838 }
|
111
|
839
|
145
|
840 Var_expression* ve = expr->var_expression();
|
|
841 if (ve != NULL
|
|
842 && nonwb_pointers != NULL
|
|
843 && nonwb_pointers->find(ve->named_object()) != nonwb_pointers->end())
|
|
844 return true;
|
|
845
|
|
846 Unary_expression* ue = expr->unary_expression();
|
|
847 if (ue == NULL || ue->op() != OPERATOR_AND)
|
|
848 return false;
|
|
849 if (this->assign_needs_write_barrier(ue->operand(), nonwb_pointers))
|
|
850 return false;
|
111
|
851 return true;
|
|
852 }
|
|
853
|
|
854 // Return a statement that sets LHS to RHS using a write barrier.
|
|
855 // ENCLOSING is the enclosing block.
|
|
856
|
|
857 Statement*
|
|
858 Gogo::assign_with_write_barrier(Function* function, Block* enclosing,
|
|
859 Statement_inserter* inserter, Expression* lhs,
|
|
860 Expression* rhs, Location loc)
|
|
861 {
|
131
|
862 if (function != NULL && (function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0)
|
111
|
863 go_error_at(loc, "write barrier prohibited");
|
|
864
|
|
865 Type* type = lhs->type();
|
|
866 go_assert(type->has_pointer());
|
|
867
|
|
868 Expression* addr;
|
|
869 if (lhs->unary_expression() != NULL
|
|
870 && lhs->unary_expression()->op() == OPERATOR_MULT)
|
|
871 addr = lhs->unary_expression()->operand();
|
|
872 else
|
|
873 {
|
|
874 addr = Expression::make_unary(OPERATOR_AND, lhs, loc);
|
|
875 addr->unary_expression()->set_does_not_escape();
|
|
876 }
|
|
877 Temporary_statement* lhs_temp = Statement::make_temporary(NULL, addr, loc);
|
|
878 inserter->insert(lhs_temp);
|
|
879 lhs = Expression::make_temporary_reference(lhs_temp, loc);
|
|
880
|
131
|
881 if (!Type::are_identical(type, rhs->type(),
|
|
882 Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
|
|
883 NULL)
|
111
|
884 && rhs->type()->interface_type() != NULL
|
|
885 && !rhs->is_variable())
|
|
886 {
|
|
887 // May need a temporary for interface conversion.
|
|
888 Temporary_statement* temp = Statement::make_temporary(NULL, rhs, loc);
|
|
889 inserter->insert(temp);
|
|
890 rhs = Expression::make_temporary_reference(temp, loc);
|
|
891 }
|
|
892 rhs = Expression::convert_for_assignment(this, type, rhs, loc);
|
|
893 Temporary_statement* rhs_temp = NULL;
|
|
894 if (!rhs->is_variable() && !rhs->is_constant())
|
|
895 {
|
|
896 rhs_temp = Statement::make_temporary(NULL, rhs, loc);
|
|
897 inserter->insert(rhs_temp);
|
|
898 rhs = Expression::make_temporary_reference(rhs_temp, loc);
|
|
899 }
|
|
900
|
131
|
901 Expression* indir =
|
|
902 Expression::make_dereference(lhs, Expression::NIL_CHECK_DEFAULT, loc);
|
111
|
903 Statement* assign = Statement::make_assignment(indir, rhs, loc);
|
|
904
|
|
905 lhs = Expression::make_temporary_reference(lhs_temp, loc);
|
|
906 if (rhs_temp != NULL)
|
|
907 rhs = Expression::make_temporary_reference(rhs_temp, loc);
|
|
908
|
|
909 Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type());
|
|
910 lhs = Expression::make_unsafe_cast(unsafe_ptr_type, lhs, loc);
|
|
911
|
145
|
912 Type* uintptr_type = Type::lookup_integer_type("uintptr");
|
111
|
913 Expression* call;
|
|
914 switch (type->base()->classification())
|
|
915 {
|
|
916 default:
|
|
917 go_unreachable();
|
|
918
|
|
919 case Type::TYPE_ERROR:
|
|
920 return assign;
|
|
921
|
|
922 case Type::TYPE_POINTER:
|
|
923 case Type::TYPE_FUNCTION:
|
|
924 case Type::TYPE_MAP:
|
|
925 case Type::TYPE_CHANNEL:
|
145
|
926 {
|
|
927 // These types are all represented by a single pointer.
|
|
928 rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
|
|
929 call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
|
|
930 }
|
111
|
931 break;
|
|
932
|
|
933 case Type::TYPE_STRING:
|
145
|
934 {
|
|
935 // Assign the length field directly.
|
|
936 Expression* llen =
|
|
937 Expression::make_string_info(indir->copy(),
|
|
938 Expression::STRING_INFO_LENGTH,
|
|
939 loc);
|
|
940 Expression* rlen =
|
|
941 Expression::make_string_info(rhs,
|
|
942 Expression::STRING_INFO_LENGTH,
|
|
943 loc);
|
|
944 Statement* as = Statement::make_assignment(llen, rlen, loc);
|
|
945 inserter->insert(as);
|
|
946
|
|
947 // Assign the data field with a write barrier.
|
|
948 lhs =
|
|
949 Expression::make_string_info(indir->copy(),
|
|
950 Expression::STRING_INFO_DATA,
|
|
951 loc);
|
|
952 rhs =
|
|
953 Expression::make_string_info(rhs,
|
|
954 Expression::STRING_INFO_DATA,
|
|
955 loc);
|
|
956 assign = Statement::make_assignment(lhs, rhs, loc);
|
|
957 lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
|
|
958 rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
|
|
959 call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
|
|
960 }
|
|
961 break;
|
|
962
|
111
|
963 case Type::TYPE_INTERFACE:
|
|
964 {
|
145
|
965 // Assign the first field directly.
|
|
966 // The first field is either a type descriptor or a method table.
|
|
967 // Type descriptors are either statically created, or created by
|
|
968 // the reflect package. For the latter the reflect package keeps
|
|
969 // all references.
|
|
970 // Method tables are either statically created or persistently
|
|
971 // allocated.
|
|
972 // In all cases they don't need a write barrier.
|
|
973 Expression* ltab =
|
|
974 Expression::make_interface_info(indir->copy(),
|
|
975 Expression::INTERFACE_INFO_METHODS,
|
|
976 loc);
|
|
977 Expression* rtab =
|
|
978 Expression::make_interface_info(rhs,
|
|
979 Expression::INTERFACE_INFO_METHODS,
|
|
980 loc);
|
|
981 Statement* as = Statement::make_assignment(ltab, rtab, loc);
|
|
982 inserter->insert(as);
|
|
983
|
|
984 // Assign the data field with a write barrier.
|
|
985 lhs =
|
|
986 Expression::make_interface_info(indir->copy(),
|
|
987 Expression::INTERFACE_INFO_OBJECT,
|
|
988 loc);
|
|
989 rhs =
|
|
990 Expression::make_interface_info(rhs,
|
|
991 Expression::INTERFACE_INFO_OBJECT,
|
|
992 loc);
|
|
993 assign = Statement::make_assignment(lhs, rhs, loc);
|
|
994 lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
|
|
995 rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
|
|
996 call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
|
111
|
997 }
|
|
998 break;
|
145
|
999
|
|
1000 case Type::TYPE_ARRAY:
|
|
1001 if (type->is_slice_type())
|
|
1002 {
|
|
1003 // Assign the lenth fields directly.
|
|
1004 Expression* llen =
|
|
1005 Expression::make_slice_info(indir->copy(),
|
|
1006 Expression::SLICE_INFO_LENGTH,
|
|
1007 loc);
|
|
1008 Expression* rlen =
|
|
1009 Expression::make_slice_info(rhs,
|
|
1010 Expression::SLICE_INFO_LENGTH,
|
|
1011 loc);
|
|
1012 Statement* as = Statement::make_assignment(llen, rlen, loc);
|
|
1013 inserter->insert(as);
|
|
1014
|
|
1015 // Assign the capacity fields directly.
|
|
1016 Expression* lcap =
|
|
1017 Expression::make_slice_info(indir->copy(),
|
|
1018 Expression::SLICE_INFO_CAPACITY,
|
|
1019 loc);
|
|
1020 Expression* rcap =
|
|
1021 Expression::make_slice_info(rhs,
|
|
1022 Expression::SLICE_INFO_CAPACITY,
|
|
1023 loc);
|
|
1024 as = Statement::make_assignment(lcap, rcap, loc);
|
|
1025 inserter->insert(as);
|
|
1026
|
|
1027 // Assign the data field with a write barrier.
|
|
1028 lhs =
|
|
1029 Expression::make_slice_info(indir->copy(),
|
|
1030 Expression::SLICE_INFO_VALUE_POINTER,
|
|
1031 loc);
|
|
1032 rhs =
|
|
1033 Expression::make_slice_info(rhs,
|
|
1034 Expression::SLICE_INFO_VALUE_POINTER,
|
|
1035 loc);
|
|
1036 assign = Statement::make_assignment(lhs, rhs, loc);
|
|
1037 lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
|
|
1038 rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
|
|
1039 call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
|
|
1040 break;
|
|
1041 }
|
|
1042 // fallthrough
|
|
1043
|
|
1044 case Type::TYPE_STRUCT:
|
|
1045 if (type->is_direct_iface_type())
|
|
1046 {
|
|
1047 rhs = Expression::unpack_direct_iface(rhs, loc);
|
|
1048 rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
|
|
1049 call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
|
|
1050 }
|
|
1051 else
|
|
1052 {
|
|
1053 // TODO: split assignments for small struct/array?
|
|
1054 rhs = Expression::make_unary(OPERATOR_AND, rhs, loc);
|
|
1055 rhs->unary_expression()->set_does_not_escape();
|
|
1056 call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3,
|
|
1057 Expression::make_type_descriptor(type, loc),
|
|
1058 lhs, rhs);
|
|
1059 }
|
|
1060 break;
|
111
|
1061 }
|
|
1062
|
|
1063 return this->check_write_barrier(enclosing, assign,
|
|
1064 Statement::make_statement(call, false));
|
|
1065 }
|
|
1066
|
|
1067 // Return a statement that tests whether write barriers are enabled
|
|
1068 // and executes either the efficient code or the write barrier
|
|
1069 // function call, depending.
|
|
1070
|
|
1071 Statement*
|
|
1072 Gogo::check_write_barrier(Block* enclosing, Statement* without,
|
|
1073 Statement* with)
|
|
1074 {
|
|
1075 Location loc = without->location();
|
|
1076 Named_object* wb = this->write_barrier_variable();
|
145
|
1077 // We pretend that writeBarrier is a uint32, so that we do a
|
|
1078 // 32-bit load. That is what the gc toolchain does.
|
|
1079 Type* void_type = Type::make_void_type();
|
|
1080 Type* unsafe_pointer_type = Type::make_pointer_type(void_type);
|
|
1081 Type* uint32_type = Type::lookup_integer_type("uint32");
|
|
1082 Type* puint32_type = Type::make_pointer_type(uint32_type);
|
111
|
1083 Expression* ref = Expression::make_var_reference(wb, loc);
|
145
|
1084 ref = Expression::make_unary(OPERATOR_AND, ref, loc);
|
|
1085 ref = Expression::make_cast(unsafe_pointer_type, ref, loc);
|
|
1086 ref = Expression::make_cast(puint32_type, ref, loc);
|
|
1087 ref = Expression::make_dereference(ref,
|
|
1088 Expression::NIL_CHECK_NOT_NEEDED, loc);
|
111
|
1089 Expression* zero = Expression::make_integer_ul(0, ref->type(), loc);
|
|
1090 Expression* cond = Expression::make_binary(OPERATOR_EQEQ, ref, zero, loc);
|
|
1091
|
|
1092 Block* then_block = new Block(enclosing, loc);
|
|
1093 then_block->add_statement(without);
|
|
1094
|
|
1095 Block* else_block = new Block(enclosing, loc);
|
|
1096 else_block->add_statement(with);
|
|
1097
|
|
1098 return Statement::make_if_statement(cond, then_block, else_block, loc);
|
|
1099 }
|