1 /**
2 ** Ucstmt.cc - Usecode compiler statements.
3 **
4 ** Written: 1/2/01 - JSF
5 **/
6
7 /*
8 Copyright (C) 2000-2013 The Exult Team
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28
29 #include <cstdio>
30 #include <cassert>
31
32 #include "ucstmt.h"
33 #include "ucexpr.h"
34 #include "ucsym.h"
35 #include "opcodes.h"
36 #include "utils.h"
37 #include "ucfun.h"
38 #include "basic_block.h"
39
40 using std::vector;
41 using std::map;
42 using std::string;
43
44 int Uc_converse_statement::nest = 0;
45
46 /*
47 * Delete.
48 */
49
~Uc_block_statement()50 Uc_block_statement::~Uc_block_statement(
51 ) {
52 // Delete all the statements.
53 for (auto *statement : statements)
54 delete statement;
55 }
56
57 /*
58 * Generate code.
59 */
60
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)61 void Uc_block_statement::gen(
62 Uc_function *fun,
63 vector<Basic_block *> &blocks, // What we are producing.
64 Basic_block *&curr, // Active block; will usually be *changed*.
65 Basic_block *end, // Fictitious exit block for function.
66 map<string, Basic_block *> &labels, // Label map for goto statements.
67 Basic_block *start, // Block used for 'continue' statements.
68 Basic_block *exit // Block used for 'break' statements.
69 ) {
70 for (auto *stmt : statements) {
71 stmt->gen(fun, blocks, curr, end, labels, start, exit);
72 }
73 }
74
75
76 /*
77 * Delete.
78 */
79
~Uc_assignment_statement()80 Uc_assignment_statement::~Uc_assignment_statement(
81 ) {
82 delete target;
83 delete value;
84 }
85
86
87 /*
88 * Generate code.
89 */
90
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)91 void Uc_assignment_statement::gen(
92 Uc_function *fun,
93 vector<Basic_block *> &blocks, // What we are producing.
94 Basic_block *&curr, // Active block; will usually be *changed*.
95 Basic_block *end, // Fictitious exit block for function.
96 map<string, Basic_block *> &labels, // Label map for goto statements.
97 Basic_block *start, // Block used for 'continue' statements.
98 Basic_block *exit // Block used for 'break' statements.
99 ) {
100 ignore_unused_variable_warning(fun, blocks, end, labels, start, exit);
101 ignore_unused_variable_warning(fun, blocks, end, labels, start, exit);
102 value->gen_value(curr); // Get value on stack.
103 target->gen_assign(curr);
104 }
105
106 /*
107 * Generate code.
108 */
109
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)110 void Uc_if_statement::gen(
111 Uc_function *fun,
112 vector<Basic_block *> &blocks, // What we are producing.
113 Basic_block *&curr, // Active block; will usually be *changed*.
114 Basic_block *end, // Fictitious exit block for function.
115 map<string, Basic_block *> &labels, // Label map for goto statements.
116 Basic_block *start, // Block used for 'continue' statements.
117 Basic_block *exit // Block used for 'break' statements.
118 ) {
119 if (!if_stmt && !else_stmt) // Optimize whole block away.
120 return;
121 // The basic block for the if code.
122 auto *if_block = new Basic_block();
123 // The basic block after the if/else blocks.
124 auto *past_if = new Basic_block();
125 blocks.push_back(if_block);
126 int ival;
127 bool const_expr = !expr || expr->eval_const(ival);
128 if (!expr)
129 // IF body unreachable except by GOTO statements.
130 // Skip IF block.
131 curr->set_targets(UC_JMP, past_if);
132 else if (ival)
133 // ELSE block unreachable except by GOTO statements.
134 // Fall-through to IF block.
135 curr->set_targets(UC_INVALID, if_block);
136 else {
137 // Gen test code & JNE.
138 expr->gen_value(curr);
139 curr->set_targets(UC_JNE, if_block);
140 }
141 if (if_stmt)
142 if_stmt->gen(fun, blocks, if_block, end, labels, start, exit);
143 if (else_stmt) {
144 // The basic block for the else code.
145 auto *else_block = new Basic_block();
146 blocks.push_back(else_block);
147 if (!expr) // Go directly to else block instead.
148 curr->set_taken(else_block);
149 else if (!ival) // Only for JNE.
150 curr->set_ntaken(else_block);
151 // JMP past ELSE code.
152 if_block->set_targets(UC_JMP, past_if);
153 // Generate else code.
154 else_stmt->gen(fun, blocks, else_block, end, labels, start, exit);
155 else_block->set_taken(past_if);
156 } else {
157 if (!const_expr) // Need to go to past-if block too.
158 curr->set_ntaken(past_if);
159 if_block->set_targets(UC_INVALID, past_if);
160 }
161 blocks.push_back(curr = past_if);
162 }
163
164 /*
165 * Delete.
166 */
167
~Uc_if_statement()168 Uc_if_statement::~Uc_if_statement(
169 ) {
170 delete expr;
171 delete if_stmt;
172 delete else_stmt;
173 }
174
175 /*
176 * Generate code.
177 */
178
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)179 void Uc_trycatch_statement::gen(
180 Uc_function *fun,
181 vector<Basic_block *> &blocks, // What we are producing.
182 Basic_block *&curr, // Active block; will usually be *changed*.
183 Basic_block *end, // Fictitious exit block for function.
184 map<string, Basic_block *> &labels, // Label map for goto statements.
185 Basic_block *start, // Block used for 'continue' statements.
186 Basic_block *exit // Block used for 'break' statements.
187 ) {
188 static int cnt = 0;
189 if (!try_stmt) // Optimize whole block away.
190 return;
191 // The basic block for the try code.
192 auto *try_block = new Basic_block();
193 // The basic block for the catch code.
194 auto *catch_block = new Basic_block();
195 // The basic block after the try/catch blocks.
196 auto *past_trycatch = new Basic_block();
197 // Gen start opcode for try block.
198 curr->set_targets(UC_TRYSTART, try_block, catch_block);
199 // Generate code for try block
200 blocks.push_back(try_block);
201 try_stmt->gen(fun, blocks, try_block, end, labels, start, exit);
202 WriteOp(try_block, UC_TRYEND);
203 // JMP past CATCH code.
204 try_block->set_targets(UC_JMP, past_trycatch);
205 // Generate a temp variable for error if needed
206 if (!catch_var) {
207 char buf[50];
208 sprintf(buf, "_tmperror_%d", cnt++);
209 // Create a 'tmp' variable.
210 catch_var = fun->add_symbol(buf);
211 assert(catch_var != nullptr);
212 }
213 // Generate error variable assignment (push is handled by abort/throw)
214 blocks.push_back(catch_block);
215 catch_var->gen_assign(catch_block);
216 // Do we have anything else to generate on catch block?
217 if (catch_stmt) {
218 // Generate catch code.
219 catch_stmt->gen(fun, blocks, catch_block, end, labels, start, exit);
220 catch_block->set_taken(past_trycatch);
221 }
222 blocks.push_back(curr = past_trycatch);
223 }
224
225 /*
226 * Delete.
227 */
228
~Uc_trycatch_statement()229 Uc_trycatch_statement::~Uc_trycatch_statement(
230 ) {
231 delete catch_var;
232 delete try_stmt;
233 delete catch_stmt;
234 }
235
236 /*
237 * Delete.
238 */
239
~Uc_breakable_statement()240 Uc_breakable_statement::~Uc_breakable_statement(
241 ) {
242 delete stmt;
243 }
244
245 /*
246 * Generate code.
247 */
248
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)249 void Uc_breakable_statement::gen(
250 Uc_function *fun,
251 vector<Basic_block *> &blocks, // What we are producing.
252 Basic_block *&curr, // Active block; will usually be *changed*.
253 Basic_block *end, // Fictitious exit block for function.
254 map<string, Basic_block *> &labels, // Label map for goto statements.
255 Basic_block *start, // Block used for 'continue' statements.
256 Basic_block *exit // Block used for 'break' statements.
257 ) {
258 ignore_unused_variable_warning(start, exit);
259 if (!stmt) // Optimize whole statement away.
260 return;
261 auto *past_block = new Basic_block();
262 stmt->gen(fun, blocks, curr, end, labels, past_block, past_block);
263 blocks.push_back(curr = past_block);
264 }
265
266 /*
267 * Delete.
268 */
269
~Uc_while_statement()270 Uc_while_statement::~Uc_while_statement(
271 ) {
272 delete expr;
273 delete stmt;
274 }
275
276 /*
277 * Generate code.
278 */
279
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)280 void Uc_while_statement::gen(
281 Uc_function *fun,
282 vector<Basic_block *> &blocks, // What we are producing.
283 Basic_block *&curr, // Active block; will usually be *changed*.
284 Basic_block *end, // Fictitious exit block for function.
285 map<string, Basic_block *> &labels, // Label map for goto statements.
286 Basic_block *start, // Block used for 'continue' statements.
287 Basic_block *exit // Block used for 'break' statements.
288 ) {
289 ignore_unused_variable_warning(start, exit);
290 if (!stmt) // Optimize whole loop away.
291 return;
292 // The start of a loop is a jump target and needs
293 // a new basic block.
294 auto *while_top = new Basic_block();
295 // Basic block past while body.
296 auto *past_while = new Basic_block();
297 // Need new block past a JNE (the test) or JMP.
298 auto *while_block = new Basic_block();
299 // Fall-through to WHILE top.
300 curr->set_taken(while_top);
301 blocks.push_back(while_top);
302 blocks.push_back(while_block);
303 if (!expr)
304 // While body unreachable except through GOTO statements.
305 // Skip WHILE body by default.
306 while_top->set_targets(UC_JMP, past_while);
307 else {
308 // Gen test code.
309 expr->gen_value(while_top);
310 // Link WHILE top to WHILE body and past-WHILE blocks.
311 while_top->set_targets(UC_JNE, while_block, past_while);
312 }
313 // Generate while body.
314 stmt->gen(fun, blocks, while_block, end, labels, while_top, past_while);
315
316 // JMP back to top.
317 while_block->set_targets(UC_JMP, while_top);
318 blocks.push_back(curr = past_while);
319 }
320
321
322 /*
323 * Delete.
324 */
325
~Uc_dowhile_statement()326 Uc_dowhile_statement::~Uc_dowhile_statement(
327 ) {
328 delete expr;
329 delete stmt;
330 }
331
332 /*
333 * Generate code.
334 */
335
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)336 void Uc_dowhile_statement::gen(
337 Uc_function *fun,
338 vector<Basic_block *> &blocks, // What we are producing.
339 Basic_block *&curr, // Active block; will usually be *changed*.
340 Basic_block *end, // Fictitious exit block for function.
341 map<string, Basic_block *> &labels, // Label map for goto statements.
342 Basic_block *start, // Block used for 'continue' statements.
343 Basic_block *exit // Block used for 'break' statements.
344 ) {
345 ignore_unused_variable_warning(start, exit);
346 if (!stmt) // Optimize whole loop away.
347 return;
348 // The start of a loop is a jump target and needs
349 // a new basic block.
350 auto *do_block = new Basic_block();
351 curr->set_taken(do_block);
352 blocks.push_back(do_block);
353 // Need new block for test as it is a jump target.
354 auto *do_test = new Basic_block();
355 // Gen test code.
356 expr->gen_value(do_test);
357 // Basic block past while body.
358 auto *past_do = new Basic_block();
359 // Jump back to top.
360 auto *do_jmp = new Basic_block();
361 do_jmp->set_targets(UC_JMP, do_block);
362 do_test->set_targets(UC_JNE, do_jmp, past_do);
363 // Generate while body.
364 stmt->gen(fun, blocks, do_block, end, labels, do_test, past_do);
365 do_block->set_targets(UC_INVALID, do_test);
366
367 blocks.push_back(do_test);
368 blocks.push_back(do_jmp);
369 blocks.push_back(curr = past_do);
370 }
371
372 /*
373 * Delete.
374 */
375
~Uc_infinite_loop_statement()376 Uc_infinite_loop_statement::~Uc_infinite_loop_statement(
377 ) {
378 delete stmt;
379 }
380
381 /*
382 * Generate code.
383 */
384
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)385 void Uc_infinite_loop_statement::gen(
386 Uc_function *fun,
387 vector<Basic_block *> &blocks, // What we are producing.
388 Basic_block *&curr, // Active block; will usually be *changed*.
389 Basic_block *end, // Fictitious exit block for function.
390 map<string, Basic_block *> &labels, // Label map for goto statements.
391 Basic_block *start, // Block used for 'continue' statements.
392 Basic_block *exit // Block used for 'break' statements.
393 ) {
394 ignore_unused_variable_warning(start, exit);
395 if (!stmt) // Optimize whole loop away.
396 return;
397 // The start of a loop is a jump target and needs
398 // a new basic block.
399 auto *loop_top = new Basic_block();
400 curr->set_taken(loop_top);
401 blocks.push_back(loop_top);
402 // Local copy.
403 Basic_block *loop_body = loop_top;
404 // Basic block past loop body.
405 auto *past_loop = new Basic_block();
406 // Generate loop body.
407 stmt->gen(fun, blocks, loop_body, end, labels, loop_top, past_loop);
408 // Jump back to top.
409 loop_body->set_targets(UC_JMP, loop_top);
410
411 blocks.push_back(curr = past_loop);
412 if (past_loop->is_orphan())
413 warning("No 'break' statements will be executed for infinite loop");
414 }
415
416 /*
417 * Delete.
418 */
419
~Uc_arrayloop_statement()420 Uc_arrayloop_statement::~Uc_arrayloop_statement(
421 ) {
422 delete stmt;
423 }
424
425 /*
426 * Finish up creation.
427 */
428
finish(Uc_function * fun)429 void Uc_arrayloop_statement::finish(
430 Uc_function *fun
431 ) {
432 char buf[100];
433 if (!index) { // Create vars. if not given.
434 sprintf(buf, "_%s_index", array->get_name());
435 index = fun->add_symbol(buf);
436 }
437 if (!array_size) {
438 sprintf(buf, "_%s_size", array->get_name());
439 array_size = fun->add_symbol(buf);
440 }
441 }
442
443 /*
444 * Generate code.
445 */
446
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)447 void Uc_arrayloop_statement::gen(
448 Uc_function *fun,
449 vector<Basic_block *> &blocks, // What we are producing.
450 Basic_block *&curr, // Active block; will usually be *changed*.
451 Basic_block *end, // Fictitious exit block for function.
452 map<string, Basic_block *> &labels, // Label map for goto statements.
453 Basic_block *start, // Block used for 'continue' statements.
454 Basic_block *exit // Block used for 'break' statements.
455 ) {
456 ignore_unused_variable_warning(start, exit);
457 if (!stmt)
458 return; // Nothing useful to do.
459 // Start of loop.
460 WriteOp(curr, UC_LOOP);
461 // The start of a loop is a jump target and needs
462 // a new basic block.
463 auto *for_top = new Basic_block();
464 curr->set_taken(for_top);
465 blocks.push_back(for_top);
466 // Body of FOR loop.
467 auto *for_body = new Basic_block();
468 blocks.push_back(for_body);
469 // Block immediatelly after FOR.
470 auto *past_for = new Basic_block();
471 UsecodeOps opcode;
472 if (array->is_static())
473 opcode = UC_LOOPTOPS;
474 else if (array->get_sym_type() == Uc_symbol::Member_var)
475 opcode = UC_LOOPTOPTHV;
476 else
477 opcode = UC_LOOPTOP;
478 for_top->set_targets(opcode, for_body, past_for);
479 WriteJumpParam2(for_top, index->get_offset());// Counter, total-count variables.
480 WriteJumpParam2(for_top, array_size->get_offset());
481 WriteJumpParam2(for_top, var->get_offset()); // Loop variable, than array.
482 WriteJumpParam2(for_top, array->get_offset());
483
484 // Generate FOR body.
485 stmt->gen(fun, blocks, for_body, end, labels, for_top, past_for);
486 // Jump back to top.
487 for_body->set_targets(UC_JMP, for_top);
488
489 blocks.push_back(curr = past_for);
490 }
491
492 /*
493 * Delete.
494 */
495
~Uc_return_statement()496 Uc_return_statement::~Uc_return_statement(
497 ) {
498 delete expr;
499 }
500
501 /*
502 * Generate code.
503 */
504
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)505 void Uc_return_statement::gen(
506 Uc_function *fun,
507 vector<Basic_block *> &blocks, // What we are producing.
508 Basic_block *&curr, // Active block; will usually be *changed*.
509 Basic_block *end, // Fictitious exit block for function.
510 map<string, Basic_block *> &labels, // Label map for goto statements.
511 Basic_block *start, // Block used for 'continue' statements.
512 Basic_block *exit // Block used for 'break' statements.
513 ) {
514 ignore_unused_variable_warning(fun, labels, start, exit);
515 if (expr) { // Returning something?
516 int ival;
517 if (expr->eval_const(ival) && !ival)
518 WriteOp(curr, UC_RETZ);
519 else {
520 expr->gen_value(curr); // Put value on stack.
521 WriteOp(curr, UC_RETV);
522 }
523 } else
524 WriteOp(curr, UC_RET);
525 curr->set_targets(UC_INVALID, end);
526 curr = new Basic_block();
527 blocks.push_back(curr);
528 }
529
530
531 /*
532 * Generate code.
533 */
534
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)535 void Uc_break_statement::gen(
536 Uc_function *fun,
537 vector<Basic_block *> &blocks, // What we are producing.
538 Basic_block *&curr, // Active block; will usually be *changed*.
539 Basic_block *end, // Fictitious exit block for function.
540 map<string, Basic_block *> &labels, // Label map for goto statements.
541 Basic_block *start, // Block used for 'continue' statements.
542 Basic_block *exit // Block used for 'break' statements.
543 ) {
544 ignore_unused_variable_warning(fun, end, labels, start);
545 curr->set_targets(UC_JMP, exit);
546 curr = new Basic_block();
547 blocks.push_back(curr);
548 }
549
550
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)551 void Uc_continue_statement::gen(
552 Uc_function *fun,
553 vector<Basic_block *> &blocks, // What we are producing.
554 Basic_block *&curr, // Active block; will usually be *changed*.
555 Basic_block *end, // Fictitious exit block for function.
556 map<string, Basic_block *> &labels, // Label map for goto statements.
557 Basic_block *start, // Block used for 'continue' statements.
558 Basic_block *exit // Block used for 'break' statements.
559 ) {
560 ignore_unused_variable_warning(fun, end, labels, exit);
561 curr->set_targets(UC_JMP, start);
562 curr = new Basic_block();
563 blocks.push_back(curr);
564 }
565
566
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)567 void Uc_label_statement::gen(
568 Uc_function *fun,
569 vector<Basic_block *> &blocks, // What we are producing.
570 Basic_block *&curr, // Active block; will usually be *changed*.
571 Basic_block *end, // Fictitious exit block for function.
572 map<string, Basic_block *> &labels, // Label map for goto statements.
573 Basic_block *start, // Block used for 'continue' statements.
574 Basic_block *exit // Block used for 'break' statements.
575 ) {
576 ignore_unused_variable_warning(fun, end, start, exit);
577 auto it = labels.find(label);
578 // Should never fail, but...
579 assert(it != labels.end());
580 Basic_block *block = it->second;
581 curr->set_taken(block);
582 blocks.push_back(curr = block);
583
584 // no code
585 }
586
587
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)588 void Uc_goto_statement::gen(
589 Uc_function *fun,
590 vector<Basic_block *> &blocks, // What we are producing.
591 Basic_block *&curr, // Active block; will usually be *changed*.
592 Basic_block *end, // Fictitious exit block for function.
593 map<string, Basic_block *> &labels, // Label map for goto statements.
594 Basic_block *start, // Block used for 'continue' statements.
595 Basic_block *exit // Block used for 'break' statements.
596 ) {
597 ignore_unused_variable_warning(fun, end, start, exit);
598 auto it = labels.find(label);
599 if (it != labels.end()) {
600 Basic_block *l = it->second;
601 curr->set_targets(UC_JMP, l);
602 curr = new Basic_block();
603 blocks.push_back(curr);
604 } else {
605 char buf[255];
606 snprintf(buf, 255, "Undeclared label: '%s'", label.c_str());
607 error(buf);
608 }
609 }
610
611 /*
612 * Generate a call to an intrinsic with 0 or 1 parameter.
613 */
Call_intrinsic(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Uc_intrinsic_symbol * intr,Uc_expression * parm0=nullptr)614 static void Call_intrinsic(
615 Uc_function *fun,
616 vector<Basic_block *> &blocks, // What we are producing.
617 Basic_block *&curr, // Active block; will usually be *changed*.
618 Basic_block *end, // Fictitious exit block for function.
619 map<string, Basic_block *> &labels, // Label map for goto statements.
620 Uc_intrinsic_symbol *intr, // What to call.
621 Uc_expression *parm0 = nullptr // Parm, or null.
622 ) {
623 // Create parms. list.
624 auto *parms = new Uc_array_expression;
625 if (parm0)
626 parms->add(parm0);
627 auto *fcall = new Uc_call_expression(intr, parms, fun);
628 Uc_call_statement fstmt(fcall);
629 fstmt.gen(fun, blocks, curr, end, labels);
630 parms->clear(); // DON'T want to delete parm0.
631 }
632
633 /*
634 * Generate code.
635 */
636
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)637 void Uc_converse_case_statement::gen(
638 Uc_function *fun,
639 vector<Basic_block *> &blocks, // What we are producing.
640 Basic_block *&curr, // Active block; will usually be *changed*.
641 Basic_block *end, // Fictitious exit block for function.
642 map<string, Basic_block *> &labels, // Label map for goto statements.
643 Basic_block *start, // Block used for 'continue' statements.
644 Basic_block *exit // Block used for 'break' statements.
645 ) {
646 if (!remove && !statements) // Optimize whole case away.
647 return;
648
649 for (auto it = string_offset.rbegin();
650 it != string_offset.rend(); ++it) {
651 // Push strings on stack; *it should always be >= 0.
652 if (is_int_32bit(*it)) {
653 WriteOp(curr, UC_PUSHS32);
654 WriteOpParam4(curr, *it);
655 } else {
656 WriteOp(curr, UC_PUSHS);
657 WriteOpParam2(curr, *it);
658 }
659 }
660 // New basic block for CASE body.
661 auto *case_body = new Basic_block();
662 blocks.push_back(case_body);
663 // Past CASE body.
664 auto *past_case = new Basic_block();
665 if (is_default())
666 curr->set_targets(UC_INVALID, case_body);
667 else {
668 curr->set_targets(UC_CMPS, case_body, past_case);
669 WriteJumpParam2(curr, string_offset.size()); // # strings on stack.
670 }
671
672 if (remove) { // Remove answer?
673 if (string_offset.size() > 1) {
674 auto *strlist = new Uc_array_expression();
675 for (int it : string_offset) {
676 auto *str = new Uc_string_expression(it);
677 strlist->add(str);
678 }
679 Call_intrinsic(fun, blocks, case_body, end, labels,
680 Uc_function::get_remove_answer(), strlist);
681 } else if (!string_offset.empty())
682 Call_intrinsic(fun, blocks, case_body, end, labels,
683 Uc_function::get_remove_answer(),
684 new Uc_string_expression(string_offset[0]));
685 else
686 Call_intrinsic(fun, blocks, case_body, end, labels,
687 Uc_function::get_remove_answer(),
688 new Uc_choice_expression());
689 }
690 if (statements) // Generate statement's code.
691 statements->gen(fun, blocks, case_body, end, labels, start, exit);
692 // Jump back to converse top.
693 case_body->set_targets(UC_JMP, start);
694 blocks.push_back(curr = past_case);
695 }
696
697 /*
698 * Initialize.
699 */
700
Uc_converse_statement(Uc_expression * a,std::vector<Uc_statement * > * cs,bool n)701 Uc_converse_statement::Uc_converse_statement(
702 Uc_expression *a,
703 std::vector<Uc_statement *> *cs,
704 bool n
705 )
706 : answers(a), cases(*cs), nestconv(n) {
707 bool has_default = false;
708 for (auto *it : cases) {
709 auto *stmt =
710 static_cast<Uc_converse_case_statement *>(it);
711 if (stmt->is_default()) {
712 if (has_default) {
713 char buf[255];
714 snprintf(buf, 255, "converse statement already has a default case.");
715 error(buf);
716 } else
717 has_default = true;
718 }
719 }
720 }
721
722 /*
723 * Delete.
724 */
725
~Uc_converse_statement()726 Uc_converse_statement::~Uc_converse_statement(
727 ) {
728 delete answers;
729 for (auto *it : cases)
730 delete it;
731 }
732
733 /*
734 * Generate code.
735 */
736
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)737 void Uc_converse_statement::gen(
738 Uc_function *fun,
739 vector<Basic_block *> &blocks, // What we are producing.
740 Basic_block *&curr, // Active block; will usually be *changed*.
741 Basic_block *end, // Fictitious exit block for function.
742 map<string, Basic_block *> &labels, // Label map for goto statements.
743 Basic_block *start, // Block used for 'continue' statements.
744 Basic_block *exit // Block used for 'break' statements.
745 ) {
746 ignore_unused_variable_warning(start, exit);
747 if (cases.empty()) // Nothing to do; optimize whole block away.
748 return;
749 if (nest++ > 0 || nestconv) // Not the outermost?
750 // Generate a 'push_answers()'.
751 Call_intrinsic(fun, blocks, curr, end, labels,
752 Uc_function::get_push_answers());
753 if (answers) // Add desired answers.
754 Call_intrinsic(fun, blocks, curr, end, labels,
755 Uc_function::get_add_answer(), answers);
756 // The start of a CONVERSE loop is a jump target and needs
757 // a new basic block.
758 auto *conv_top = new Basic_block();
759 curr->set_taken(conv_top);
760 blocks.push_back(conv_top);
761 // Need new block as it is past a jump.
762 auto *conv_body = new Basic_block();
763 blocks.push_back(conv_body);
764 // Block past the CONVERSE loop.
765 auto *past_conv = new Basic_block();
766 WriteOp(past_conv, UC_CONVERSELOC);
767 conv_top->set_targets(UC_CONVERSE, conv_body, past_conv);
768 // Generate loop body.
769 Uc_converse_case_statement *def = nullptr;
770 for (auto *it : cases) {
771 auto *stmt =
772 static_cast<Uc_converse_case_statement *>(it);
773 if (stmt->is_default())
774 def = stmt;
775 else
776 stmt->gen(fun, blocks, conv_body, end, labels, conv_top, past_conv);
777 }
778 if (def)
779 def->gen(fun, blocks, conv_body, end, labels, conv_top, past_conv);
780 // Jump back to top.
781 conv_body->set_targets(UC_JMP, conv_top);
782 blocks.push_back(curr = past_conv);
783
784 if (--nest > 0 || nestconv) // Not the outermost?
785 // Generate a 'pop_answers()'.
786 Call_intrinsic(fun, blocks, curr, end, labels,
787 Uc_function::get_pop_answers());
788 }
789
790 /*
791 * Delete.
792 */
793
~Uc_switch_expression_case_statement()794 Uc_switch_expression_case_statement::~Uc_switch_expression_case_statement(
795 ) {
796 delete check;
797 }
798
799 /*
800 * Generate code.
801 */
802
gen_check(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * case_block)803 int Uc_switch_expression_case_statement::gen_check(
804 Uc_function *fun,
805 vector<Basic_block *> &blocks, // What we are producing.
806 Basic_block *&curr, // Active block; will usually be *changed*.
807 Basic_block *end, // Fictitious exit block for function.
808 map<string, Basic_block *> &labels, // Label map for goto statements.
809 Basic_block *case_block // Pointer to the case statements.
810 ) {
811 ignore_unused_variable_warning(fun, end, labels);
812 check->gen_value(curr);
813 WriteOp(curr, UC_CMPNE);
814 auto *block = new Basic_block();
815 curr->set_targets(UC_JNE, block, case_block);
816 blocks.push_back(curr = block);
817 return 1;
818 }
819
820 /*
821 * Generate code.
822 */
823
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)824 void Uc_switch_case_statement::gen(
825 Uc_function *fun,
826 vector<Basic_block *> &blocks, // What we are producing.
827 Basic_block *&curr, // Active block; will usually be *changed*.
828 Basic_block *end, // Fictitious exit block for function.
829 map<string, Basic_block *> &labels, // Label map for goto statements.
830 Basic_block *start, // Block used for 'continue' statements.
831 Basic_block *exit // Block used for 'break' statements.
832 ) {
833 statements->gen(fun, blocks, curr, end, labels, start, exit);
834 }
835
836 /*
837 * Delete.
838 */
839
~Uc_switch_statement()840 Uc_switch_statement::~Uc_switch_statement(
841 ) {
842 delete cond;
843 for (auto *it : cases)
844 delete it;
845 }
846
847 /*
848 * Initialize.
849 */
850
Uc_switch_statement(Uc_expression * v,std::vector<Uc_statement * > * cs)851 Uc_switch_statement::Uc_switch_statement(
852 Uc_expression *v,
853 std::vector<Uc_statement *> *cs
854 )
855 : cond(v), cases(*cs) {
856 bool has_default = false;
857 for (auto *it : cases) {
858 auto *stmt =
859 static_cast<Uc_switch_case_statement *>(it);
860 if (stmt->is_default()) {
861 if (has_default) {
862 char buf[255];
863 snprintf(buf, 255, "switch statement already has a default case.");
864 error(buf);
865 } else
866 has_default = true;
867 }
868 }
869 }
870
871 /*
872 * Generate code.
873 */
874
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)875 void Uc_switch_statement::gen(
876 Uc_function *fun,
877 vector<Basic_block *> &blocks, // What we are producing.
878 Basic_block *&curr, // Active block; will usually be *changed*.
879 Basic_block *end, // Fictitious exit block for function.
880 map<string, Basic_block *> &labels, // Label map for goto statements.
881 Basic_block *start, // Block used for 'continue' statements.
882 Basic_block *exit // Block used for 'break' statements.
883 ) {
884 ignore_unused_variable_warning(exit);
885 auto *var = new Uc_var_expression(cond->need_var(curr, fun));
886 vector<Basic_block *> case_blocks;
887 Basic_block *def_case = nullptr;
888 for (auto *it : cases) {
889 auto *stmt =
890 dynamic_cast<Uc_switch_case_statement *>(it);
891 if (stmt->is_default()) { // Store the default case iterator.
892 def_case = new Basic_block();
893 case_blocks.push_back(def_case);
894 } else {
895 // Generate the conditional jumps and the case basic blocks.
896 var->gen_value(curr);
897 // Case block.
898 auto *case_code = new Basic_block();
899 stmt->gen_check(fun, blocks, curr, end, labels, case_code);
900 case_blocks.push_back(case_code);
901 }
902 }
903 // All done with it.
904 delete var;
905 // Past SWITCH block.
906 auto *past_switch = new Basic_block();
907 // For all other cases, the default jump.
908 if (def_case)
909 curr->set_targets(UC_JMP, def_case);
910 else
911 curr->set_targets(UC_JMP, past_switch);
912 for (size_t i = 0; i < cases.size(); i++) {
913 auto *stmt =
914 static_cast<Uc_switch_case_statement *>(cases[i]);
915 Basic_block *block = case_blocks[i];
916 // Link cases (for fall-through).
917 if (i > 0)
918 curr->set_targets(UC_INVALID, block);
919 blocks.push_back(curr = block);
920 stmt->gen(fun, blocks, curr, end, labels, start, past_switch);
921 }
922 curr->set_targets(UC_INVALID, past_switch);
923 blocks.push_back(curr = past_switch);
924 }
925
926 /*
927 * Generate code.
928 */
929
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)930 void Uc_message_statement::gen(
931 Uc_function *fun,
932 vector<Basic_block *> &blocks, // What we are producing.
933 Basic_block *&curr, // Active block; will usually be *changed*.
934 Basic_block *end, // Fictitious exit block for function.
935 map<string, Basic_block *> &labels, // Label map for goto statements.
936 Basic_block *start, // Block used for 'continue' statements.
937 Basic_block *exit // Block used for 'break' statements.
938 ) {
939 ignore_unused_variable_warning(blocks, end, labels, start, exit);
940 if (!msgs)
941 return;
942 const std::vector<Uc_expression *> &exprs = msgs->get_exprs();
943 for (auto *msg : exprs) {
944 // A known string?
945 int offset = msg->get_string_offset();
946 if (offset >= 0) {
947 if (is_int_32bit(offset)) {
948 WriteOp(curr, UC_ADDSI32);
949 WriteOpParam4(curr, offset);
950 } else {
951 WriteOp(curr, UC_ADDSI);
952 WriteOpParam2(curr, offset);
953 }
954 } else {
955 Uc_var_symbol *var = msg->need_var(curr, fun);
956 if (var) { // Shouldn't fail.
957 WriteOp(curr, UC_ADDSV);
958 WriteOpParam2(curr, var->get_offset());
959 }
960 }
961 }
962 }
963
964 /*
965 * Generate code.
966 */
967
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)968 void Uc_say_statement::gen(
969 Uc_function *fun,
970 vector<Basic_block *> &blocks, // What we are producing.
971 Basic_block *&curr, // Active block; will usually be *changed*.
972 Basic_block *end, // Fictitious exit block for function.
973 map<string, Basic_block *> &labels, // Label map for goto statements.
974 Basic_block *start, // Block used for 'continue' statements.
975 Basic_block *exit // Block used for 'break' statements.
976 ) {
977 // Add the messages.
978 Uc_message_statement::gen(fun, blocks, curr, end, labels, start, exit);
979 WriteOp(curr, UC_SAY); // Show on screen.
980 }
981
982 /*
983 * Create.
984 */
985
Uc_call_statement(Uc_call_expression * f)986 Uc_call_statement::Uc_call_statement(
987 Uc_call_expression *f
988 ) : function_call(f) {
989 if (function_call)
990 function_call->set_no_return();
991 }
992
993 /*
994 * Delete.
995 */
996
~Uc_call_statement()997 Uc_call_statement::~Uc_call_statement(
998 ) {
999 delete function_call;
1000 }
1001
1002 /*
1003 * Generate code.
1004 */
1005
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)1006 void Uc_call_statement::gen(
1007 Uc_function *fun,
1008 vector<Basic_block *> &blocks, // What we are producing.
1009 Basic_block *&curr, // Active block; will usually be *changed*.
1010 Basic_block *end, // Fictitious exit block for function.
1011 map<string, Basic_block *> &labels, // Label map for goto statements.
1012 Basic_block *start, // Block used for 'continue' statements.
1013 Basic_block *exit // Block used for 'break' statements.
1014 ) {
1015 ignore_unused_variable_warning(fun, blocks, end, labels, start, exit);
1016 function_call->gen_value(curr); // (We set 'no_return'.)
1017 }
1018
1019 /*
1020 * Generate code.
1021 */
1022
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)1023 void Uc_abort_statement::gen(
1024 Uc_function *fun,
1025 vector<Basic_block *> &blocks, // What we are producing.
1026 Basic_block *&curr, // Active block; will usually be *changed*.
1027 Basic_block *end, // Fictitious exit block for function.
1028 map<string, Basic_block *> &labels, // Label map for goto statements.
1029 Basic_block *start, // Block used for 'continue' statements.
1030 Basic_block *exit // Block used for 'break' statements.
1031 ) {
1032 ignore_unused_variable_warning(fun, labels, start, exit);
1033 if (expr) {
1034 expr->gen_value(curr);
1035 WriteOp(curr, UC_THROW);
1036 } else {
1037 WriteOp(curr, UC_ABRT);
1038 }
1039 curr->set_targets(UC_INVALID, end);
1040 curr = new Basic_block();
1041 blocks.push_back(curr);
1042 }
1043
1044 /*
1045 * Delete.
1046 */
1047
~Uc_abort_statement()1048 Uc_abort_statement::~Uc_abort_statement(
1049 ) {
1050 delete expr;
1051 }
1052
1053 /*
1054 * Generate code.
1055 */
1056
gen(Uc_function * fun,vector<Basic_block * > & blocks,Basic_block * & curr,Basic_block * end,map<string,Basic_block * > & labels,Basic_block * start,Basic_block * exit)1057 void Uc_delete_statement::gen(
1058 Uc_function *fun,
1059 vector<Basic_block *> &blocks, // What we are producing.
1060 Basic_block *&curr, // Active block; will usually be *changed*.
1061 Basic_block *end, // Fictitious exit block for function.
1062 map<string, Basic_block *> &labels, // Label map for goto statements.
1063 Basic_block *start, // Block used for 'continue' statements.
1064 Basic_block *exit // Block used for 'break' statements.
1065 ) {
1066 ignore_unused_variable_warning(fun, blocks, end, labels, start, exit);
1067 if (!expr)
1068 return;
1069 expr->gen_value(curr);
1070 }
1071