自制编译器:后端代码生成(二)

本文介绍了自制编译器中后端代码生成的过程,涉及stmts和stmt的不同类型及其对应的生成式。内容涵盖continue、break、变量声明、类初始化、设置值等语句的生成,以及ids类型的处理,包括局部变量、对象成员变量、数组元素的赋值。同时,提到了CodeGenHelper的JMP方法和ids的genCode逻辑。
摘要由CSDN通过智能技术生成

(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();
		}
		
	}
对于type=0,没必要生成任何代码;对于type=1,生成stmt的代码然后递归再生成stmts的代码;对于type=2,首先生成条件expr的代码,在这段代码执行过后,会将结果放在堆栈顶,然后加入code(0x18)进行跳转,但此刻的跳转地址还不能确定,因此先要生成stmts1的代码,之后回填跳转地址,再生成stmts3的代码;对于type=3,和type=2类似,但要在stmts1代码之后加入无条件跳转指令,跳转到else块后面,无条件跳转的方法是首先压入0,再使用0x18也就是ifz进行跳转;对于type=4要稍微复杂些,首先要计算循环开始的地址(包括判断expr),然后生成循环体代码,得到循环结束地址,再回填循环判断的相关代码,除此之外,还要回填此循环体内出现的所有break和continue语句的地址。


(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();
		}
	}
stmt代码比较简单,对于break和continue,只需要加入一个跳转语句,然后把此跳转语句加入到相应的列表里(因为循环是嵌套的,所以使用堆栈来表示这一关系),由外层循环体进行地址回填即可,使用了CodeGenHelper的JMP方法作为辅助函数;对于其它类型则调用其相应节点的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);
		}
	}
首先ag.genCode()把所有参数压栈,然后根据类别判断是初始化对象还是数组来使用0x1C或者0x1D指令并把类型作为操作数,当此条指令执行完之后,堆栈顶此时放着返回的对象或数组的句柄,接下来的任务是将该句柄赋值给ids。

接着判断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);
		}
		
	}
setvalue和classinit的代码几乎一样,唯一的区别是classinit中需要调用new和newarray指令得到需要给ids赋的值,而setvalue则是通过计算expr来得到,即使用ep.genCode();


(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。


(未完待续)




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值