现代编译原理--第五章(活动记录)

  (转载请表明出处  http://www.cnblogs.com/BlackWalnut/p/4559245.html )

第五章是到目前位置最让我头疼的一章,不是因为难,是因为对最终的目的不太明确。整章前半部分用十分精简的语言介绍了一个新的概念,活动记录,也就是在函数调用时,一个调用栈究竟发生了什么事。但是,在最终编码实现过程中,总不能理解作者为什么给了这些变量?或者说,完全不知道作者究竟想让我完成一个怎样的功能。纠结了好几天,后来索性往后继续看,看看能不能找到其他的线索。直到看完后一章,硬着头皮写了一半的时候,才豁然开朗。原来作者是这个意图!所以,如果你也不知道这章程序最终要得到一个什么样的结果,建议把后面一章也读了,因为这两章讲的是同一个东西的不同部分。

  先看看一些理论知识吧。首先,这个活动记录,就是我们通常意义上的函数调用堆栈,所以又叫栈帧。是内存中的一个区域,在这个区域中包含了一个函数中所有参数,局部变量,临时变量,返回值等信息,如果这种语言还允许嵌套的函数声明,那么,还要有一个叫做静态链的东西。这些东西在不同的目标机器上对应的布局是不一样的,书上介绍了一种标准布局。

  那么,一个函数怎么去访问他自己的变量呢?用的是(栈指针+偏移量),因为对cpu而言,加法运算是比较简单的,所以,在实际中,栈指针是位于低地址,表示栈的下界,而帧指针位于高地址,表示栈的上界。这样就会发现,其实,栈顶的地址要比栈底的地址低。

  其实并不是所有参数都要放入栈(内存)中的,有些变量可以放入寄存器中,这样可以省去很多的访存时间。但是,一个程序可以有多个函数,寄存器的个数却是有限。那么,当一个函数调用另一个函数,且都要使用同一个寄存器的时候,谁去将原来的寄存器中的内容的保存起来呢?如果是调用其他函数的函数负责,则成为调用者保护,如果是被调用函数负责,那么就是被调用者保护。

    通常情况下,我们会将传入一个函数的参数放入寄存器中,但是,寄存器的数量是有限,放不下的怎么办?例如,对于f(n1 , n2 , ...,nx)的函数,我们会将前k个,假如k为4,放入寄存器中,剩下的x-k个我们就放入调用函数f的函数的栈帧的末尾,紧邻着栈指针,这x-k个区域就称为传出参数。但是这x-k块区域对于被调用函数f来说就被称为传入参数。思考一下,如果f又去调用了其他函数呢?那么那k个参数就要被移出寄存器,放到f中称为局部变量的区域中,这块区域是紧挨着传出参数(传入参数,对函数f而言)。

  那么,除了以上参数传递过程中有变量要被移出寄存器,还有其他一些情况需要将寄存器中的参数放到内存中。比如,在一个函数定义中取了一个外部函数中的一个变量的地址(此时要求语言支持函数内部定义新的函数),那么这个变量就要写回内存,要不然,你取到的地址是寄存器地址,如果在以后的处理过程中这个变量被移出了寄存器,岂不是就找不到了么。

  当然,还有很多情况需要考虑,这里就不一一列举了,虎书上已经讲的很明白了,并且,对于所有本来在寄存器中,后来被移动内存中的变量称为逃逸。那么,如何在一个函数内部取用外部函数的变量呢?这就要用一个称为静态来链的东西,这个东西就是能让你在一个函数内部顺藤摸瓜的找到你需要的变量,类似于一个链表。

  好了,说了这么多,我们来看看最后的实现吧。本章要实现的,其实就是这个栈。在前几次的数据结构中保留了一个称为scape的区域没有用,这是个bool变量,使用这个变量来标记tiger中一个变量是不是逃逸的。如何计算一个变量是不是逃逸的呢?因为这些逃逸信息只有在完成这个程序扫描后才能知道。所以,对得到的抽象语法树进行两次扫描,第一次得到变量的逃逸信息,第二次才是对对语法树进行类型检测,同时生成栈帧。

  我的代码并没有实现对抽象语法的逃逸变量检测,我默认所有的变量都是可以逃逸的。所以本次代码的主要部分就是frame.h以及frame.cpp,并且不涉及到对这frame.h中函数的真正使用。对其使用的部分将在下一章中体现。以下是源码:

  

#ifndef FRAME_H_
#define FRAME_H_
#include "temp.h"
#include "util.h"
#include "tree.h"

const int F_wordSize = 4 ;
typedef struct F_frame_ *F_frame ;
typedef struct F_access_ *F_access ;
typedef struct F_accesslist_ *F_accesslist ;
struct F_accesslist_ { F_access head; F_accesslist tail; };
typedef struct F_frag_ *  F_frag ;
typedef struct F_fragList_ * F_fragList ;

struct F_frame_ {
                 Temp_label name ;
                 int framesize ;
                 F_accesslist formals;
                 F_accesslist locals;
                 };
struct F_access_ {
                  enum { inFrame , inReg } kind  ;
                  union{
                      int offset ;
                      Temp_temp reg ;
                       } u ;
                  };
struct F_frag_ {
                enum { F_stringFrag , F_procFrag } kind ;
                union {
                    struct {
                             Temp_label label ;
                             string str ;
                           } stringg ;
                    struct {
                             T_stm body ;
                             F_frame frame ;
                            } proc ;
                        }u ;
                };
struct F_fragList_{  F_frag head ; F_fragList tail; };

F_frag F_StringFrag( Temp_label label , string str) ;
F_frag F_ProcFrag ( T_stm stm , F_frame frame ) ;

F_fragList F_FragList(F_frag frag , F_fragList tail) ;

Temp_temp F_FP(void);
Temp_temp F_RV(void) ;  

F_frame F_newframe(Temp_label name , U_boolList formals) ;
F_accesslist F_Accesslist(F_access head , F_accesslist tail) ;

Temp_label F_name(F_frame f) ;
F_accesslist F_formals(F_frame f) ;
F_access F_allocLoacl(F_frame f  , bool escape );

F_access InFrame(int offset) ;
F_access InReg(Temp_temp reg) ;

T_exp F_Exp(F_access acc, T_exp framePtr);
T_exp F_externalCall(string s , T_expList explist);

T_stm F_procEntryExit1(F_frame frame , T_stm stm) ;
#endif
#include "frame.h"
#include "tree.h"
const int offset = -4 ;

static Temp_temp fp = NULL;
Temp_temp F_FP()
{
    if(fp==NULL)
    {
        fp = Temp_newtemp();

    }
    return fp;
}
static Temp_temp rv = NULL ;
Temp_temp F_RV()
{
    if (rv == NULL )
    {
        rv = Temp_newtemp() ;
    }
    return rv ;
}
F_access F_allocLoacl(F_frame f , bool escape )
{
    F_access access ;
    if (escape == true)
    {
        access = InFrame(f->framesize) ;
        f->framesize -= offset;
    }
    else
    {
        access = InReg(Temp_newtemp()) ;
    }
    f->locals = F_Accesslist(access , f->locals) ;
    return access ;
}

F_access InFrame(int offset)
{
    F_access tmp = (F_access) checked_malloc(sizeof(*tmp)) ;
    tmp->kind = F_access_::inFrame ;
    tmp->u.offset = offset ;
    return tmp ;
}

F_access InReg(Temp_temp reg)
{
    F_access tmp = (F_access) checked_malloc(sizeof(*tmp)) ;
    tmp->kind = F_access_::inReg ;
    tmp->u.reg = reg ;
    return tmp ;
}

F_frame F_newframe(Temp_label name , U_boolList formals)
{
    F_frame frame =(F_frame) checked_malloc(sizeof(*frame)) ;
    frame->name = name ;
    frame->formals = NULL ;
    U_boolList par = formals ;
    F_access acc ;
    frame->framesize = 0 ;
    while(par != NULL)
    {
        if (par->head)
        {
          acc = InFrame(frame->framesize) ;
          frame->framesize -= offset ;
        }
        else
        {
         acc = InReg(Temp_newtemp()) ;
        }
        frame->formals = F_Accesslist(acc , frame->formals) ;
        par = par->tail ;
    }
   frame->locals = NULL ;
   return frame ;
}

F_accesslist F_Accesslist(F_access head , F_accesslist tail)
{
    F_accesslist tmp = (F_accesslist)checked_malloc(sizeof(*tmp)) ;
    tmp->head = head ;
    tmp->tail = tail ;
    return tmp ;
}

T_exp F_Exp(F_access acc, T_exp framePtr)
{
    if (acc->kind == F_access_::inFrame )
    {
        return T_Mem(T_Binop(T_plus, framePtr, T_Const(acc->u.offset)));
    }
    return  T_Temp(acc->u.reg);
}

F_frag F_StringFrag(Temp_label label , string str)
{
    F_frag tmp = (F_frag) checked_malloc(sizeof(*tmp)) ;
    tmp->kind = F_frag_::F_stringFrag ;
    tmp->u.stringg.label = label ;
    tmp->u.stringg.str = str ;
    return tmp ;
}

F_frag F_ProcFrag( T_stm stm , F_frame frame )
{
    F_frag tmp = (F_frag) checked_malloc(sizeof(*tmp)) ;
    tmp->kind = F_frag_::F_procFrag ;
    tmp->u.proc.body = stm ;
    tmp->u.proc.frame = frame ;
    return tmp ;
}

F_fragList F_FragList(F_frag frag , F_fragList tail)
{
    F_fragList tmp = (F_fragList) checked_malloc(sizeof(*tmp)) ;
    tmp->head = frag ;
    tmp->tail = tail ;
    return tmp;
}
T_stm F_procEntryExit1(F_frame frame , T_stm stm)
{
    return stm ;
}

F_accesslist F_formals(F_frame frame)
{
    return frame->formals;
}
时间: 2024-10-07 17:26:23

现代编译原理--第五章(活动记录)的相关文章

【读书笔记】《编译原理》第一章 引论

第一章 引论 第一章 引论 1 语言处理器 2 一个编译器的结构 3 程序设计语言发展历程 5 编译技术的应用 1.1 语言处理器 编译器compiler:将源程序翻译成目标程序,生成目标代码快速,错误诊断效果差. 解释器interpreter:用户提供源程序和输入,产生输出,较慢,错误诊断效果好. java语言处理:Java源程序->字节码bytecode->虚拟机解释执行 语言处理系统:源程序--预处理器preprocessor--经过预处理的源程序--编译器--目标汇编程序(便于输出调试

编译原理-第三章 词法分析-3.7 从正则表达式到自动机-从正则表达式构造NFA

基于MYT算法从正则表达式构造NFA 基本思想: 性质: 对于加括号的正则式(s),使用N(s)本身作为它的NFA 一.构造识别ε和字母表中一个符号的NFA 1.特点 仅一个接受状态,它没有向外的转换 2.示例 二.构造识别主算符为选择正则式的NFA 1.特点 仅一个接受状态,它没有向外的转换 2.示例 三.构造识别主算符为连接正则式的NFA 1.特点 仅一个接受状态,它没有向外的转换 2.示例 四.构造识别主算符为闭包正则式的NFA 1.特点 仅一个接受状态,它没有向外的转换 2.示例 五.例

编译原理 十五

1.语法文法G[E]如下所示:  E→E+T | E-T | T  T→T* F | T/F | F  F→P^ F | P  P→(E) | i  要求构造出符合语义分析要求的属性文法描述(主要写生成四元式的部分). E→E1+T1 {E.place:=newtemp; emit(E.place , ":=" ,E1.place,'+' , T1.place)} (+, E1.val ,T1.val,E.val) E→E1-T1 {E.place:=newtemp; emit(E.p

现代编译原理--第零章(含代码)

<现代编译原理>,俗称,虎书.因为这本书对实践的要求比较高,所以选择了这本书来作为编译原理的学习书籍,想一步一步的记录下来,最终完成一个完整的编译器.但是,一个人看书总是感觉很孤独.今天看第一章的题目,看完了都不知道要干什么.无奈找了一本中文版的,翻译的也不如人意,还不如看英文的.最后去晚上找了半天才找到别人写的第一章作业运行后,才知道要实现什么功能.然后自己徒手开始写,居然没有逻辑bug的就完了.呵呵.突然感觉网上的资料太少,所以写这一个系列的文章也想把志同道合的聚集起来,大家一起来讨论虎书

软考之路(五)---编译原理 概念篇

从组成原理到编译原理,都是相互联系的,学习的知识都是在层层推进,顺利成章的,整个软考的所有的知识都可以从计算机的发展(组成原理)--->软件的开发(软件工程)--->软件的算法设计(数据结构)-->高级语言写的系统--->在计算机上识别(编译原理)--->各个系统的背后数据的支持--->(数据库)---->---网络知识的应用---->网络基础--,可以把整个所有的软考的课本给串起来,这些都是构成现在丰富世界的必不可少的条件. 当然由于机器语言由0.1组成,

现代编译原理--第六章(中间树 IR Tree 含源码)

(转载请表明出处   http://www.cnblogs.com/BlackWalnut/p/4559717.html ) 这一章,就虎书而言,理论知识点是及其少的,就介绍了为什么要有一个中间表示树.看下面这张图就能理解为什么了. 但是章的代码量却是挺多的.在写代码之前,如果不懂整个代码的布局,是很难了解书上那写代码是对应那些功能,以及书上没有给出的代码,我应该这么完善.那么,我就我自己的理解来说一下到目前为止(翻译成中间表示树以后,编译器的前端就算基本完成了),整个代码的布局是什么样. 首先

第五章--破解原理

从本章开始,我们来一步一步学习Crack软件(80%读者昏死过去,且不省人世...另有20%在寻找附近可以用来打人的东西) 不可不说一下学习破解的三个阶段: 初级,修改程序,用ultraedit等工具修改exe文件,称暴力破解,简称爆破 中级,追出软件的注册码 高级,写出注册机 先 说这爆破.所谓爆破,就是指通过修改可执行文件的源文件,来达到相应的目的.你不明白?呵呵,举个例子好了,比如说某共享软件,它比较用户输入的注册码, 如果用户输入的,跟它通过用户名(或其它)算出来的注册码相等的话(也就是

编译原理第一章学习(习题解答)

编译原理 第一章 引论 1.1 练习 1.编译器和解释器之间的区别是什么? 首先,编译器是一个软件系统或者说是一个程序,解释器是语言处理器.其次,编译器是把程序翻译成能被计算机执行的形式并报告翻译过程中发现的源程序的错误,解释器是直接利用用户提供的输入执行源程序中指定的操作. 2.编译器相对于解释器的优点是什么?解释器相对于编译器的优点是什么? 在把用户输入映射成为输出的过程中,由一个编译器产生的机器语言目标程序通常比一个解释器快很多.然而,解释器的错误诊断效果比编译器更好,因为它是逐句翻译源程

【知识强化】第五章 中央处理器 5.4 控制器的功能和工作原理

下面我们进入第五章的第四节,控制器的功能和工作原理. 那么首先,我们来看一下本章的一些内容.我们再对我们之前讲过的内容进行一些梳理,我们本章,第五章,中央处理器,也就是CPU,要学五节的内容.第一节我们讲了CPU的功能和基本结构,我们讲解了运算器的功能和结构,以及控制器的功能和结构.接下来我们讲解了指令的一个执行过程,我们讲解了指令周期的概念,以及一个数据流,几种数据流我花了大量的篇幅给大家做了 原文地址:https://www.cnblogs.com/ZHONGZHENHUA/p/115112