/* %Z%%M% %I% %E% */ /* * Copyright (c) 1990, 1991, 1992, 1993, 1995 by Wayne C. Gramlich. * All rights reserved. * * Permission to use, copy, modify, distribute, and sell this software * for any purpose is hereby granted without fee provided that the above * copyright notice and this permission are retained. The author makes * no representations about the suitability of this software for any purpose. * It is provided "as is" without express or implied warranty. */ /* This file contains the code generator for statements: */ #ifndef CALL_DEFS_H #include "call_defs.h" #endif #ifndef ERROR_EXPORTS_H #include "error_exports.h" #endif #ifndef EXP_DEFS_H #include "exp_defs.h" #endif #ifndef FLAGS_DEFS_H #include "flags_defs.h" #endif #ifndef GEN_DEFS_H #include "gen_defs.h" #endif #ifndef LINT_H #include "lint.h" #endif #ifndef MSG_EXPORTS_H #include "msg_exports.h" #endif #ifndef OBJECT_DEFS_H #include "object_defs.h" #endif #ifndef ROUTINE_TYPES_H #include "routine_types.h" #endif #ifndef STATEMENT_DEFS_H #include "statement_defs.h" #endif #ifndef STR_EXPORTS_H #include "str_exports.h" #endif #ifndef TABLE_EXPORTS_H #include "table_exports.h" #endif #ifndef TYPE_DEFS_H #include "type_defs.h" #endif #ifndef UNIX_ASSERT_H #include "unix_assert.h" #endif #ifndef UNIX_unistd_H #include "unix_unistd.h" #endif #ifndef VECTOR_DEFS_H #include "vector_defs.h" #endif LOCAL void statement_gen(Statement, Gen, int); LOCAL Statement_loop statement_loop_find(Str, Gen); /* * statement_breakpoint(breakpoint, gen, indent) * This routine will output breakpoint code for "breakpoint" indended by * "indent" if "gen"->debug is 1. */ void statement_breakpoint( Breakpoint breakpoint, Gen gen, int indent) { int number; if (gen->debug) { number = breakpoint->number; assert(number >= 0); gen_out(gen, "%\tbreak___point(%d);\n", indent, number); } } /* * statement_gen(statement, gen, indent) * This routine will generate code for "statement" indented by "indent" * using "gen" to control the code generation. */ void statement_gen( Statement statement, Gen gen, int indent) { switch (statement->kind) { case Statement_kind_assert: { statement_breakpoint(statement->breakpoint, gen, indent); gen_out(gen, "%\tif (!(", indent); call_gen(statement->value.exp->call, 0, gen); gen_out(gen, ")) {\n"); gen_out(gen, "%\trun__time__assertion_failed(%\", %d);\n", indent + 1, gen->flags->in_file->full, msg_line_get(gen->msg, statement->position)); gen_out(gen, "%\t}\n", indent); break; } case Statement_kind_break: case Statement_kind_continue: { Str comment; Str keyword; Statement_label label; Statement_loop loop; Str name; label = statement->value.label; name = label->name; loop = statement_loop_find(name, gen); if (loop == (Statement_loop)0) { /* Error message already generated */ break; } statement_breakpoint(statement->breakpoint, gen, indent); comment = statement->comment; switch (statement->kind) { case Statement_kind_break: keyword = "break"; if (loop->in_switch || !loop->is_inner_most) { label->use_goto = 1; loop->need_break = 1; } break; case Statement_kind_continue: keyword = "continue"; if (!loop->is_inner_most) { label->use_goto = 1; loop->need_continue = 1; } break; } if (label->use_goto) { if (loop->name == (Str)0) { gen_out(gen, "%\tgoto %s___%d;%\n", indent, keyword, loop->number, comment); } else { gen_out(gen, "%\tgoto %s___%s;%\n", indent, keyword, loop->name, comment); } } else { gen_out(gen, "%\t%s;%\n", indent, keyword, comment); } break; } case Statement_kind_eval: statement_breakpoint(statement->breakpoint, gen, indent); call_list_gen(statement->calls, indent, gen); break; case Statement_kind_extract: { Exp exp; Statement_extract extract; Str first_tag_name; int index; int size; Statement_tag tag; Str tag_field_type; Str tag_name; Vec(Str) tag_names; Vec(Statement_tag) tags; Type_def type_def; Type_ref type_ref; Type_refs type_refs; Type_variant variant; statement_breakpoint(statement->breakpoint, gen, indent); gen_out(gen, "%\t{\n", indent); indent++; extract = statement->value.extract; exp = extract->exp; type_refs = exp->type_refs; assert(type_refs != (Type_refs)0); assert(type_refs_size(type_refs) == 1); type_ref = type_refs_fetch(type_refs, 0); type_def = type_def_table_lookup(gen->type_def_table, type_ref->name); if ((type_def == (Type_def)0) || (type_def->kind != Type_kind_variant)) { /* Error message already generated: */ break; } variant = type_def->value.variant; gen_out(gen, "%\t%T _temp1_;\n", indent, type_def->name); gen_out(gen, "%\tunion %s___variant _temp2_;\n", indent, type_def->name); gen_out(gen, "\n"); call_list_gen(statement->calls, indent, gen); gen_out(gen, "%\t_temp1_ = ", indent); call_gen(exp->call, 0, gen); gen_out(gen, ";%\n", statement->comment); gen_out(gen, "%\t_temp2_._entire_ = _temp1_->_entire_;\n", indent); gen_out(gen, "%\tswitch (_temp2_._tag_.%s) {\n", indent, variant->tag_field->name); tags = extract->tags; vec_append(Statement_loop, gen->loop_stack, (Statement_loop)0); size = vec_size(Statement_tag, tags); indent++; size = vec_size(Statement_tag, tags); for (index = 0; index < size; index++) { tag = vec_fetch(Statement_tag, tags, index); tag_names = tag->tag_names; if (vec_empty(Str, tag_names)) { gen_out(gen, "%\tdefault:%\n", indent, tag->comment); } else { first_tag_name = (Str)0; VEC_LOOP(Str, tag_names, tag_name) { if (first_tag_name == (Str)0) { first_tag_name = tag_name; } tag_field_type = variant->tag_field->type_ref->name; gen_out(gen, "%\tcase %s__item__%s:\n", indent, tag_field_type, tag_name); } gen_out(gen, "%\t", indent + 1); gen_out(gen, "%s%S = _temp2_.%S;%\n", gen->frame, tag->var_name, first_tag_name, tag->comment); } statement_list_gen(tag->statements, gen, indent + 1); if (index + 1 != size) { gen_out(gen, "%\tbreak;\n", indent + 1); } } indent--; (void)vec_pop(Statement_loop, gen->loop_stack); gen_out(gen, "%\t}\n", indent); indent--; gen_out(gen, "%\t}\n", indent); break; } case Statement_kind_if: { Statement_cond cond; Vec(Statement_cond) conds; int debug; int else_count; int index; debug = gen->debug; else_count = 0; conds = statement->value.xif; VEC_LOOP(Statement_cond, conds, cond) { index = vec_index(Statement_cond, conds); if (debug) { if (index == 0) { /* if */ statement_breakpoint( cond->breakpoint, gen, indent); gen_out(gen, "%\tif (", indent); call_gen(cond->exp->call, 0, gen); gen_out(gen, ") {%\n", cond->comment); statement_list_gen( cond->statements, gen, indent + 1); } else if (cond->exp == (Exp)0) { /* else */ gen_out(gen, "%\t} else {%\n", indent + index - 1, cond->comment); statement_list_gen( cond->statements, gen, indent + index); } else { /* else_if */ gen_out(gen, "%\t} else {%\n", indent + index - 1, cond->comment); statement_breakpoint( cond->breakpoint, gen, indent + index); else_count++; gen_out(gen, "%\tif (", indent + index); call_gen(cond->exp->call, 0, gen); gen_out(gen, ") {\n"); statement_list_gen( cond->statements, gen, indent + index + 1); } } else { if (index == 0) { call_list_gen(cond->calls, indent, gen); gen_out(gen, "%\tif (", indent); call_gen(cond->exp->call, 0, gen); gen_out(gen, ") {%\n", cond->comment); } else if (cond->exp == (Exp)0) { assert((cond->calls == (Vec(Call))0) || (vec_empty(Call, cond->calls))); gen_out(gen, "%\t} else {%\n", indent, cond->comment); } else { assert((cond->calls == (Vec(Call))0) || (vec_empty(Call, cond->calls))); gen_out(gen, "%\t} else if (", indent); call_gen(cond->exp->call, 0, gen); gen_out(gen, ") {%\n", cond->comment); } statement_list_gen(cond->statements, gen, indent + 1); } } if (debug) { while (--else_count >= 0) { gen_out(gen, "%\t}\n", indent + else_count + 1); } } gen_out(gen, "%\t}\n", indent); break; } case Statement_kind_initialize: { Statement_initialize initialize; statement_breakpoint(statement->breakpoint, gen, indent); gen_out(gen, "%\t{\n", indent); initialize = statement->value.initialize; call_list_gen(statement->calls, indent, gen); if (initialize->exp != (Exp)0) { gen_out(gen, "%\t%s%S = ", indent + 1, gen->frame, initialize->var_name); call_gen(initialize->exp->call, 0, gen); gen_out(gen, ";\n"); } statement_list_gen(initialize->statements, gen, indent + 1); gen_out(gen, "%\t}\n", indent); break; } case Statement_kind_loop: { Statement_loop loop; loop = statement->value.loop; statement_breakpoint(statement->breakpoint, gen, indent); gen_out(gen, "%\twhile (1) {%\n", indent, statement->comment); vec_append(Statement_loop, gen->loop_stack, loop); statement_list_gen(loop->statements, gen, indent + 1); (void)vec_pop(Statement_loop, gen->loop_stack); if (loop->need_continue) { if (loop->name == (Str)0) { gen_out(gen, "%\t continue___%d:;\n", indent, loop->number); } else { gen_out(gen, "%\t continue___%s:;\n", indent, loop->name); } } gen_out(gen, "%\t}\n", indent); if (loop->need_break) { if (loop->name == (Str)0) { gen_out(gen, "%\t break___%d:;\n", indent - 1, loop->number); } else { gen_out(gen, "%\t break___%s:;\n", indent - 1, loop->name); } } break; } case Statement_kind_none: if (statement->comment == (Str)0) { gen_out(gen, "\n"); } else { gen_out(gen, "%\t%\n", indent, statement->comment); } break; case Statement_kind_return: { Exp exp; Vec(Exp) exps; int index; int size; exps = statement->value.exps; size = vec_size(Exp, exps); statement_breakpoint(statement->breakpoint, gen, indent); call_list_gen(statement->calls, indent, gen); switch (size) { case 0: assert_fail(); break; case 1: exp = vec_fetch(Exp, exps, 0); if (gen->debug) { gen_out(gen, "%\t%sreturn__value__0 = ", indent, gen->frame); call_gen(exp->call, 0, gen); gen_out(gen, ";%\n", statement->comment); gen_out(gen, "%\tgoto epilog;\n", indent); } else { gen_out(gen, "%\treturn ", indent); call_gen(exp->call, 0, gen); gen_out(gen, ";%\n", statement->comment); } break; default: { Type_refs type_refs; if (gen->debug) { gen_out(gen, "%\t{\n", indent); indent++; for (index = 0; index < size; index++) { exp = vec_fetch(Exp, exps, index); gen_out(gen, "%\t%sreturn__value__%d = ", indent, gen->frame, index); call_gen(exp->call, 0, gen); gen_out(gen, ";\n"); } gen_out(gen, "%\tgoto epilog;\n", indent); indent--; gen_out(gen, "%\t}\n", indent); } else { gen_out(gen, "%\t{\n", indent); indent++; type_refs = exp_list_types_extract(exps, gen); gen_out(gen, "%\t%s x__;\n", indent, type_refs_multiple_string(type_refs, gen->heap)); for (index = 0; index < size; index++) { exp = vec_fetch(Exp, exps, index); gen_out(gen, "%\tx__.x%d = ", indent, index); call_gen(exp->call, 0, gen); gen_out(gen, ";\n"); } gen_out(gen, "%\treturn x__;\n", indent); indent--; gen_out(gen, "%\t}\n", indent); } break; } } break; } case Statement_kind_switch: { Call call; Statement_case xcase; Vec(Statement_case) cases; Exp exp; Vec(Exp) exps; int index; Statement last_statement; int last_statement_index; Vec(Statement) statements; int size; Statement_switch xswitch; Type_def type_def; Type_def_table type_def_table; Type_ref type_ref; Type_refs type_refs; statement_breakpoint(statement->breakpoint, gen, indent); xswitch = statement->value.xswitch; call = xswitch->exp->call; type_refs = call->type_refs; if (type_refs_size(type_refs) != 1) { msg_out(gen->msg, statement->position, "Switch expression returns %d types", type_refs_size(type_refs)); return; } type_ref = type_refs_fetch(type_refs, 0); type_def_table = gen->type_tables->type_def_table; type_def = type_def_table_lookup(type_def_table, type_ref->name); if ((type_def == (Type_def)0) || (type_def->kind != Type_kind_enumeration)) { /* Error message already generated: */ break; } if (gen->debug && type_def->imported) { gen_out(gen, "%\tswitch (%s___convert[(int)", indent, type_ref->name); call_gen(call, 0, gen); gen_out(gen, "]) {%\n", statement->comment); gen_out(gen, "%\tcase %s__item___none:\n", indent + 1, type_ref->name); gen_out(gen, "%\trun__time__bad__case();\n", indent + 2); gen_out(gen, "%\tbreak;\n", indent + 2); } else { gen_out(gen, "%\tswitch ((%s___type)", indent, type_ref->name); call_gen(call, 0, gen); gen_out(gen, ") {%\n", statement->comment); } cases = xswitch->cases; vec_append(Statement_loop, gen->loop_stack, (Statement_loop)0); size = vec_size(Statement_case, cases); for (index = 0; index < size; index++) { xcase = vec_fetch(Statement_case, cases, index); exps = xcase->exps; if (vec_empty(Exp, exps)) { gen_out(gen, "%\tdefault:%\n", indent + 1, xcase->comment); } else { VEC_LOOP(Exp, exps, exp) { Object_ref object_ref; assert(exp->call->type == Call_type_object); object_ref = exp->call->value.object; gen_out(gen, "%\tcase %s__item__%s:\n", indent + 1, object_ref->type_ref->name, object_ref->name); } } statements = xcase->statements; statement_list_gen(statements, gen, indent + 2); if (vec_empty(Statement, statements)) { gen_out(gen, "%\tbreak;\n", indent + 2); } else { last_statement_index = vec_size(Statement, statements) - 1; last_statement = vec_fetch(Statement, statements, last_statement_index); switch (last_statement->kind) { case Statement_kind_break: case Statement_kind_continue: case Statement_kind_return: break; default: if (index + 1 != size) { gen_out(gen, "%\tbreak;\n", indent + 2); } } } } (void)vec_pop(Statement_loop, gen->loop_stack); gen_out(gen, "%\t}\n", indent); break; } case Statement_kind_until: case Statement_kind_while: { Str comment; Statement_loop loop; loop = statement_loop_find((Str)0, gen); if (loop == (Statement_loop)0) { /* Error already generated */ break; } statement_breakpoint(statement->breakpoint, gen, indent); comment = statement->comment; call_list_gen(statement->calls, indent, gen); switch (statement->kind) { case Statement_kind_while: gen_out(gen, "%\tif (!(", indent); call_gen(statement->value.exp->call, 0, gen); gen_out(gen, ")) {%\n", comment); break; case Statement_kind_until: gen_out(gen, "%\tif (", indent); call_gen(statement->value.exp->call, 0, gen); gen_out(gen, ") {%\n", comment); break; } if (loop->is_inner_most && !loop->in_switch) { gen_out(gen, "%\tbreak;\n", indent + 1); } else { if (loop->name == (Str)0) { gen_out(gen, "%\tgoto break___%d;\n", indent + 1, loop->number); } else { gen_out(gen, "%\tgoto break___%s;\n", indent + 1, loop->name); } } gen_out(gen, "%\t}\n", indent); break; } default: error_abort("Not implemented"); } } /* * statement_list_gen(statements, gen, indent) * This routine will generate code for each statement in "statements" * indented by "indent" using "gen" to control the code generation. */ void statement_list_gen( Vec(Statement) statements, Gen gen, int indent) { Statement statement; VEC_LOOP(Statement, statements, statement) { statement_gen(statement, gen, indent); } } /* * statement_loop_find(name, gen) * This routine will find the first loop object in "gen"->loop_stack * that matches "name". (Statement_loop)0 is returned if there is * no match. */ Statement_loop statement_loop_find( Str name, Gen gen) { int in_switch; int index; Statement_loop inner_loop; Statement_loop loop; Vec(Statement_loop) loop_stack; int size; in_switch = 0; inner_loop = (Statement_loop)0; loop_stack = gen->loop_stack; size = vec_size(Statement_loop, loop_stack); for (index = size - 1; index >= 0; index--) { loop = vec_fetch(Statement_loop, loop_stack, index); if (loop == (Statement_loop)0) { in_switch = 1; } else { if (inner_loop == (Statement_loop)0) { inner_loop = loop; } if ((name == (Str)0) || ((loop->name != (Str)0) && strequal(name, loop->name))) { loop->is_inner_most = (loop == inner_loop); loop->in_switch = in_switch; return loop; } } } return (Statement_loop)0; }