*** empty log message ***
This commit is contained in:
465
lang/cem/cemcom/arith.c
Normal file
465
lang/cem/cemcom/arith.c
Normal file
@@ -0,0 +1,465 @@
|
||||
/* $Header$ */
|
||||
/* A R I T H M E T I C C O N V E R S I O N S */
|
||||
|
||||
/* This file contains the routines for the various conversions that
|
||||
may befall operands in C. It is structurally a mess, but I haven't
|
||||
decided yet whether I can't find the right structure or the
|
||||
semantics of C is a mess.
|
||||
*/
|
||||
|
||||
#include "botch_free.h"
|
||||
#include "nobitfield.h"
|
||||
#include "alloc.h"
|
||||
#include "idf.h"
|
||||
#include "arith.h"
|
||||
#include "type.h"
|
||||
#include "label.h"
|
||||
#include "expr.h"
|
||||
#include "Lpars.h"
|
||||
#include "storage.h"
|
||||
#include "field.h"
|
||||
#include "mes.h"
|
||||
|
||||
extern char *symbol2str();
|
||||
extern char options[];
|
||||
|
||||
int
|
||||
arithbalance(e1p, oper, e2p) /* RM 6.6 */
|
||||
struct expr **e1p, **e2p;
|
||||
{
|
||||
/* The expressions *e1p and *e2p are balanced to be operands
|
||||
of the arithmetic operator oper.
|
||||
*/
|
||||
register int t1, t2, u1, u2;
|
||||
|
||||
t1 = any2arith(e1p, oper);
|
||||
t2 = any2arith(e2p, oper);
|
||||
|
||||
/* Now t1 and t2 are either INT or LONG or DOUBLE */
|
||||
if (t1 == DOUBLE && t2 != DOUBLE)
|
||||
t2 = int2float(e2p, double_type);
|
||||
else
|
||||
if (t2 == DOUBLE && t1 != DOUBLE)
|
||||
t1 = int2float(e1p, double_type);
|
||||
else
|
||||
if (t1 == DOUBLE)
|
||||
return DOUBLE;
|
||||
|
||||
/* Now they are INT or LONG */
|
||||
u1 = (*e1p)->ex_type->tp_unsigned;
|
||||
u2 = (*e2p)->ex_type->tp_unsigned;
|
||||
|
||||
/* if either is long, the other will be */
|
||||
if (t1 == LONG && t2 != LONG)
|
||||
t2 = int2int(e2p, u2 ? ulong_type : long_type);
|
||||
else
|
||||
if (t2 == LONG && t1 != LONG)
|
||||
t1 = int2int(e1p, u1 ? ulong_type : long_type);
|
||||
|
||||
/* if either is unsigned, the other will be */
|
||||
if (u1 && !u2)
|
||||
t2 = int2int(e2p, (t1 == LONG) ? ulong_type : uint_type);
|
||||
else
|
||||
if (!u1 && u2)
|
||||
t1 = int2int(e1p, (t2 == LONG) ? ulong_type : uint_type);
|
||||
|
||||
return t1;
|
||||
}
|
||||
|
||||
relbalance(e1p, oper, e2p)
|
||||
register struct expr **e1p, **e2p;
|
||||
{
|
||||
/* The expressions *e1p and *e2p are balanced to be operands
|
||||
of the relational operator oper.
|
||||
*/
|
||||
if ((*e1p)->ex_type->tp_fund == FUNCTION)
|
||||
function2pointer(e1p);
|
||||
if ((*e2p)->ex_type->tp_fund == FUNCTION)
|
||||
function2pointer(e2p);
|
||||
if ((*e1p)->ex_type->tp_fund == POINTER)
|
||||
ch76pointer(e2p, oper, (*e1p)->ex_type);
|
||||
else
|
||||
if ((*e2p)->ex_type->tp_fund == POINTER)
|
||||
ch76pointer(e1p, oper, (*e2p)->ex_type);
|
||||
else
|
||||
if ( (*e1p)->ex_type == (*e2p)->ex_type &&
|
||||
(*e1p)->ex_type->tp_fund == ENUM
|
||||
)
|
||||
{}
|
||||
else
|
||||
arithbalance(e1p, oper, e2p);
|
||||
}
|
||||
|
||||
ch76pointer(expp, oper, tp)
|
||||
register struct expr **expp;
|
||||
register struct type *tp;
|
||||
{
|
||||
/* Checks whether *expp may be compared to tp using oper,
|
||||
as described in chapter 7.6 and 7.7.
|
||||
tp is known to be a pointer.
|
||||
*/
|
||||
if ((*expp)->ex_type->tp_fund == POINTER) {
|
||||
if ((*expp)->ex_type != tp)
|
||||
ch7cast(expp, oper, tp);
|
||||
}
|
||||
else
|
||||
if ( is_integral_type((*expp)->ex_type) &&
|
||||
( !options['R'] /* we don't care */ ||
|
||||
(oper == EQUAL || oper == NOTEQUAL || oper == ':')
|
||||
)
|
||||
) /* ch 7.7 */
|
||||
ch7cast(expp, CAST, tp);
|
||||
else {
|
||||
if ((*expp)->ex_type != error_type)
|
||||
error("%s on %s and pointer",
|
||||
symbol2str(oper),
|
||||
symbol2str((*expp)->ex_type->tp_fund)
|
||||
);
|
||||
(*expp)->ex_type = error_type;
|
||||
ch7cast(expp, oper, tp);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
any2arith(expp, oper)
|
||||
register struct expr **expp;
|
||||
{
|
||||
/* Turns any expression into int_type, long_type or
|
||||
double_type.
|
||||
*/
|
||||
int fund = (*expp)->ex_type->tp_fund;
|
||||
|
||||
switch (fund) {
|
||||
case CHAR:
|
||||
case SHORT:
|
||||
int2int(expp,
|
||||
(*expp)->ex_type->tp_unsigned ? uint_type : int_type);
|
||||
break;
|
||||
case INT:
|
||||
case LONG:
|
||||
break;
|
||||
case ENUM:
|
||||
if ( is_test_op(oper) || oper == '=' || oper == PARCOMMA ||
|
||||
oper == ',' || oper == ':' ||
|
||||
( !options['R'] &&
|
||||
(is_arith_op(oper) || is_asgn_op(oper))
|
||||
)
|
||||
)
|
||||
{}
|
||||
else
|
||||
warning("%s on enum", symbol2str(oper));
|
||||
int2int(expp, int_type);
|
||||
break;
|
||||
case FLOAT:
|
||||
float2float(expp, double_type);
|
||||
break;
|
||||
case DOUBLE:
|
||||
break;
|
||||
#ifndef NOBITFIELD
|
||||
case FIELD:
|
||||
field2arith(expp);
|
||||
break;
|
||||
#endif NOBITFIELD
|
||||
default:
|
||||
error("operator %s on non-numerical operand (%s)",
|
||||
symbol2str(oper), symbol2str(fund));
|
||||
case ERRONEOUS:
|
||||
free_expression(*expp);
|
||||
*expp = intexpr((arith)1, INT);
|
||||
break;
|
||||
}
|
||||
|
||||
return (*expp)->ex_type->tp_fund;
|
||||
}
|
||||
|
||||
struct expr *
|
||||
arith2arith(tp, oper, expr)
|
||||
struct type *tp;
|
||||
int oper;
|
||||
struct expr *expr;
|
||||
{
|
||||
/* arith2arith constructs a new expression containing a
|
||||
run-time conversion between some arithmetic types.
|
||||
*/
|
||||
register struct expr *new = new_expr();
|
||||
|
||||
clear((char *)new, sizeof(struct expr));
|
||||
new->ex_file = expr->ex_file;
|
||||
new->ex_line = expr->ex_line;
|
||||
new->ex_type = tp;
|
||||
new->ex_class = Type;
|
||||
return new_oper(tp, new, oper, expr);
|
||||
}
|
||||
|
||||
int
|
||||
int2int(expp, tp)
|
||||
register struct expr **expp;
|
||||
struct type *tp;
|
||||
{
|
||||
/* The expression *expp, which is of some integral type, is
|
||||
converted to the integral type tp.
|
||||
*/
|
||||
|
||||
if (is_cp_cst(*expp)) {
|
||||
(*expp)->ex_type = tp;
|
||||
cut_size(*expp);
|
||||
}
|
||||
else {
|
||||
*expp = arith2arith(tp, INT2INT, *expp);
|
||||
}
|
||||
return (*expp)->ex_type->tp_fund;
|
||||
}
|
||||
|
||||
int
|
||||
int2float(expp, tp)
|
||||
struct expr **expp;
|
||||
struct type *tp;
|
||||
{
|
||||
/* The expression *expp, which is of some integral type, is
|
||||
converted to the floating type tp.
|
||||
*/
|
||||
|
||||
fp_used = 1;
|
||||
*expp = arith2arith(tp, INT2FLOAT, *expp);
|
||||
return (*expp)->ex_type->tp_fund;
|
||||
}
|
||||
|
||||
float2int(expp, tp)
|
||||
struct expr **expp;
|
||||
struct type *tp;
|
||||
{
|
||||
/* The expression *expp, which is of some floating type, is
|
||||
converted to the integral type tp.
|
||||
*/
|
||||
|
||||
fp_used = 1;
|
||||
*expp = arith2arith(tp, FLOAT2INT, *expp);
|
||||
}
|
||||
|
||||
float2float(expp, tp)
|
||||
struct expr **expp;
|
||||
struct type *tp;
|
||||
{
|
||||
/* The expression *expp, which is of some floating type, is
|
||||
converted to the floating type tp.
|
||||
There is no need for an explicit conversion operator
|
||||
if the expression is a constant.
|
||||
*/
|
||||
|
||||
fp_used = 1;
|
||||
if ((*expp)->ex_class == Float) {
|
||||
(*expp)->ex_type = tp;
|
||||
}
|
||||
else {
|
||||
*expp = arith2arith(tp, FLOAT2FLOAT, *expp);
|
||||
}
|
||||
}
|
||||
|
||||
array2pointer(expp)
|
||||
struct expr **expp;
|
||||
{
|
||||
/* The expression, which must be an array, it is converted
|
||||
to a pointer.
|
||||
*/
|
||||
(*expp)->ex_type =
|
||||
construct_type(POINTER, (*expp)->ex_type->tp_up, (arith)0);
|
||||
}
|
||||
|
||||
function2pointer(expp)
|
||||
struct expr **expp;
|
||||
{
|
||||
/* The expression, which must be a function, it is converted
|
||||
to a pointer to the function.
|
||||
*/
|
||||
(*expp)->ex_type =
|
||||
construct_type(POINTER, (*expp)->ex_type, (arith)0);
|
||||
}
|
||||
|
||||
opnd2integral(expp, oper)
|
||||
struct expr **expp;
|
||||
int oper;
|
||||
{
|
||||
register int fund = (*expp)->ex_type->tp_fund;
|
||||
|
||||
if (fund != INT && fund != LONG) {
|
||||
if (fund != ERRONEOUS)
|
||||
error("%s operand to %s",
|
||||
symbol2str(fund), symbol2str(oper));
|
||||
*expp = intexpr((arith)1, INT);
|
||||
/* fund = INT; */
|
||||
}
|
||||
}
|
||||
|
||||
opnd2logical(expp, oper)
|
||||
struct expr **expp;
|
||||
int oper;
|
||||
{
|
||||
register int fund;
|
||||
|
||||
if ((*expp)->ex_type->tp_fund == FUNCTION)
|
||||
function2pointer(expp);
|
||||
#ifndef NOBITFIELD
|
||||
else
|
||||
if ((*expp)->ex_type->tp_fund == FIELD)
|
||||
field2arith(expp);
|
||||
#endif NOBITFIELD
|
||||
|
||||
fund = (*expp)->ex_type->tp_fund;
|
||||
|
||||
switch (fund) {
|
||||
|
||||
case CHAR:
|
||||
case SHORT:
|
||||
case INT:
|
||||
case LONG:
|
||||
case ENUM:
|
||||
case POINTER:
|
||||
case FLOAT:
|
||||
case DOUBLE:
|
||||
break;
|
||||
default:
|
||||
error("%s operand to %s",
|
||||
symbol2str(fund), symbol2str(oper));
|
||||
case ERRONEOUS:
|
||||
*expp = intexpr((arith)1, INT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
opnd2test(expp, oper)
|
||||
struct expr **expp;
|
||||
{
|
||||
opnd2logical(expp, oper);
|
||||
if ((*expp)->ex_class == Oper && is_test_op((*expp)->OP_OPER))
|
||||
{ /* It is already a test */ }
|
||||
else
|
||||
ch7bin(expp, NOTEQUAL, intexpr((arith)0, INT));
|
||||
}
|
||||
|
||||
int
|
||||
is_test_op(oper)
|
||||
{
|
||||
switch (oper) {
|
||||
case '<':
|
||||
case '>':
|
||||
case LESSEQ:
|
||||
case GREATEREQ:
|
||||
case EQUAL:
|
||||
case NOTEQUAL:
|
||||
case '!':
|
||||
case AND:
|
||||
case OR: /* && and || also impose a test */
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
||||
int
|
||||
is_arith_op(oper)
|
||||
{
|
||||
switch (oper) {
|
||||
case '*':
|
||||
case '/':
|
||||
case '%':
|
||||
case '+':
|
||||
case '-':
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
case '&':
|
||||
case '^':
|
||||
case '|':
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
is_asgn_op(oper)
|
||||
{
|
||||
switch (oper) {
|
||||
case '=':
|
||||
case PLUSAB:
|
||||
case MINAB:
|
||||
case TIMESAB:
|
||||
case DIVAB:
|
||||
case MODAB:
|
||||
case LEFTAB:
|
||||
case RIGHTAB:
|
||||
case ANDAB:
|
||||
case ORAB:
|
||||
case XORAB:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
any2opnd(expp, oper)
|
||||
struct expr **expp;
|
||||
{
|
||||
if (!*expp)
|
||||
return;
|
||||
switch ((*expp)->ex_type->tp_fund) { /* RM 7.1 */
|
||||
case CHAR:
|
||||
case SHORT:
|
||||
case ENUM:
|
||||
case FLOAT:
|
||||
any2arith(expp, oper);
|
||||
break;
|
||||
case ARRAY:
|
||||
array2pointer(expp);
|
||||
break;
|
||||
#ifndef NOBITFIELD
|
||||
case FIELD:
|
||||
field2arith(expp);
|
||||
break;
|
||||
#endif NOBITFIELD
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NOBITFIELD
|
||||
field2arith(expp)
|
||||
struct expr **expp;
|
||||
{
|
||||
/* The expression to extract the bitfield value from the
|
||||
memory word is put in the tree.
|
||||
*/
|
||||
register struct type *tp = (*expp)->ex_type->tp_up;
|
||||
register struct field *fd = (*expp)->ex_type->tp_field;
|
||||
register struct type *atype = tp->tp_unsigned ? uword_type : word_type;
|
||||
|
||||
(*expp)->ex_type = atype;
|
||||
|
||||
if (atype->tp_unsigned) { /* don't worry about the sign bit */
|
||||
ch7bin(expp, RIGHT, intexpr((arith)fd->fd_shift, INT));
|
||||
ch7bin(expp, '&', intexpr(fd->fd_mask, INT));
|
||||
}
|
||||
else { /* take care of the sign bit: sign extend if needed */
|
||||
register arith bits_in_type = atype->tp_size * 8;
|
||||
|
||||
ch7bin(expp, LEFT,
|
||||
intexpr(bits_in_type - fd->fd_width - fd->fd_shift, INT)
|
||||
);
|
||||
ch7bin(expp, RIGHT, intexpr(bits_in_type - fd->fd_width, INT));
|
||||
}
|
||||
ch7cast(expp, CAST, tp); /* restore its original type */
|
||||
}
|
||||
#endif NOBITFIELD
|
||||
|
||||
/* switch_sign_fp() negates the given floating constant expression
|
||||
The lexical analyser has reserved an extra byte of space in front
|
||||
of the string containing the representation of the floating
|
||||
constant. This byte contains the '-' character and we have to
|
||||
take care of the first byte the fl_value pointer points to.
|
||||
*/
|
||||
switch_sign_fp(expr)
|
||||
struct expr *expr;
|
||||
{
|
||||
if (*(expr->FL_VALUE) == '-')
|
||||
++(expr->FL_VALUE);
|
||||
else
|
||||
--(expr->FL_VALUE);
|
||||
}
|
||||
Reference in New Issue
Block a user