• 编译一日一练(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,这些就不需要我们自己考虑了。

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

    1. // tranlsater inter-mediate code, with input is inter-mediate code, and output is assmeble code
    2. public static String translate_single_code(String input)
    3. {
    4. String assemble = "";
    5. String[] sub = input.split(" ");
    6. if(sub[2].charAt(0) >= '0' && sub[2].charAt(0) <= '9')
    7. {
    8. assemble += "mov ax," + sub[2] + "\n";
    9. }
    10. else
    11. {
    12. assemble += "load ax,[" + sub[2] + "]\n";
    13. }
    14. assemble += "mov bx," + sub[4] + "\n";
    15. assemble += "div ax, bx\n";
    16. assemble += "store [" + sub[1] + "],ax\n";
    17. return assemble;
    18. }

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

    1. public static String translate_code(String input)
    2. {
    3. String[] sub;
    4. String str = "";
    5. if(input == "")
    6. {
    7. return "";
    8. }
    9. sub = input.split("\n");
    10. for(int i = 0; i < sub.length; i++)
    11. {
    12. str += translate_single_code(sub[i]);
    13. }
    14. return str;
    15. }
    16. }

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

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

    1. C:\Users\feixiaoxing\Desktop\DIYCompiler\day10>java Parse 6/3
    2. tmp1
    3. 6
    4. 3
    5. mov ax,6
    6. mov bx,3
    7. div ax, bx
    8. store [tmp1], ax
    9. C:\Users\feixiaoxing\Desktop\DIYCompiler\day10>java Parse 6/3/2
    10. tmp2
    11. tmp1
    12. 6
    13. 3
    14. 2
    15. mov ax,6
    16. mov bx,3
    17. div ax, bx
    18. store [tmp1], ax
    19. load ax,[tmp1]
    20. mov bx,2
    21. div ax, bx
    22. store [tmp2], ax
    23. C:\Users\feixiaoxing\Desktop\DIYCompiler\day10>java Parse 6/3/2/1
    24. tmp3
    25. tmp2
    26. tmp1
    27. 6
    28. 3
    29. 2
    30. 1
    31. mov ax,6
    32. mov bx,3
    33. div ax, bx
    34. store [tmp1], ax
    35. load ax,[tmp1]
    36. mov bx,2
    37. div ax, bx
    38. store [tmp2], ax
    39. load ax,[tmp2]
    40. mov bx,1
    41. div ax, bx
    42. store [tmp3], ax

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

    1. options {
    2. STATIC = false;
    3. }
    4. PARSER_BEGIN(Parse)
    5. import java.io.*;
    6. class node
    7. {
    8. public node left;
    9. public node right;
    10. public String node_type;
    11. public String node_name;
    12. public int depth;
    13. node() {this.left = this.right = null;}
    14. public void set_left_node(node left) { this.left = left;}
    15. public node get_left_node() { return this.left;}
    16. public void set_right_node(node right) { this.right = right;}
    17. public node get_right_node() {return this.right;}
    18. public void set_node_type(String node_type) {this.node_type = node_type;}
    19. public String get_node_type() {return this.node_type;}
    20. public void set_node_name(String node_name) {this.node_name = node_name;}
    21. public String get_node_name() {return this.node_name;}
    22. public void set_depth(int depth) {this.depth = depth;}
    23. public int get_depth() {return this.depth;}
    24. public int calculate_depth() { // node depth should be calculated before print it, so just calculate root node
    25. int left_depth = 0;
    26. int right_depth = 0;
    27. int final_depth = 0;
    28. if(get_left_node() == null && get_right_node() == null) {
    29. set_depth(1);
    30. return 1;
    31. }
    32. if(null != get_left_node()) {
    33. left_depth = get_left_node().calculate_depth();
    34. }
    35. if(null != get_right_node()){
    36. right_depth = get_right_node().calculate_depth();
    37. }
    38. final_depth = (left_depth > right_depth) ? (left_depth+1) :(right_depth +1);
    39. set_depth(final_depth);
    40. return final_depth;
    41. }
    42. public void print_node(int start_depth, int start_point) // add print node function
    43. {
    44. int i = 0;
    45. for(i =0; i < (start_point - start_depth*5); i++){
    46. System.out.print(" ");
    47. }
    48. if(get_node_type() != "value_node")
    49. {
    50. System.out.println(get_node_name());
    51. }
    52. else
    53. {
    54. System.out.println(((value_node)this).get_value());
    55. }
    56. if(get_left_node() != null) get_left_node().print_node(start_depth -1, start_point);
    57. if(get_right_node() != null) get_right_node().print_node(start_depth -1, start_point);
    58. }
    59. }
    60. class value_node extends node
    61. {
    62. public int value;
    63. value_node() { set_node_type("value_node");}
    64. public int get_value() { return this.value;}
    65. public void set_value(int value) {this.value = value;}
    66. }
    67. class div_node extends node
    68. {
    69. div_node() { set_node_type("/");}
    70. public int get_value() {
    71. int left = 0, right = 0;
    72. // get left node
    73. if(get_left_node().get_node_type() == "/")
    74. {
    75. left = ((div_node)get_left_node()).get_value();
    76. }
    77. else
    78. {
    79. left = ((value_node)get_left_node()).get_value();
    80. }
    81. // get right node
    82. if(get_right_node() == null)
    83. {
    84. return left;
    85. }
    86. else
    87. {
    88. right = ((value_node)get_right_node()).get_value();
    89. return left/right;
    90. }
    91. }
    92. // add semantic check
    93. public int check_value() {
    94. if(get_right_node() != null)
    95. {
    96. if( 0 == ((value_node)get_right_node()).get_value())
    97. {
    98. System.out.println("Div by Zero");
    99. return -1;
    100. }
    101. }
    102. if(get_left_node().get_node_type() == "/")
    103. {
    104. return ((div_node)(get_left_node())).check_value();
    105. }
    106. return 1;
    107. }
    108. // generate inter-mediate code
    109. public String generate_intermediate_code()
    110. {
    111. int value;
    112. String str = "";
    113. if(get_left_node().get_node_type() == "/")
    114. {
    115. str = ((div_node)get_left_node()).generate_intermediate_code();
    116. }
    117. if(null == get_right_node())
    118. {
    119. return "";
    120. }
    121. set_node_name("tmp" + Integer.toString(Parse.allocate_index()));
    122. if(get_left_node().get_node_type() == "/")
    123. {
    124. str += "div " + get_node_name() + " " + get_left_node().get_node_name() + " / " + Integer.toString(((value_node)get_right_node()).get_value()) + "\n";
    125. }
    126. else
    127. {
    128. str += "div " + get_node_name() + " " + Integer.toString(((value_node)get_left_node()).get_value()) + " / " + Integer.toString(((value_node)get_right_node()).get_value()) + "\n";
    129. }
    130. return str;
    131. }
    132. }
    133. public class Parse {
    134. public static int index = 0;
    135. public static void main(String[] args) {
    136. for (String arg : args) {
    137. try {
    138. div_node div = evaluate(arg);
    139. String str = div.generate_intermediate_code();
    140. System.out.println("");
    141. div.calculate_depth();
    142. div.print_node(div.get_depth(), div.get_depth()*5);
    143. System.out.println("");
    144. System.out.println(translate_code(str));
    145. //System.out.println(div.check_value());
    146. } catch (ParseException ex) {
    147. System.err.println(ex.getMessage());
    148. }
    149. }
    150. }
    151. public static div_node evaluate(String src) throws ParseException {
    152. Reader reader = new StringReader(src);
    153. return new Parse(reader).expr();
    154. }
    155. // important index
    156. public static int allocate_index()
    157. {
    158. index += 1;
    159. return index;
    160. }
    161. // tranlsater inter-mediate code, with input is inter-mediate code, and output is assmeble code
    162. public static String translate_single_code(String input)
    163. {
    164. String assemble = "";
    165. String[] sub = input.split(" ");
    166. if(sub[2].charAt(0) >= '0' && sub[2].charAt(0) <= '9')
    167. {
    168. assemble += "mov ax," + sub[2] + "\n";
    169. }
    170. else
    171. {
    172. assemble += "load ax,[" + sub[2] + "]\n";
    173. }
    174. assemble += "mov bx," + sub[4] + "\n";
    175. assemble += "div ax, bx\n";
    176. assemble += "store [" + sub[1] + "],ax\n";
    177. return assemble;
    178. }
    179. public static String translate_code(String input)
    180. {
    181. String[] sub;
    182. String str = "";
    183. if(input == "")
    184. {
    185. return "";
    186. }
    187. sub = input.split("\n");
    188. for(int i = 0; i < sub.length; i++)
    189. {
    190. str += translate_single_code(sub[i]);
    191. }
    192. return str;
    193. }
    194. }
    195. PARSER_END(Parse)
    196. SKIP: { <[" ", "\t", "\r", "\n"]> }
    197. TOKEN: {
    198. <INTEGER: (["0"-"9"])+>
    199. }
    200. div_node expr() throws NumberFormatException :
    201. {
    202. Token a ;
    203. Token b ;
    204. div_node div;
    205. }
    206. {
    207. a = <INTEGER>
    208. {
    209. value_node node_a = new value_node();
    210. node_a.set_value(Integer.parseInt( a.image ));
    211. div = new div_node();
    212. div.set_left_node(node_a);
    213. }
    214. (
    215. "/" b = <INTEGER>
    216. {
    217. value_node node_b = new value_node();
    218. node_b.set_value(Integer.parseInt( b.image ));
    219. // important code about node adding
    220. if(div.get_right_node() == null)
    221. {
    222. div.set_right_node(node_b);
    223. }
    224. else
    225. {
    226. div_node prev = div;
    227. div = new div_node();
    228. div.set_left_node(prev);
    229. div.set_right_node(node_b);
    230. }
    231. }
    232. )*
    233. <EOF>
    234. { return div ; }
    235. }

  • 相关阅读:
    C++笔记之临时变量与临时对象与匿名对象
    java单向循环链表(含约瑟夫问题代码展示)
    注入攻击
    郑州什么企业会使用灵活用工平台?
    网络安全(4)
    用springboot改写传智健康项目问题
    springmvc总结
    Prometheus+Grafana环境搭建(window)
    【Python百日进阶-WEB开发-冲進Flask】Day182 - Flask蓝图与模板继承
    虚拟机配置完NAT模式之后可以和主机ping通但是ping 百度显示:网络不可达
  • 原文地址:https://blog.csdn.net/feixiaoxing/article/details/127681638