终于来到了最后一步——目标代码生成
本次的实验目标是将生成的中间代码四元式翻译成x86汇编语言
原理其实就是根据不同的四元式对应不同的语句输出为汇编代码即可
根据要求是只需要满足3个测试用例即可,再多只需要继续添加翻译语句就行
一、1.
由于本次实验提供的测试用例的输入输出函数,Mars_PrintInt,Mars_PrintStr,Mars_GetInt在x86中并未定义。所以需要通过借用对应的c语言函数,又由于x86中对应的printf和scanf的调用处理较为特殊,所以在此处对于这一类函数调用进行特殊处理。首先由于我生成的四元式中的函数调用形式为:先用param表示要传入的变量,再使用call来进行函数调用,所以,通过维护一个ParamStack,将遇到param四元式时,将内容压入,即要传入的参数,当遇到call四元式时,将ParamStack中的变量均弹出,即可进行函数调用。所以,对于Mars_PrintInt,Mars_PrintStr,Mars_GetInt这三个特殊函数,对应的输出字符串首先定义在x86代码的.data段,以便后面调用。
2.
由于.data段通常存放的是程序的全局变量,但由于本次实验的测试用例均不含全局变量,所以我并未处理全局变量。
3.
由于生成的四元式的op字段已基本类似于x86汇编语言的指令,所以,根据四元式的op字段,选择对应的x86指令。由于本次实验只要求通过3个测试用例即可,所以本次实验只编写了需要用到的指令。
4.
对于四元式中频繁出现的%1,%2之类的临时变量,本次实验选择将其都定义为局部变量,不进行寄存器分配。我们通过维护符号表,将TemporaryValue统统加入对应函数的符号表中,这样在访问到func四元式时,即可访问对应的函数的符号表,将其中的变量通过local关键字进行定义。
二、1.在BIT-MiniCC-mastersrcbitminisysminiccncgen中创建MyCodeGen.java文件
package bit.minisys.minicc.ncgen;
import bit.minisys.minicc.icgen.LabelGenerator;
import bit.minisys.minicc.icgen.MyICBuilder;
import bit.minisys.minicc.icgen.Quat;
import bit.minisys.minicc.icgen.TemporaryValue;
import bit.minisys.minicc.parser.ast.*;
import bit.minisys.minicc.pp.internal.B;
import bit.minisys.minicc.semantic.FuncTable;
import bit.minisys.minicc.semantic.VarTable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
public class MyCodeGen {
public static String CodeType;
private static int Tab;
public String ASMName;
public static MyICBuilder Builder;
private static boolean printStr;
private static boolean printflag;
private static boolean printInt;
private static boolean GetInt;
private static boolean GetIntFunc;
private static String funcname;
public static Stack ParamStack;
public static Map TempValue;
public static List print_scanf;
public static StringBuilder stringBuilder;
private static String jumpOP;
private static boolean array;
public static String arrayName;
public static String AstStr(ASTNode node){
if (node == null) {
return "";
}else if (node instanceof ASTIdentifier) {
return ((ASTIdentifier)node).value;
}else if (node instanceof ASTIntegerConstant) {
return ((ASTIntegerConstant)node).value+"";
}else if (node instanceof TemporaryValue) {
return ((TemporaryValue)node).name();
}
else if(node instanceof ASTStringConstant){
return ((ASTStringConstant) node).value;
}
else if(node instanceof LabelGenerator){
return ((LabelGenerator) node).name();
}
else {
return "";
}
}
public MyCodeGen(String type,MyICBuilder icBuilder){
CodeType=type;
Tab=0;
Builder=icBuilder;
printInt=false;
array=false;
printStr=false;
GetInt=false;
GetIntFunc=false;
printflag=false;
stringBuilder = new StringBuilder();
ParamStack=new Stack();
TempValue=new HashMap();
print_scanf=new LinkedList<>();
funcname="null";
}
public static void GenTab(){
for(int i=0;i
2.修改BIT-MiniCC-mastersrcbitminisysminiccMyMiniCompiler.java
package bit.minisys.minicc;
import MyCGrammer.MyCGrammerLexer;
import MyCGrammer.MyCGrammerParser;
import bit.minisys.minicc.icgen.MyICBuilder;
import bit.minisys.minicc.ncgen.MyCodeGen;
import bit.minisys.minicc.parser.MyListener;
import bit.minisys.minicc.parser.ast.ASTNode;
import bit.minisys.minicc.scanner.MyScanner;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MyMiniCompiler {
public static void main(String[] args)throws IOException {
String inputFile = "输入文件路径";
InputStream is = System.in;
is = new FileInputStream(inputFile);
ANTLRInputStream input = new ANTLRInputStream(is);
MyCGrammerLexer lexer = new MyCGrammerLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
MyCGrammerParser parser = new MyCGrammerParser(tokens);
ParseTree tree = parser.compilationUnit();
String fName = inputFile.trim();
String temp[] = fName.split("\\");
String tokenFileName =temp[temp.length - 1] + ".tokens";
MyScanner myScanner = new MyScanner(tokenFileName,tokens);
String jsonFileName = temp[temp.length - 1] + ".json";
ParseTreeWalker walker = new ParseTreeWalker();
MyListener listener = new MyListener();
listener.oFile=jsonFileName;
walker.walk(listener, tree);
String icfileName = temp[temp.length - 1] + ".ic";
String errorfileName = temp[temp.length - 1] + ".error";
String symbolName = temp[temp.length - 1] + ".symbol";
MyICBuilder icBuilder = new MyICBuilder();
icBuilder.Errorfilename=errorfileName;
icBuilder.Icfilename=icfileName;
icBuilder.Symbolname=symbolName;
ASTNode node = listener.NodeStack.peek();
icBuilder.test(node);
String asmName=temp[temp.length - 1]+".asm";
MyCodeGen codeGen = new MyCodeGen("x86",icBuilder);
codeGen.ASMName=asmName;
codeGen.run();
}
}
3.结果展示
1_fibonacci.c.asm
.386
.model flat, stdcall
option casemap : none
includelib msvcrt.lib
includelib ucrt.lib
includelib legacy_stdio_definitions.lib
printf proto c:dword,:vararg
scanf proto c:dword,:vararg
.data
Mars_PrintInt byte "%d",0ah,0
Mars_PrintStr0 byte "Please input a number:",0ah,0
Mars_PrintStr1 byte "This number's fibonacci value is :",0ah,0
Mars_GetInt byte "%d",0
.code
fibonacci proc far C num:dword
local res:dword
local @1:dword
local @2:dword
local @3:dword
local @4:dword
local @5:dword
local @6:dword
@1If:
mov eax, num
cmp eax, 1
jnb @1Else
push 0
pop res
jmp @1Endif
@1Else:
@2If:
mov eax, num
cmp eax, 2
jnbe @2Else
push 1
pop res
jmp @2Endif
@2Else:
mov eax ,num
mov ebx ,1
sub eax, ebx
mov @3, eax
invoke fibonacci, @3
mov @4, eax
mov eax ,num
mov ebx ,2
sub eax, ebx
mov @5, eax
invoke fibonacci, @5
mov @6, eax
mov eax ,@4
mov ebx ,@6
add eax, ebx
mov res, eax
@2Endif:
@1Endif:
mov eax, res
ret
fibonacci endp
main proc
local @7:dword
local n:dword
local @8:dword
local res:dword
invoke printf, addr Mars_PrintStr0
lea eax, [@7]
push eax
push offset Mars_GetInt
call scanf
push @7
pop n
invoke fibonacci, n
mov @8, eax
push @8
pop res
invoke printf, addr Mars_PrintStr1
mov eax,res
invoke printf, addr Mars_PrintInt, eax
mov eax, 0
ret
main endp
end main
放到配置好的汇编环境下执行,完全正确
以上就是本次迷你C语言编译器的编写过程,其实lab6,lab7,lab8都不难,只要lab5做好了,就真正的理解了编译的过程,后面的工作就是翻译了,希望大家有所收获。



