编译器架构的王者LLVM——(3)用代码生成代码
来源:互联网 发布:淘宝助理如何描述宝贝 编辑:程序博客网 时间:2024/05/20 08:23
LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 —— 题记
用代码生成代码
LLVM的开发思路很简单,就是用C++代码去不断生成llvm字节码。
RedApple语言示例
这是我花了两周多的时间制作的一门实验型语言,主要是想验证一个编译器的设计思路,宏翻译系统。
它的架构和一般的编译器很不一样,首先,编译器前端会先将语法转换为很通用的AST语法树节点,一般的编译器,往往是直接在这些节点上进行语义分析,然后进行代码生成。
这次我采用了类似lisp的表示方法,将源文件转换为语法树,然后遍历整棵语法树,根据上面标注的宏提示,去按照各个宏的规则进行翻译工作。
整个编译器1500行左右的代码,非常的小巧,不过功能也比较有限,而且好多地方还不完善,主要支持的就是函数的定义,结构体的定义,函数调用,结构体访问,分配内存,基本逻辑控制语句这些基本的特性。
大家可以作为学习llvm的一个示例吧。
Github地址:https://github.com/sunxfancy/RedApple
同样,非常精品的示例还推荐大家看以下两位网友写的:
构建Toy编译器:基于Flex、Bison和LLVM
http://lesliezhu.github.io/public/write-your-toy-compiler.html
用LLVM来开发自己的编译器系列
http://my.oschina.net/linlifeng/blog/97457
当然,这些示例不是说要大家一下都看懂,那么也就没有教程的意义了,下面我会继续介绍各个关键的LLVM平台API以及相关工具链。大家可以将以上三个项目和LLVM官网example中的作为参考,在实践中加以印证。
工具链简介
以上三个最常用,其他小工具备用
唉,太多了,好多我也木有用过,还有需要的请查看官方文档:
http://llvm.org/docs/CommandGuide/index.html
常用类
尝试先来生成个小函数
就拿printf开练吧,这个函数第一有用,第二简单,第三只要声明不要内容。
void register_printf(llvm::Module *module) { std::vector<llvm::Type*> printf_arg_types; // 这里是参数表 printf_arg_types.push_back(llvm::Type::getInt8PtrTy(module->getContext())); llvm::FunctionType* printf_type = llvm::FunctionType::get( llvm::Type::getInt32Ty(module->getContext()), printf_arg_types, true); // 这里的true表示后面接不定参数 llvm::Function *func = llvm::Function::Create( printf_type, llvm::Function::ExternalLinkage, llvm::Twine("printf"), module ); func->setCallingConv(llvm::CallingConv::C); // 一定注意调用方式的正确性}
怎么样,是不是也很简单?
编写主函数和调试上下文
下面我们来编写一个主函数,来测试一下我们的函数是否正确,这里,也是LLVM最核心的启动和调试流程。
int main(){ InitializeNativeTarget(); LLVMContext Context; Module* M = new Module("main", Context); register_printf(M); // 校验问题, 这个函数需要一个输出流来打印错误信息 if (verifyModule(*M, &errs())) { errs() << "构建LLVM字节码出错!\n"; exit(1); } // 输出llvm字节码 outs() << "LLVM module:\n\n" << *M; outs() << "\n\n"; outs().flush(); // 输出二进制BitCode到.bc文件 std::error_code ErrInfo; raw_ostream *out = new raw_fd_ostream("a.bc", ErrInfo, sys::fs::F_None); WriteBitcodeToFile(M, *out); out->flush(); delete out; // 关闭LLVM释放内存 llvm_shutdown(); return 0;}
运行效果:
对了,我们好像没有提该引用哪些头文件,请见附录
附:完整示例
只是头文件有点长,具体功能有的我也记不清了,一般我是习惯性把一片粘过去 →_→
/* * @Author: sxf* @Date: 2015-11-06 20:37:15* @Last Modified by: sxf* @Last Modified time: 2015-11-06 20:46:43*/#include "llvm/IR/Verifier.h"#include "llvm/ExecutionEngine/GenericValue.h"#include "llvm/ExecutionEngine/Interpreter.h"#include "llvm/IR/Constants.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/IRBuilder.h"#include "llvm/Support/ManagedStatic.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Bitcode/ReaderWriter.h"#include "llvm/Support/FileSystem.h"#include "llvm/IR/ValueSymbolTable.h"using namespace llvm;void register_printf(llvm::Module *module) { std::vector<llvm::Type*> printf_arg_types; printf_arg_types.push_back(llvm::Type::getInt8PtrTy(module->getContext())); llvm::FunctionType* printf_type = llvm::FunctionType::get( llvm::Type::getInt32Ty(module->getContext()), printf_arg_types, true); llvm::Function *func = llvm::Function::Create( printf_type, llvm::Function::ExternalLinkage, llvm::Twine("printf"), module ); func->setCallingConv(llvm::CallingConv::C);}int main(){ InitializeNativeTarget(); LLVMContext Context; Module* M = new Module("main", Context); register_printf(M); // 校验问题, 这个函数需要一个输出流来打印错误信息 if (verifyModule(*M, &errs())) { errs() << "构建LLVM字节码出错!\n"; exit(1); } // 输出llvm字节码 outs() << "LLVM module:\n\n" << *M; outs() << "\n\n"; outs().flush(); // 输出二进制BitCode到.bc文件 std::error_code ErrInfo; raw_ostream *out = new raw_fd_ostream("a.bc", ErrInfo, sys::fs::F_None); WriteBitcodeToFile(M, *out); out->flush(); delete out; // 关闭LLVM释放内存 llvm_shutdown(); return 0;}
- 编译器架构的王者LLVM——(3)用代码生成代码
- 编译器架构的王者LLVM——(2)开发LLVM项目
- 编译器架构的王者LLVM——(1)现代编译器架构
- 编译器架构的王者LLVM——(4)简单的词法和语法分析
- 编译器架构的王者LLVM——(5)语法树模型的基本结构
- 编译器架构的王者LLVM——(6)多遍翻译的宏翻译系统
- 编译器架构的王者LLVM——(7)函数的翻译方法
- 编译器架构的王者LLVM——(8)函数的调用及基本运算符
- 编译器架构的王者LLVM——(9)栈式符号表的构建
- 编译器架构的王者LLVM——(10)变量的存储与读取
- 编译器架构的王者LLVM——(11)深入理解GetElementPtr
- 编译器架构的王者LLVM——(12)使用JIT引擎
- LLVM (4) 11.3 LLVM的代码表示:LLVM IR
- LLVM每日谈之二十一 一些关于编译器和LLVM/Clang的代码
- LLVM代码研读(3) --- LLVM后端(1): 概述
- 自制编译器:后端代码生成(一)
- 自制编译器:后端代码生成(二)
- 自制编译器:后端代码生成(三)
- 黑马程序员——OC基础---Foundation之数组
- mysql数据库乱码问题
- Android——PreferenceActivity简单使用
- Attribute在.net编程中的应用(四)
- 日常小结-eclipse安装PyDev插件
- 编译器架构的王者LLVM——(3)用代码生成代码
- CommandTimeOut
- Attribute在.net编程中的应用(五)
- pl/sql
- 网络
- java 两张思维导图助力Java入门
- 计算机视觉中经常需要识别或者定位某些几何图形,比如直线、圆、椭圆,还有其他一些图形。检测直线的霍夫变换提供了在图像中寻找直线的一种算法,是最简单的一种情形,后来发展到检测圆、椭圆、还有一般图形的霍夫变
- LeetCode题解:Longest Increasing Subsequence O(N^2解法)
- 2015年360公司面试