annotate gcc/go/gofrontend/wb.cc @ 111:04ced10e8804

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