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