编译器架构的王者LLVM——(10)变量的存储与读取

LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 —— 题记

版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 http://blog.csdn.net/xfxyy_sxfancy

变量的存储与读取

变量是一款编程语言中的核心,说编译语言是一种符号处理工具,其实是有些道理的。栈式符号表可以方便的记录编译过程中的变量和语法符号,我们上节已经了解了其中的实现方法。那么,还有没有其他的办法能够简单的实现变量的存取呢?

LLVM的内置符号表

其实LLVM还提供了一个内部符号表,这个和我们的符号表不一样,它的符号是以函数为界的,函数内的是局部符号,外面的是全局符号。这个符号表的作用,主要是供LLVM找到各个底层的语法元素而设计的,所以它的功能较为有限。

例如下面这段字节码:

define void @print(i64 %k1) {
entry:
    ...
}

我们可以通过符号表,找到k1这个元素。

这个符号表的获取也很简单,只要你有basicblock,你就能够找到这个符号表的指针:

    BasicBlock* bb = context->getNowBlock();
    ValueSymbolTable* st = bb->getValueSymbolTable();
    Value* v = st->lookup(value);

栈上变量空间的分配,AllocaInst语句

AllocaInst是LLVM的一条标准语句,负责栈上空间的分配,你无需考虑栈的增长的操作,它会自动帮你完成,并返回给你对应空间的指针。

千万不要认为这个语句能够动态分配堆内存,堆内存实际上是通过调用Malloc语句来分配的。

    %k = alloca i64

以上语句,会让k的类型变为你分配类型的指针。

这个语句的C++接口非常的好用,像这样:

AllocaInst *alloc = new AllocaInst(t, var_name, context->getNowBlock());

t对应分配的类型,var_name对应语句返回的那个变量名(上面的‘k’),最后一个参数当然是插入的basicblock。

这时,返回的语句,就代表k这个指针了。

变量的存储

LLVM中,变量的存储,都需要知道要存储地址的指针,注意,一定是指针,而不是值。

原型:

    StoreInst (Value *Val, Value *Ptr, bool isVolatile, BasicBlock *InsertAtEnd)

使用示例:

    new StoreInst(value2, value1, false, context->getNowBlock());

这个value1,就是目标的存储指针,而value2则是要放入的值。false表示不是易变的,这个参数相当与C语言中的volatile关键字,主要是防止这个变量在重复读取时的编译器优化。因为一般的编译器优化,都会将一个变量在没有改变情况下的多次读取,认为取到同一个值,虽然这在多线程和硬中断的环境下并不成立。

变量的读取

变量的读取,就用Load语句:

LoadInst (Value *Ptr, const Twine &NameStr, bool isVolatile, unsigned Align, BasicBlock *InsertAtEnd)

使用示例:

new LoadInst(v, "", false, bb);

我们这里暂时没有考虑内存对齐的问题,当然,一般在Clang中,都是4字节对齐的。我们注意到,其实Load语句也是从指针中取值的,返回的则是一个值类型。

打造一个赋值语句

赋值语句其实是一个挺尴尬的语句,左边要赋值的,应该是一个指针地址,而右边的部分,则应该是一个获取到的值。而之前我们的运算,函数调用等等,绝大部分都是依赖值类型的。

我们先要为变量实现一个值的获取,这部分因为很通用,我们放到IDNode节点的代码生成中:

Value* IDNode::codeGen(CodeGenContext* context) {
    BasicBlock* bb = context->getNowBlock();
    ValueSymbolTable* st = bb->getValueSymbolTable();
    Value* v = st->lookup(value);
    if (v == NULL || v->hasName() == false) {
        errs() << "undeclared variable " << value << "\n";
        return NULL;
    }
    Value* load = new LoadInst(v, "", false, bb);
    return load;
}

value是我们类的成员变量,记录的是变量名。

然而赋值语句有时还会要求获取到的是指针,不是值,现在我们要为赋值语句实现一个符号指针的获取:

Value* IDNode::codeGen(CodeGenContext* context) {
    BasicBlock* bb = context->getNowBlock();
    ValueSymbolTable* st = bb->getValueSymbolTable();
    Value* v = st->lookup(value);
    if (v == NULL || v->hasName() == false) {
        errs() << "undeclared variable " << value << "\n";
        return NULL;
    }
    if (context->isSave()) return v; // 我们在上下文类中记录了一个变量,看当前状态是存还是取
    Value* load = new LoadInst(v, "", false, bb);
    return load;
}

那么我们在调用时,只需要这样做:

static Value* opt2_macro(CodeGenContext* context, Node* node) {
    std::string opt = node->getStr();

    Node* op1 = (node = node->getNext());
    if (node == NULL) return NULL;
    Node* op2 = (node = node->getNext());
    if (node == NULL) return NULL;

    if (opt == "=") {
        context->setIsSave(true); // 这两句设置的目前是为下面的节点解析时,返回指针而不是load后的值
        Value* ans1 = op1->codeGen(context);
        context->setIsSave(false);
        Value* ans2 = op2->codeGen(context);
        return new StoreInst(ans2, ans1, false, context->getNowBlock());
    }

    ...
}

其实我们这里也可以单独实现一个函数来处理这个功能,但由于两个函数功能太像,所以也不怎么想添加一个类似的函数了。

这个部分暂时先这样处理一下,待整体结构完善后,应该有更好的实现方法。

时间: 2024-10-24 21:26:08

编译器架构的王者LLVM——(10)变量的存储与读取的相关文章

编译器架构的王者LLVM——(8)函数的调用及基本运算符

LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 -- 题记 版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 http://blog.csdn.net/xfxyy_sxfancy 函数的调用及基本运算符 之前我们提到了函数的定义,那么,定义好的函数如何调用才行呢?今天我们就来了解一下,函数的调用. 函数调用的宏形式 我们去读之前对函数调用的语法树翻译形式: printf("%d\

编译器架构的王者LLVM——(9)栈式符号表的构建

LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 -- 题记 版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 http://blog.csdn.net/xfxyy_sxfancy 栈式符号表的构建 栈式符号表对于一款编译器,无疑是核心的组件. 无论你在做什么符号扫描,那么都离不开符号表,如何得知一个符号是否定义,以及它的类型,那么唯有查看符号表中的记录. 栈式符号表并不复杂,但思

编译器架构的王者LLVM——(7)函数的翻译方法

LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 -- 题记 版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 http://blog.csdn.net/xfxyy_sxfancy 函数的翻译方法 前面介绍了许多编译器架构上面的特点,如何组织语法树.如果多遍扫描语法树.今天开始,我们就要设计本编译器中最核心的部分了,如何设计一个编译时宏,再利用LLVM按顺序生成模块. 设计宏 我们

编译器架构的王者LLVM——(5)语法树模型的基本结构

LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 -- 题记 版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 http://blog.csdn.net/xfxyy_sxfancy 语法树模型的基本结构 上次我们看了Lex和Yacc的翻译文件,可能一些朋友并不了解其中的执行部分,而且,对这个抽象语法树是怎么构建起来的还不清楚.今天我们就再详细介绍一下如果方便的构建一棵抽象语法树(A

编译器架构的王者LLVM——(11)深入理解GetElementPtr

LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 -- 题记 版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 http://blog.csdn.net/xfxyy_sxfancy 深入理解GetElementPtr LLVM平台,和C语言极为类似,强类型,需要复杂的指针操作,基于系统的符号调用等.而LLVM的指针操作指令,GetElementPtr,几乎是所有指针计算的关键,而理

大型网站架构不得不考虑的10个问题

大型网站架构不得不考虑的10个问题 这里的大型网站架构只包括高互动性高交互性的数据型大型网站,基于大家众所周知的原因,我们就不谈新闻类和一些依靠HTML静态化就可以实现的架构了,我们以高负载高数据交换高数据流动性的网站为例,比如海内,开心网等类似的web2.0系列架构.我们这里不讨论是PHP还是JSP或者.NET环境,我们从架构的方面去看问题,实现语言方面并不是问题,语言的优势在于实现而不是好坏,不论你选择任何语言,架构都是必须要面对的. 这里讨论一下大型网站需要注意和考虑的问题 1.海量数据的

Linux添加环境变量与GCC编译器添加INCLUDE与LIB环境变量

对所有用户有效在/etc/profile增加以下内容.只对当前用户有效在Home目录下的.bashrc或.bash_profile里增加下面的内容:(注意:等号前面不要加空格,否则可能出现 command not found) #在PATH中找到可执行文件程序的路径.export PATH =$PATH:$HOME/bin #gcc找到头文件的路径C_INCLUDE_PATH=/usr/include/libxml2:/MyLibexport C_INCLUDE_PATH #g++找到头文件的路

C++变量的存储类别与作用域

总结一下C++中变量的存储类别以及变量的作用域. (1)标示符的存储类别决定了标示符在内存中存在的时间(我们可以理解标示符就是确定一个变量的符号,也就是我们所说的变量名) 二:存储类别 (1)静态存储类别:静态存数类别变量(我们简称静态变量),从程序的开始处就存在,其生命期伴随整个程序. (2)自动存储类别:当变量时自动存储类别时,变量在进入到定义它们的程序快时定义它,在离开它们所在的程序块(作用域)时销毁它,因此成为自动变量.其中关键字auto和register用来声明自动类型的变量, 三:自

c语言 变量的存储类别以及对应的内存分配?

<h4><strong>1.变量的存储类别</strong></h4>从变量值存在的角度来分,可以分为静态存储方式和动态存储方式.所谓静态存储方式指在程序运行期间由系统分配固定的存储空间的方式(<strong>程序开始执行时分配,在程序完毕时释放,在程序过程中它们占据国定的存储单元,而不是动态分配和释放</strong>).而动态存储方式在运行期间根据需要进行动态存储方式(<strong>在程序过程中申请和释放的一些空间&