📄 op.c
字号:
/*@A (C) 1992 Allen I. Holub */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tools/debug.h>
#include <tools/hash.h>
#include <tools/l.h>
#include <tools/compiler.h>
#include <tools/c-code.h>
#include <tools/occs.h>
#include "symtab.h"
#include "value.h"
#include "proto.h"
#include "label.h"
/* OP.C This file contains support subroutines for the arithmetic */
/* operations in c.y. */
symbol *Undecl = NULL; /* When an undeclared symbol is used in an expression,
* it is added to the symbol table to suppress subse-
* quent error messages. This is a pointer to the head
* of a linked list of such undeclared symbols. It is
* purged by purge_undecl() at the end of compound
* statements, at which time error messages are also
* generated.
*/
static int make_types_match P((value **v1p, value **v2p ));
static int do_binary_const P((value **v1p, int op, value **v2p ));
static symbol *find_field P((structdef *s, char *field_name ));
static char *access_with P((value *val ));
static void dst_opt P((value **lp, value **rp, int commut ));
/*----------------------------------------------------------------------*/
value *do_name( yytext, sym )
char *yytext; /* Lexeme */
symbol *sym; /* Symbol-table entry for id, NULL if none. */
{
link *chain_end, *lp ;
value *synth;
char buf[ VALNAME_MAX ];
/* This routine usually returns a logical lvalue for the referenced symbol.
* The symbol's type chain is copied into the value and value->name is a
* string, which when used as an operand, evaluates to the address of the
* object. Exceptions are aggregate types (arrays and structures), which
* generate pointer temporary variables, initialize the temporaries to point
* at the first element of the aggregate, and return an rvalue that
* references that pointer. The type chain is still just copied from the
* source symbol, so a structure or pointer must be interpreted as a pointer
* to the first element/field by the other code-generation subroutines.
* It's also important that the indirection processing (* [] . ->) set up
* the same sort of object when the referenced object is an aggregate.
*
* Note that !sym must be checked twice, below. The problem is the same one
* we had earlier with typedefs. A statement like foo(){int x;x=1;} fails
* because the second x is read when the semicolon is shifted---before
* putting x into the symbol table. You can't use the trick used earlier
* because def_list is used all over the place, so the symbol table entries
* can't be made until local_defs->def_list is processed. The simplest
* solution is just to call findsym() if NULL was returned from the scanner.
*
* The second if(!sym) is needed when the symbol really isn't there.
*/
if( !sym )
sym = (symbol *) findsym( Symbol_tab, yytext );
if( !sym )
sym = make_implicit_declaration( yytext, &Undecl );
if( IS_CONSTANT(sym->etype) ) /* it's an enum member */
{
if( IS_INT(sym->type) )
synth = make_icon( NULL, sym->type->V_INT );
else
{
yyerror("Unexpected noninteger constant\n");
synth = make_icon( NULL, 0 );
}
}
else
{
gen_comment("%s", sym->name); /* Next instruction will have symbol */
/* name as a comment. */
if( !(lp = clone_type( sym->type, &chain_end)) )
{
yyerror("INTERNAL do_name: Bad type chain\n" );
synth = make_icon( NULL, 0 );
}
else if( IS_AGGREGATE(sym->type) )
{
/* Manufacture pointer to first element */
sprintf(buf, "&%s(%s)",
IS_ARRAY(sym->type) ? get_prefix(lp) : BYTE_PREFIX,
sym->rname );
synth = tmp_create(sym->type, 0);
gen( "=", synth->name, buf );
}
else
{
synth = new_value();
synth->lvalue = 1 ;
synth->type = lp ;
synth->etype = chain_end;
synth->sym = sym ;
if( sym->implicit || IS_FUNCT(lp) )
strcpy( synth->name, sym->rname );
else
sprintf(synth->name,
(chain_end->SCLASS == FIXED) ? "&%s(&%s)" : "&%s(%s)",
get_prefix(lp), sym->rname);
}
}
return synth;
}
/*----------------------------------------------------------------------*/
symbol *make_implicit_declaration( name, undeclp )
char *name;
symbol **undeclp;
{
/* Create a symbol for the name, put it into the symbol table, and add it
* to the linked list pointed to by *undeclp. The symbol is an int. The
* level field is used for the line number.
*/
symbol *sym;
link *lp;
extern int yylineno; /* created by LeX */
extern char *yytext;
lp = new_link();
lp->class = SPECIFIER;
lp->NOUN = INT;
sym = new_symbol( name, 0 );
sym->implicit = 1;
sym->type = sym->etype = lp;
sym->level = yylineno; /* Use the line number for the declaration */
/* level so that you can get at it later */
/* if an error message is printed. */
sprintf( sym->rname, "_%1.*s", sizeof(sym->rname)-2, yytext );
addsym ( Symbol_tab, sym );
sym->next = *undeclp; /* Link into undeclared list. */
*undeclp = sym;
return sym;
}
/*----------------------------------------------------------------------*/
PUBLIC void purge_undecl()
{
/* Go through the undeclared list. If something is a function, leave it in
* the symbol table as an implicit declaration, otherwise print an error
* message and remove it from the table. This routine is called at the
* end of every subroutine.
*/
symbol *sym, *cur ;
for( sym = Undecl; sym; )
{
cur = sym; /* remove current symbol from list */
sym = sym->next;
cur->next = NULL;
if( cur->implicit )
{
yyerror("%s (used on line %d) undeclared\n", cur->name, cur->level);
delsym( Symbol_tab, cur );
discard_symbol( cur );
}
}
Undecl = NULL;
}
value *do_unop( op, val )
int op;
value *val;
{
char *op_buf = "=?" ;
int i;
if( op != '!' ) /* ~ or unary - */
{
if( !IS_CHAR(val->type) && !IS_INT(val->type) )
yyerror( "Unary operator requires integral argument\n" );
else if( IS_UNSIGNED(val->type) && op == '-' )
yyerror( "Minus has no meaning on an unsigned operand\n" );
else if( IS_CONSTANT( val->type ) )
do_unary_const( op, val );
else
{
op_buf[1] = op;
gen( op_buf, val->name, val->name );
}
}
else /* ! */
{
if( IS_AGGREGATE( val->type ) )
yyerror("May not apply ! operator to aggregate type\n");
else if( IS_INT_CONSTANT( val->type ) )
do_unary_const( '!', val );
else
{
gen( "EQ", rvalue(val), "0" ); /* EQ(x, 0) */
gen( "goto%s%d", L_TRUE, i = tf_label() ); /* goto T000; */
val = gen_false_true( i, val ); /* fall thru to F */
}
}
return val;
}
/*----------------------------------------------------------------------*/
void do_unary_const( op, val )
int op;
value *val;
{
/* Handle unary constants by modifying the constant's internal value
* according to the incomming operator. No code is generated.
*/
link *t = val->type;
if( IS_INT(t) )
{
switch( op )
{
case '~': t->V_INT = ~t->V_INT; break;
case '-': t->V_INT = -t->V_INT; break;
case '!': t->V_INT = !t->V_INT; break;
}
}
else if( IS_LONG(t) )
{
switch( op )
{
case '~': t->V_LONG = ~t->V_LONG; break;
case '-': t->V_LONG = -t->V_LONG; break;
case '!': t->V_LONG = !t->V_LONG; break;
}
}
else
yyerror("INTERNAL do_unary_const: unexpected type\n");
}
/*----------------------------------------------------------------------*/
int tf_label()
{ /* Return the numeric component of a label for use as */
static int label; /* a true/false target for one of the statements */
return ++label; /* processed by gen_false_true(). */
}
/*----------------------------------------------------------------------*/
value *gen_false_true( labelnum, val )
int labelnum;
value *val;
{
/* Generate code to assign true or false to the temporary represented by
* val. Also, create a temporary to hold the result, using val if it's
* already the correct type. Return a pointer to the target. The FALSE
* code is at the top. If val is NULL, create a temporary. Labelnum must
* have been returned from a previous tf_label() call.
*/
if( !val )
val = tmp_create( NULL, 0 );
else if( !val->is_tmp || !IS_INT(val->type) )
{
release_value( val );
val = tmp_create( NULL, 0 );
}
gen( ":%s%d", L_FALSE, labelnum ); /* F000: */
gen( "=", val->name, "0" ); /* t0 = 0 */
gen( "goto%s%d", L_END, labelnum ); /* goto E000; */
gen( ":%s%d", L_TRUE, labelnum ); /* T000: */
gen( "=", val->name, "1" ); /* t0 = 1; */
gen( ":%s%d", L_END, labelnum ); /* E000: */
return val;
}
value *incop( is_preincrement, op, val ) /* ++ or -- */
int is_preincrement; /* Is preincrement or predecrement. */
int op; /* '-' for decrement, '+' for increment */
value *val; /* lvalue to modify. */
{
char *name;
char *out_op = (op == '+') ? "+=%s%d" : "-=%s%d" ;
int inc_amt ;
/* You must use rvalue_name() in the following code because rvalue()
* modifies the val itself--the name field might change. Here, you must use
* the same name both to create the temporary and do the increment so you
* can't let the name be modified.
*/
if( !val->lvalue )
yyerror("%c%c: lvalue required\n", op, op );
else
{
inc_amt = (IS_POINTER(val->type)) ? get_sizeof(val->type->next) : 1 ;
name = rvalue_name( val );
if( is_preincrement )
{
gen( out_op, name, inc_amt );
val = tmp_gen( val->type, val );
}
else /* Postincrement. */
{
val = tmp_gen( val->type, val );
gen( out_op, name, inc_amt );
}
}
return val;
}
value *addr_of( val )
value *val;
{
/* Process the & operator. Since the incoming value already holds the
* desired address, all you need do is change the type (add an explicit
* pointer at the far left) and change it into an rvalue. The first argument
* is returned.
*/
link *p;
if( val->lvalue )
{
p = new_link();
p->DCL_TYPE = POINTER;
p->next = val->type;
val->type = p;
val->lvalue = 0;
}
else if( !IS_AGGREGATE(val->type) )
yyerror( "(&) lvalue required\n" );
return val;
}
value *indirect( offset, ptr )
value *offset; /* Offset factor (NULL if it's a pointer). */
value *ptr; /* Pointer that holds base address. */
{
/* Do the indirection, If no offset is given, we're doing a *, otherwise
* we're doing ptr[offset]. Note that, strictly speaking, you should create
* the following dumb code:
*
* t0 = rvalue( ptr ); (if ptr isn't a temporary)
* t0 += offset (if doing [offset])
* t1 = *t0; (creates a rvalue)
* lvalue attribute = &t1
*
* but the first instruction is necessary only if ptr is not already a
* temporary, the second only if we're processing square brackets.
*
* The last two operations cancel if the input is a pointer to a pointer. In
* this case all you need to do is remove one * declarator from the type
* chain. Otherwise, you have to create a temporary to take care of the type
* conversion.
*/
link *tmp ;
value *synth ;
int objsize ; /* Size of object pointed to (in bytes) */
if( !IS_PTR_TYPE(ptr->type) )
yyerror( "Operand for * or [N] Must be pointer type\n" );
rvalue( ptr ); /* Convert to rvalue internally. The "name" field */
/* is modified by removing leading &'s from logical */
/* lvalues. */
if( offset ) /* Process an array offset. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -