007a63d529
Add long long type, but without literals; you can't say '123LL' yet. You can try constant operations, like `(long long)123 + 1`, but the compiler's `arith` type might not be wide enough. Conversions, shifts, and some other operations don't work in i386 ncg; I am using a union instead of conversions: union q { long long ll; unsigned long long ull; int i[2]; }; Hack plat/linux386/descr to enable long long (size 8, alignment 4) only for this platform. The default for other platforms is to disable long long (size -1). In lang/cem/cemcom.ansi, - BigPars, SmallPars: Add default size, alignment of long long. - align.h: Add lnglng_align. - arith.c: Convert arithmetic operands to long long or unsigned long long when necessary; avoid conversion from long long to long. Allow long long as an arithmetic, integral, or logical operand. - ch3.c: Handle long long like int and long when erroneously applying a selector, like `long long ll; ll.member` or `ll->member`. Add long long to integral and arithmetic types. - code.c: Add long long to type stabs for debugging. - conversion.c: Add long long to integral conversions. - cstoper.c: Write masks up to full_mask[8]. Add FIXME comment. - declar.g: Parse `long long` in code. - decspecs.c: Understand long long in type declarations. - eval.c: Add long long to operations, to generate code like `adi 8`. Don't use `ldc` with constant over 4 bytes. - ival.g: Allow long long in initializations. - main.c: Set lnglng_type and related values. - options.c: Add option like `-Vq8.4` to set long long to size 8, alignment 4. I chose 'q', because Perl's pack and Ruby's Array#pack use 'q' for 64-bit or long long values; it might be a reference to BSD's old quad_t alias for long long. - sizes.h: Add lnglng_size. - stab.c: Allow long long when writing the type stab for debugging. Switch from calculating the ranges to hardcoding them in strings; add 8-byte ranges as a special case. This also hardcodes the unsigned 4-byte range as "0;-1". Before it was either "0;-1" or "0;4294967295", depending on sizeof(long) in the compiler. - struct.c: Try long long bitfield, but it will probably give the error, "bit field type long long does not fit in a word". - switch.c: Update comment. - tokenname.c: Define LNGLNG (long long) like LNGDBL (long double). - type.c, type.str: Add lnglng_type and ulnglng_type. Add function no_long_long() to check if long long is disabled.
245 lines
6.2 KiB
C
245 lines
6.2 KiB
C
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*/
|
|
/* $Id$ */
|
|
/* S W I T C H - S T A T E M E N T A D M I N I S T R A T I O N */
|
|
|
|
#include <assert.h>
|
|
#include "parameters.h"
|
|
#ifndef LINT
|
|
#include <em.h>
|
|
#else
|
|
#include "l_em.h"
|
|
#endif /* LINT */
|
|
#include <ack_string.h>
|
|
#include <alloc.h>
|
|
#include "Lpars.h"
|
|
#include "label.h"
|
|
#include <flt_arith.h>
|
|
#include "arith.h"
|
|
#include "switch.h"
|
|
#include "code.h"
|
|
#include "expr.h"
|
|
#include "type.h"
|
|
#include "sizes.h"
|
|
#include "switch.h"
|
|
#include "eval.h"
|
|
#include "ch3.h"
|
|
#include "error.h"
|
|
|
|
extern char options[];
|
|
|
|
int density = DENSITY;
|
|
|
|
static int compact(int nr, arith low, arith 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 && diff / nr <= (density - 1)));
|
|
}
|
|
|
|
static struct switch_hdr *switch_stack = 0;
|
|
|
|
/* (EB 86.05.20) The following rules hold for switch statements:
|
|
- the expression E in "switch(E)" shall have integral type (3.6.4.2)
|
|
- the expression E in "case E:" is converted to the promoted type
|
|
of the controlling expression
|
|
For simplicity, we suppose int_size == word_size.
|
|
*/
|
|
|
|
void code_startswitch(struct expr **expp)
|
|
{
|
|
/* Check the expression, stack a new case header and
|
|
fill in the necessary fields.
|
|
*/
|
|
register label l_table = text_label();
|
|
register label l_break = text_label();
|
|
register struct switch_hdr *sh = new_switch_hdr();
|
|
int fund = any2arith(expp, SWITCH);
|
|
/* INT, LONG, LNGLNG, FLOAT, DOUBLE or LNGDBL */
|
|
|
|
switch (fund) {
|
|
case FLOAT:
|
|
case DOUBLE:
|
|
case LNGDBL:
|
|
error("floating point type in switch");
|
|
erroneous2int(expp);
|
|
break;
|
|
}
|
|
stack_stmt(l_break, NO_LABEL);
|
|
sh->sh_break = l_break;
|
|
/* sh->sh_default = 0; */
|
|
sh->sh_table = l_table;
|
|
/* sh->sh_nrofentries = 0; */
|
|
sh->sh_type = (*expp)->ex_type; /* the expression switched */
|
|
/* sh->sh_entries = (struct case_entry *) 0; -- case-entry list */
|
|
sh->sh_expr = *expp;
|
|
#ifdef LINT
|
|
code_expr(sh->sh_expr, RVAL, TRUE, NO_LABEL, NO_LABEL);
|
|
#endif
|
|
sh->next = switch_stack; /* push onto switch-stack */
|
|
switch_stack = sh;
|
|
C_bra(l_table); /* goto start of switch_table */
|
|
}
|
|
|
|
void code_endswitch(void)
|
|
{
|
|
register struct switch_hdr *sh = switch_stack;
|
|
register label tablabel;
|
|
register struct case_entry *ce;
|
|
arith size = sh->sh_type->tp_size;
|
|
|
|
if (sh->sh_default == 0) /* no default occurred yet */
|
|
sh->sh_default = sh->sh_break;
|
|
|
|
#ifndef LINT
|
|
C_bra(sh->sh_break); /* skip the switch table now */
|
|
C_df_ilb(sh->sh_table); /* switch table entry */
|
|
/* evaluate the switch expr. */
|
|
code_expr(sh->sh_expr, RVAL, TRUE, NO_LABEL, NO_LABEL);
|
|
if (sh->sh_nrofentries <= 1) {
|
|
if (sh->sh_nrofentries) {
|
|
load_cst(sh->sh_lowerbd, size);
|
|
C_cms(size);
|
|
C_zeq(sh->sh_entries->ce_label);
|
|
}
|
|
else C_asp(size);
|
|
C_bra(sh->sh_default);
|
|
}
|
|
else {
|
|
tablabel = data_label(); /* the rom must have a label */
|
|
C_df_dlb(tablabel);
|
|
C_rom_ilb(sh->sh_default);
|
|
if (compact(sh->sh_nrofentries, sh->sh_lowerbd, sh->sh_upperbd)) {
|
|
/* CSA */
|
|
register arith val;
|
|
|
|
C_rom_icon(long2str((long)sh->sh_lowerbd,10), size);
|
|
C_rom_icon(long2str((long)(sh->sh_upperbd - sh->sh_lowerbd),10),
|
|
size);
|
|
ce = sh->sh_entries;
|
|
for (val = sh->sh_lowerbd; val <= sh->sh_upperbd; val++) {
|
|
assert(ce);
|
|
if (val == ce->ce_value) {
|
|
C_rom_ilb(ce->ce_label);
|
|
ce = ce->next;
|
|
}
|
|
else
|
|
C_rom_ilb(sh->sh_default);
|
|
}
|
|
C_lae_dlb(tablabel, (arith)0); /* perform the switch */
|
|
C_csa(size);
|
|
}
|
|
else { /* CSB */
|
|
C_rom_icon(long2str((long)sh->sh_nrofentries,10),size);
|
|
for (ce = sh->sh_entries; ce; ce = ce->next) {
|
|
/* generate the entries: value + prog.label */
|
|
C_rom_icon(long2str((long)ce->ce_value,10),size);
|
|
C_rom_ilb(ce->ce_label);
|
|
}
|
|
C_lae_dlb(tablabel, (arith)0); /* perform the switch */
|
|
C_csb(size);
|
|
}
|
|
}
|
|
C_df_ilb(sh->sh_break);
|
|
#endif
|
|
|
|
switch_stack = sh->next; /* unstack the switch descriptor */
|
|
for (ce = sh->sh_entries; ce;) { /* free allocated switch structure */
|
|
register struct case_entry *tmp = ce->next;
|
|
|
|
free_case_entry(ce);
|
|
ce = tmp;
|
|
}
|
|
free_switch_hdr(sh);
|
|
unstack_stmt();
|
|
}
|
|
|
|
void code_case(struct expr *expr)
|
|
{
|
|
register arith val;
|
|
register struct case_entry *ce;
|
|
register struct switch_hdr *sh = switch_stack;
|
|
|
|
assert(is_cp_cst(expr));
|
|
if (sh == 0) {
|
|
error("case statement not in switch");
|
|
return;
|
|
}
|
|
if (expr->ex_flags & EX_ERROR) /* is probably 0 anyway */
|
|
return;
|
|
ch3cast(&expr, CASE, sh->sh_type);
|
|
ce = new_case_entry();
|
|
C_df_ilb(ce->ce_label = text_label());
|
|
ce->ce_value = val = expr->VL_VALUE;
|
|
if (sh->sh_entries == 0) { /* first case entry */
|
|
/* ce->next = (struct case_entry *) 0; */
|
|
sh->sh_entries = ce;
|
|
sh->sh_lowerbd = sh->sh_upperbd = val;
|
|
sh->sh_nrofentries = 1;
|
|
}
|
|
else { /* second etc. case entry; put ce into proper place */
|
|
register struct case_entry *c1 = sh->sh_entries, *c2 = 0;
|
|
|
|
if (val < sh->sh_lowerbd)
|
|
sh->sh_lowerbd = val;
|
|
else
|
|
if (val > sh->sh_upperbd)
|
|
sh->sh_upperbd = val;
|
|
while (c1 && c1->ce_value < ce->ce_value) {
|
|
c2 = c1;
|
|
c1 = c1->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 to be non-empty.
|
|
*/
|
|
if (c1) {
|
|
if (c1->ce_value == ce->ce_value) {
|
|
error("multiple case entry for value %ld",
|
|
ce->ce_value);
|
|
free_case_entry(ce);
|
|
return;
|
|
}
|
|
if (c2) {
|
|
ce->next = c2->next;
|
|
c2->next = ce;
|
|
}
|
|
else {
|
|
ce->next = sh->sh_entries;
|
|
sh->sh_entries = ce;
|
|
}
|
|
}
|
|
else {
|
|
assert(c2);
|
|
ce->next = (struct case_entry *) 0;
|
|
c2->next = ce;
|
|
}
|
|
(sh->sh_nrofentries)++;
|
|
}
|
|
}
|
|
|
|
void code_default(void)
|
|
{
|
|
register struct switch_hdr *sh = switch_stack;
|
|
|
|
if (sh == 0) {
|
|
error("default statement not in switch");
|
|
return;
|
|
}
|
|
if (sh->sh_default != 0) {
|
|
error("multiple entry for default in switch");
|
|
return;
|
|
}
|
|
|
|
C_df_ilb(sh->sh_default = text_label());
|
|
}
|