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