所謂的編譯器後端的作用就是将文法樹翻譯成目标機器碼。所謂目标機器碼,考慮到直接翻譯成具體平台(如X86,ARM等)過于複雜,是以先設計一個虛拟機,并翻譯成這個虛拟機的機器碼。
對于虛拟機以及其指令格式可參考這篇文章http://blog.csdn.net/roger__wong/article/details/8947720,如何去嘗試實作這個虛拟機是在我的另外一個系列的部落格裡進行論述。
本篇文章從以下是那個方面來論述:後端架構與關鍵資料結構、節點翻譯方式。
1、後端架構和關鍵資料結構
後端接受前端的文法樹作為輸入,對于其每一個節點根據節點類型的不同産生不同的代碼。但在實作過程中為了簡單友善,我并沒有把後端抽象出一個單獨的子產品,而是在文法樹每一個節點的基礎上增加了一個genCode方法,通過調用這個方法來生成該節點及其所有孩子節點(通過遞歸)的代碼。
其次編譯器後端直接生成Class檔案(檔案結構也在上文提到的部落格中有說明),程式中後端首先構造一個ClassOfClass的實體,然後再調用此類的方法生成Class檔案:
[java] view plain copy
- 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();
- }
- }
- }
其中field結構:
[java] view plain copy
- 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結構
[java] view plain copy
- 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();
- }
- }
其中Code結構
[java] view plain copy
- 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();
- }
[java] view plain copy
- }
完成一個Class的後端代碼的生成工作,隻需要調用文法樹根節點(classdef)的genCode即可完成,因為根節點會不斷的遞歸調用其子樹的genCode方法,是以在遞歸調用的時候需要某些機制進行各方法之間資訊的傳遞,這裡建立新類BackendClassManager來進行資訊的傳遞,消除耦合。
[java] view plain copy
- 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();
- }
- }
cc代表目前正在編譯的class。
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節點
[java] view plain copy
- public void genCode() {
- BackendClassManager.cc.name=cn.toString();
- cb.genCode();
- }
代碼很簡單,首先把正在編譯的類名設定成classdef中出現的類名,然後調用classbody的genCode方法。
(2)classbody
[java] view plain copy
- public void genCode() {
- if(cb!=null)
- {
- cb.genCode();
- }
- }
依然很簡單,如果classmembers不為空,則調用classmembers的genCode方法。
值得注意的是,這些方法本身并沒有生成目标代碼乃是因為一個類的定義本身并不包含任何邏輯,而代碼本身是對邏輯的闡述,是以在類聲明、函數聲明、成員變量聲明等沒有生成任何有意義的代碼也就不值得奇怪了。
(3)classmembers
[java] view plain copy
- 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();
- }
- }
根據此classmembers的類型,對membervardeclare、memberfuncdeclare、constructor調用genCode方法,最後對下一個classmemebers調用genCode方法,這和本系列第一篇部落格中的遞推式是對應的。
(4)membervardeclare
[java] view plain copy
- 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);
- }
依然沒有任何代碼生成,隻是将成員變量的資訊放到ClassOfClass對象的fields域中。
(5)memberfundeclare
[java] view plain copy
- 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
[java] view plain copy
- 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
[java] view plain copy
- 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
接下來給出生成後端代碼的代碼:
[html] view plain copy
- 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
接下來給出代碼:
[java] view plain copy
- 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兩條指令:
[java] view plain copy
- 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
代碼:
[java] view plain copy
- 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
代碼:
[java] view plain copy
- 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
[java] view plain copy
- 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,也可能是個對象的句柄。
遞推式:
[java] view plain copy
- ids->
- * id| type 1
- id.ids| type 2
- id[expr] type 3
- this| type=4
代碼:
[java] view plain copy
- 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
[java] view plain copy
- 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)
[java] view plain copy
- 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
[java] view plain copy
- 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
[java] view plain copy
- 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);
- }
- }
- }
比較運算符的genCode雖然代碼比較長但是邏輯很簡單。
首先需要根據待比較的expr來決定是否進行類型轉換,目前在語言的設計中,隻有double和int是可比的,其它類型的比較雖然在編譯器不會報錯但在運作時中會進行報錯。
其次根據類型來調用不同的比較指令,再次根據邏輯需要進行二次比較(因為比較指令會根據大于小于等于傳回1、0或-1,和運算符并不等價,需要通過比較兩次使之和運算符等價)。
大體上代碼就這麼多,目前的編譯器已經可以編譯出正确的位元組碼了,但目前代碼還無法執行,需要等到Runtime寫好之後才能運作。
是以本系列部落格到這裡暫時告一段落,若給語言再添加一些進階點的特性,也需要等到我把Runtime寫好再說了。