256 lines
5.4 KiB
C
256 lines
5.4 KiB
C
/* 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 */
|
|
#include "debug.h"
|
|
|
|
#include <alloc.h>
|
|
#include <assert.h>
|
|
#include <em.h>
|
|
|
|
#include "LLlex.h"
|
|
#include "Lpars.h"
|
|
#include "chk_expr.h"
|
|
#include "density.h"
|
|
#include "main.h"
|
|
#include "node.h"
|
|
#include "type.h"
|
|
|
|
struct case_hdr {
|
|
struct case_hdr *ch_next; /* in the free list */
|
|
int ch_nrofentries; /* number of cases */
|
|
struct type *ch_type; /* type of case expression */
|
|
arith ch_lowerbd; /* lowest case label */
|
|
arith ch_upperbd; /* highest case label */
|
|
struct case_entry *ch_entries; /* the cases */
|
|
};
|
|
|
|
/* ALLOCDEF "case_hdr" 5 */
|
|
|
|
struct case_entry {
|
|
struct case_entry *ce_next; /* next in list */
|
|
arith ce_value; /* value of case label */
|
|
label ce_label; /* generated label */
|
|
};
|
|
|
|
/* ALLOCDEF "case_entry" 10 */
|
|
|
|
/* 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
|
|
might also be reasonable. On these machines the density of jump tables
|
|
may be lower.
|
|
*/
|
|
#define compact(nr, low, up) (nr != 0 && (up - low) / nr <= DENSITY)
|
|
|
|
CaseExpr(nd)
|
|
struct node *nd;
|
|
{
|
|
/* Check the expression and generate code for it
|
|
*/
|
|
|
|
register struct node *expp = nd->nd_left;
|
|
|
|
if( !ChkExpression(expp) ) return;
|
|
MarkUsed(expp);
|
|
|
|
if( !(expp->nd_type->tp_fund & T_ORDINAL) ) {
|
|
node_error(expp, "case-expression must be ordinal");
|
|
return;
|
|
}
|
|
|
|
if( !err_occurred ) {
|
|
CodePExpr(expp);
|
|
C_bra(nd->nd_lab);
|
|
}
|
|
}
|
|
|
|
CaseEnd(nd, exit_label)
|
|
struct node *nd;
|
|
label exit_label;
|
|
{
|
|
/* Stack a new case header and fill in the necessary fields.
|
|
*/
|
|
register struct case_hdr *ch = new_case_hdr();
|
|
register struct node *right;
|
|
|
|
assert(nd->nd_class == Link && nd->nd_symb == CASE);
|
|
|
|
ch->ch_type = nd->nd_left->nd_type;
|
|
|
|
right = nd->nd_right;
|
|
|
|
/* Now, create case label list
|
|
*/
|
|
while( right ) {
|
|
assert(right->nd_class == Link && right->nd_symb == ':');
|
|
|
|
if( !AddCases(ch, right->nd_left, right->nd_lab) ) {
|
|
FreeCh(ch);
|
|
return;
|
|
}
|
|
right = right->nd_right;
|
|
}
|
|
|
|
if( !err_occurred )
|
|
CaseCode(nd->nd_lab, ch, exit_label);
|
|
|
|
FreeCh(ch);
|
|
}
|
|
|
|
FreeCh(ch)
|
|
register struct case_hdr *ch;
|
|
{
|
|
/* free the allocated case structure
|
|
*/
|
|
register struct case_entry *ce;
|
|
|
|
ce = ch->ch_entries;
|
|
while( ce ) {
|
|
struct case_entry *tmp = ce->ce_next;
|
|
|
|
free_case_entry(ce);
|
|
ce = tmp;
|
|
}
|
|
|
|
free_case_hdr(ch);
|
|
}
|
|
|
|
AddCases(ch, nd, CaseLabel)
|
|
register struct case_hdr *ch;
|
|
register struct node *nd;
|
|
label CaseLabel;
|
|
{
|
|
while( nd ) {
|
|
if( !AddOneCase(ch, nd, CaseLabel) )
|
|
return 0;
|
|
nd = nd->nd_next;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
AddOneCase(ch, nd, lbl)
|
|
register struct case_hdr *ch;
|
|
register struct node *nd;
|
|
label lbl;
|
|
{
|
|
register struct case_entry *ce = new_case_entry();
|
|
register struct case_entry *c1 = ch->ch_entries, *c2 = 0;
|
|
|
|
ce->ce_value = nd->nd_INT;
|
|
ce->ce_label = lbl;
|
|
if( !TstCompat(ch->ch_type, nd->nd_type) ) {
|
|
node_error(nd, "case-statement: type incompatibility in case");
|
|
free_case_entry(ce);
|
|
return 0;
|
|
}
|
|
if( bounded(ch->ch_type) ) {
|
|
arith lo, hi;
|
|
|
|
getbounds(ch->ch_type, &lo, &hi);
|
|
if( ce->ce_value < lo || ce->ce_value > hi )
|
|
warning("case-statement: constant out of bounds");
|
|
}
|
|
|
|
if( !ch->ch_entries ) {
|
|
/* first case entry
|
|
*/
|
|
ce->ce_next = (struct case_entry *) 0;
|
|
ch->ch_entries = ce;
|
|
ch->ch_lowerbd = ch->ch_upperbd = ce->ce_value;
|
|
ch->ch_nrofentries = 1;
|
|
}
|
|
else {
|
|
/* second etc. case entry
|
|
find the proper place to put ce into the list
|
|
*/
|
|
|
|
if( ce->ce_value < ch->ch_lowerbd )
|
|
ch->ch_lowerbd = ce->ce_value;
|
|
else if( ce->ce_value > ch->ch_upperbd )
|
|
ch->ch_upperbd = ce->ce_value;
|
|
|
|
while( c1 && c1->ce_value < ce->ce_value ) {
|
|
c2 = c1;
|
|
c1 = c1->ce_next;
|
|
}
|
|
/* 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 ) {
|
|
node_error(nd,
|
|
"case-statement: multiple case entry");
|
|
free_case_entry(ce);
|
|
return 0;
|
|
}
|
|
if( c2 ) {
|
|
ce->ce_next = c2->ce_next;
|
|
c2->ce_next = ce;
|
|
}
|
|
else {
|
|
ce->ce_next = ch->ch_entries;
|
|
ch->ch_entries = ce;
|
|
}
|
|
}
|
|
else {
|
|
assert(c2);
|
|
|
|
ce->ce_next = (struct case_entry *) 0;
|
|
c2->ce_next = ce;
|
|
}
|
|
(ch->ch_nrofentries)++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
CaseCode(lbl, ch, exit_label)
|
|
label lbl;
|
|
struct case_hdr *ch;
|
|
label exit_label;
|
|
{
|
|
label CaseDescrLab = ++data_label; /* rom must have a label */
|
|
|
|
register struct case_entry *ce;
|
|
register arith val;
|
|
|
|
C_df_dlb(CaseDescrLab);
|
|
C_rom_icon("0", pointer_size);
|
|
|
|
if( compact(ch->ch_nrofentries, ch->ch_lowerbd, ch->ch_upperbd) ) {
|
|
/* CSA */
|
|
C_rom_cst(ch->ch_lowerbd);
|
|
C_rom_cst(ch->ch_upperbd - ch->ch_lowerbd);
|
|
ce = ch->ch_entries;
|
|
for( val = ch->ch_lowerbd; val <= ch->ch_upperbd; val++ ) {
|
|
assert(ce);
|
|
if( val == ce->ce_value ) {
|
|
C_rom_ilb(ce->ce_label);
|
|
ce = ce->ce_next;
|
|
}
|
|
else
|
|
C_rom_icon("0", pointer_size);
|
|
}
|
|
C_df_ilb(lbl);
|
|
C_lae_dlb(CaseDescrLab, (arith) 0);
|
|
C_csa(word_size);
|
|
}
|
|
else {
|
|
/* CSB */
|
|
C_rom_cst((arith) ch->ch_nrofentries);
|
|
for( ce = ch->ch_entries; ce; ce = ce->ce_next ) {
|
|
C_rom_cst(ce->ce_value);
|
|
C_rom_ilb(ce->ce_label);
|
|
}
|
|
C_df_ilb(lbl);
|
|
C_lae_dlb(CaseDescrLab, (arith) 0);
|
|
C_csb(word_size);
|
|
}
|
|
|
|
C_df_ilb(exit_label);
|
|
}
|