📄 cg.c
字号:
} /* void cg_cond( char *op,
SYMB *a,
int l ) */
void cg_arg( SYMB *a )
/* 实参语句的翻译: 实参语句出现在函数调用语句之前, 而实参的内存地址应该在被
函数栈的活动记录之后, 因此在此不能增加tos的方式分配内存, 我们用next_arg记
录实参的偏移量, 即实参的相对于当前栈顶的偏移量应是: tos + VAR_OFF +
next_arg, 其中VAR_OFF是活记录的长度, 注意, 按照这样的假设, 实参语句之后
只能出现函数调用语句 */
{
int r = get_rreg( a ) ;
printf( " STI R%d,%d(R%d)\n", r, tos + VAR_OFF + next_arg,
R_P ) ;
next_arg += 4 ;
} /* void cg_arg( SYMB *a ) */
void cg_call( int f,
SYMB *res )
/* 函数调用语句将翻译为:
LDA f(R0),R2
STI R1,tos(R1)
LDA tos(R1),R1
BAL R2,R3
( STI R4,res )
即: 将被调用函数指针装入R2, 将当前栈指针装入当前栈顶作为第一条活记录装
入栈顶, 将当前栈顶指针作为被调用函数的栈指针装入R1, 转移指令到被调用函数,
同时将函数调用之后的第一条指令装入R3, 如果函数有返回值, 则将函数的返回值
R4装入到res的内存中. 重新对next_arg置0 */
{
flush_all() ; /* 刷新寄存器 */
next_arg = 0 ;
printf( " LDA L%d,R%d\n", f, R_CALL ) ;
printf( " STI R%d,%d(R%d)\n", R_P, tos, R_P ) ;
printf( " LDA %d(R%d),R%d\n", tos, R_P, R_P ) ;
printf( " BAL R%d,R%d\n", R_CALL, R_RET ) ;
if( res != NULL ) /* 是否有返回值 */
insert_desc( R_RES, res, MODIFIED ) ;
} /* void cg_call( int f,
SYMB *res ) */
void cg_return( SYMB *a )
/* 函数返回语句将翻译为下述汇编指令:
( LDI a,R4 )
LDI 4(R1),R2
LDI 0(R1),R1
BAL R2,R3
即, 将返回值装入寄存器R4中, 将活记录中的指令指令装入R2, 将调用函数的
栈指针装入R1, 将指令转移R2, 如果a是空, 则不装载返回值 */
{
if( a != NULL )
{
spill_one( R_RES ) ;
load_reg( R_RES, a ) ;
}
printf( " LDI %d(R%d),R%d\n", PC_OFF, R_P, R_CALL ) ;
printf( " LDI %d(R%d),R%d\n", P_OFF, R_P, R_P ) ;
printf( " BAL R%d,R%d\n", R_CALL, R_RET ) ;
} /* void cg_return( SYMB *a ) */
void cg_sys( char *fn ) /* 文件名 */
/* copy库函数到标准输出 */
{
FILE *fd = fopen( fn, "r" ) ; /* 库函数名 */
int c ;
if( fd == NULL )
{
error( "cannot open system file" ) ;
exit( 0 ) ;
}
while((c = getc( fd )) != EOF )
putchar( c ) ;
fclose( fd ) ;
} /* void cg_sys( char *fn ) */
void cg_strings( void )
/* 字符串常量将以静态的方式保存, 在汇编代码中通过标号的方式访问这些静态数据,
因此, 在目标汇编语言中, 通过连续的虚拟汇编指令DB保存字符串常量, 注意在字
符串的结尾处加上字符串结束标记ASCII 0 */
{
int i ;
for( i = 0 ; i < HASHSIZE ; i++) /* 扫描整个符号表, 寻找字符串常量 */
{
SYMB *sl ;
for( sl = symbtab[i] ; sl != NULL ; sl = sl->next )
if( sl->type == T_TEXT )
cg_str( sl ) ;
}
printf( "L0:\n" ) ;
} /* void cg_strings( void ) */
void cg_str( SYMB *s )
/* 对每个字符串常量生成数据DB指令 */
{
char *t = s->TEXT1 ; /* 字符串首字符 */
int i ;
printf( "L%d:\n", s->VAL2 ) ; /* 首先产生标号语句 */
for( i = 1 ; t[i + 1] != EOS ; i++ )
if( t[i] == '\\' )
switch( t[++i] )
{
case 'n':
printf( " DB %d\n", '\n' ) ;
break ;
case '\"':
printf( " DB %d\n", '\"' ) ;
break ;
}
else
printf( " DB %d\n", t[i] ) ;
printf( " DB 0\n" ) ; /* 字符串结束标记 */
} /* void cg_str( SYMB *s ) */
/* 以下的函数是在翻译时对寄存器进行管理的有关函数 */
void flush_all( void )
/* 刷新所有的寄存器, 在函数调用时使用 */
{
int r ;
spill_all() ;
for( r = R_GEN ; r < R_MAX ; r++ ) /* 刷新寄存器标记 */
clear_desc( r ) ;
} /* void flush_all( void ) */
void spill_all( void )
/* 回填所有的寄存器到对应的内存中 */
{
int r ;
for( r = R_GEN ; r < R_MAX ; r++ )
spill_one( r ) ;
} /* spill_all( void ) */
void spill_one( int r )
/* 回填指定的寄存器到对应的内存中 */
{
if( rdesc[r].modified ) /* 是否被修改 */
{
printf( " STI R%d,%d(R%d)\n", r, rdesc[r].name->ADDR2,
R_P ) ;
rdesc[r].modified = UNMODIFIED ;
}
} /* void spill_one( int r ) */
void load_reg( int r, /* 寄存器编号 */
SYMB *n ) /* 变量名 */
/* "load_reg()" 装载一个值到指定的寄存器中, 如果该变脸已在寄存器中, 则用指令
LDR从寄存器中直接装载; 如果要装载字符串常量和常量, 则用LDA; 而对变量则用
LDI装载 */
{
int s ;
/* 查看n是否已在寄存其中 */
for( s = 0 ; s < R_MAX ; s++ )
if( rdesc[s].name == n )
{
printf( " LDR R%d,R%d\n", s, r ) ;
insert_desc( r, n, rdesc[s].modified ) ;
/* 标记和原有的寄存器相同 */
return ;
}
/* 不在寄存器中, 分情况讨论 */
switch( n->type )
{
case T_INT: /* 常量 */
printf( " LDA %d(R0),R%d\n", n->VAL1, r ) ;
/* 由于R0总是0, 因此, R0的值加上n的数值为n本身 */
break ;
case T_VAR: /* 变量 */
printf( " LDI %d(R%d),R%d\n", n->ADDR2, R_P, r ) ;
break ;
case T_TEXT: /* 字符串常量 */
printf( " LDA L%d,R%d\n", n->VAL2, r ) ;
break ;
}
insert_desc( r, n, UNMODIFIED ) ; /* 设置标记为未修改 */
} /* void load_reg( int r,
SYMB *n ) */
void clear_desc( int r ) /* 寄存器编号 */
/* 清除指定的寄存器 */
{
rdesc[r].name = NULL ;
} /* void clear_desc( int r ) */
void insert_desc( int r,
SYMB *n,
int mod )
/* 装入一个指定的寄存器. */
{
rdesc[r].name = n ;
rdesc[r].modified = mod ;
} /* void insert_desc( int r,
SYMB *n,
int mod ) */
/* These two routines implement the simple register allocation algorithm
described in chapter 10. "get_rreg()" gets a register that will hold an
operand and be overwritten by a result. "get_areg()" gets a register that
will hold an operand that will no be overwritten. */
int get_rreg( SYMB *c )
/* 获得第一个寄存器, 由于运算是在寄存器上进行, 因此, 对于运算:
a := b op c
首先要将c装入一个寄存器中, 我们通过申请的方式来装载c, 使寄存器达到最优
的效率, 申请的原则是: 1. 如果c已在某一个寄存器中, 则回填该寄存器并返回
该寄存器编码; 2. 空寄存器; 3. 修改标记为假的寄存器; 4. 修改标记为真的
寄存器, 这时需要回填该寄存器 */
{
int r ; /* 对寄存器数组遍历的变量 */
for( r = R_GEN ; r < R_MAX ; r++ ) /* 已在一寄存器中 */
if( rdesc[r].name == c )
{
spill_one( r ) ;
return r ;
}
for( r = R_GEN ; r < R_MAX ; r++ )
if( rdesc[r].name == NULL ) /* 使用空寄存器 */
{
load_reg( r, c ) ;
return r ;
}
for( r = R_GEN ; r < R_MAX ; r++ )
if( !rdesc[r].modified ) /* 使用修改标记为假的寄存器 */
{
clear_desc( r ) ;
load_reg( r, c ) ;
return r ;
}
spill_one( R_GEN ) ; /* 使用修改标记为真的寄存器 */
clear_desc( R_GEN ) ;
load_reg( R_GEN, c ) ;
return R_GEN ;
} /* int get_rreg( SYMB *c ) */
int get_areg( SYMB *b,
int cr ) /* 已使用寄存器编号 */
/* 同上一样, 但是, 在使用非空寄存器时, 一定不能使用cr */
{
int r ; /* 对寄存器数组遍历的变量 */
for( r = R_ZERO ; r < R_MAX ; r++ )
if( rdesc[r].name == b ) /* 已在一寄存器中 */
return r ;
for( r = R_GEN ; r < R_MAX ; r++ )
if( rdesc[r].name == NULL ) /* 使用空寄存器 */
{
load_reg( r, b ) ;
return r ;
}
for( r = R_GEN ; r < R_MAX ; r++ )
if( !rdesc[r].modified && (r != cr)) /* 使用修改标记为假的寄存器 */
{
clear_desc( r ) ;
load_reg( r, b ) ;
return r ;
}
for( r = R_GEN ; r < R_MAX ; r++ )
if( r != cr ) /* 使用修改标记为真的寄存器 */
{
spill_one( r ) ;
clear_desc( r ) ;
load_reg( r, b ) ;
return r ;
}
} /* int get_areg( SYMB *b,
int cr ) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -