本次大作业我做的是完善 microc程序 的一些c语言语法,是基于 microc 程序,在此基础上自己添加一些内容完成的。
- 改进了类型系统,纠正了char类型的不合理设计,新增了float类型
- 改进了变量定义,增加了带初始值的变量定义。
- 增加了条件表达式
- 增加了自增和自减运算
- 改进了循环语句,增加了两种新的循环:for和do while,对现有的所有循环支持了break和continue
- 增加了switch case语句,支持break,但不支持default
# 编译解释器 interpc.exe 命令行程序
dotnet restore interpc.fsproj # 可选
dotnet clean interpc.fsproj # 可选
dotnet build -v n interpc.fsproj # 构建./bin/Debug/net5.0/interpc.exe ,-v n查看详细生成过程
# 执行解释器
./bin/Debug/net5.0/interpc.exe ex1.c 8
dotnet run -p interpc.fsproj ex1.c 8
dotnet run -p interpc.fsproj -g ex1.c 8 //显示token AST 等调试信息
# one-liner
# 自行修改 interpc.fsproj 依次解释多个源文件
dotnet build -t:ccrun interpc.fsproj
# 生成扫描器
# macos or linux
dotnet "~/.nuget/packages/fslexyacc/10.2.0/build/fsyacc/netcoreapp3.1/fsyacc.dll" -o "CLex.fs" --module CLex --unicode CLex.fsl
# windows
dotnet "%homepath%\.nuget\packages\fslexyacc\10.2.0\build\/fslex/netcoreapp3.1\fslex.dll" -o "CLex.fs" --module CLex --unicode CLex.fsl
# 生成分析器
# macos or linux
dotnet "~/.nuget/packages/fslexyacc/10.2.0/build/fsyacc/netcoreapp3.1/fsyacc.dll" -o "CPar.fs" --module CPar CPar.fsy
# windows
dotnet "%homepath%\.nuget\packages\fslexyacc\10.2.0\build\/fsyacc/netcoreapp3.1\fsyacc.dll" -o "CPar.fs" --module CPar CPar.fsy
# 命令行运行程序
dotnet fsi
#r "nuget: FsLexYacc";; //添加包引用
#load "Absyn.fs" "Debug.fs" "CPar.fs" "CLex.fs" "Parse.fs" "Interp.fs" "ParseAndRun.fs" ;;
open ParseAndRun;; //导入模块 ParseAndRun
fromFile "example\ex1.c";; //显示 ex1.c的语法树
run (fromFile "example\ex1.c") [17];; //解释执行 ex1.c
run (fromFile "example\ex11.c") [8];; //解释执行 ex11.c
Debug.debug <- true //打开调试
run (fromFile "example\ex1.c") [8];; //解释执行 ex1.c
run (fromFile "example\ex11.c") [8];; //解释执行 ex11.
#q;;
解释器的主入口 是 interp.fs 中的 run 函数,具体看代码的注释
# 构建 microc.exe 编译器程序
dotnet restore microc.fsproj # 可选
dotnet clean microc.fsproj # 可选
dotnet build microc.fsproj # 构建 ./bin/Debug/net5.0/microc.exe
dotnet run -p microc.fsproj example/ex1.c # 执行编译器,编译 ex1.c,并输出 ex1.out 文件
dotnet run -p microc.fsproj -g example/ex1.c # -g 查看调试信息
./bin/Debug/net5.0/microc.exe -g example/ex1.c # 直接执行构建的.exe文件,同上效果
dotnet built -t:ccrun microc.fsproj # 编译并运行 example 目录下多个文件
dotnet built -t:cclean microc.fsproj # 清除生成的文件
# 启动fsi
dotnet fsi
#r "nuget: FsLexYacc";;
#load "Absyn.fs" "CPar.fs" "CLex.fs" "Debug.fs" "Parse.fs" "Machine.fs" "Backend.fs" "Comp.fs" "ParseAndComp.fs";;
# 运行编译器
open ParseAndComp;;
compileToFile (fromFile "example\ex1.c") "ex1";;
Debug.debug <- true # 打开调试
compileToFile (fromFile "example\ex4.c") "ex4";; # 观察变量在环境上的分配
#q;;
# fsi 中运行
#time "on";; // 打开时间跟踪
# 参考A. 中的命令 比较下解释执行解释执行 与 编译执行 ex11.c 的速度
dotnet restore microcc.fsproj
dotnet clean microcc.fsproj
dotnet build microcc.fsproj # 构建编译器
dotnet run -p microcc.fsproj ex11.c # 执行编译器
./bin/Debug/net5.0/microcc.exe ex11.c # 直接执行
dotnet fsi -r ./bin/Debug/net5.0/FsLexYacc.Runtime.dll Absyn.fs CPar.fs CLex.fs Parse.fs Machine.fs Contcomp.fs ParseAndContcomp.fs
open ParseAndContcomp;;
contCompileToFile (fromFile "example\ex11.c") "ex11.out";;
#q;;
- 运行下面的命令 查看 fac 0 , fac 3 的栈帧
- 理解栈式虚拟机执行流程
dotnet clean machine.csproj
dotnet run -p machine.csproj ex9.out 3 # 运行虚拟机,执行 ex9.out
./bin/Debug/net5.0/machine.exe ex9.out 3
./bin/Debug/net5.0/machine.exe -t ex9.out 0 // 运行虚拟机,执行 ex9.out ,-t 查看跟踪信息
./bin/Debug/net5.0/machine.exe -t ex9.out 3 // 运行虚拟机,执行 ex9.out ,-t 查看跟踪信息
# 编译 c 虚拟机
gcc -o machine.exe machine.c
# 虚拟机执行指令
machine.exe ex9.out 3
# 调试执行指令
machine.exe -trace ex9.out 0 # -trace 并查看跟踪信息
machine.exe -trace ex9.out 3
javac Machine.java
java Machine ex9.out 3
javac Machinetrace.java
java Machinetrace ex9.out 0
java Machinetrace ex9.out 3
#Linux
$sudo apt-get install build-essential nasm gcc
# Windows
# nasm 汇编器
https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/win64/
# gcc 编译器
https://github.com/jmeubank/tdm-gcc/releases/download/v9.2.0-tdm-1/tdm-gcc-9.2.0.exe
栈式虚拟机指令编译到x86_64,简单示例
分步构建
# 生成 ex1.asm 汇编码 nasm 格式
dotnet run -p microc.fsproj example/ex1.c
# 汇编生成目标文件
nasm -f win64 example/ex1.asm -o example/ex1.o # win
# nasm -f elf64 ex1.asm -o ex1.o # linux
# 编译运行时文件
gcc -c driver.c
# 链接运行时,生成可执行程序
gcc -g -o example/ex1.exe driver.o example/ex1.o
# 执行
./example/ex1.exe 8
单步构建
# 使用 build target 编译 ex1.c
# 可修改 microc.fsproj 编译其他案例文件
dotnet build -t:ccrunx86 microc.fsproj
- caller
- 调用函数前,在栈上放置函数参数,个数为m
- 将rbp入栈,调用函数
- callee
- 保存返回地址r,老的栈帧指针bp
- 将参数搬移到本函数的栈帧初始位置
- 将rbp设置到第一个参数的位置
- rbx 保存函数的返回值
- 如数组 a[2] 的元素按次序排在栈上,末尾保存数组变量a,内容是首元素 e0的地址
- e0, e1, a
访问数组,先通过BP
得到a
的位置,然后通过a
得到首地址 e0,最后计算数组下标偏移地址,访问对应数组元素
- 全局变量在栈上依次保存,x86 汇编中,glovar 是全局变量的起始地址
- *(p + 2) 指针运算不支持