- 项目链接:https://github.com/FeliGame/FeCompiler
- 本项目中的文法和AST数据结构设计参考了北京大学编译器实践项目pku-minic.github.io/online-doc/
- 本博客会同步更新开发进度,欢迎各位交流、批评!
简介
Fe语言是一种语法类似C的高级语言,由于笔者将重点放在编译器的语义分析和代码优化上,因此目前阶段语法和C不会有什么不同。
该编译器会生成Koopa IR,并基于Koopa IR生成目标汇编代码(RISC-V)。
源代码结构
- README 展示了具体的文法规则
- fe.l 词法分析
- fe.y 语法分析
- ast.hpp 语法制导翻译的抽象语法树(目前实现是基于DFS的LR语法制导翻译),其中的Dump函数能够生成Koopa IR并导入到文件
- main.cpp 输入fe源代码,选择输出文件、代码类型等(.fe->.koopa或者.fe->.koopa->.riscv)
- riscv_gen.cpp 将分析出来的Koopa IR翻译成RISC-V
- sbt.hpp 符号表,其中维护了:
-
- BLOCK_HASH 维护【块ID, 块指针】的哈希表,其中块数据结构BlockNode的属性有父块指针、子块指针组,以此构成一个块树;
-
- TYPE_HASH 用于实现【类型名】到【类型ID】的转换;
-
- VAR_SBT、CONST_SBT、TYPE_SBT 基于vector< unordered_map < int, SBTNode> >实现的符号表。
目前进度
相较本系列上一篇博客(单语句计算器),可以实现多语句,常量的声明、定义,以及字面量的运算表达等,新增机制:
- 引入了继承属性,成为LL语法分析,基本实现方式是在调用子节点Dump函数之前向子节点赋当前的继承属性值,而综合属性则在Dump函数调用之后由子节点赋给父节点;
- ast.hpp中加入继承属性isConst,实现了【常量合并】的中间代码优化,大幅度减少了中间代码的指令数,例如:
int main() {
return 1 + 3 * 4;
}
/*
优化前生成中间代码Koopa IR如下:
fun @main(): i32 {
%entry:
%1 = add 1, 12
ret %1
}
常量合并优化:
fun @main(): i32 {
%entry:
ret 13
}
*/
原理:常量(包括字面量)之间的运算结果仍然是一个常量,因此当前节点isConst值设为真;若isConst值为真,则可以将常量合并的运算结果存放到综合属性r_val,并返回给归纳后的非终结符节点,以供后续使用;
- 为了方便开发和debug,ast.hpp中加入debug_mode选项开关,设置为true时,可以输出中间代码生成时的AST层次结构,例如一段代码的输出如下:
int main() {
const int a = 1 + 1;
}
/*
debug_mode开启时,运行编译器会在cerr错误流输出如下信息:
comp_unit
|func_def
|func_def
||func_type
||block
block_item
|decl
||const_decl
b_type
|||const_defs
||||const_def
|||||const_init_val
||||||exp
|||||||lor
||||||||land
|||||||||eq
||||||||||rel
|||||||||||add
|||||||||||add
||||||||||||add
|||||||||||||mul
||||||||||||||unary
||||||||||||mul
|||||||||||||unary
*/
效果展示
int main() {
// 忽略我的存在
/*
fa的发放啊\n\nff
*/
const int a = 1 + 2 * 3;
return a;
}
/*
输出的中间代码:
fun @main(): i32 {
%entry:
ret 7
}
*/
下一步加入
- 变量声明和定义
解决的难题
- SBT数据结构及相关函数调用的设计