java开发C语言编译器:把C实现的快速排序算法编译成jvm字节码

有了前面一系列的铺垫和准备后,我们终于能走到至关重要的一刻。在本节,我们将用C语言开发快速排序算法,然后利用我们的编译器把它编译成java字节码,让C语言编写的快速排序算法能在java虚拟机上顺利执行,完成本节内容后,编译器可以正确的将下列代码编译成java字节码:

1void quicksort(int A[10], int p, int r) { 2    int x; 3    int i; 4    i = p - 1; 5    int j; 6    int t; 7    int v; 8    v = r - 1; 9    if (p < r) { 10       x = A[r]; 11 12        for (j = p; j <= v; j++) { 13            if (A[j] <= x) {   14                 i++; 15                 t = A[i]; 16                 A[i] = A[j]; 17                 A[j] = t; 18            } 19        } 20 21        v = i + 1; 22        t = A[v]; 23        A[v] = A[r]; 24        A[r] = t; 25 26         t = v - 1; 27         quicksort(A, p,  t); 28         t = v + 1; 29         quicksort(A, t,  r); 30    } 31 32} 33 34void main () { 35    int a[10]; 36    int i; 37    int t; 38    printf("before quick sort:"); 39    for(i = 0; i < 10; i++) { 40        t = (10 - i); 41        a[i] = t; 42        printf("value of a[%d] is %d", i, a[i]); 43    }   44 45 46    quicksort(a, 0, 9); 47 48    printf("after quick sort:"); 49    for (i = 0; i < 10; i++) { 50        printf("value of a[%d] is %d", i, a[i]); 51    } 52 53}

上面的C代码是根据《算法导论》所编写的实现快速排序的算法,主函数先初始化一个乱序的数组,然后通过调用quicksort函数实现排序。我一直把编译器能够解释编译C语言快速排序的代码作为章节的终点,一来快速排序算法的实现包含了循环,ifelse分支判断,递归等编程语言的关键要素,能正确解释和编译它意味着编译器达到了一定的成熟度。而本节完成后,我们的编译器能正确编译快速排序的C语言实现后,整个编译器实现课程经历两年时光,也该画上句号了。

我们看看代码的实现,这次代码与前面代码的一大不同之处就是函数的递归调用。quicksort函数中会调用它自己,因此编译器在实现时,需要注意这个特点。原来我们实现函数的编译时,编译器会解读代码,直到函数第一次被调用时,才会把被调函数编译成字节码,但这里,被调函数在执行时会调用它自己,如果对原来的逻辑不加处理,那么编译器会反复的为quicksort函数生成代码,陷入到一种死循环的状态。负责函数调用的代码处于UnaryNodeExecutor中,代码修改如下:

1case CGrammarInitializer.Unary_LP_RP_TO_Unary: 2        case CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary: 3            //先获得函数名 4            boolean reEntry = false; 5            String funcName = (String)root.getChildren().get(0).getAttribute(ICodeKey.TEXT); 6            //change here 7            /* 8             * 如果函数名被记录过,那表明现在的函数调用其实是递归调用 9             */ 10            if (funcName != "" && funcName.equals(BaseExecutor.funcName)) { 11                reEntry = true; 12            } 13 14 15            ArrayList<Object> argList = null; 16            ArrayList<Object> symList = null; 17 18            if (production == CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary) { 19                ICodeNode argsNode = root.getChildren().get(1); 20                argList = (ArrayList<Object>)argsNode.getAttribute(ICodeKey.VALUE); 21                symList = (ArrayList<Object>)argsNode.getAttribute(ICodeKey.SYMBOL); 22                FunctionArgumentList.getFunctionArgumentList().setFuncArgList(argList);     23                FunctionArgumentList.getFunctionArgumentList().setFuncArgSymbolList(symList); 24            } 25 26            //找到函数执行树头节点 27            ICodeNode func = CodeTreeBuilder.getCodeTreeBuilder().getFunctionNodeByName(funcName); 28            if (func != null) { 29                //change here push parameters before calling function 30                /* 31                 * 函数调用时,把当前被调用的函数名记录下来,如果函数体内发送递归调用,那么编译器还会再次进入到 32                 * 这里,如果进入时判断到函数名跟我们这里存储的函数名一致,那表明发生了递归调用。 33                 */ 34                BaseExecutor.funcName = funcName; 35                int count = 0; 36                while (count < argList.size()) { 37                    Object objVal = argList.get(count); 38                    Object objSym = symList.get(count); 39                    if (objSym != null) { 40                        Symbol param = (Symbol)objSym; 41                        int idx = generator.getLocalVariableIndex(param); 42                        if (param.getDeclarator(Declarator.ARRAY) != null) { 43                            generator.emit(Instruction.ALOAD, "" + idx); 44                        } else { 45                            generator.emit(Instruction.ILOAD, ""+idx); 46                        } 47                    } else { 48                        int v = (int)objVal; 49                        generator.emit(Instruction.SIPUSH, ""+v); 50                    } 51 52                    count++; 53                } 54                //problem here handle reentry 55                if (BaseExecutor.isCompileMode == true && reEntry == false) { 56                    /* 57                     * 在编译状态下,遇到函数自我递归调用则不需要再次为函数生成代码,只需要生成invoke指令即可 58                     */ 59                    Executor executor = ExecutorFactory.getExecutorFactory().getExecutor(func); 60                    ProgramGenerator.getInstance().setInstructionBuffered(true); 61                    executor.Execute(func); 62                    symbol = (Symbol)root.getChildren().get(0).getAttribute(ICodeKey.SYMBOL); 63                    emitReturnInstruction(symbol); 64                    ProgramGenerator.getInstance().emitDirective(Directive.END_METHOD); 65                    ProgramGenerator.getInstance().setInstructionBuffered(false); 66                    ProgramGenerator.getInstance().popFuncName(); 67                } 68                compileFunctionCall(funcName); 69 70 71                Object returnVal = func.getAttribute(ICodeKey.VALUE); 72                if (returnVal != null) { 73                    System.out.println("function call with name " + funcName + " has return value that is " + returnVal.toString()); 74                    root.setAttribute(ICodeKey.VALUE, returnVal); 75                } 76 77            } else { 78                ClibCall libCall = ClibCall.getInstance(); 79                if (libCall.isAPICall(funcName)) { 80                    Object obj = libCall.invokeAPI(funcName); 81                    root.setAttribute(ICodeKey.VALUE, obj); 82                } 83            } 84 85 86            break;

当编译器解析到代码中发生函数调用时,它会把被调函数的名字记录下来,然后判断这个名字是否被记录过,如果前面有过记录,那么这次进入表明函数发生了递归调用,于是就不再执行函数对应的执行树,如果函数是第一次被调用,那么就执行函数对应的执行树,在执行过程中就可以把函数编译成字节码。

除了上面的改动之后,还有不少地方需要相应的修改,具体的调试演示过程请点击‘阅读额原文'查看视频。上面代码完成后,运行编译器,给定的C语言代码编译出的java汇编代码如下:

1.class public CSourceToJava 2.super java/lang/Object 3 4.method public static main([Ljava/lang/String;)V 5    sipush    10 6    newarray    int 7    astore    1 8    sipush    0 9    istore    2 10    sipush    0 11    istore    0 12    getstatic    java/lang/System/out Ljava/io/PrintStream; 13    ldc    "before quick sort:" 14    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 15    getstatic    java/lang/System/out Ljava/io/PrintStream; 16    ldc    " 17" 18    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 19    sipush    0 20    istore    2 21 22loop0: 23    iload    2 24    sipush    10 25if_icmpge branch0 26    sipush    10 27    iload    2 28    isub 29    istore    0 30    aload    1 31    iload    2 32    iload    0 33    iastore 34    aload    1 35    iload    2 36    iaload 37    istore    3 38    iload    2 39    istore    4 40    getstatic    java/lang/System/out Ljava/io/PrintStream; 41    ldc    "value of a[" 42    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 43    getstatic    java/lang/System/out Ljava/io/PrintStream; 44    iload    4 45    invokevirtual    java/io/PrintStream/print(I)V 46    getstatic    java/lang/System/out Ljava/io/PrintStream; 47    ldc    "] is " 48    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 49    getstatic    java/lang/System/out Ljava/io/PrintStream; 50    iload    3 51    invokevirtual    java/io/PrintStream/print(I)V 52    getstatic    java/lang/System/out Ljava/io/PrintStream; 53    ldc    " 54" 55    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 56    iload    2 57    sipush    1 58    iadd 59    istore    2 60goto loop0 61branch0: 62    aload    1 63    sipush    0 64    sipush    9 65    invokestatic    CSourceToJava/quicksort([III)V 66    getstatic    java/lang/System/out Ljava/io/PrintStream; 67    ldc    "after quick sort:" 68    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 69    getstatic    java/lang/System/out Ljava/io/PrintStream; 70    ldc    " 71" 72    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 73    sipush    0 74    istore    2 75 76loop2: 77    iload    2 78    sipush    10 79if_icmpge branch4 80    aload    1 81    iload    2 82    iaload 83    istore    3 84    iload    2 85    istore    4 86    getstatic    java/lang/System/out Ljava/io/PrintStream; 87    ldc    "value of a[" 88    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 89    getstatic    java/lang/System/out Ljava/io/PrintStream; 90    iload    4 91    invokevirtual    java/io/PrintStream/print(I)V 92    getstatic    java/lang/System/out Ljava/io/PrintStream; 93    ldc    "] is " 94    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 95    getstatic    java/lang/System/out Ljava/io/PrintStream; 96    iload    3 97    invokevirtual    java/io/PrintStream/print(I)V 98    getstatic    java/lang/System/out Ljava/io/PrintStream; 99    ldc    " 100" 101    invokevirtual    java/io/PrintStream/print(Ljava/lang/String;)V 102    iload    2 103    sipush    1 104    iadd 105    istore    2 106goto loop2 107branch4: 108    return 109.end method 110.method public static quicksort([III)V 111    sipush    0 112    istore    7 113    sipush    0 114    istore    6 115    iload    1 116    sipush    1 117    isub 118    istore    6 119    sipush    0 120    istore    5 121    sipush    0 122    istore    4 123    sipush    0 124    istore    3 125    iload    2 126    sipush    1 127    isub 128    istore    3 129    iload    1 130    iload    2 131if_icmpge branch1 132 133    aload    0 134    iload    2 135    iaload 136    istore    7 137    iload    1 138    istore    5 139 140loop1: 141 142    iload    5 143    iload    3 144if_icmpgt ibranch1 145 146    aload    0 147    iload    5 148    iaload 149    iload    7 150if_icmpgt ibranch2 151 152    iload    6 153    sipush    1 154    iadd 155    istore    6 156    aload    0 157    iload    6 158    iaload 159    istore    4 160    aload    0 161    iload    6 162    aload    0 163    iload    5 164    iaload 165    iastore 166    aload    0 167    iload    5 168    iload    4 169    iastore 170ibranch2: 171 172    iload    5 173    sipush    1 174    iadd 175    istore    5 176goto loop1 177 178ibranch1: 179 180    iload    6 181    sipush    1 182    iadd 183    istore    3 184    aload    0 185    iload    3 186    iaload 187    istore    4 188    aload    0 189    iload    3 190    aload    0 191    iload    2 192    iaload 193    iastore 194    aload    0 195    iload    2 196    iload    4 197    iastore 198    iload    3 199    sipush    1 200    isub 201    istore    4 202    aload    0 203    iload    1 204    iload    4 205    invokestatic    CSourceToJava/quicksort([III)V 206    iload    3 207    sipush    1 208    iadd 209    istore    4 210    aload    0 211    iload    4 212    iload    2 213    invokestatic    CSourceToJava/quicksort([III)V 214branch1: 215 216    return 217.end method 218 219.end class

上面的代码转换成jvm字节码运行后结果如下:

编译原理几乎是计算机专业中最晦涩难懂的课程。很多学生学这门课只不过是为了通过考试,学完后对编译原理之精妙仍然是摸不着头脑。而很多教这门课的老师,也只不过是混口饭吃,他自己未必对编译原理有多少深入的了解和把握,于是与其昏昏,使人昭昭。毕业多年后,回过头来反省我所承受的教育,我发现我们的教育总是流于表面的肤浅,给学生展示的始终是冰山的一角,对冰山下的巨大形体去置若罔闻,于是整个系统虽然培养出大量的计算机专业人员,但有能力对计算机知识具备深入见解的人凤毛麟角,很多人其实是走上工作岗位后,通过大量的生产实践才开始对计算机知识有了一定程度的深入窥探的,我就是其中之一。

计算机始终是一门理论结合实践的科学。光有理论却不能实践,那么理论看起来晦涩难懂,听起来虚儿巴脑,于是美妙的智慧结晶在应试教育体制下变成了虚张声势的怪兽,让学习它的人惊恐不慌,以为自己要被这只巨大的怪兽所吞灭。我是过来人,知道这种关说不练假把式的巨大危害,那种理论讲起来头头是道,搞得我晕头转向,处处受挫的煎熬感真是不忍回忆,我真心希望通过动手实践,能够让那些有志于在科技行业大展身手的年轻人不要再走我的老路。

如果人类只会谈情说爱,那么早就灭绝了。因此爱的核心在做不在说,科学技术的理解和掌握更是如此,动手吧!Just Fuck It!

本文分享自微信公众号 - Coding迪斯尼(gh_c9f933e7765d)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )