编译器一日一练(DIY系列之汇编输出)

Posted 嵌入式-老费

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器一日一练(DIY系列之汇编输出)相关的知识,希望对你有一定的参考价值。

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        代码地址:https://github.com/feixiaoxing/DIYCompiler/blob/master/day10/Parse.jj 

        前面的几章,我们陆续讨论了词法分析、语法树、复杂语法树、语义分析、中间代码输出这些环节,今天终于到了汇编代码这个大结局了。很多人都认为编译器,就是把源代码编译成二进制文件。这其实是一个误会。大部分编译器的工作就是把source code变成汇编代码而已,剩下来的可以由专门的工具将这些汇编代码转成二进制文件,比如binutils等等。当然,如果生成的代码不适用于cpu来执行,而是用于虚拟机vm解析的,类似于jvm这样,其实也是可以的。

        汇编的输出也涉及到几个方面。第一,不同os支持的汇编格式不一样,比如windows支持的汇编文件和linux支持的文件格式就是不一样的;第二,除了输出函数代码之外,全局变量的输出,第三方函数的引用这些都是要考虑进去的;第三,函数的压栈、出栈部分,这个需要依据不同的cpu进行不同的处理;第四,函数入参、返回值寄存器的选择一般是固定的,生成汇编的时候要注意下;第五,通用寄存器的选择,有的比较简单,比如一些脚本语言只有3~4个寄存器,完全谈不上寄存器选择,有的比如arm、mips、powerpc这些,多达32个寄存器,中间选择的空间很大;第六,汇编的优化,比如有两个相连的指令 store [tmp1], ax; mov ax, [tmp1],那这两条指令就可以删除;第七,指令的选择。

        上面虽然说了这么多,回到今天四则运算的汇编输出,其实就没有这么难了。我们可以选用x86的ax和bx寄存器,处理的其实就是除法指令。但是有一点大家需要注意下,中间代码和汇编代码之间并非是一一对应的关系,很多时候,需要用好几条汇编指令才能对应一条中间代码,这些都是常有的事情。当然,如果后端接入的是llvm,这些就不需要我们自己考虑了。

        鉴于之前已经输出了中间代码,这里直接看汇编是如何输出的即可,

	// tranlsater inter-mediate code, with input is inter-mediate code, and output is assmeble code
	public static String translate_single_code(String input)
	
		String assemble = "";

		String[] sub = input.split(" ");
		if(sub[2].charAt(0) >= '0' && sub[2].charAt(0) <= '9')
		
			assemble += "mov ax," + sub[2] + "\\n";
		
		else
		
			assemble += "load ax,[" + sub[2] + "]\\n";
		

		assemble += "mov bx," + sub[4] + "\\n";
		assemble += "div ax, bx\\n";
		assemble += "store [" + sub[1] + "],ax\\n";

		return assemble;
	

         这是翻译单条中间代码的函数。翻译的方法不难,主要是判断第一个除数是来自于整数,还是来自于临时变量。如果是整数,那么直接用mov将数据拷贝到ax即可。要不然,就将数据从mem加载到ax。剩下的动作就是常规操作,将另外一个数据拷贝到bx,开始除法运算,一般cpu除法的结果保存在ax,这个时候只需要将ax用store命令保存到mem变量就可以了。注意,这里的ax、bx来自于cpu空间,而临时变量只能是mem空间里面。

	public static String translate_code(String input)
	
		String[] sub;
		String str = "";
	
		if(input == "")
		
			return "";
		

		sub = input.split("\\n");
		for(int i = 0; i < sub.length; i++)
		
			str += translate_single_code(sub[i]);
		

		return str;
		

        有了单条中间代码的翻译,多条中间代码的翻译就很好理解了。只需要用换行符号\\n分割之后,拿出来一条一条翻译即可。

        等到这一切都做好之后,可以看一下翻译的效果如何,

C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day10>java Parse 6/3

tmp1
     6
     3

mov ax,6
mov bx,3
div ax, bx
store [tmp1], ax


C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day10>java Parse 6/3/2

tmp2
     tmp1
          6
          3
     2

mov ax,6
mov bx,3
div ax, bx
store [tmp1], ax
load ax,[tmp1]
mov bx,2
div ax, bx
store [tmp2], ax


C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day10>java Parse 6/3/2/1

tmp3
     tmp2
          tmp1
               6
               3
          2
     1

mov ax,6
mov bx,3
div ax, bx
store [tmp1], ax
load ax,[tmp1]
mov bx,2
div ax, bx
store [tmp2], ax
load ax,[tmp2]
mov bx,1
div ax, bx
store [tmp3], ax

        整体来看,输出的效果尚可接受,不过汇编还有很大的优化空间,比如store和load语句其实是可以删除的。优化的意思是说,原来的汇编语言也是对的,但是某些语句删除了之后,效果可以更好。最后,为了便于大家学习,这里在此贴出完成的jj文件供大家参考,

options 
    STATIC = false;

 
PARSER_BEGIN(Parse)
import java.io.*;

class node

	public node left;
	public node right;
	public String node_type;
	public String node_name;
	public int depth;
	
	node() this.left = this.right = null;
	public void set_left_node(node left)  this.left = left;
	public node get_left_node()  return this.left;
	
	public void set_right_node(node right)  this.right = right;
	public node get_right_node() return this.right;
	
	public void set_node_type(String node_type) this.node_type = node_type;
	public String get_node_type() return this.node_type;
	
	public void set_node_name(String node_name) this.node_name = node_name;
	public String get_node_name() return this.node_name;

	public void set_depth(int depth) this.depth = depth;
	public int get_depth() return this.depth;
	public int calculate_depth()  // node depth should be calculated before print it, so just calculate root node

		int left_depth = 0;
		int right_depth = 0;
		int final_depth = 0;
		if(get_left_node() == null && get_right_node() == null) 
			set_depth(1);
			return 1;
		
		
		if(null != get_left_node()) 
			left_depth = get_left_node().calculate_depth();
		

		if(null != get_right_node())
			right_depth = get_right_node().calculate_depth();
		
		
		final_depth = (left_depth > right_depth) ? (left_depth+1) :(right_depth +1);
		set_depth(final_depth);
		return final_depth;
	
	
	public void print_node(int start_depth, int start_point) // add print node function
	
		int i = 0;
		
		for(i =0; i < (start_point - start_depth*5); i++)
			System.out.print(" ");
		
		
		if(get_node_type() != "value_node")
		
			System.out.println(get_node_name());
		
		else
		
			System.out.println(((value_node)this).get_value());
		

		if(get_left_node() != null) get_left_node().print_node(start_depth -1, start_point);
		if(get_right_node() != null) get_right_node().print_node(start_depth -1, start_point);
	


class value_node extends node

	public int value;
	
	value_node()  set_node_type("value_node");
	public int get_value()  return this.value;
	public void set_value(int value) this.value = value;


class div_node extends node

	div_node()  set_node_type("/");
	
	public int get_value() 
		int left = 0, right = 0;
		
		// get left node
		if(get_left_node().get_node_type() == "/")
		
			left = ((div_node)get_left_node()).get_value();
		
		else
		
			left = ((value_node)get_left_node()).get_value();
		

		// get right node
		if(get_right_node() == null)
		
			return left;
		
		else
		
			right = ((value_node)get_right_node()).get_value();
			return left/right;
		
	

	// add semantic check
	public int check_value() 
	
		if(get_right_node() != null)
		
			if( 0 == ((value_node)get_right_node()).get_value())
			
				System.out.println("Div by Zero");
				return -1;
			
		
		
		if(get_left_node().get_node_type() == "/")
		
			return ((div_node)(get_left_node())).check_value();
		

		return 1;
	
	
	// generate inter-mediate code
	public String generate_intermediate_code()
	
		int value;
		String str = "";
		
		if(get_left_node().get_node_type() == "/")
		
			str = ((div_node)get_left_node()).generate_intermediate_code();
		
		
		if(null == get_right_node())
		
			return "";
		

		set_node_name("tmp" + Integer.toString(Parse.allocate_index()));
		if(get_left_node().get_node_type() == "/")
		
			str += "div " + get_node_name() + " " + get_left_node().get_node_name() + " / " + Integer.toString(((value_node)get_right_node()).get_value()) + "\\n";
		
		else
		
			str += "div " + get_node_name() + " " + Integer.toString(((value_node)get_left_node()).get_value()) + " / " + Integer.toString(((value_node)get_right_node()).get_value()) + "\\n";
		

		return str;
	


public class Parse 

    public static int index = 0;

    public static void main(String[] args) 
        for (String arg : args) 
            try 
				div_node div = evaluate(arg);
				String str = div.generate_intermediate_code();

				System.out.println("");
				div.calculate_depth();
				div.print_node(div.get_depth(), div.get_depth()*5);

				System.out.println("");
				System.out.println(translate_code(str));

                //System.out.println(div.check_value());
             catch (ParseException ex) 
                System.err.println(ex.getMessage());
            
        
    
 
    public static div_node evaluate(String src) throws ParseException 
        Reader reader = new StringReader(src);
        return  new Parse(reader).expr();
    
	
	// important index
	public static int allocate_index()
	
		index += 1;
		return index;
	

	// tranlsater inter-mediate code, with input is inter-mediate code, and output is assmeble code
	public static String translate_single_code(String input)
	
		String assemble = "";

		String[] sub = input.split(" ");
		if(sub[2].charAt(0) >= '0' && sub[2].charAt(0) <= '9')
		
			assemble += "mov ax," + sub[2] + "\\n";
		
		else
		
			assemble += "load ax,[" + sub[2] + "]\\n";
		

		assemble += "mov bx," + sub[4] + "\\n";
		assemble += "div ax, bx\\n";
		assemble += "store [" + sub[1] + "],ax\\n";

		return assemble;
	

	public static String translate_code(String input)
	
		String[] sub;
		String str = "";
	
		if(input == "")
		
			return "";
		

		sub = input.split("\\n");
		for(int i = 0; i < sub.length; i++)
		
			str += translate_single_code(sub[i]);
		

		return str;
		

PARSER_END(Parse)
 
SKIP:  <[" ", "\\t", "\\r", "\\n"]> 
TOKEN: 
    <INTEGER: (["0"-"9"])+>

 
div_node expr() throws NumberFormatException :

    Token a ;
    Token b ;
    div_node div;


	a = <INTEGER> 
	 
		value_node node_a = new value_node();
		node_a.set_value(Integer.parseInt( a.image ));
		
		div = new div_node();
		div.set_left_node(node_a);
		
	
	
	(
	"/" b = <INTEGER>
	
		value_node node_b = new value_node();
		node_b.set_value(Integer.parseInt( b.image ));

		// important code about node adding
		if(div.get_right_node() == null)
		
			div.set_right_node(node_b);
		
		else
		
			div_node prev = div;
			div = new div_node();
			
			div.set_left_node(prev);
			div.set_right_node(node_b);
		
	
	)*
	
	<EOF>
	 return div ; 

以上是关于编译器一日一练(DIY系列之汇编输出)的主要内容,如果未能解决你的问题,请参考以下文章

编译器一日一练(DIY系列之汇编优化)

编译器一日一练(DIY系列之汇编优化)

编译器一日一练(DIY系列之语义分析)

编译器一日一练(DIY系列之语义分析)

编译器一日一练(DIY系列之语法树打印)

编译器一日一练(DIY系列之语法树打印)