ack/lang/m2/comp/casestat.C

332 lines
7.7 KiB
C++
Raw Normal View History

/*
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
* See the copyright notice in the ACK home directory, in the file "Copyright".
*
* Author: Ceriel J.H. Jacobs
*/
1986-05-01 19:06:53 +00:00
/* C A S E S T A T E M E N T C O D E G E N E R A T I O N */
/* $Header$ */
1986-10-06 20:36:30 +00:00
/* Generation of case statements is done by first creating a
description structure for the statement, build a list of the
case-labels, then generating a case description in the code,
and generating either CSA or CSB, and then generating code for the
cases themselves.
*/
1986-05-01 19:06:53 +00:00
#include "debug.h"
#include <em_label.h>
#include <em_arith.h>
1986-07-08 14:59:02 +00:00
#include <em_code.h>
1986-05-01 19:06:53 +00:00
#include <alloc.h>
#include <assert.h>
#include "Lpars.h"
#include "type.h"
#include "LLlex.h"
#include "node.h"
1986-05-21 18:32:20 +00:00
#include "desig.h"
1986-06-20 14:36:49 +00:00
#include "walk.h"
#include "chk_expr.h"
#include "def.h"
1986-05-01 19:06:53 +00:00
#include "density.h"
struct switch_hdr {
1986-10-06 20:36:30 +00:00
label sh_break; /* label of statement after this one */
label sh_default; /* label of ELSE part, or 0 */
int sh_nrofentries; /* number of cases */
t_type *sh_type; /* type of case expression */
1986-10-06 20:36:30 +00:00
arith sh_lowerbd; /* lowest case label */
arith sh_upperbd; /* highest case label */
struct case_entry *sh_entries; /* the cases with their generated
labels
*/
1986-05-01 19:06:53 +00:00
};
1986-10-06 20:36:30 +00:00
/* STATICALLOCDEF "switch_hdr" 5 */
1986-05-01 19:06:53 +00:00
struct case_entry {
struct case_entry *ce_next; /* next in list */
1986-10-06 20:36:30 +00:00
label ce_label; /* generated label */
arith ce_value; /* value of case label */
1986-05-01 19:06:53 +00:00
};
1986-10-06 20:36:30 +00:00
/* STATICALLOCDEF "case_entry" 20 */
1986-05-01 19:06:53 +00:00
/* The constant DENSITY determines when CSA and when CSB instructions
are generated. Reasonable values are: 2, 3, 4.
On machines that have lots of address space and memory, higher values
1986-10-06 20:36:30 +00:00
might also be reasonable. On these machines the density of jump tables
1986-05-01 19:06:53 +00:00
may be lower.
*/
compact(nr, low, up)
arith low, up;
{
/* Careful! up - low might not fit in an arith. And then,
the test "up-low < 0" might also not work to detect this
situation! Or is this just a bug in the M68020/M68000?
*/
arith diff = up - low;
return (nr != 0 && diff >= 0 && fit(diff, (int) word_size) &&
diff / nr <= (DENSITY - 1));
}
1986-05-01 19:06:53 +00:00
int
CaseCode(nd, exitlabel, end_reached)
t_node *nd;
1986-05-01 19:06:53 +00:00
label exitlabel;
{
/* Check the expression, stack a new case header and
fill in the necessary fields.
1986-10-06 20:36:30 +00:00
"exitlabel" is the exit-label of the closest enclosing
LOOP-statement, or 0.
1986-05-01 19:06:53 +00:00
*/
register struct switch_hdr *sh = new_switch_hdr();
register t_node *pnode = nd;
1986-05-01 19:06:53 +00:00
register struct case_entry *ce;
register arith val;
1986-10-06 20:36:30 +00:00
label CaseDescrLab;
int rval;
1986-05-01 19:06:53 +00:00
1986-05-28 18:36:51 +00:00
assert(pnode->nd_class == Stat && pnode->nd_symb == CASE);
1986-05-01 19:06:53 +00:00
if (ChkExpression(pnode->nd_left)) {
MkCoercion(&(pnode->nd_left),BaseType(pnode->nd_left->nd_type));
CodePExpr(pnode->nd_left);
}
1986-05-28 18:36:51 +00:00
sh->sh_type = pnode->nd_left->nd_type;
1986-06-20 14:36:49 +00:00
sh->sh_break = ++text_label;
1986-05-01 19:06:53 +00:00
/* Now, create case label list
*/
while (pnode = pnode->nd_right) {
1986-05-01 19:06:53 +00:00
if (pnode->nd_class == Link && pnode->nd_symb == '|') {
if (pnode->nd_left) {
1986-10-06 20:36:30 +00:00
/* non-empty case
*/
1986-06-20 14:36:49 +00:00
pnode->nd_lab = ++text_label;
AddCases(sh, /* to descriptor */
pnode->nd_left->nd_left,
/* of case labels */
pnode->nd_lab
/* and code label */
);
1986-05-01 19:06:53 +00:00
}
}
else {
/* Else part
*/
1986-06-10 13:18:52 +00:00
1986-06-20 14:36:49 +00:00
sh->sh_default = ++text_label;
1986-10-06 20:36:30 +00:00
break;
1986-05-01 19:06:53 +00:00
}
}
1987-11-13 16:19:51 +00:00
if (!sh->sh_nrofentries) {
1986-11-05 14:33:00 +00:00
/* There were no cases, so we have to check the case-expression
here
*/
if (! (sh->sh_type->tp_fund & T_DISCRETE)) {
node_error(nd, "illegal type in CASE-expression");
}
}
1986-05-01 19:06:53 +00:00
/* Now generate code for the switch itself
1986-10-06 20:36:30 +00:00
First the part that CSA and CSB descriptions have in common.
1986-05-01 19:06:53 +00:00
*/
1986-10-06 20:36:30 +00:00
CaseDescrLab = ++data_label; /* the rom must have a label */
C_df_dlb(CaseDescrLab);
1986-05-01 19:06:53 +00:00
if (sh->sh_default) C_rom_ilb(sh->sh_default);
1986-06-20 14:36:49 +00:00
else C_rom_ucon("0", pointer_size);
1986-05-01 19:06:53 +00:00
if (compact(sh->sh_nrofentries, sh->sh_lowerbd, sh->sh_upperbd)) {
1986-10-06 20:36:30 +00:00
/* CSA
*/
1986-05-01 19:06:53 +00:00
ce = sh->sh_entries;
C_rom_cst((arith) 0);
C_rom_cst(sh->sh_upperbd - sh->sh_lowerbd);
for (val = sh->sh_lowerbd; val <= sh->sh_upperbd; val++) {
1986-05-01 19:06:53 +00:00
assert(ce);
if (val == ce->ce_value) {
C_rom_ilb(ce->ce_label);
ce = ce->ce_next;
1986-05-01 19:06:53 +00:00
}
else if (sh->sh_default) C_rom_ilb(sh->sh_default);
1986-06-20 14:36:49 +00:00
else C_rom_ucon("0", pointer_size);
1986-05-01 19:06:53 +00:00
}
C_loc(sh->sh_lowerbd);
C_sbu(word_size);
c_lae_dlb(CaseDescrLab); /* perform the switch */
1986-05-01 19:06:53 +00:00
C_csa(word_size);
}
1986-10-06 20:36:30 +00:00
else {
/* CSB
*/
1986-05-01 19:06:53 +00:00
C_rom_cst((arith)sh->sh_nrofentries);
for (ce = sh->sh_entries; ce; ce = ce->ce_next) {
1986-10-06 20:36:30 +00:00
/* generate the entries: value + prog.label
*/
1986-05-01 19:06:53 +00:00
C_rom_cst(ce->ce_value);
C_rom_ilb(ce->ce_label);
}
c_lae_dlb(CaseDescrLab); /* perform the switch */
1986-05-01 19:06:53 +00:00
C_csb(word_size);
}
/* Now generate code for the cases
*/
pnode = nd;
rval = 0;
while (pnode = pnode->nd_right) {
1986-05-01 19:06:53 +00:00
if (pnode->nd_class == Link && pnode->nd_symb == '|') {
if (pnode->nd_left) {
rval |= LblWalkNode(pnode->nd_lab,
pnode->nd_left->nd_right,
exitlabel, end_reached);
1986-05-01 19:06:53 +00:00
C_bra(sh->sh_break);
}
}
else {
/* Else part
*/
assert(sh->sh_default != 0);
rval |= LblWalkNode(sh->sh_default,
pnode, exitlabel, end_reached);
1986-10-06 20:36:30 +00:00
break;
1986-05-01 19:06:53 +00:00
}
}
def_ilb(sh->sh_break);
1986-05-01 19:06:53 +00:00
FreeSh(sh);
return rval;
1986-05-01 19:06:53 +00:00
}
FreeSh(sh)
1986-10-06 20:36:30 +00:00
register struct switch_hdr *sh;
1986-05-01 19:06:53 +00:00
{
/* free the allocated switch structure
*/
register struct case_entry *ce;
ce = sh->sh_entries;
while (ce) {
struct case_entry *tmp = ce->ce_next;
1986-05-01 19:06:53 +00:00
free_case_entry(ce);
ce = tmp;
}
free_switch_hdr(sh);
}
AddCases(sh, node, lbl)
struct switch_hdr *sh;
register t_node *node;
1986-05-01 19:06:53 +00:00
label lbl;
{
/* Add case labels to the case label list
*/
if (node->nd_class == Link) {
if (node->nd_symb == UPTO) {
assert(node->nd_left->nd_class == Value);
assert(node->nd_right->nd_class == Value);
1986-05-28 18:36:51 +00:00
1986-05-01 19:06:53 +00:00
node->nd_type = node->nd_left->nd_type;
node->nd_INT = node->nd_left->nd_INT;
for (;;) {
AddOneCase(sh, node, lbl);
if (node->nd_INT == node->nd_right->nd_INT) {
break;
}
node->nd_INT++;
1986-05-01 19:06:53 +00:00
}
return;
1986-05-01 19:06:53 +00:00
}
assert(node->nd_symb == ',');
AddCases(sh, node->nd_left, lbl);
AddCases(sh, node->nd_right, lbl);
return;
1986-05-01 19:06:53 +00:00
}
assert(node->nd_class == Value);
AddOneCase(sh, node, lbl);
1986-05-01 19:06:53 +00:00
}
AddOneCase(sh, node, lbl)
register struct switch_hdr *sh;
t_node *node;
1986-05-01 19:06:53 +00:00
label lbl;
{
register struct case_entry *ce = new_case_entry();
register struct case_entry *c1 = sh->sh_entries, *c2 = 0;
int fund = sh->sh_type->tp_fund;
1986-05-01 19:06:53 +00:00
ce->ce_label = lbl;
ce->ce_value = node->nd_INT;
if (! ChkCompat(&node, sh->sh_type, "case")) {
1986-05-01 19:06:53 +00:00
}
if (sh->sh_entries == 0) {
1986-10-06 20:36:30 +00:00
/* first case entry
*/
ce->ce_next = (struct case_entry *) 0;
1986-05-01 19:06:53 +00:00
sh->sh_entries = ce;
sh->sh_lowerbd = sh->sh_upperbd = ce->ce_value;
sh->sh_nrofentries = 1;
}
else {
1986-10-06 20:36:30 +00:00
/* second etc. case entry
find the proper place to put ce into the list
*/
1986-05-01 19:06:53 +00:00
if (chk_bounds(ce->ce_value, sh->sh_lowerbd, fund)) {
1986-05-28 18:36:51 +00:00
sh->sh_lowerbd = ce->ce_value;
}
else if (! chk_bounds(ce->ce_value, sh->sh_upperbd, fund)) {
1986-05-28 18:36:51 +00:00
sh->sh_upperbd = ce->ce_value;
}
while (c1 && !chk_bounds(ce->ce_value, c1->ce_value, fund)) {
1986-05-01 19:06:53 +00:00
c2 = c1;
c1 = c1->ce_next;
1986-05-01 19:06:53 +00:00
}
/* At this point three cases are possible:
1: c1 != 0 && c2 != 0:
insert ce somewhere in the middle
2: c1 != 0 && c2 == 0:
insert ce right after the head
3: c1 == 0 && c2 != 0:
append ce to last element
The case c1 == 0 && c2 == 0 cannot occur, since
the list is guaranteed not to be empty.
*/
if (c1) {
if (c1->ce_value == ce->ce_value) {
1986-05-14 09:03:51 +00:00
node_error(node, "multiple case entry for value %ld", ce->ce_value);
1986-05-01 19:06:53 +00:00
free_case_entry(ce);
}
if (c2) {
ce->ce_next = c2->ce_next;
c2->ce_next = ce;
1986-05-01 19:06:53 +00:00
}
else {
ce->ce_next = sh->sh_entries;
1986-05-01 19:06:53 +00:00
sh->sh_entries = ce;
}
}
else {
assert(c2);
ce->ce_next = (struct case_entry *) 0;
c2->ce_next = ce;
1986-05-01 19:06:53 +00:00
}
(sh->sh_nrofentries)++;
}
}