Mercurial > hg > CbC > CbC_gcc
comparison gcc/go/gofrontend/wb.cc @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 84e7813d76e9 |
comparison
equal
deleted
inserted
replaced
68:561a7518be6b | 111:04ced10e8804 |
---|---|
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 | |
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. | |
23 | |
24 class Mark_address_taken : public Traverse | |
25 { | |
26 public: | |
27 Mark_address_taken(Gogo* gogo) | |
28 : Traverse(traverse_expressions), | |
29 gogo_(gogo) | |
30 { } | |
31 | |
32 int | |
33 expression(Expression**); | |
34 | |
35 private: | |
36 Gogo* gogo_; | |
37 }; | |
38 | |
39 // Mark variable addresses taken. | |
40 | |
41 int | |
42 Mark_address_taken::expression(Expression** pexpr) | |
43 { | |
44 Expression* expr = *pexpr; | |
45 Unary_expression* ue = expr->unary_expression(); | |
46 if (ue != NULL) | |
47 ue->check_operand_address_taken(this->gogo_); | |
48 return TRAVERSE_CONTINUE; | |
49 } | |
50 | |
51 // Add write barriers to the IR. This are required by the concurrent | |
52 // garbage collector. A write barrier is needed for any write of a | |
53 // pointer into memory controlled by the garbage collector. Write | |
54 // barriers are not required for writes to local variables that live | |
55 // on the stack. Write barriers are only required when the runtime | |
56 // enables them, which can be checked using a run time check on | |
57 // runtime.writeBarrier.enabled. | |
58 // | |
59 // Essentially, for each assignment A = B, where A is or contains a | |
60 // pointer, and where A is not, or at any rate may not be, a stack | |
61 // variable, we rewrite it into | |
62 // if runtime.writeBarrier.enabled { | |
63 // typedmemmove(typeof(A), &A, &B) | |
64 // } else { | |
65 // A = B | |
66 // } | |
67 // | |
68 // The test of runtime.writeBarrier.Enabled is implemented by treating | |
69 // the variable as a *uint32, and testing *runtime.writeBarrier != 0. | |
70 // This is compatible with the definition in the runtime package. | |
71 // | |
72 // For types that are pointer shared (pointers, maps, chans, funcs), | |
73 // we replaced the call to typedmemmove with writebarrierptr(&A, B). | |
74 // As far as the GC is concerned, all pointers are the same, so it | |
75 // doesn't need the type descriptor. | |
76 // | |
77 // There are possible optimizations that are not implemented. | |
78 // | |
79 // runtime.writeBarrier can only change when the goroutine is | |
80 // preempted, which in practice means when a call is made into the | |
81 // runtime package, so we could optimize by only testing it once | |
82 // between function calls. | |
83 // | |
84 // A slice could be handled with a call to writebarrierptr plus two | |
85 // integer moves. | |
86 | |
87 // Traverse the IR adding write barriers. | |
88 | |
89 class Write_barriers : public Traverse | |
90 { | |
91 public: | |
92 Write_barriers(Gogo* gogo) | |
93 : Traverse(traverse_functions | traverse_variables | traverse_statements), | |
94 gogo_(gogo), function_(NULL) | |
95 { } | |
96 | |
97 int | |
98 function(Named_object*); | |
99 | |
100 int | |
101 variable(Named_object*); | |
102 | |
103 int | |
104 statement(Block*, size_t* pindex, Statement*); | |
105 | |
106 private: | |
107 // General IR. | |
108 Gogo* gogo_; | |
109 // Current function. | |
110 Function* function_; | |
111 }; | |
112 | |
113 // Traverse a function. Just record it for later. | |
114 | |
115 int | |
116 Write_barriers::function(Named_object* no) | |
117 { | |
118 go_assert(this->function_ == NULL); | |
119 this->function_ = no->func_value(); | |
120 int t = this->function_->traverse(this); | |
121 this->function_ = NULL; | |
122 | |
123 if (t == TRAVERSE_EXIT) | |
124 return t; | |
125 return TRAVERSE_SKIP_COMPONENTS; | |
126 } | |
127 | |
128 // Insert write barriers for a global variable: ensure that variable | |
129 // initialization is handled correctly. This is rarely needed, since | |
130 // we currently don't enable background GC until after all global | |
131 // variables are initialized. But we do need this if an init function | |
132 // calls runtime.GC. | |
133 | |
134 int | |
135 Write_barriers::variable(Named_object* no) | |
136 { | |
137 // We handle local variables in the variable declaration statement. | |
138 // We only have to handle global variables here. | |
139 if (!no->is_variable()) | |
140 return TRAVERSE_CONTINUE; | |
141 Variable* var = no->var_value(); | |
142 if (!var->is_global()) | |
143 return TRAVERSE_CONTINUE; | |
144 | |
145 // Nothing to do if there is no initializer. | |
146 Expression* init = var->init(); | |
147 if (init == NULL) | |
148 return TRAVERSE_CONTINUE; | |
149 | |
150 // Nothing to do for variables that do not contain any pointers. | |
151 if (!var->type()->has_pointer()) | |
152 return TRAVERSE_CONTINUE; | |
153 | |
154 // Nothing to do if the initializer is static. | |
155 init = Expression::make_cast(var->type(), init, var->location()); | |
156 if (!var->has_pre_init() && init->is_static_initializer()) | |
157 return TRAVERSE_CONTINUE; | |
158 | |
159 // Nothing to do for a type that can not be in the heap, or a | |
160 // pointer to a type that can not be in the heap. | |
161 if (!var->type()->in_heap()) | |
162 return TRAVERSE_CONTINUE; | |
163 if (var->type()->points_to() != NULL && !var->type()->points_to()->in_heap()) | |
164 return TRAVERSE_CONTINUE; | |
165 | |
166 // Otherwise change the initializer into a pre_init assignment | |
167 // statement with a write barrier. | |
168 | |
169 // We can't check for a dependency of the variable on itself after | |
170 // we make this change, because the preinit statement will always | |
171 // depend on the variable (since it assigns to it). So check for a | |
172 // self-dependency now. | |
173 this->gogo_->check_self_dep(no); | |
174 | |
175 // Replace the initializer. | |
176 Location loc = init->location(); | |
177 Expression* ref = Expression::make_var_reference(no, loc); | |
178 ref->var_expression()->set_in_lvalue_pos(); | |
179 | |
180 Statement_inserter inserter(this->gogo_, var); | |
181 Statement* s = this->gogo_->assign_with_write_barrier(NULL, NULL, &inserter, | |
182 ref, init, loc); | |
183 | |
184 var->add_preinit_statement(this->gogo_, s); | |
185 var->clear_init(); | |
186 | |
187 return TRAVERSE_CONTINUE; | |
188 } | |
189 | |
190 // Insert write barriers for statements. | |
191 | |
192 int | |
193 Write_barriers::statement(Block* block, size_t* pindex, Statement* s) | |
194 { | |
195 switch (s->classification()) | |
196 { | |
197 default: | |
198 break; | |
199 | |
200 case Statement::STATEMENT_VARIABLE_DECLARATION: | |
201 { | |
202 Variable_declaration_statement* vds = | |
203 s->variable_declaration_statement(); | |
204 Named_object* no = vds->var(); | |
205 Variable* var = no->var_value(); | |
206 | |
207 // We may need to emit a write barrier for the initialization | |
208 // of the variable. | |
209 | |
210 // Nothing to do for a variable with no initializer. | |
211 Expression* init = var->init(); | |
212 if (init == NULL) | |
213 break; | |
214 | |
215 // Nothing to do if the variable is not in the heap. Only | |
216 // local variables get declaration statements, and local | |
217 // variables on the stack do not require write barriers. | |
218 if (!var->is_in_heap()) | |
219 break; | |
220 | |
221 // Nothing to do if the variable does not contain any pointers. | |
222 if (!var->type()->has_pointer()) | |
223 break; | |
224 | |
225 // Nothing to do for a type that can not be in the heap, or a | |
226 // pointer to a type that can not be in the heap. | |
227 if (!var->type()->in_heap()) | |
228 break; | |
229 if (var->type()->points_to() != NULL | |
230 && !var->type()->points_to()->in_heap()) | |
231 break; | |
232 | |
233 // Otherwise initialize the variable with a write barrier. | |
234 | |
235 Function* function = this->function_; | |
236 Location loc = init->location(); | |
237 Statement_inserter inserter(block, pindex); | |
238 | |
239 // Insert the variable declaration statement with no | |
240 // initializer, so that the variable exists. | |
241 var->clear_init(); | |
242 inserter.insert(s); | |
243 | |
244 // Create a statement that initializes the variable with a | |
245 // write barrier. | |
246 Expression* ref = Expression::make_var_reference(no, loc); | |
247 Statement* assign = this->gogo_->assign_with_write_barrier(function, | |
248 block, | |
249 &inserter, | |
250 ref, init, | |
251 loc); | |
252 | |
253 // Replace the old variable declaration statement with the new | |
254 // initialization. | |
255 block->replace_statement(*pindex, assign); | |
256 } | |
257 break; | |
258 | |
259 case Statement::STATEMENT_ASSIGNMENT: | |
260 { | |
261 Assignment_statement* as = s->assignment_statement(); | |
262 Expression* lhs = as->lhs(); | |
263 Expression* rhs = as->rhs(); | |
264 | |
265 // We may need to emit a write barrier for the assignment. | |
266 | |
267 if (!this->gogo_->assign_needs_write_barrier(lhs)) | |
268 break; | |
269 | |
270 // Change the assignment to use a write barrier. | |
271 Function* function = this->function_; | |
272 Location loc = as->location(); | |
273 Statement_inserter inserter = Statement_inserter(block, pindex); | |
274 Statement* assign = this->gogo_->assign_with_write_barrier(function, | |
275 block, | |
276 &inserter, | |
277 lhs, rhs, | |
278 loc); | |
279 block->replace_statement(*pindex, assign); | |
280 } | |
281 break; | |
282 } | |
283 | |
284 return TRAVERSE_CONTINUE; | |
285 } | |
286 | |
287 // The write barrier pass. | |
288 | |
289 void | |
290 Gogo::add_write_barriers() | |
291 { | |
292 Mark_address_taken mat(this); | |
293 this->traverse(&mat); | |
294 | |
295 Write_barriers wb(this); | |
296 this->traverse(&wb); | |
297 } | |
298 | |
299 // Return the runtime.writeBarrier variable. | |
300 | |
301 Named_object* | |
302 Gogo::write_barrier_variable() | |
303 { | |
304 static Named_object* write_barrier_var; | |
305 if (write_barrier_var == NULL) | |
306 { | |
307 Location bloc = Linemap::predeclared_location(); | |
308 | |
309 // We pretend that writeBarrier is a uint32, so that we do a | |
310 // 32-bit load. That is what the gc toolchain does. | |
311 Type* uint32_type = Type::lookup_integer_type("uint32"); | |
312 Variable* var = new Variable(uint32_type, NULL, true, false, false, | |
313 bloc); | |
314 | |
315 bool add_to_globals; | |
316 Package* package = this->add_imported_package("runtime", "_", false, | |
317 "runtime", "runtime", | |
318 bloc, &add_to_globals); | |
319 write_barrier_var = Named_object::make_variable("writeBarrier", | |
320 package, var); | |
321 } | |
322 | |
323 return write_barrier_var; | |
324 } | |
325 | |
326 // Return whether an assignment that sets LHS needs a write barrier. | |
327 | |
328 bool | |
329 Gogo::assign_needs_write_barrier(Expression* lhs) | |
330 { | |
331 // Nothing to do if the variable does not contain any pointers. | |
332 if (!lhs->type()->has_pointer()) | |
333 return false; | |
334 | |
335 // Nothing to do for an assignment to a temporary. | |
336 if (lhs->temporary_reference_expression() != NULL) | |
337 return false; | |
338 | |
339 // Nothing to do for an assignment to a sink. | |
340 if (lhs->is_sink_expression()) | |
341 return false; | |
342 | |
343 // Nothing to do for an assignment to a local variable that is not | |
344 // on the heap. | |
345 Var_expression* ve = lhs->var_expression(); | |
346 if (ve != NULL) | |
347 { | |
348 Named_object* no = ve->named_object(); | |
349 if (no->is_variable()) | |
350 { | |
351 Variable* var = no->var_value(); | |
352 if (!var->is_global() && !var->is_in_heap()) | |
353 return false; | |
354 } | |
355 else if (no->is_result_variable()) | |
356 { | |
357 Result_variable* rvar = no->result_var_value(); | |
358 if (!rvar->is_in_heap()) | |
359 return false; | |
360 } | |
361 } | |
362 | |
363 // Nothing to do for a type that can not be in the heap, or a | |
364 // pointer to a type that can not be in the heap. | |
365 if (!lhs->type()->in_heap()) | |
366 return false; | |
367 if (lhs->type()->points_to() != NULL && !lhs->type()->points_to()->in_heap()) | |
368 return false; | |
369 | |
370 // Write barrier needed in other cases. | |
371 return true; | |
372 } | |
373 | |
374 // Return a statement that sets LHS to RHS using a write barrier. | |
375 // ENCLOSING is the enclosing block. | |
376 | |
377 Statement* | |
378 Gogo::assign_with_write_barrier(Function* function, Block* enclosing, | |
379 Statement_inserter* inserter, Expression* lhs, | |
380 Expression* rhs, Location loc) | |
381 { | |
382 if (function != NULL | |
383 && ((function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0 | |
384 || (function->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0)) | |
385 go_error_at(loc, "write barrier prohibited"); | |
386 | |
387 Type* type = lhs->type(); | |
388 go_assert(type->has_pointer()); | |
389 | |
390 Expression* addr; | |
391 if (lhs->unary_expression() != NULL | |
392 && lhs->unary_expression()->op() == OPERATOR_MULT) | |
393 addr = lhs->unary_expression()->operand(); | |
394 else | |
395 { | |
396 addr = Expression::make_unary(OPERATOR_AND, lhs, loc); | |
397 addr->unary_expression()->set_does_not_escape(); | |
398 } | |
399 Temporary_statement* lhs_temp = Statement::make_temporary(NULL, addr, loc); | |
400 inserter->insert(lhs_temp); | |
401 lhs = Expression::make_temporary_reference(lhs_temp, loc); | |
402 | |
403 if (!Type::are_identical(type, rhs->type(), false, NULL) | |
404 && rhs->type()->interface_type() != NULL | |
405 && !rhs->is_variable()) | |
406 { | |
407 // May need a temporary for interface conversion. | |
408 Temporary_statement* temp = Statement::make_temporary(NULL, rhs, loc); | |
409 inserter->insert(temp); | |
410 rhs = Expression::make_temporary_reference(temp, loc); | |
411 } | |
412 rhs = Expression::convert_for_assignment(this, type, rhs, loc); | |
413 Temporary_statement* rhs_temp = NULL; | |
414 if (!rhs->is_variable() && !rhs->is_constant()) | |
415 { | |
416 rhs_temp = Statement::make_temporary(NULL, rhs, loc); | |
417 inserter->insert(rhs_temp); | |
418 rhs = Expression::make_temporary_reference(rhs_temp, loc); | |
419 } | |
420 | |
421 Expression* indir = Expression::make_unary(OPERATOR_MULT, lhs, loc); | |
422 Statement* assign = Statement::make_assignment(indir, rhs, loc); | |
423 | |
424 lhs = Expression::make_temporary_reference(lhs_temp, loc); | |
425 if (rhs_temp != NULL) | |
426 rhs = Expression::make_temporary_reference(rhs_temp, loc); | |
427 | |
428 Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type()); | |
429 lhs = Expression::make_unsafe_cast(unsafe_ptr_type, lhs, loc); | |
430 | |
431 Expression* call; | |
432 switch (type->base()->classification()) | |
433 { | |
434 default: | |
435 go_unreachable(); | |
436 | |
437 case Type::TYPE_ERROR: | |
438 return assign; | |
439 | |
440 case Type::TYPE_POINTER: | |
441 case Type::TYPE_FUNCTION: | |
442 case Type::TYPE_MAP: | |
443 case Type::TYPE_CHANNEL: | |
444 // These types are all represented by a single pointer. | |
445 call = Runtime::make_call(Runtime::WRITEBARRIERPTR, loc, 2, lhs, rhs); | |
446 break; | |
447 | |
448 case Type::TYPE_STRING: | |
449 case Type::TYPE_STRUCT: | |
450 case Type::TYPE_ARRAY: | |
451 case Type::TYPE_INTERFACE: | |
452 { | |
453 rhs = Expression::make_unary(OPERATOR_AND, rhs, loc); | |
454 rhs->unary_expression()->set_does_not_escape(); | |
455 call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3, | |
456 Expression::make_type_descriptor(type, loc), | |
457 lhs, rhs); | |
458 } | |
459 break; | |
460 } | |
461 | |
462 return this->check_write_barrier(enclosing, assign, | |
463 Statement::make_statement(call, false)); | |
464 } | |
465 | |
466 // Return a statement that tests whether write barriers are enabled | |
467 // and executes either the efficient code or the write barrier | |
468 // function call, depending. | |
469 | |
470 Statement* | |
471 Gogo::check_write_barrier(Block* enclosing, Statement* without, | |
472 Statement* with) | |
473 { | |
474 Location loc = without->location(); | |
475 Named_object* wb = this->write_barrier_variable(); | |
476 Expression* ref = Expression::make_var_reference(wb, loc); | |
477 Expression* zero = Expression::make_integer_ul(0, ref->type(), loc); | |
478 Expression* cond = Expression::make_binary(OPERATOR_EQEQ, ref, zero, loc); | |
479 | |
480 Block* then_block = new Block(enclosing, loc); | |
481 then_block->add_statement(without); | |
482 | |
483 Block* else_block = new Block(enclosing, loc); | |
484 else_block->add_statement(with); | |
485 | |
486 return Statement::make_if_statement(cond, then_block, else_block, loc); | |
487 } |