]>
Commit | Line | Data |
---|---|---|
b4c522fa | 1 | /* toir.cc -- Lower D frontend statements to GCC trees. |
a5544970 | 2 | Copyright (C) 2006-2019 Free Software Foundation, Inc. |
b4c522fa IB |
3 | |
4 | GCC is free software; you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation; either version 3, or (at your option) | |
7 | any later version. | |
8 | ||
9 | GCC is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with GCC; see the file COPYING3. If not see | |
16 | <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | #include "config.h" | |
19 | #include "system.h" | |
20 | #include "coretypes.h" | |
21 | ||
22 | #include "dmd/aggregate.h" | |
23 | #include "dmd/declaration.h" | |
24 | #include "dmd/expression.h" | |
25 | #include "dmd/identifier.h" | |
26 | #include "dmd/init.h" | |
27 | #include "dmd/statement.h" | |
28 | ||
29 | #include "tree.h" | |
30 | #include "tree-iterator.h" | |
31 | #include "options.h" | |
32 | #include "stmt.h" | |
33 | #include "fold-const.h" | |
34 | #include "diagnostic.h" | |
35 | #include "stringpool.h" | |
36 | #include "function.h" | |
37 | #include "toplev.h" | |
38 | ||
39 | #include "d-tree.h" | |
40 | ||
41 | ||
42 | /* Update data for defined and undefined labels when leaving a scope. */ | |
43 | ||
44 | bool | |
45 | pop_binding_label (Statement * const &, d_label_entry *ent, binding_level *bl) | |
46 | { | |
47 | binding_level *obl = bl->level_chain; | |
48 | ||
49 | if (ent->level == bl) | |
50 | { | |
51 | if (bl->kind == level_try) | |
52 | ent->in_try_scope = true; | |
53 | else if (bl->kind == level_catch) | |
54 | ent->in_catch_scope = true; | |
55 | ||
56 | ent->level = obl; | |
57 | } | |
58 | else if (ent->fwdrefs) | |
59 | { | |
60 | for (d_label_use_entry *ref = ent->fwdrefs; ref; ref = ref->next) | |
61 | ref->level = obl; | |
62 | } | |
63 | ||
64 | return true; | |
65 | } | |
66 | ||
67 | /* At the end of a function, all labels declared within the function | |
68 | go out of scope. BLOCK is the top-level block for the function. */ | |
69 | ||
70 | bool | |
71 | pop_label (Statement * const &s, d_label_entry *ent, tree block) | |
72 | { | |
73 | if (!ent->bc_label) | |
74 | { | |
75 | /* Put the labels into the "variables" of the top-level block, | |
76 | so debugger can see them. */ | |
77 | if (DECL_NAME (ent->label)) | |
78 | { | |
79 | gcc_assert (DECL_INITIAL (ent->label) != NULL_TREE); | |
80 | DECL_CHAIN (ent->label) = BLOCK_VARS (block); | |
81 | BLOCK_VARS (block) = ent->label; | |
82 | } | |
83 | } | |
84 | ||
85 | d_function_chain->labels->remove (s); | |
86 | ||
87 | return true; | |
88 | } | |
89 | ||
90 | /* The D front-end does not use the 'binding level' system for a symbol table, | |
91 | however it has been the goto structure for tracking code flow. | |
92 | Primarily it is only needed to get debugging information for local variables | |
93 | and otherwise support the back-end. */ | |
94 | ||
95 | void | |
96 | push_binding_level (level_kind kind) | |
97 | { | |
98 | /* Add it to the front of currently active scopes stack. */ | |
99 | binding_level *new_level = ggc_cleared_alloc<binding_level> (); | |
100 | new_level->level_chain = current_binding_level; | |
101 | new_level->kind = kind; | |
102 | ||
103 | current_binding_level = new_level; | |
104 | } | |
105 | ||
106 | tree | |
107 | pop_binding_level (void) | |
108 | { | |
109 | binding_level *level = current_binding_level; | |
110 | current_binding_level = level->level_chain; | |
111 | ||
112 | tree block = make_node (BLOCK); | |
113 | BLOCK_VARS (block) = level->names; | |
114 | BLOCK_SUBBLOCKS (block) = level->blocks; | |
115 | ||
116 | /* In each subblock, record that this is its superior. */ | |
117 | for (tree t = level->blocks; t; t = BLOCK_CHAIN (t)) | |
118 | BLOCK_SUPERCONTEXT (t) = block; | |
119 | ||
120 | if (level->kind == level_function) | |
121 | { | |
122 | /* Dispose of the block that we just made inside some higher level. */ | |
123 | DECL_INITIAL (current_function_decl) = block; | |
124 | BLOCK_SUPERCONTEXT (block) = current_function_decl; | |
125 | ||
126 | /* Pop all the labels declared in the function. */ | |
127 | if (d_function_chain->labels) | |
128 | d_function_chain->labels->traverse<tree, &pop_label> (block); | |
129 | } | |
130 | else | |
131 | { | |
132 | /* Any uses of undefined labels, and any defined labels, now operate | |
133 | under constraints of next binding contour. */ | |
134 | if (d_function_chain && d_function_chain->labels) | |
135 | { | |
136 | language_function *f = d_function_chain; | |
137 | f->labels->traverse<binding_level *, &pop_binding_label> (level); | |
138 | } | |
139 | ||
140 | current_binding_level->blocks | |
141 | = block_chainon (current_binding_level->blocks, block); | |
142 | } | |
143 | ||
144 | TREE_USED (block) = 1; | |
145 | return block; | |
146 | } | |
147 | ||
148 | /* Create an empty statement tree rooted at T. */ | |
149 | ||
150 | void | |
151 | push_stmt_list (void) | |
152 | { | |
153 | tree t = alloc_stmt_list (); | |
154 | vec_safe_push (d_function_chain->stmt_list, t); | |
155 | d_keep (t); | |
156 | } | |
157 | ||
158 | /* Finish the statement tree rooted at T. */ | |
159 | ||
160 | tree | |
161 | pop_stmt_list (void) | |
162 | { | |
163 | tree t = d_function_chain->stmt_list->pop (); | |
164 | ||
165 | /* If the statement list is completely empty, just return it. This is just | |
166 | as good as build_empty_stmt, with the advantage that statement lists | |
167 | are merged when they are appended to one another. So using the | |
168 | STATEMENT_LIST avoids pathological buildup of EMPTY_STMT_P statements. */ | |
169 | if (TREE_SIDE_EFFECTS (t)) | |
170 | { | |
171 | /* If the statement list contained exactly one statement, then extract | |
172 | it immediately. */ | |
173 | tree_stmt_iterator i = tsi_start (t); | |
174 | ||
175 | if (tsi_one_before_end_p (i)) | |
176 | { | |
177 | tree u = tsi_stmt (i); | |
178 | tsi_delink (&i); | |
179 | free_stmt_list (t); | |
180 | t = u; | |
181 | } | |
182 | } | |
183 | ||
184 | return t; | |
185 | } | |
186 | ||
187 | /* T is an expression statement. Add it to the statement-tree. */ | |
188 | ||
189 | void | |
190 | add_stmt (tree t) | |
191 | { | |
192 | /* Ignore (void) 0; expression statements received from the frontend. | |
193 | Likewise void_node is used when contracts become nops in release code. */ | |
194 | if (t == void_node || IS_EMPTY_STMT (t)) | |
195 | return; | |
196 | ||
197 | /* At this point, we no longer care about the value of expressions, | |
198 | so if there's no side-effects, then don't add it. */ | |
199 | if (!TREE_SIDE_EFFECTS (t)) | |
200 | return; | |
201 | ||
202 | if (TREE_CODE (t) == COMPOUND_EXPR) | |
203 | { | |
204 | /* Push out each comma expressions as separate statements. */ | |
205 | add_stmt (TREE_OPERAND (t, 0)); | |
206 | add_stmt (TREE_OPERAND (t, 1)); | |
207 | } | |
208 | else | |
209 | { | |
210 | /* Force the type to be void so we don't need to create a temporary | |
211 | variable to hold the inner expression. */ | |
212 | if (TREE_CODE (t) == CLEANUP_POINT_EXPR) | |
213 | TREE_TYPE (t) = void_type_node; | |
214 | ||
215 | /* Append the expression to the statement list. | |
216 | Make sure it has a proper location. */ | |
217 | if (EXPR_P (t) && !EXPR_HAS_LOCATION (t)) | |
218 | SET_EXPR_LOCATION (t, input_location); | |
219 | ||
220 | tree stmt_list = d_function_chain->stmt_list->last (); | |
221 | append_to_statement_list_force (t, &stmt_list); | |
222 | } | |
223 | } | |
224 | ||
225 | /* Implements the visitor interface to build the GCC trees of all Statement | |
226 | AST classes emitted from the D Front-end. | |
227 | All visit methods accept one parameter S, which holds the frontend AST | |
228 | of the statement to compile. They also don't return any value, instead | |
229 | generated code are pushed to add_stmt(), which appends them to the | |
230 | statement list in the current_binding_level. */ | |
231 | ||
232 | class IRVisitor : public Visitor | |
233 | { | |
234 | using Visitor::visit; | |
235 | ||
236 | FuncDeclaration *func_; | |
237 | ||
238 | /* Stack of labels which are targets for "break" and "continue", | |
239 | linked through TREE_CHAIN. */ | |
240 | tree break_label_; | |
241 | tree continue_label_; | |
242 | ||
243 | public: | |
244 | IRVisitor (FuncDeclaration *fd) | |
245 | { | |
246 | this->func_ = fd; | |
247 | this->break_label_ = NULL_TREE; | |
248 | this->continue_label_ = NULL_TREE; | |
249 | } | |
250 | ||
251 | /* Helper for generating code for the statement AST class S. | |
252 | Sets up the location of the statement before lowering. */ | |
253 | ||
254 | void build_stmt (Statement *s) | |
255 | { | |
256 | location_t saved_location = input_location; | |
257 | input_location = make_location_t (s->loc); | |
258 | s->accept (this); | |
259 | input_location = saved_location; | |
260 | } | |
261 | ||
262 | /* Start a new scope for a KIND statement. | |
263 | Each user-declared variable will have a binding contour that begins | |
264 | where the variable is declared and ends at its containing scope. */ | |
265 | ||
266 | void start_scope (level_kind kind) | |
267 | { | |
268 | push_binding_level (kind); | |
269 | push_stmt_list (); | |
270 | } | |
271 | ||
272 | /* Leave scope pushed by start_scope, returning a new bind_expr if | |
273 | any variables where declared in the scope. */ | |
274 | ||
275 | tree end_scope (void) | |
276 | { | |
277 | tree block = pop_binding_level (); | |
278 | tree body = pop_stmt_list (); | |
279 | ||
280 | if (! BLOCK_VARS (block)) | |
281 | return body; | |
282 | ||
283 | tree bind = build3 (BIND_EXPR, void_type_node, | |
284 | BLOCK_VARS (block), body, block); | |
285 | TREE_SIDE_EFFECTS (bind) = 1; | |
286 | return bind; | |
287 | } | |
288 | ||
289 | /* Like end_scope, but also push it into the outer statement-tree. */ | |
290 | ||
291 | void finish_scope (void) | |
292 | { | |
293 | tree scope = this->end_scope (); | |
294 | add_stmt (scope); | |
295 | } | |
296 | ||
297 | /* Return TRUE if IDENT is the current function return label. */ | |
298 | ||
299 | bool is_return_label (Identifier *ident) | |
300 | { | |
301 | if (this->func_->returnLabel) | |
302 | return this->func_->returnLabel->ident == ident; | |
303 | ||
304 | return false; | |
305 | } | |
306 | ||
307 | /* Define a label, specifying the location in the source file. | |
308 | Return the LABEL_DECL node for the label. */ | |
309 | ||
310 | tree define_label (Statement *s, Identifier *ident = NULL) | |
311 | { | |
312 | tree label = this->lookup_label (s, ident); | |
313 | gcc_assert (DECL_INITIAL (label) == NULL_TREE); | |
314 | ||
315 | d_label_entry *ent = d_function_chain->labels->get (s); | |
316 | gcc_assert (ent != NULL); | |
317 | ||
318 | /* Mark label as having been defined. */ | |
319 | DECL_INITIAL (label) = error_mark_node; | |
320 | ||
321 | ent->level = current_binding_level; | |
322 | ||
323 | for (d_label_use_entry *ref = ent->fwdrefs; ref ; ref = ref->next) | |
324 | this->check_previous_goto (ent->statement, ref); | |
325 | ent->fwdrefs = NULL; | |
326 | ||
327 | return label; | |
328 | } | |
329 | ||
330 | /* Emit a LABEL expression. */ | |
331 | ||
332 | void do_label (tree label) | |
333 | { | |
334 | /* Don't write out label unless it is marked as used by the frontend. | |
335 | This makes auto-vectorization possible in conditional loops. | |
336 | The only excemption to this is in the LabelStatement visitor, | |
337 | in which all computed labels are marked regardless. */ | |
338 | if (TREE_USED (label)) | |
339 | add_stmt (build1 (LABEL_EXPR, void_type_node, label)); | |
340 | } | |
341 | ||
342 | /* Emit a goto expression to LABEL. */ | |
343 | ||
344 | void do_jump (tree label) | |
345 | { | |
346 | add_stmt (fold_build1 (GOTO_EXPR, void_type_node, label)); | |
347 | TREE_USED (label) = 1; | |
348 | } | |
349 | ||
350 | /* Check that a new jump at statement scope FROM to a label declared in | |
351 | statement scope TO is valid. */ | |
352 | ||
353 | void check_goto (Statement *from, Statement *to) | |
354 | { | |
355 | d_label_entry *ent = d_function_chain->labels->get (to); | |
356 | gcc_assert (ent != NULL); | |
357 | ||
358 | /* If the label hasn't been defined yet, defer checking. */ | |
359 | if (! DECL_INITIAL (ent->label)) | |
360 | { | |
361 | d_label_use_entry *fwdref = ggc_alloc<d_label_use_entry> (); | |
362 | fwdref->level = current_binding_level; | |
363 | fwdref->statement = from; | |
364 | fwdref->next = ent->fwdrefs; | |
365 | ent->fwdrefs = fwdref; | |
366 | return; | |
367 | } | |
368 | ||
369 | if (ent->in_try_scope) | |
370 | error_at (make_location_t (from->loc), "cannot goto into try block"); | |
371 | else if (ent->in_catch_scope) | |
372 | error_at (make_location_t (from->loc), "cannot goto into catch block"); | |
373 | } | |
374 | ||
375 | /* Check that a previously seen jump to a newly defined label is valid. | |
376 | S is the label statement; FWDREF is the jump context. This is called | |
377 | for both user-defined and case labels. */ | |
378 | ||
379 | void check_previous_goto (Statement *s, d_label_use_entry *fwdref) | |
380 | { | |
381 | for (binding_level *b = current_binding_level; b ; b = b->level_chain) | |
382 | { | |
383 | if (b == fwdref->level) | |
384 | break; | |
385 | ||
386 | if (b->kind == level_try || b->kind == level_catch) | |
387 | { | |
388 | location_t location; | |
389 | ||
390 | if (s->isLabelStatement ()) | |
391 | { | |
392 | location = make_location_t (fwdref->statement->loc); | |
393 | if (b->kind == level_try) | |
394 | error_at (location, "cannot goto into try block"); | |
395 | else | |
396 | error_at (location, "cannot goto into catch block"); | |
397 | } | |
398 | else if (s->isCaseStatement ()) | |
399 | { | |
400 | location = make_location_t (s->loc); | |
401 | error_at (location, "case cannot be in different " | |
402 | "try block level from switch"); | |
403 | } | |
404 | else if (s->isDefaultStatement ()) | |
405 | { | |
406 | location = make_location_t (s->loc); | |
407 | error_at (location, "default cannot be in different " | |
408 | "try block level from switch"); | |
409 | } | |
410 | else | |
411 | gcc_unreachable (); | |
412 | } | |
413 | } | |
414 | } | |
415 | ||
416 | /* Get or build LABEL_DECL using the IDENT and statement block S given. */ | |
417 | ||
418 | tree lookup_label (Statement *s, Identifier *ident = NULL) | |
419 | { | |
420 | /* You can't use labels at global scope. */ | |
421 | if (d_function_chain == NULL) | |
422 | { | |
423 | error ("label %s referenced outside of any function", | |
424 | ident ? ident->toChars () : "(unnamed)"); | |
425 | return NULL_TREE; | |
426 | } | |
427 | ||
428 | /* Create the label htab for the function on demand. */ | |
429 | if (!d_function_chain->labels) | |
430 | { | |
431 | d_function_chain->labels | |
432 | = hash_map<Statement *, d_label_entry>::create_ggc (13); | |
433 | } | |
434 | ||
435 | d_label_entry *ent = d_function_chain->labels->get (s); | |
436 | if (ent != NULL) | |
437 | return ent->label; | |
438 | else | |
439 | { | |
440 | tree name = ident ? get_identifier (ident->toChars ()) : NULL_TREE; | |
441 | tree decl = build_decl (make_location_t (s->loc), LABEL_DECL, | |
442 | name, void_type_node); | |
443 | DECL_CONTEXT (decl) = current_function_decl; | |
444 | DECL_MODE (decl) = VOIDmode; | |
445 | ||
446 | /* Create new empty slot. */ | |
447 | ent = ggc_cleared_alloc<d_label_entry> (); | |
448 | ent->statement = s; | |
449 | ent->label = decl; | |
450 | ||
451 | bool existed = d_function_chain->labels->put (s, *ent); | |
452 | gcc_assert (!existed); | |
453 | ||
454 | return decl; | |
455 | } | |
456 | } | |
457 | ||
458 | /* Get the LABEL_DECL to represent a break or continue for the | |
459 | statement S given. BC indicates which. */ | |
460 | ||
461 | tree lookup_bc_label (Statement *s, bc_kind bc) | |
462 | { | |
463 | tree vec = this->lookup_label (s); | |
464 | ||
465 | /* The break and continue labels are put into a TREE_VEC. */ | |
466 | if (TREE_CODE (vec) == LABEL_DECL) | |
467 | { | |
468 | d_label_entry *ent = d_function_chain->labels->get (s); | |
469 | gcc_assert (ent != NULL); | |
470 | ||
471 | vec = make_tree_vec (2); | |
472 | TREE_VEC_ELT (vec, bc_break) = ent->label; | |
473 | ||
474 | /* Build the continue label. */ | |
475 | tree label = build_decl (make_location_t (s->loc), LABEL_DECL, | |
476 | NULL_TREE, void_type_node); | |
477 | DECL_CONTEXT (label) = current_function_decl; | |
478 | DECL_MODE (label) = VOIDmode; | |
479 | TREE_VEC_ELT (vec, bc_continue) = label; | |
480 | ||
481 | ent->label = vec; | |
482 | ent->bc_label = true; | |
483 | } | |
484 | ||
485 | return TREE_VEC_ELT (vec, bc); | |
486 | } | |
487 | ||
488 | /* Set and return the current break label for the current block. */ | |
489 | ||
490 | tree push_break_label (Statement *s) | |
491 | { | |
492 | tree label = this->lookup_bc_label (s->getRelatedLabeled (), bc_break); | |
493 | DECL_CHAIN (label) = this->break_label_; | |
494 | this->break_label_ = label; | |
495 | return label; | |
496 | } | |
497 | ||
498 | /* Finish with the current break label. */ | |
499 | ||
500 | void pop_break_label (tree label) | |
501 | { | |
502 | gcc_assert (this->break_label_ == label); | |
503 | this->break_label_ = DECL_CHAIN (this->break_label_); | |
504 | this->do_label (label); | |
505 | } | |
506 | ||
507 | /* Set and return the continue label for the current block. */ | |
508 | ||
509 | tree push_continue_label (Statement *s) | |
510 | { | |
511 | tree label = this->lookup_bc_label (s->getRelatedLabeled (), bc_continue); | |
512 | DECL_CHAIN (label) = this->continue_label_; | |
513 | this->continue_label_ = label; | |
514 | return label; | |
515 | } | |
516 | ||
517 | /* Finish with the current continue label. */ | |
518 | ||
519 | void pop_continue_label (tree label) | |
520 | { | |
521 | gcc_assert (this->continue_label_ == label); | |
522 | this->continue_label_ = DECL_CHAIN (this->continue_label_); | |
523 | this->do_label (label); | |
524 | } | |
525 | ||
526 | /* Visitor interfaces. */ | |
527 | ||
528 | ||
529 | /* This should be overridden by each statement class. */ | |
530 | ||
531 | void visit (Statement *) | |
532 | { | |
533 | gcc_unreachable (); | |
534 | } | |
535 | ||
536 | /* The frontend lowers `scope (exit/failure/success)' statements as | |
537 | try/catch/finally. At this point, this statement is just an empty | |
538 | placeholder. Maybe the frontend shouldn't leak these. */ | |
539 | ||
540 | void visit (OnScopeStatement *) | |
541 | { | |
542 | } | |
543 | ||
544 | /* If statements provide simple conditional execution of statements. */ | |
545 | ||
546 | void visit (IfStatement *s) | |
547 | { | |
548 | this->start_scope (level_cond); | |
549 | ||
550 | /* Build the outer 'if' condition, which may produce temporaries | |
551 | requiring scope destruction. */ | |
552 | tree ifcond = convert_for_condition (build_expr_dtor (s->condition), | |
553 | s->condition->type); | |
554 | tree ifbody = void_node; | |
555 | tree elsebody = void_node; | |
556 | ||
557 | /* Build the 'then' branch. */ | |
558 | if (s->ifbody) | |
559 | { | |
560 | push_stmt_list (); | |
561 | this->build_stmt (s->ifbody); | |
562 | ifbody = pop_stmt_list (); | |
563 | } | |
564 | ||
565 | /* Now build the 'else' branch, which may have nested 'else if' parts. */ | |
566 | if (s->elsebody) | |
567 | { | |
568 | push_stmt_list (); | |
569 | this->build_stmt (s->elsebody); | |
570 | elsebody = pop_stmt_list (); | |
571 | } | |
572 | ||
573 | /* Wrap up our constructed if condition into a COND_EXPR. */ | |
574 | tree cond = build_vcondition (ifcond, ifbody, elsebody); | |
575 | add_stmt (cond); | |
576 | ||
577 | /* Finish the if-then scope. */ | |
578 | this->finish_scope (); | |
579 | } | |
580 | ||
581 | /* Should there be any `pragma (...)' statements requiring code generation, | |
582 | here would be the place to do it. For now, all pragmas are handled | |
583 | by the frontend. */ | |
584 | ||
585 | void visit (PragmaStatement *) | |
586 | { | |
587 | } | |
588 | ||
589 | /* The frontend lowers `while (...)' statements as `for (...)' loops. | |
590 | This visitor is not strictly required other than to enforce that | |
591 | these kinds of statements never reach here. */ | |
592 | ||
593 | void visit (WhileStatement *) | |
594 | { | |
595 | gcc_unreachable (); | |
596 | } | |
597 | ||
598 | /* Do while statments implement simple loops. The body is executed, then | |
599 | the condition is evaluated. */ | |
600 | ||
601 | void visit (DoStatement *s) | |
602 | { | |
603 | tree lbreak = this->push_break_label (s); | |
604 | ||
605 | this->start_scope (level_loop); | |
606 | if (s->_body) | |
607 | { | |
608 | tree lcontinue = this->push_continue_label (s); | |
609 | this->build_stmt (s->_body); | |
610 | this->pop_continue_label (lcontinue); | |
611 | } | |
612 | ||
613 | /* Build the outer 'while' condition, which may produce temporaries | |
614 | requiring scope destruction. */ | |
615 | tree exitcond = convert_for_condition (build_expr_dtor (s->condition), | |
616 | s->condition->type); | |
617 | add_stmt (build_vcondition (exitcond, void_node, | |
618 | build1 (GOTO_EXPR, void_type_node, lbreak))); | |
619 | TREE_USED (lbreak) = 1; | |
620 | ||
621 | tree body = this->end_scope (); | |
622 | add_stmt (build1 (LOOP_EXPR, void_type_node, body)); | |
623 | ||
624 | this->pop_break_label (lbreak); | |
625 | } | |
626 | ||
627 | /* For statements implement loops with initialization, test, and | |
628 | increment clauses. */ | |
629 | ||
630 | void visit (ForStatement *s) | |
631 | { | |
632 | tree lbreak = this->push_break_label (s); | |
633 | this->start_scope (level_loop); | |
634 | ||
635 | if (s->_init) | |
636 | this->build_stmt (s->_init); | |
637 | ||
638 | if (s->condition) | |
639 | { | |
640 | tree exitcond = convert_for_condition (build_expr_dtor (s->condition), | |
641 | s->condition->type); | |
642 | add_stmt (build_vcondition (exitcond, void_node, | |
643 | build1 (GOTO_EXPR, void_type_node, | |
644 | lbreak))); | |
645 | TREE_USED (lbreak) = 1; | |
646 | } | |
647 | ||
648 | if (s->_body) | |
649 | { | |
650 | tree lcontinue = this->push_continue_label (s); | |
651 | this->build_stmt (s->_body); | |
652 | this->pop_continue_label (lcontinue); | |
653 | } | |
654 | ||
655 | if (s->increment) | |
656 | { | |
657 | /* Force side effects? */ | |
658 | add_stmt (build_expr_dtor (s->increment)); | |
659 | } | |
660 | ||
661 | tree body = this->end_scope (); | |
662 | add_stmt (build1 (LOOP_EXPR, void_type_node, body)); | |
663 | ||
664 | this->pop_break_label (lbreak); | |
665 | } | |
666 | ||
667 | /* The frontend lowers `foreach (...)' statements as `for (...)' loops. | |
668 | This visitor is not strictly required other than to enforce that | |
669 | these kinds of statements never reach here. */ | |
670 | ||
671 | void visit (ForeachStatement *) | |
672 | { | |
673 | gcc_unreachable (); | |
674 | } | |
675 | ||
676 | /* The frontend lowers `foreach (...; [x..y])' statements as `for (...)' | |
677 | loops. This visitor is not strictly required other than to enforce that | |
678 | these kinds of statements never reach here. */ | |
679 | ||
680 | void visit (ForeachRangeStatement *) | |
681 | { | |
682 | gcc_unreachable (); | |
683 | } | |
684 | ||
685 | /* Jump to the associated exit label for the current loop. If IDENT | |
686 | for the Statement is not null, then the label is user defined. */ | |
687 | ||
688 | void visit (BreakStatement *s) | |
689 | { | |
690 | if (s->ident) | |
691 | { | |
692 | /* The break label may actually be some levels up. | |
693 | eg: on a try/finally wrapping a loop. */ | |
694 | LabelStatement *label = this->func_->searchLabel (s->ident)->statement; | |
695 | gcc_assert (label != NULL); | |
696 | Statement *stmt = label->statement->getRelatedLabeled (); | |
697 | this->do_jump (this->lookup_bc_label (stmt, bc_break)); | |
698 | } | |
699 | else | |
700 | this->do_jump (this->break_label_); | |
701 | } | |
702 | ||
703 | /* Jump to the associated continue label for the current loop. If IDENT | |
704 | for the Statement is not null, then the label is user defined. */ | |
705 | ||
706 | void visit (ContinueStatement *s) | |
707 | { | |
708 | if (s->ident) | |
709 | { | |
710 | LabelStatement *label = this->func_->searchLabel (s->ident)->statement; | |
711 | gcc_assert (label != NULL); | |
712 | this->do_jump (this->lookup_bc_label (label->statement, | |
713 | bc_continue)); | |
714 | } | |
715 | else | |
716 | this->do_jump (this->continue_label_); | |
717 | } | |
718 | ||
719 | /* A goto statement jumps to the statement identified by the given label. */ | |
720 | ||
721 | void visit (GotoStatement *s) | |
722 | { | |
723 | gcc_assert (s->label->statement != NULL); | |
724 | gcc_assert (s->tf == s->label->statement->tf); | |
725 | ||
726 | /* If no label found, there was an error. */ | |
727 | tree label = this->lookup_label (s->label->statement, s->label->ident); | |
728 | this->do_jump (label); | |
729 | ||
730 | /* Need to error if the goto is jumping into a try or catch block. */ | |
731 | this->check_goto (s, s->label->statement); | |
732 | } | |
733 | ||
734 | /* Statements can be labeled. A label is an identifier that precedes | |
735 | a statement. */ | |
736 | ||
737 | void visit (LabelStatement *s) | |
738 | { | |
739 | LabelDsymbol *sym; | |
740 | ||
741 | if (this->is_return_label (s->ident)) | |
742 | sym = this->func_->returnLabel; | |
743 | else | |
744 | sym = this->func_->searchLabel (s->ident); | |
745 | ||
746 | /* If no label found, there was an error. */ | |
747 | tree label = this->define_label (sym->statement, sym->ident); | |
748 | TREE_USED (label) = 1; | |
749 | ||
750 | this->do_label (label); | |
751 | ||
752 | if (this->is_return_label (s->ident) && this->func_->fensure != NULL) | |
753 | this->build_stmt (this->func_->fensure); | |
754 | else if (s->statement) | |
755 | this->build_stmt (s->statement); | |
756 | } | |
757 | ||
758 | /* A switch statement goes to one of a collection of case statements | |
759 | depending on the value of the switch expression. */ | |
760 | ||
761 | void visit (SwitchStatement *s) | |
762 | { | |
763 | this->start_scope (level_switch); | |
764 | tree lbreak = this->push_break_label (s); | |
765 | ||
766 | tree condition = build_expr_dtor (s->condition); | |
767 | Type *condtype = s->condition->type->toBasetype (); | |
768 | ||
769 | /* A switch statement on a string gets turned into a library call, | |
770 | which does a binary lookup on list of string cases. */ | |
771 | if (s->condition->type->isString ()) | |
772 | { | |
773 | Type *etype = condtype->nextOf ()->toBasetype (); | |
774 | libcall_fn libcall; | |
775 | ||
776 | switch (etype->ty) | |
777 | { | |
778 | case Tchar: | |
779 | libcall = LIBCALL_SWITCH_STRING; | |
780 | break; | |
781 | ||
782 | case Twchar: | |
783 | libcall = LIBCALL_SWITCH_USTRING; | |
784 | break; | |
785 | ||
786 | case Tdchar: | |
787 | libcall = LIBCALL_SWITCH_DSTRING; | |
788 | break; | |
789 | ||
790 | default: | |
791 | ::error ("switch statement value must be an array of " | |
792 | "some character type, not %s", etype->toChars ()); | |
793 | gcc_unreachable (); | |
794 | } | |
795 | ||
796 | /* Apparently the backend is supposed to sort and set the indexes | |
797 | on the case array, have to change them to be usable. */ | |
798 | Type *satype = condtype->sarrayOf (s->cases->dim); | |
799 | vec<constructor_elt, va_gc> *elms = NULL; | |
800 | ||
801 | s->cases->sort (); | |
802 | ||
803 | for (size_t i = 0; i < s->cases->dim; i++) | |
804 | { | |
805 | CaseStatement *cs = (*s->cases)[i]; | |
806 | cs->index = i; | |
807 | ||
808 | if (cs->exp->op != TOKstring) | |
809 | s->error ("case '%s' is not a string", cs->exp->toChars ()); | |
810 | else | |
811 | { | |
812 | tree exp = build_expr (cs->exp, true); | |
813 | CONSTRUCTOR_APPEND_ELT (elms, size_int (i), exp); | |
814 | } | |
815 | } | |
816 | ||
817 | /* Build static declaration to reference constructor. */ | |
818 | tree ctor = build_constructor (build_ctype (satype), elms); | |
819 | tree decl = build_artificial_decl (TREE_TYPE (ctor), ctor); | |
820 | TREE_READONLY (decl) = 1; | |
821 | d_pushdecl (decl); | |
822 | rest_of_decl_compilation (decl, 1, 0); | |
823 | ||
824 | /* Pass it as a dynamic array. */ | |
825 | decl = d_array_value (build_ctype (condtype->arrayOf ()), | |
826 | size_int (s->cases->dim), | |
827 | build_address (decl)); | |
828 | ||
829 | condition = build_libcall (libcall, Type::tint32, 2, decl, condition); | |
830 | } | |
831 | else if (!condtype->isscalar ()) | |
832 | { | |
833 | error ("cannot handle switch condition of type %s", | |
834 | condtype->toChars ()); | |
835 | gcc_unreachable (); | |
836 | } | |
837 | ||
838 | condition = fold (condition); | |
839 | ||
840 | /* Build LABEL_DECLs now so they can be refered to by goto case. | |
841 | Also checking the jump from the switch to the label is allowed. */ | |
842 | if (s->cases) | |
843 | { | |
844 | for (size_t i = 0; i < s->cases->dim; i++) | |
845 | { | |
846 | CaseStatement *cs = (*s->cases)[i]; | |
847 | tree caselabel = this->lookup_label (cs); | |
848 | ||
849 | /* Write cases as a series of if-then-else blocks. | |
850 | if (condition == case) | |
851 | goto caselabel; */ | |
852 | if (s->hasVars) | |
853 | { | |
854 | tree ifcase = build2 (EQ_EXPR, build_ctype (condtype), | |
855 | condition, build_expr_dtor (cs->exp)); | |
856 | tree ifbody = fold_build1 (GOTO_EXPR, void_type_node, | |
857 | caselabel); | |
858 | tree cond = build_vcondition (ifcase, ifbody, void_node); | |
859 | TREE_USED (caselabel) = 1; | |
860 | LABEL_VARIABLE_CASE (caselabel) = 1; | |
861 | add_stmt (cond); | |
862 | } | |
863 | ||
864 | this->check_goto (s, cs); | |
865 | } | |
866 | ||
867 | if (s->sdefault) | |
868 | { | |
869 | tree defaultlabel = this->lookup_label (s->sdefault); | |
870 | ||
871 | /* The default label is the last 'else' block. */ | |
872 | if (s->hasVars) | |
873 | { | |
874 | this->do_jump (defaultlabel); | |
875 | LABEL_VARIABLE_CASE (defaultlabel) = 1; | |
876 | } | |
877 | ||
878 | this->check_goto (s, s->sdefault); | |
879 | } | |
880 | } | |
881 | ||
882 | /* Switch body goes in its own statement list. */ | |
883 | push_stmt_list (); | |
884 | if (s->_body) | |
885 | this->build_stmt (s->_body); | |
886 | ||
887 | tree casebody = pop_stmt_list (); | |
888 | ||
889 | /* Wrap up constructed body into a switch_expr, unless it was | |
890 | converted to an if-then-else expression. */ | |
891 | if (s->hasVars) | |
892 | add_stmt (casebody); | |
893 | else | |
894 | { | |
895 | tree switchexpr = build2 (SWITCH_EXPR, TREE_TYPE (condition), | |
896 | condition, casebody); | |
897 | add_stmt (switchexpr); | |
898 | SWITCH_ALL_CASES_P (switchexpr) = 1; | |
899 | } | |
900 | ||
901 | SWITCH_BREAK_LABEL_P (lbreak) = 1; | |
902 | ||
903 | /* If the switch had any 'break' statements, emit the label now. */ | |
904 | this->pop_break_label (lbreak); | |
905 | this->finish_scope (); | |
906 | } | |
907 | ||
908 | /* Declare the case label associated with the current SwitchStatement. */ | |
909 | ||
910 | void visit (CaseStatement *s) | |
911 | { | |
912 | /* Emit the case label. */ | |
913 | tree label = this->define_label (s); | |
914 | ||
915 | if (LABEL_VARIABLE_CASE (label)) | |
916 | this->do_label (label); | |
917 | else | |
918 | { | |
919 | tree casevalue; | |
920 | if (s->exp->type->isscalar ()) | |
921 | casevalue = build_expr (s->exp); | |
922 | else | |
923 | casevalue = build_integer_cst (s->index, build_ctype (Type::tint32)); | |
924 | ||
925 | tree caselabel = build_case_label (casevalue, NULL_TREE, label); | |
926 | add_stmt (caselabel); | |
927 | } | |
928 | ||
929 | /* Now do the body. */ | |
930 | if (s->statement) | |
931 | this->build_stmt (s->statement); | |
932 | } | |
933 | ||
934 | /* Declare the default label associated with the current SwitchStatement. */ | |
935 | ||
936 | void visit (DefaultStatement *s) | |
937 | { | |
938 | /* Emit the default case label. */ | |
939 | tree label = this->define_label (s); | |
940 | ||
941 | if (LABEL_VARIABLE_CASE (label)) | |
942 | this->do_label (label); | |
943 | else | |
944 | { | |
945 | tree caselabel = build_case_label (NULL_TREE, NULL_TREE, label); | |
946 | add_stmt (caselabel); | |
947 | } | |
948 | ||
949 | /* Now do the body. */ | |
950 | if (s->statement) | |
951 | this->build_stmt (s->statement); | |
952 | } | |
953 | ||
954 | /* Implements 'goto default' by jumping to the label associated with | |
955 | the DefaultStatement in a switch block. */ | |
956 | ||
957 | void visit (GotoDefaultStatement *s) | |
958 | { | |
959 | tree label = this->lookup_label (s->sw->sdefault); | |
960 | this->do_jump (label); | |
961 | } | |
962 | ||
963 | /* Implements 'goto case' by jumping to the label associated with the | |
964 | CaseStatement in a switch block. */ | |
965 | ||
966 | void visit (GotoCaseStatement *s) | |
967 | { | |
968 | tree label = this->lookup_label (s->cs); | |
969 | this->do_jump (label); | |
970 | } | |
971 | ||
972 | /* Throw a SwitchError exception, called when a switch statement has | |
973 | no DefaultStatement, yet none of the cases match. */ | |
974 | ||
975 | void visit (SwitchErrorStatement *s) | |
976 | { | |
977 | add_stmt (d_assert_call (s->loc, LIBCALL_SWITCH_ERROR)); | |
978 | } | |
979 | ||
980 | /* A return statement exits the current function and supplies its return | |
981 | value, if the return type is not void. */ | |
982 | ||
983 | void visit (ReturnStatement *s) | |
984 | { | |
985 | if (s->exp == NULL || s->exp->type->toBasetype ()->ty == Tvoid) | |
986 | { | |
987 | /* Return has no value. */ | |
988 | add_stmt (return_expr (NULL_TREE)); | |
989 | return; | |
990 | } | |
991 | ||
992 | TypeFunction *tf = (TypeFunction *)this->func_->type; | |
993 | Type *type = this->func_->tintro != NULL | |
994 | ? this->func_->tintro->nextOf () : tf->nextOf (); | |
995 | ||
996 | if ((this->func_->isMain () || this->func_->isCMain ()) | |
997 | && type->toBasetype ()->ty == Tvoid) | |
998 | type = Type::tint32; | |
999 | ||
1000 | if (this->func_->nrvo_can && this->func_->nrvo_var) | |
1001 | { | |
1002 | /* Just refer to the DECL_RESULT; this differs from using | |
1003 | NULL_TREE in that it indicates that we care about the value | |
1004 | of the DECL_RESULT. */ | |
1005 | tree decl = DECL_RESULT (get_symbol_decl (this->func_)); | |
1006 | add_stmt (return_expr (decl)); | |
1007 | } | |
1008 | else | |
1009 | { | |
1010 | /* Convert for initializing the DECL_RESULT. */ | |
1011 | tree expr = build_return_dtor (s->exp, type, tf); | |
1012 | add_stmt (expr); | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | /* Evaluate the enclosed expression, and add it to the statement list. */ | |
1017 | ||
1018 | void visit (ExpStatement *s) | |
1019 | { | |
1020 | if (s->exp) | |
1021 | { | |
1022 | /* Expression may produce temporaries requiring scope destruction. */ | |
1023 | tree exp = build_expr_dtor (s->exp); | |
1024 | add_stmt (exp); | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | /* Evaluate all enclosed statements. */ | |
1029 | ||
1030 | void visit (CompoundStatement *s) | |
1031 | { | |
1032 | if (s->statements == NULL) | |
1033 | return; | |
1034 | ||
1035 | for (size_t i = 0; i < s->statements->dim; i++) | |
1036 | { | |
1037 | Statement *statement = (*s->statements)[i]; | |
1038 | ||
1039 | if (statement != NULL) | |
1040 | this->build_stmt (statement); | |
1041 | } | |
1042 | } | |
1043 | ||
1044 | /* The frontend lowers `foreach (Tuple!(...))' statements as an unrolled loop. | |
1045 | These are compiled down as a `do ... while (0)', where each unrolled loop | |
1046 | is nested inside and given their own continue label to jump to. */ | |
1047 | ||
1048 | void visit (UnrolledLoopStatement *s) | |
1049 | { | |
1050 | if (s->statements == NULL) | |
1051 | return; | |
1052 | ||
1053 | tree lbreak = this->push_break_label (s); | |
1054 | this->start_scope (level_loop); | |
1055 | ||
1056 | for (size_t i = 0; i < s->statements->dim; i++) | |
1057 | { | |
1058 | Statement *statement = (*s->statements)[i]; | |
1059 | ||
1060 | if (statement != NULL) | |
1061 | { | |
1062 | tree lcontinue = this->push_continue_label (statement); | |
1063 | this->build_stmt (statement); | |
1064 | this->pop_continue_label (lcontinue); | |
1065 | } | |
1066 | } | |
1067 | ||
1068 | this->do_jump (this->break_label_); | |
1069 | ||
1070 | tree body = this->end_scope (); | |
1071 | add_stmt (build1 (LOOP_EXPR, void_type_node, body)); | |
1072 | ||
1073 | this->pop_break_label (lbreak); | |
1074 | } | |
1075 | ||
1076 | /* Start a new scope and visit all nested statements, wrapping | |
1077 | them up into a BIND_EXPR at the end of the scope. */ | |
1078 | ||
1079 | void visit (ScopeStatement *s) | |
1080 | { | |
1081 | if (s->statement == NULL) | |
1082 | return; | |
1083 | ||
1084 | this->start_scope (level_block); | |
1085 | this->build_stmt (s->statement); | |
1086 | this->finish_scope (); | |
1087 | } | |
1088 | ||
1089 | /* A with statement is a way to simplify repeated references to the same | |
1090 | object, where the handle is either a class or struct instance. */ | |
1091 | ||
1092 | void visit (WithStatement *s) | |
1093 | { | |
1094 | this->start_scope (level_with); | |
1095 | ||
1096 | if (s->wthis) | |
1097 | { | |
1098 | /* Perform initialisation of the 'with' handle. */ | |
1099 | ExpInitializer *ie = s->wthis->_init->isExpInitializer (); | |
1100 | gcc_assert (ie != NULL); | |
1101 | ||
1102 | declare_local_var (s->wthis); | |
1103 | tree init = build_expr_dtor (ie->exp); | |
1104 | add_stmt (init); | |
1105 | } | |
1106 | ||
1107 | if (s->_body) | |
1108 | this->build_stmt (s->_body); | |
1109 | ||
1110 | this->finish_scope (); | |
1111 | } | |
1112 | ||
1113 | /* Implements 'throw Object'. Frontend already checks that the object | |
1114 | thrown is a class type, but does not check if it is derived from | |
1115 | Object. Foreign objects are not currently supported at run-time. */ | |
1116 | ||
1117 | void visit (ThrowStatement *s) | |
1118 | { | |
1119 | ClassDeclaration *cd = s->exp->type->toBasetype ()->isClassHandle (); | |
1120 | InterfaceDeclaration *id = cd->isInterfaceDeclaration (); | |
1121 | tree arg = build_expr_dtor (s->exp); | |
1122 | ||
1123 | if (!flag_exceptions) | |
1124 | { | |
1125 | static int warned = 0; | |
1126 | if (!warned) | |
1127 | { | |
1128 | error_at (make_location_t (s->loc), "exception handling disabled, " | |
1129 | "use -fexceptions to enable"); | |
1130 | warned = 1; | |
1131 | } | |
1132 | } | |
1133 | ||
1134 | if (cd->isCPPclass () || (id != NULL && id->isCPPclass ())) | |
1135 | error_at (make_location_t (s->loc), "cannot throw C++ classes"); | |
1136 | else if (cd->com || (id != NULL && id->com)) | |
1137 | error_at (make_location_t (s->loc), "cannot throw COM objects"); | |
1138 | else | |
1139 | arg = build_nop (build_ctype (get_object_type ()), arg); | |
1140 | ||
1141 | add_stmt (build_libcall (LIBCALL_THROW, Type::tvoid, 1, arg)); | |
1142 | } | |
1143 | ||
1144 | /* Build a try-catch statement, one of the building blocks for exception | |
1145 | handling generated by the frontend. This is also used to implement | |
1146 | `scope (failure)' statements. */ | |
1147 | ||
1148 | void visit (TryCatchStatement *s) | |
1149 | { | |
1150 | this->start_scope (level_try); | |
1151 | if (s->_body) | |
1152 | this->build_stmt (s->_body); | |
1153 | ||
1154 | tree trybody = this->end_scope (); | |
1155 | ||
1156 | /* Try handlers go in their own statement list. */ | |
1157 | push_stmt_list (); | |
1158 | ||
1159 | if (s->catches) | |
1160 | { | |
1161 | for (size_t i = 0; i < s->catches->dim; i++) | |
1162 | { | |
1163 | Catch *vcatch = (*s->catches)[i]; | |
1164 | ||
1165 | this->start_scope (level_catch); | |
1166 | ||
1167 | tree ehptr = builtin_decl_explicit (BUILT_IN_EH_POINTER); | |
1168 | tree catchtype = build_ctype (vcatch->type); | |
1169 | tree object = NULL_TREE; | |
1170 | ||
1171 | ehptr = build_call_expr (ehptr, 1, integer_zero_node); | |
1172 | ||
1173 | /* Retrieve the internal exception object, which could be for a | |
1174 | D or C++ catch handler. This is different from the generic | |
1175 | exception pointer returned from gcc runtime. */ | |
1176 | Type *tcatch = vcatch->type->toBasetype (); | |
1177 | ClassDeclaration *cd = tcatch->isClassHandle (); | |
1178 | ||
1179 | libcall_fn libcall = (cd->isCPPclass ()) ? LIBCALL_CXA_BEGIN_CATCH | |
1180 | : LIBCALL_BEGIN_CATCH; | |
1181 | object = build_libcall (libcall, vcatch->type, 1, ehptr); | |
1182 | ||
1183 | if (vcatch->var) | |
1184 | { | |
1185 | tree var = get_symbol_decl (vcatch->var); | |
1186 | tree init = build_assign (INIT_EXPR, var, object); | |
1187 | ||
1188 | declare_local_var (vcatch->var); | |
1189 | add_stmt (init); | |
1190 | } | |
1191 | else | |
1192 | { | |
1193 | /* Still need to emit a call to __gdc_begin_catch() to | |
1194 | remove the object from the uncaught exceptions list. */ | |
1195 | add_stmt (object); | |
1196 | } | |
1197 | ||
1198 | if (vcatch->handler) | |
1199 | this->build_stmt (vcatch->handler); | |
1200 | ||
1201 | tree catchbody = this->end_scope (); | |
1202 | ||
1203 | /* Need to wrap C++ handlers in a try/finally block to signal | |
1204 | the end catch callback. */ | |
1205 | if (cd->isCPPclass ()) | |
1206 | { | |
1207 | tree endcatch = build_libcall (LIBCALL_CXA_END_CATCH, | |
1208 | Type::tvoid, 0); | |
1209 | catchbody = build2 (TRY_FINALLY_EXPR, void_type_node, | |
1210 | catchbody, endcatch); | |
1211 | } | |
1212 | ||
1213 | add_stmt (build2 (CATCH_EXPR, void_type_node, | |
1214 | catchtype, catchbody)); | |
1215 | } | |
1216 | } | |
1217 | ||
1218 | tree catches = pop_stmt_list (); | |
1219 | ||
1220 | /* Back-end expects all catches in a TRY_CATCH_EXPR to be enclosed in a | |
1221 | statement list, however pop_stmt_list may optimize away the list | |
1222 | if there is only a single catch to push. */ | |
1223 | if (TREE_CODE (catches) != STATEMENT_LIST) | |
1224 | { | |
1225 | tree stmt_list = alloc_stmt_list (); | |
1226 | append_to_statement_list_force (catches, &stmt_list); | |
1227 | catches = stmt_list; | |
1228 | } | |
1229 | ||
1230 | add_stmt (build2 (TRY_CATCH_EXPR, void_type_node, trybody, catches)); | |
1231 | } | |
1232 | ||
1233 | /* Build a try-finally statement, one of the building blocks for exception | |
1234 | handling generated by the frontend. This is also used to implement | |
1235 | `scope (exit)' statements. */ | |
1236 | ||
1237 | void visit (TryFinallyStatement *s) | |
1238 | { | |
1239 | this->start_scope (level_try); | |
1240 | if (s->_body) | |
1241 | this->build_stmt (s->_body); | |
1242 | ||
1243 | tree trybody = this->end_scope (); | |
1244 | ||
1245 | this->start_scope (level_finally); | |
1246 | if (s->finalbody) | |
1247 | this->build_stmt (s->finalbody); | |
1248 | ||
1249 | tree finally = this->end_scope (); | |
1250 | ||
1251 | add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node, trybody, finally)); | |
1252 | } | |
1253 | ||
1254 | /* The frontend lowers `synchronized (...)' statements as a call to | |
1255 | monitor/critical enter and exit wrapped around try/finally. | |
1256 | This visitor is not strictly required other than to enforce that | |
1257 | these kinds of statements never reach here. */ | |
1258 | ||
1259 | void visit (SynchronizedStatement *) | |
1260 | { | |
1261 | gcc_unreachable (); | |
1262 | } | |
1263 | ||
1264 | /* D Inline Assembler is not implemented, as it would require writing | |
1265 | an assembly parser for each supported target. Instead we leverage | |
1266 | GCC extended assembler using the GccAsmStatement class. */ | |
1267 | ||
1268 | void visit (AsmStatement *) | |
1269 | { | |
1270 | sorry ("D inline assembler statements are not supported in GDC."); | |
1271 | } | |
1272 | ||
1273 | /* Build a GCC extended assembler expression, whose components are | |
1274 | an INSN string, some OUTPUTS, some INPUTS, and some CLOBBERS. */ | |
1275 | ||
1276 | void visit (GccAsmStatement *s) | |
1277 | { | |
1278 | StringExp *insn = (StringExp *)s->insn; | |
1279 | tree outputs = NULL_TREE; | |
1280 | tree inputs = NULL_TREE; | |
1281 | tree clobbers = NULL_TREE; | |
1282 | tree labels = NULL_TREE; | |
1283 | ||
1284 | /* Collect all arguments, which may be input or output operands. */ | |
1285 | if (s->args) | |
1286 | { | |
1287 | for (size_t i = 0; i < s->args->dim; i++) | |
1288 | { | |
1289 | Identifier *name = (*s->names)[i]; | |
1290 | const char *sname = name ? name->toChars () : NULL; | |
1291 | tree id = name ? build_string (strlen (sname), sname) : NULL_TREE; | |
1292 | ||
1293 | StringExp *constr = (StringExp *)(*s->constraints)[i]; | |
1294 | const char *cstring = (const char *)(constr->len | |
1295 | ? constr->string : ""); | |
1296 | tree str = build_string (constr->len, cstring); | |
1297 | ||
1298 | Expression *earg = (*s->args)[i]; | |
1299 | tree val = build_expr (earg); | |
1300 | ||
1301 | if (i < s->outputargs) | |
1302 | { | |
1303 | tree arg = build_tree_list (id, str); | |
1304 | outputs = chainon (outputs, build_tree_list (arg, val)); | |
1305 | } | |
1306 | else | |
1307 | { | |
1308 | tree arg = build_tree_list (id, str); | |
1309 | inputs = chainon (inputs, build_tree_list (arg, val)); | |
1310 | } | |
1311 | } | |
1312 | } | |
1313 | ||
1314 | /* Collect all clobber arguments. */ | |
1315 | if (s->clobbers) | |
1316 | { | |
1317 | for (size_t i = 0; i < s->clobbers->dim; i++) | |
1318 | { | |
1319 | StringExp *clobber = (StringExp *)(*s->clobbers)[i]; | |
1320 | const char *cstring = (const char *)(clobber->len | |
1321 | ? clobber->string : ""); | |
1322 | ||
1323 | tree val = build_string (clobber->len, cstring); | |
1324 | clobbers = chainon (clobbers, build_tree_list (0, val)); | |
1325 | } | |
1326 | } | |
1327 | ||
1328 | /* Collect all goto labels, these should have been already checked | |
1329 | by the front-end, so pass down the label symbol to the back-end. */ | |
1330 | if (s->labels) | |
1331 | { | |
1332 | for (size_t i = 0; i < s->labels->dim; i++) | |
1333 | { | |
1334 | Identifier *ident = (*s->labels)[i]; | |
1335 | GotoStatement *gs = (*s->gotos)[i]; | |
1336 | ||
1337 | gcc_assert (gs->label->statement != NULL); | |
1338 | gcc_assert (gs->tf == gs->label->statement->tf); | |
1339 | ||
1340 | const char *sident = ident->toChars (); | |
1341 | tree name = build_string (strlen (sident), sident); | |
1342 | tree label = this->lookup_label (gs->label->statement, | |
1343 | gs->label->ident); | |
1344 | TREE_USED (label) = 1; | |
1345 | ||
1346 | labels = chainon (labels, build_tree_list (name, label)); | |
1347 | } | |
1348 | } | |
1349 | ||
1350 | /* Do some extra validation on all input and output operands. */ | |
1351 | const char *insnstring = (const char *)(insn->len ? insn->string : ""); | |
1352 | tree string = build_string (insn->len, insnstring); | |
1353 | string = resolve_asm_operand_names (string, outputs, inputs, labels); | |
1354 | ||
1355 | if (s->args) | |
1356 | { | |
1357 | unsigned noutputs = s->outputargs; | |
1358 | unsigned ninputs = (s->args->dim - noutputs); | |
1359 | const char **oconstraints = XALLOCAVEC (const char *, noutputs); | |
1360 | bool allows_mem, allows_reg, is_inout; | |
1361 | size_t i; | |
1362 | tree t; | |
1363 | ||
1364 | for (i = 0, t = outputs; t != NULL_TREE; t = TREE_CHAIN (t), i++) | |
1365 | { | |
1366 | tree output = TREE_VALUE (t); | |
1367 | const char *constraint | |
1368 | = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); | |
1369 | ||
1370 | oconstraints[i] = constraint; | |
1371 | ||
1372 | if (parse_output_constraint (&constraint, i, ninputs, noutputs, | |
1373 | &allows_mem, &allows_reg, &is_inout)) | |
1374 | { | |
1375 | /* If the output argument is going to end up in memory. */ | |
1376 | if (!allows_reg) | |
1377 | d_mark_addressable (output); | |
1378 | } | |
1379 | else | |
1380 | output = error_mark_node; | |
1381 | ||
1382 | TREE_VALUE (t) = output; | |
1383 | } | |
1384 | ||
1385 | for (i = 0, t = inputs; t != NULL_TREE; t = TREE_CHAIN (t), i++) | |
1386 | { | |
1387 | tree input = TREE_VALUE (t); | |
1388 | const char *constraint | |
1389 | = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); | |
1390 | ||
1391 | if (parse_input_constraint (&constraint, i, ninputs, noutputs, 0, | |
1392 | oconstraints, &allows_mem, &allows_reg)) | |
1393 | { | |
1394 | /* If the input argument is going to end up in memory. */ | |
1395 | if (!allows_reg && allows_mem) | |
1396 | d_mark_addressable (input); | |
1397 | } | |
1398 | else | |
1399 | input = error_mark_node; | |
1400 | ||
1401 | TREE_VALUE (t) = input; | |
1402 | } | |
1403 | } | |
1404 | ||
1405 | tree exp = build5 (ASM_EXPR, void_type_node, string, | |
1406 | outputs, inputs, clobbers, labels); | |
1407 | SET_EXPR_LOCATION (exp, make_location_t (s->loc)); | |
1408 | ||
1409 | /* If the extended syntax was not used, mark the ASM_EXPR. */ | |
1410 | if (s->args == NULL && s->clobbers == NULL) | |
1411 | ASM_INPUT_P (exp) = 1; | |
1412 | ||
1413 | /* Asm statements are treated as volatile unless 'pure'. */ | |
1414 | ASM_VOLATILE_P (exp) = !(s->stc & STCpure); | |
1415 | ||
1416 | add_stmt (exp); | |
1417 | } | |
1418 | ||
1419 | /* Import symbols from another module. */ | |
1420 | ||
1421 | void visit (ImportStatement *s) | |
1422 | { | |
1423 | if (s->imports == NULL) | |
1424 | return; | |
1425 | ||
1426 | for (size_t i = 0; i < s->imports->dim; i++) | |
1427 | { | |
1428 | Dsymbol *dsym = (*s->imports)[i]; | |
1429 | ||
1430 | if (dsym != NULL) | |
1431 | build_decl_tree (dsym); | |
1432 | } | |
1433 | } | |
1434 | }; | |
1435 | ||
1436 | /* Main entry point for the IRVisitor interface to generate | |
1437 | code for the body of function FD. */ | |
1438 | ||
1439 | void | |
1440 | build_function_body (FuncDeclaration *fd) | |
1441 | { | |
1442 | IRVisitor v = IRVisitor (fd); | |
1443 | location_t saved_location = input_location; | |
1444 | input_location = make_location_t (fd->loc); | |
1445 | v.build_stmt (fd->fbody); | |
1446 | input_location = saved_location; | |
1447 | } |