自制编译器:后端代码生成
所谓的编译器后端的作用就是将语法树翻译成目标机器码。所谓目标机器码,考虑到直接翻译成具体平台(如X86,ARM等)过于复杂,因此先设计一个虚拟机,并翻译成这个虚拟机的机器码。对于虚拟机以及其指令格式可参考这篇文章http://blog.csdn.net/roger__wong/article/details/8947720,如何去尝试实现这个虚拟机是在我的另外一个系列的博客里进行论述。
所谓的编译器后端的作用就是将语法树翻译成目标机器码。所谓目标机器码,考虑到直接翻译成具体平台(如X86,ARM等)过于复杂,因此先设计一个虚拟机,并翻译成这个虚拟机的机器码。
对于虚拟机以及其指令格式可参考这篇文章http://blog.csdn.net/roger__wong/article/details/8947720,如何去尝试实现这个虚拟机是在我的另外一个系列的博客里进行论述。
本篇文章从以下是那个方面来论述:后端架构与关键数据结构、节点翻译方式。
1、后端架构和关键数据结构
后端接受前端的语法树作为输入,对于其每一个节点根据节点类型的不同产生不同的代码。但在实现过程中为了简单方便,我并没有把后端抽象出一个单独的模块,而是在语法树每一个节点的基础上增加了一个genCode方法,通过调用这个方法来生成该节点及其所有孩子节点(通过递归)的代码。
其次编译器后端直接生成Class文件(文件结构也在上文提到的博客中有说明),程序中后端首先构造一个ClassOfClass的实体,然后再调用此类的方法生成Class文件:
- public class ClassOfClass {
- public static int isPublic=1;
- public static int isStatic=2;
- public ArrayList<field> fields;
- public ArrayList<function> functions;
- public ArrayList<String> constPool;
- public String name;
- public ClassOfClass()
- {
- constPool=new ArrayList<String>();
- fields=new ArrayList<field>();
- functions=new ArrayList<function>();
- }
- public void WriteClassFile(String path)
- {
- try {
- PrintWriter pw=new PrintWriter(new FileOutputStream(path));
- pw.println(name);
- pw.println(fields.size());
- for(field f:fields)
- {
- pw.println(f.toString());
- }
- pw.println(functions.size());
- for(function f:functions)
- {
- pw.println(f.toString());
- }
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- public class field {
- public int head;
- public String type;
- public String fieldname;
- @Override
- public String toString()
- {
- StringBuffer sb=new StringBuffer();
- sb.append(head);
- sb.append(" ");
- sb.append(type);
- sb.append(" ");
- sb.append(fieldname);
- return sb.toString();
- }
- }
其中Function结构
- public class function {
- public int head;
- public String rettype;
- public int argnum;
- public ArrayList<String> args;
- public ArrayList<Code> codes;
- public function()
- {
- args=new ArrayList<String>();
- codes=new ArrayList<Code>();
- }
- @Override
- public String toString()
- {
- StringBuffer sb=new StringBuffer();
- sb.append(head);
- sb.append(" ");
- sb.append(rettype);
- sb.append(" ");
- sb.append(args.size());
- sb.append(" ");
- for(String s:args)
- {
- sb.append(s+" ");
- }
- sb.append("\r\n");
- sb.append("{");
- for(int i=0;i<=codes.size()-1;i++)
- {
- sb.append(i+":"+codes.get(i).toString()+"\r\n");
- }
- sb.append("\r\n");
- sb.append("}");
- return sb.toString();
- }
- }
- public class Code {
- public int Opcode;//操作码
- public ArrayList<String> Operands;
- public Code(int op)
- {
- Operands=new ArrayList<String>();
- Opcode=op;
- }
- @Override
- public String toString()
- {
- StringBuffer sb=new StringBuffer();
- sb.append(Opcode);
- sb.append(" ");
- for(String s:Operands)
- {
- sb.append(s+" ");
- }
- return sb.toString();
- }
- }
完成一个Class的后端代码的生成工作,只需要调用语法树根节点(classdef)的genCode即可完成,因为根节点会不断的递归调用其子树的genCode方法,因此在递归调用的时候需要某些机制进行各方法之间信息的传递,这里建立新类BackendClassManager来进行信息的传递,消除耦合。
- public class BackendClassManager {
- public static ClassOfClass cc=new ClassOfClass();//正在生成的class对象
- public static function tFunc;//正在生成代码的函数
- public static memberfuncdeclare mfc;//正在生成代码的语法树中的memberfuncdeclare节点
- public static constructor ct;//正在生成代码的构造函数节点
- public static HashMap<String,Integer> nameSlot=new HashMap<String,Integer>();//局部变量和局部变量表槽的对应hash
- public static expr expr1,expr2;//正在翻译的 expr
- public static Stack<ArrayList<Code>> loopcontinue=new Stack<ArrayList<Code>>();//在循环语句中出现的continue语句,用于回填地址
- public static Stack<ArrayList<Code>> loopbreak=new Stack<ArrayList<Code>>();//在循环语句中出现的break语句,用于回填地址
- public static void WriteToClassFile()
- {
- String path="E:/test.class";
- cc.WriteClassFile(path);
- }
- public static void genCode(classdef cd)
- {
- cd.genCode();
- }
- }
tFunc代表正在生成代码的函数,也就是所有genCode的方法都要把生成的代码填充到tFunc的codes域中。
memberfuncdeclare代表语法树中正在生成后端代码的memberfuncdeclare节点,该节点和其子树包含此函数的所有代码。
nameSlot对应源码中出现的局部变量与目标代码中的局部变量表槽的一个关系,因为目标代码将不再会出现局部变量名这个概念,所以需要一个hash在编译时进行对应。
expr1和expr2对应正在翻译的expr,在某些运算符需要进行类型转换时需要用到正在翻译的表达式的信息。
loopcontinue和loopbreak用于循环语句的地址回填,因为一个循环在翻译的过程中,其break需要跳转到的地址是还未确定的,需要整个循环翻译完之后对目标地址进行回填。continue虽然可以确定目标地址,但是在continue对应的语句stmt节点无法知道循环的开始地址,需要通过某些机制让stmt节点知道此循环的开始地址,因此也把continue语句进行回填处理。
除此之外还是用了一个类CodeGenHelper来封装一些常用的代码序列,比如i2d,jmp等,目的是为了简化之后的目标代码的生成。
2、节点代码生成
按照从顶至低的方式依次分析。
(1)classdef节点
- public void genCode() {
- BackendClassManager.cc.name=cn.toString();
- cb.genCode();
- }
(2)classbody
- public void genCode() {
- if(cb!=null)
- {
- cb.genCode();
- }
- }
值得注意的是,这些方法本身并没有生成目标代码乃是因为一个类的定义本身并不包含任何逻辑,而代码本身是对逻辑的阐述,所以在类声明、函数声明、成员变量声明等没有生成任何有意义的代码也就不值得奇怪了。
(3)classmembers
- public void genCode() {
- if(type==0)
- {
- ((membervardeclare)declare).genCode();
- }
- else if(type==1)
- {
- ((memberfuncdeclare)declare).genCode();
- }
- else if(type==2)
- {
- ct.genCode();
- }
- if(cm!=null)
- {
- cm.genCode();
- }
- }
(4)membervardeclare
- public void genCode() {
- // TODO Auto-generated method stub
- field fd=new field();
- ArrayList<field> fs=BackendClassManager.cc.fields;
- if(af.toString().equals("public"))
- {
- fd.head+=ClassOfClass.isPublic;
- }
- if(isstatic==true)
- {
- fd.head+=ClassOfClass.isStatic;
- }
- fd.type=tp.toString();
- fd.fieldname=ID.toString();
- fs.add(fd);
- }
(5)memberfundeclare
- public void genCode() {
- function func=new function();
- BackendClassManager.tFunc=func;
- BackendClassManager.cc.functions.add(func);
- BackendClassManager.ct=null;
- BackendClassManager.mfc=this;
- BackendClassManager.nameSlot=new HashMap<String,Integer>();
- if(af.toString().equals("public"))
- {
- func.head+=ClassOfClass.isPublic;
- }
- func.rettype=tp.toString();
- if(da!=null)
- {
- ArrayList<type> al=da.gettypelist();
- func.argnum=al.size();
- for(type tp:al)
- {
- func.args.add(tp.toString());
- }
- ArrayList<id> tal=da.getidlist();
- BackendClassManager.nameSlot.put("this", 0);
- for(int i=0;i<=tal.size()-1;i++)
- {
- BackendClassManager.nameSlot.put(tal.get(i).toString(), i+1);
- }
- }
- else
- {
- func.argnum=0;
- }
- fb.genCode();
- }
首先建立一个新的function对象,并把该对象设置为BackendClassManager.tFun,说明之后所有genCode都为这个函数生成的代码,并把这个函数加到classofClass对象的functions域中;判断该函数的返回值类型、是否是public、是否是静态,并把相关信息记录到function对象中重置nameslot,将函数所有参数压入nameslot表中,并注意this也当做参数放入表中,然后调用functionbody的genCode,为该函数生成代码。
(6)constructor
- public void genCode() {
- function func=new function();
- BackendClassManager.tFunc=func;
- BackendClassManager.ct=this;
- BackendClassManager.nameSlot=new HashMap<String,Integer>();
- if(af.toString().equals("public"))
- {
- func.head+=ClassOfClass.isPublic;
- }
- func.rettype="NULL";
- if(da!=null)
- {
- ArrayList<type> al=da.gettypelist();
- func.argnum=al.size();
- func.argnum=al.size();
- for(type tp:al)
- {
- func.args.add(tp.toString());
- }
- ArrayList<id> tal=da.getidlist();
- BackendClassManager.nameSlot.put("this", 0);
- for(int i=0;i<=tal.size()-1;i++)
- {
- BackendClassManager.nameSlot.put(tal.get(i).toString(), i+1);
- }
- }
- else
- {
- func.argnum=0;
- }
- ss.genCode();
- }
(7)funcbody
- public void genCode() {
- // TODO Auto-generated method stub
- ss.genCode();
- returnexpr.genCode();
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- Code code=new Code(0x19);
- al.add(code);
- }
首先stmts生成代码,然后为返回表达式生成代码,最后在codes中加入Code(0x19),也就是返回指令。
值得注意的是,这里做了一个约定,在expr的genCode中,总是把该expr的结果放在expr代码执行后的栈顶,因此函数返回时实际上返回的是栈顶元素的值。
(8)stmts
stmts逻辑上代表一个语句块或一组语句块,对应的生成式和在节点中使用的type如下:
stmts --> NUL| type-->0
stmt stmts| type-->1
if(expr) { stmts} stmts| type-->2
if(expr) {stmts} else {stmts} stmts| type-->3
while(expr) { stmts} stmts| type-->4
接下来给出生成后端代码的代码:
- public void genCode() {
- if(type==1)
- {
- st.genCode();
- stmts1.genCode();
- }
- else if(type==2)
- {
- condition.genCode();
- Code code=new Code(0x18);
- BackendClassManager.tFunc.codes.add(code);
- stmts1.genCode();
- code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
- stmts3.genCode();
- }
- else if(type==3)
- {
- condition.genCode();
- Code code=new Code(0x18);
- code.Opcode=0x18;
- BackendClassManager.tFunc.codes.add(code);
- stmts1.genCode();
- Code code1=new Code(0x01);
- code1.Operands.add(String.valueOf(0));
- BackendClassManager.tFunc.codes.add(code1);
- Code code2=new Code(0x18);
- BackendClassManager.tFunc.codes.add(code2);
- code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
- stmts2.genCode();
- code2.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
- stmts3.genCode();
- }
- else if(type==4)
- {
- ArrayList<Code> albreak=new ArrayList<Code>();
- ArrayList<Code> alcontinue=new ArrayList<Code>();
- BackendClassManager.loopbreak.add(albreak);
- BackendClassManager.loopcontinue.add(alcontinue);//循环入口,首先判断expr
- int pos=BackendClassManager.tFunc.codes.size();
- condition.genCode();
- //跳转指令
- Code code=new Code(0x18);
- BackendClassManager.tFunc.codes.add(code);
- //循环体
- stmts1.genCode();
- // code.Operands.add(String.valueOf(end));//表达式回填
- Code code1=new Code(0x01);
- code1.Operands.add(String.valueOf(0));
- BackendClassManager.tFunc.codes.add(code1);//压入0
- Code code2=new Code(0x18);
- code2.Operands.add(String.valueOf(pos));
- BackendClassManager.tFunc.codes.add(code2);//跳转到循环入口
- int end=BackendClassManager.tFunc.codes.size();
- code.Operands.add(String.valueOf(end));//表达式回填
- for(Code c:albreak)
- {
- c.Operands.add(String.valueOf(end));
- }
- for(Code c:alcontinue)
- {
- c.Operands.add(String.valueOf(pos));
- }
- BackendClassManager.loopbreak.pop();
- BackendClassManager.loopcontinue.pop();
- stmts3.genCode();
- }
- }
(9)stmt
首先给出语句stmt的生成式:
stmt --> continue;| type=0
break;| type=1
var-declare;| type=2
class-init;| type=3
setvalue;| type=4
expr;| type=5
接下来给出代码:
- public void genCode() {
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- if(type==0)
- {
- CodeGenHelper.JMP(al, 0);
- BackendClassManager.loopcontinue.peek().add(al.get(al.size()-1));
- }
- if(type==1)
- {
- CodeGenHelper.JMP(al, 0);
- BackendClassManager.loopbreak.peek().add(al.get(al.size()-1));
- }
- if(type==5)
- {
- ep.genCode();
- }
- if(type==2)
- {
- vc.genCode();
- }
- if(type==3)
- {
- ci.genCode();
- }
- if(type==4)
- {
- sv.genCode();
- }
- }
另外给出CodeGenHelper的JMP方法,封装了压入0和ifz两条指令:
- public static void JMP(ArrayList<Code> al,int pos)
- {
- Code code1=new Code(0x01);
- code1.Operands.add(String.valueOf(0));
- BackendClassManager.tFunc.codes.add(code1);//压入0
- Code code2=new Code(0x18);
- code2.Operands.add(String.valueOf(pos));
- BackendClassManager.tFunc.codes.add(code2);//跳转
- }
(10)classinit
递推式:class-init --> ids = new
type ( args )| type[expr]
type: 0 1
代码:
- public void genCode() {
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- ag.genCode();
- if(type==0)
- {
- Code code=new Code(0x1C);
- code.Operands.add(tp.toString());
- al.add(code);
- }
- else
- {
- Code code=new Code(0x1D);
- code.Operands.add(tp.toString());
- al.add(code);
- }
- if(is.type==1)
- {
- int slot=CodeGenHelper.SearchByName(is.ID.toString());
- Code code=new Code(0x05);
- code.Operands.add(String.valueOf(slot));
- al.add(code);
- }
- if(is.type==2)
- {
- if(is.getLastIDS().type!=3)
- {
- CodeGenHelper.PutObjectToStack(al, is);
- CodeGenHelper.ChangeStackTopEle(al);
- CodeGenHelper.CodePutField(al, is.getLastID().toString());
- }
- else
- {
- int value=CodeGenHelper.StoreToLocalTable(al);
- CodeGenHelper.PutObjectToStack(al, is);
- CodeGenHelper.CodeGetField(al, is.getLastID().toString());
- int array=CodeGenHelper.StoreToLocalTable(al);
- is.getLastIDS().EXPR.genCode();
- CodeGenHelper.LoadToStack(al, array);
- CodeGenHelper.LoadToStack(al, value);
- Code code=new Code(0x26);
- al.add(code);
- }
- }
- if(is.type==3)
- {
- int slot=CodeGenHelper.StoreToLocalTable(al);
- is.EXPR.genCode();
- CodeGenHelper.LoadToStackByName(al, is.ID.toString());
- CodeGenHelper.LoadToStack(al, slot);
- Code code=new Code(0x26);
- al.add(code);
- }
- }
接着判断ids的类型,ids的推导式如下:
ids->
* id| type 1
id.ids| type 2
id[expr] type 3
this| type=4
如果type=1,则说明是一个局部变量,从nameSlot中找到该局部变量在局部变量表里的位置(相关操作封装到了CodeGenHelper的searchByName中),赋值给这个槽即可。
如果type=2,说明是某个对象的成员变量,则需要先把这个对象压入堆栈,再通过putfield指令给此对象赋值,若是某个对象的成员变量中的某个元素(即该成员变量是个数组),则需要使用给数组赋值的指令。
如果type=3,说明给数组某个元素赋值,需要先把下标计算出来,然后通过相应指令给数组赋值。
其中的很多操作都封装在了CodeGenHelper中。
(11)vardeclare
递推式:
var-declare --> type args|type[] args
代码:
- public void genCode() {
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- ArrayList<ids> idlist=ags.getidsList();
- for(ids id:idlist)
- {
- BackendClassManager.nameSlot.put(id.getLastID().toString(), BackendClassManager.nameSlot.size());
- }
- }
(12)setvalue
setvalue--> ids = expr
- public void genCode() {
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- ep.genCode();
- if(is.type==1)
- {
- int slot=CodeGenHelper.SearchByName(is.ID.toString());
- Code code=new Code(0x05);
- code.Operands.add(String.valueOf(slot));
- al.add(code);
- }
- if(is.type==2)
- {
- if(is.getLastIDS().type!=3)
- {
- CodeGenHelper.PutObjectToStack(al, is);
- CodeGenHelper.ChangeStackTopEle(al);
- CodeGenHelper.CodePutField(al, is.getLastID().toString());
- }
- else
- {
- int value=CodeGenHelper.StoreToLocalTable(al);
- CodeGenHelper.PutObjectToStack(al, is);
- CodeGenHelper.CodeGetField(al, is.getLastID().toString());
- int array=CodeGenHelper.StoreToLocalTable(al);
- is.getLastIDS().EXPR.genCode();
- CodeGenHelper.LoadToStack(al, array);
- CodeGenHelper.LoadToStack(al, value);
- Code code=new Code(0x26);
- al.add(code);
- }
- }
- if(is.type==3)
- {
- int slot=CodeGenHelper.StoreToLocalTable(al);
- is.EXPR.genCode();
- CodeGenHelper.LoadToStackByName(al, is.ID.toString());
- CodeGenHelper.LoadToStack(al, slot);
- Code code=new Code(0x26);
- al.add(code);
- }
- }
(13)ids
ids的genCode逻辑是指将此ids的值放到堆栈顶,此值或许是个int、double,也可能是个对象的句柄。
递推式:
- ids->
- * id| type 1
- id.ids| type 2
- id[expr] type 3
- this| type=4
代码:
- public void genCode() {
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- if(type==1)
- {
- Integer slot=BackendClassManager.nameSlot.get(ID.toString());
- if(slot!=null)
- {
- Code code=new Code(0x03);
- code.Operands.add(String.valueOf(slot));
- al.add(code);
- }
- else
- {
- //error
- }
- }
- if(type==2)
- {
- int slot=BackendClassManager.nameSlot.get(ID.toString());
- Code code=new Code(0x03);
- code.Operands.add(String.valueOf(slot));
- al.add(code);
- IDS.genCode2nd();
- }
- if(type==3)
- {
- EXPR.genCode();//压入expr值
- int slot=BackendClassManager.nameSlot.get(ID.toString());
- Code code=new Code(0x03);
- code.Operands.add(String.valueOf(slot));
- al.add(code);//压入array
- code=new Code(0x04);
- al.add(code);//读取数组元素
- }
- if(type==4)
- {
- int slot=BackendClassManager.nameSlot.get("this");
- Code code=new Code(0x01);
- code.Operands.add(String.valueOf(slot));
- }
- }
- public void genCode2nd() {
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- if(type==1)
- {
- Code code=new Code(0x20);
- code.Operands.add(ID.toString());
- al.add(code);
- }
- if(type==2)
- {
- Code code=new Code(0x20);
- code.Operands.add(ID.toString());
- al.add(code);
- IDS.genCode2nd();
- }
- if(type==3)
- {
- EXPR.genCode();//压入expr值
- Code code=new Code(0x20);
- code.Operands.add(ID.toString());
- al.add(code);//压入数组
- code=new Code(0x04);
- al.add(code);//读取数组元素
- }
- if(type==4)
- {
- //error
- }
- }
由于ids的结构不对称,所以不能使用递归的方式来获得其值。举例:id.id,第一个id的处理方式是通过局部变量表中拿到其句柄,第二个id的处理方式要通过getfield来拿到其值。因此这两种处理方式对应的是genCode和genCode2nd。
只有当第一次调用的时候(也就是其它节点调用)调用genCode,ids节点调用均调用genCode2nd。
(14)expr
/* Type
* expr --> (expr) 0
ids| 1
number| 2
literal| 3
func-call| 4
expr ops expr| 5
*/
- public void genCode() {
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- if(Type==0)
- {
- expr1.genCode();
- }
- else if(Type==1)
- {
- ids.genCode();
- }
- else if(Type==2)
- {
- Code code;
- if(number.isInt==true)
- {
- code=new Code(0x01);
- code.Operands.add(number.toString());
- }
- else
- {
- code=new Code(0x02);
- code.Operands.add(number.toString());
- }
- al.add(code);
- }
- else if(Type==3)
- {
- literal.genCode();
- }
- else if(Type==4)
- {
- fc.genCode();
- }
- else if(Type==5)
- {
- expr1.genCode();
- Code code=new Code(0x05);
- int pos=BackendClassManager.nameSlot.keySet().size();
- BackendClassManager.nameSlot.put(this.toString(), pos);
- code.Operands.add(String.valueOf(pos));
- al.add(code);
- expr2.genCode();
- code=new Code(0x03);
- code.Operands.add(String.valueOf(pos));
- al.add(code);
- BackendClassManager.expr1=expr1;
- BackendClassManager.expr2=expr2;
- op.genCode();
- }
- }
expr的genCode方法产生的效果是把此expr的值放到栈顶。
如果type=0,则递归调用括号里的expr的genCode;如果type=1,则把ids的值放到栈顶;如果type=2,先判断立即数的类型之后再压入立即数;如果type=3,把字符串的句柄压入栈顶;type=4调用funccall的genCode;type=5,先生成expr1的字节码,然后将结果暂存到局部变量表,之后生成expr2的字节码,再把之前的结果压栈,调用op的genCode代码为运算符生成字节码。值得注意的是,调用op的genCode之前首先要把参与运算的expr放到全局变量里,op的字节码生成过程需要使用这两个expr的信息。
(15)funccall
func-call --> ids . func-name(NUL|args)
- public void genCode() {
- // TODO Auto-generated method stub
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- memberfuncdeclare mfc=(memberfuncdeclare)SyntaxTreeGenerator.getFunctions().get(fn.toString()).value;
- ArrayList<ids> ags=ag.getidsList();
- ArrayList<Integer> poses=new ArrayList<Integer>();
- for(int i=0;i<=ags.size()-1;i++)
- {
- ags.get(i).genCode();
- poses.add(CodeGenHelper.StoreToLocalTable(al));
- }
- for(int i=0;i<=poses.size()-1;i++)
- {
- CodeGenHelper.LoadToStack(al, poses.get(i));
- }
- if(mfc.isstatic==true)
- {
- Code code=new Code(0x1B);
- code.Operands.add(IDS.toString()+"."+fn.toString());
- al.add(code);
- }
- else
- {
- IDS.genCode();
- Code code=new Code(0x1A);
- code.Operands.add(fn.toString());
- al.add(code);
- }
- }
首先所有参数压栈,然后虚拟机执行call指令,给新函数分配执行环境(栈帧),把之前压栈的参数放到局部变量表中(如果有this参数的话this也要以参数的形式放到局部变量表中);函数执行完之后虚拟机释放资源,并把堆栈顶元素(返回值)放到其调用者的堆栈顶。
首先先从符号表中找到这个函数(封装在了SyntaxTreeGenerator中),然后得到所有参数,并存储到局部变量表中(之所以这么做因为参数的获取过程可能会把堆栈顺序打乱),接着把之前存过的所有参数压入堆栈,再判断该函数是否是个静态函数来使用不同的字节码进行调用。
(16)ops
ops --> bitop | logiop | artmop | cprop
- public void genCode() {
- if(type==TYPE_BITOP)
- {
- bo.genCode();
- }
- else if(type==TYPE_LOGIOP)
- {
- lo.genCode();
- }
- else if(type==TYPE_ARMTOP)
- {
- ao.genCode();
- }
- else if(type==TYPE_CPROP)
- {
- co.genCode();
- }
- }
根据不同的类型调用不同op的genCode方法。
(17)cprop
- public void genCode() {
- expr expr1,expr2;
- ArrayList<Code> al=BackendClassManager.tFunc.codes;
- expr1=BackendClassManager.expr1;
- expr2=BackendClassManager.expr2;
- boolean intint=true;
- if(expr1.tp.toString().equals("int") && expr2.tp.toString().equals("int"))
- {
- intint=true;
- }
- if(expr1.tp.toString().equals("double") && expr2.tp.toString().equals("double"))
- {
- intint=false;
- }
- if(expr1.tp.toString().equals("int") && expr2.tp.toString().equals("double"))
- {
- CodeGenHelper.i2d(al);
- intint=false;
- }
- if(expr1.tp.toString().equals("double") && expr2.tp.toString().equals("int"))
- {
- int pos=CodeGenHelper.StoreToLocalTable(al);
- Code code=new Code(0x23);
- al.add(code);
- CodeGenHelper.i2d(al);
- CodeGenHelper.LoadToStack(al, pos);
- intint=false;
- }
- if(value.equals(">"))
- {
- if(intint==true)
- {
- Code code=new Code(0x16);//比较
- al.add(code);
- code=new Code(0x01);
- code.Operands.add("1");//压入1
- al.add(code);
- code=new Code(0x16);//和1比较
- al.add(code);
- code=new Code(0x24);//取非
- al.add(code);
- }
- else
- {
- Code code=new Code(0x17);//比较
- al.add(code);
- code=new Code(0x02);
- code.Operands.add("1");//压入1
- al.add(code);
- code=new Code(0x17);//和1比较
- al.add(code);
- code=new Code(0x24);//取非
- al.add(code);
- }
- }
- if(value.equals("<"))
- {
- if(intint==true)
- {
- Code code=new Code(0x16);//比较
- al.add(code);
- code=new Code(0x01);
- code.Operands.add("-1");//压入1
- al.add(code);
- code=new Code(0x16);//和1比较
- al.add(code);
- code=new Code(0x24);//取非
- al.add(code);
- }
- else
- {
- Code code=new Code(0x17);//比较
- al.add(code);
- code=new Code(0x02);
- code.Operands.add("-1");//压入1
- al.add(code);
- code=new Code(0x17);//和1比较
- al.add(code);
- code=new Code(0x24);//取非
- al.add(code);
- }
- }
- if(value.equals(">="))
- {
- if(intint==true)
- {
- Code code=new Code(0x16);//比较
- al.add(code);
- code=new Code(0x01);
- code.Operands.add("-1");//压入1
- al.add(code);
- code=new Code(0x16);//和1比较
- al.add(code);
- }
- else
- {
- Code code=new Code(0x17);//比较
- al.add(code);
- code=new Code(0x02);
- code.Operands.add("-1");//压入1
- al.add(code);
- code=new Code(0x17);//和1比较
- al.add(code);
- code=new Code(0x24);//取非
- al.add(code);
- }
- }
- if(value.equals("<="))
- {
- if(intint==true)
- {
- Code code=new Code(0x16);//比较
- al.add(code);
- code=new Code(0x01);
- code.Operands.add("1");//压入1
- al.add(code);
- code=new Code(0x16);//和1比较
- al.add(code);
- }
- else
- {
- Code code=new Code(0x17);//比较
- al.add(code);
- code=new Code(0x02);
- code.Operands.add("1");//压入1
- al.add(code);
- code=new Code(0x17);//和1比较
- al.add(code);
- }
- }
- if(value.equals("=="))
- {
- if(intint==true)
- {
- Code code=new Code(0x16);//比较
- al.add(code);
- code=new Code(0x01);
- code.Operands.add("0");//压入1
- al.add(code);
- code=new Code(0x16);//和0比较
- al.add(code);
- code=new Code(0x24);//取非
- al.add(code);
- }
- else
- {
- Code code=new Code(0x17);//比较
- al.add(code);
- code=new Code(0x02);
- code.Operands.add("0");//压入1
- al.add(code);
- code=new Code(0x17);//和0比较
- al.add(code);
- code=new Code(0x24);//取非
- al.add(code);
- }
- }
- if(value.equals("!="))
- {
- if(intint==true)
- {
- Code code=new Code(0x16);//比较
- al.add(code);
- code=new Code(0x01);
- code.Operands.add("0");//压入1
- al.add(code);
- code=new Code(0x16);//和0比较
- al.add(code);
- }
- else
- {
- Code code=new Code(0x17);//比较
- al.add(code);
- code=new Code(0x02);
- code.Operands.add("0");//压入1
- al.add(code);
- code=new Code(0x17);//和0比较
- al.add(code);
- }
- }
- }
首先需要根据待比较的expr来决定是否进行类型转换,目前在语言的设计中,只有double和int是可比的,其它类型的比较虽然在编译器不会报错但在运行时中会进行报错。
其次根据类型来调用不同的比较指令,再次根据逻辑需要进行二次比较(因为比较指令会根据大于小于等于返回1、0或-1,和运算符并不等价,需要通过比较两次使之和运算符等价)。
大体上代码就这么多,目前的编译器已经可以编译出正确的字节码了,但目前代码还无法执行,需要等到Runtime写好之后才能运行。
因此本系列博客到这里暂时告一段落,若给语言再添加一些高级点的特性,也需要等到我把Runtime写好再说了。
更多推荐
所有评论(0)