有了前面一系列的鋪墊和準(zhǔn)備后,我們終于能走到至關(guān)重要的一刻。在本節(jié)超凳,我們將用C語言開發(fā)快速排序算法钞澳,然后利用我們的編譯器把它編譯成java字節(jié)碼怠惶,讓C語言編寫的快速排序算法能在java虛擬機(jī)上順利執(zhí)行,完成本節(jié)內(nèi)容后轧粟,編譯器可以正確的將下列代碼編譯成java字節(jié)碼:
void quicksort(int A[10], int p, int r) {
int x;
int i;
i = p - 1;
int j;
int t;
int v;
v = r - 1;
if (p < r) {
x = A[r];
for (j = p; j <= v; j++) {
if (A[j] <= x) {
i++;
t = A[i];
A[i] = A[j];
A[j] = t;
}
}
v = i + 1;
t = A[v];
A[v] = A[r];
A[r] = t;
t = v - 1;
quicksort(A, p, t);
t = v + 1;
quicksort(A, t, r);
}
}
void main () {
int a[10];
int i;
int t;
printf("before quick sort:");
for(i = 0; i < 10; i++) {
t = (10 - i);
a[i] = t;
printf("value of a[%d] is %d", i, a[i]);
}
quicksort(a, 0, 9);
printf("after quick sort:");
for (i = 0; i < 10; i++) {
printf("value of a[%d] is %d", i, a[i]);
}
}
上面的C代碼是根據(jù)《算法導(dǎo)論》所編寫的實(shí)現(xiàn)快速排序的算法策治,主函數(shù)先初始化一個(gè)亂序的數(shù)組,然后通過調(diào)用quicksort函數(shù)實(shí)現(xiàn)排序兰吟。我一直把編譯器能夠解釋編譯C語言快速排序的代碼作為章節(jié)的終點(diǎn)通惫,一來快速排序算法的實(shí)現(xiàn)包含了循環(huán),ifelse分支判斷混蔼,遞歸等編程語言的關(guān)鍵要素履腋,能正確解釋和編譯它意味著編譯器達(dá)到了一定的成熟度。而本節(jié)完成后,我們的編譯器能正確編譯快速排序的C語言實(shí)現(xiàn)后遵湖,整個(gè)編譯器實(shí)現(xiàn)課程經(jīng)歷兩年時(shí)光悔政,也該畫上句號(hào)了。
我們看看代碼的實(shí)現(xiàn)奄侠,這次代碼與前面代碼的一大不同之處就是函數(shù)的遞歸調(diào)用卓箫。quicksort函數(shù)中會(huì)調(diào)用它自己,因此編譯器在實(shí)現(xiàn)時(shí)垄潮,需要注意這個(gè)特點(diǎn)烹卒。原來我們實(shí)現(xiàn)函數(shù)的編譯時(shí),編譯器會(huì)解讀代碼弯洗,直到函數(shù)第一次被調(diào)用時(shí)旅急,才會(huì)把被調(diào)函數(shù)編譯成字節(jié)碼,但這里牡整,被調(diào)函數(shù)在執(zhí)行時(shí)會(huì)調(diào)用它自己藐吮,如果對原來的邏輯不加處理,那么編譯器會(huì)反復(fù)的為quicksort函數(shù)生成代碼逃贝,陷入到一種死循環(huán)的狀態(tài)谣辞。負(fù)責(zé)函數(shù)調(diào)用的代碼處于UnaryNodeExecutor中,代碼修改如下:
case CGrammarInitializer.Unary_LP_RP_TO_Unary:
case CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary:
//先獲得函數(shù)名
boolean reEntry = false;
String funcName = (String)root.getChildren().get(0).getAttribute(ICodeKey.TEXT);
//change here
/*
* 如果函數(shù)名被記錄過沐扳,那表明現(xiàn)在的函數(shù)調(diào)用其實(shí)是遞歸調(diào)用
*/
if (funcName != "" && funcName.equals(BaseExecutor.funcName)) {
reEntry = true;
}
ArrayList<Object> argList = null;
ArrayList<Object> symList = null;
if (production == CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary) {
ICodeNode argsNode = root.getChildren().get(1);
argList = (ArrayList<Object>)argsNode.getAttribute(ICodeKey.VALUE);
symList = (ArrayList<Object>)argsNode.getAttribute(ICodeKey.SYMBOL);
FunctionArgumentList.getFunctionArgumentList().setFuncArgList(argList);
FunctionArgumentList.getFunctionArgumentList().setFuncArgSymbolList(symList);
}
//找到函數(shù)執(zhí)行樹頭節(jié)點(diǎn)
ICodeNode func = CodeTreeBuilder.getCodeTreeBuilder().getFunctionNodeByName(funcName);
if (func != null) {
//change here push parameters before calling function
/*
* 函數(shù)調(diào)用時(shí)泥从,把當(dāng)前被調(diào)用的函數(shù)名記錄下來,如果函數(shù)體內(nèi)發(fā)送遞歸調(diào)用沪摄,那么編譯器還會(huì)再次進(jìn)入到
* 這里躯嫉,如果進(jìn)入時(shí)判斷到函數(shù)名跟我們這里存儲(chǔ)的函數(shù)名一致,那表明發(fā)生了遞歸調(diào)用杨拐。
*/
BaseExecutor.funcName = funcName;
int count = 0;
while (count < argList.size()) {
Object objVal = argList.get(count);
Object objSym = symList.get(count);
if (objSym != null) {
Symbol param = (Symbol)objSym;
int idx = generator.getLocalVariableIndex(param);
if (param.getDeclarator(Declarator.ARRAY) != null) {
generator.emit(Instruction.ALOAD, "" + idx);
} else {
generator.emit(Instruction.ILOAD, ""+idx);
}
} else {
int v = (int)objVal;
generator.emit(Instruction.SIPUSH, ""+v);
}
count++;
}
//problem here handle reentry
if (BaseExecutor.isCompileMode == true && reEntry == false) {
/*
* 在編譯狀態(tài)下祈餐,遇到函數(shù)自我遞歸調(diào)用則不需要再次為函數(shù)生成代碼,只需要生成invoke指令即可
*/
Executor executor = ExecutorFactory.getExecutorFactory().getExecutor(func);
ProgramGenerator.getInstance().setInstructionBuffered(true);
executor.Execute(func);
symbol = (Symbol)root.getChildren().get(0).getAttribute(ICodeKey.SYMBOL);
emitReturnInstruction(symbol);
ProgramGenerator.getInstance().emitDirective(Directive.END_METHOD);
ProgramGenerator.getInstance().setInstructionBuffered(false);
ProgramGenerator.getInstance().popFuncName();
}
compileFunctionCall(funcName);
Object returnVal = func.getAttribute(ICodeKey.VALUE);
if (returnVal != null) {
System.out.println("function call with name " + funcName + " has return value that is " + returnVal.toString());
root.setAttribute(ICodeKey.VALUE, returnVal);
}
} else {
ClibCall libCall = ClibCall.getInstance();
if (libCall.isAPICall(funcName)) {
Object obj = libCall.invokeAPI(funcName);
root.setAttribute(ICodeKey.VALUE, obj);
}
}
break;
當(dāng)編譯器解析到代碼中發(fā)生函數(shù)調(diào)用時(shí)哄陶,它會(huì)把被調(diào)函數(shù)的名字記錄下來帆阳,然后判斷這個(gè)名字是否被記錄過,如果前面有過記錄屋吨,那么這次進(jìn)入表明函數(shù)發(fā)生了遞歸調(diào)用蜒谤,于是就不再執(zhí)行函數(shù)對應(yīng)的執(zhí)行樹,如果函數(shù)是第一次被調(diào)用离赫,那么就執(zhí)行函數(shù)對應(yīng)的執(zhí)行樹芭逝,在執(zhí)行過程中就可以把函數(shù)編譯成字節(jié)碼。
除了上面的改動(dòng)之后渊胸,還有不少地方需要相應(yīng)的修改旬盯,具體的調(diào)試演示過程請參看視頻用java開發(fā)C語言編譯器。上面代碼完成后,運(yùn)行編譯器胖翰,給定的C語言代碼編譯出的java匯編代碼如下:
.class public CSourceToJava
.super java/lang/Object
.method public static main([Ljava/lang/String;)V
sipush 10
newarray int
astore 1
sipush 0
istore 2
sipush 0
istore 0
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "before quick sort:"
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "
"
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
sipush 0
istore 2
loop0:
iload 2
sipush 10
if_icmpge branch0
sipush 10
iload 2
isub
istore 0
aload 1
iload 2
iload 0
iastore
aload 1
iload 2
iaload
istore 3
iload 2
istore 4
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "value of a["
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
iload 4
invokevirtual java/io/PrintStream/print(I)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "] is "
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
iload 3
invokevirtual java/io/PrintStream/print(I)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "
"
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
iload 2
sipush 1
iadd
istore 2
goto loop0
branch0:
aload 1
sipush 0
sipush 9
invokestatic CSourceToJava/quicksort([III)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "after quick sort:"
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "
"
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
sipush 0
istore 2
loop2:
iload 2
sipush 10
if_icmpge branch4
aload 1
iload 2
iaload
istore 3
iload 2
istore 4
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "value of a["
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
iload 4
invokevirtual java/io/PrintStream/print(I)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "] is "
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
iload 3
invokevirtual java/io/PrintStream/print(I)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "
"
invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
iload 2
sipush 1
iadd
istore 2
goto loop2
branch4:
return
.end method
.method public static quicksort([III)V
sipush 0
istore 7
sipush 0
istore 6
iload 1
sipush 1
isub
istore 6
sipush 0
istore 5
sipush 0
istore 4
sipush 0
istore 3
iload 2
sipush 1
isub
istore 3
iload 1
iload 2
if_icmpge branch1
aload 0
iload 2
iaload
istore 7
iload 1
istore 5
loop1:
iload 5
iload 3
if_icmpgt ibranch1
aload 0
iload 5
iaload
iload 7
if_icmpgt ibranch2
iload 6
sipush 1
iadd
istore 6
aload 0
iload 6
iaload
istore 4
aload 0
iload 6
aload 0
iload 5
iaload
iastore
aload 0
iload 5
iload 4
iastore
ibranch2:
iload 5
sipush 1
iadd
istore 5
goto loop1
ibranch1:
iload 6
sipush 1
iadd
istore 3
aload 0
iload 3
iaload
istore 4
aload 0
iload 3
aload 0
iload 2
iaload
iastore
aload 0
iload 2
iload 4
iastore
iload 3
sipush 1
isub
istore 4
aload 0
iload 1
iload 4
invokestatic CSourceToJava/quicksort([III)V
iload 3
sipush 1
iadd
istore 4
aload 0
iload 4
iload 2
invokestatic CSourceToJava/quicksort([III)V
branch1:
return
.end method
.end class
上面的代碼轉(zhuǎn)換成jvm字節(jié)碼運(yùn)行后結(jié)果如下:
編譯原理幾乎是計(jì)算機(jī)專業(yè)中最晦澀難懂的課程接剩。很多學(xué)生學(xué)這門課只不過是為了通過考試,學(xué)完后對編譯原理之精妙仍然是摸不著頭腦萨咳。而很多教這門課的老師懊缺,也只不過是混口飯吃,他自己未必對編譯原理有多少深入的了解和把握培他,于是與其昏昏鹃两,使人昭昭。畢業(yè)多年后舀凛,回過頭來反省我所承受的教育俊扳,我發(fā)現(xiàn)我們的教育總是流于表面的膚淺,給學(xué)生展示的始終是冰山的一角猛遍,對冰山下的巨大形體去置若罔聞馋记,于是整個(gè)系統(tǒng)雖然培養(yǎng)出大量的計(jì)算機(jī)專業(yè)人員,但有能力對計(jì)算機(jī)知識(shí)具備深入見解的人鳳毛麟角懊烤,很多人其實(shí)是走上工作崗位后梯醒,通過大量的生產(chǎn)實(shí)踐才開始對計(jì)算機(jī)知識(shí)有了一定程度的深入窺探的,我就是其中之一腌紧。
計(jì)算機(jī)始終是一門理論結(jié)合實(shí)踐的科學(xué)茸习。光有理論卻不能實(shí)踐,那么理論看起來晦澀難懂寄啼,聽起來虛兒巴腦逮光,于是美妙的智慧結(jié)晶在應(yīng)試教育體制下變成了虛張聲勢的怪獸代箭,讓學(xué)習(xí)它的人驚恐不慌墩划,以為自己要被這只巨大的怪獸所吞滅。我是過來人嗡综,知道這種關(guān)說不練假把式的巨大危害乙帮,那種理論講起來頭頭是道,搞得我暈頭轉(zhuǎn)向极景,處處受挫的煎熬感真是不忍回憶察净,我真心希望通過動(dòng)手實(shí)踐,能夠讓那些有志于在科技行業(yè)大展身手的年輕人不要再走我的老路盼樟。
如果人類只會(huì)談情說愛氢卡,那么早就滅絕了。因此愛的核心在做不在說晨缴,科學(xué)技術(shù)的理解和掌握更是如此译秦,動(dòng)手吧!Just Fuck It!
更多技術(shù)信息,包括操作系統(tǒng)筑悴,編譯器们拙,面試算法,機(jī)器學(xué)習(xí)阁吝,人工智能砚婆,請關(guān)照我的公眾號(hào):