《自制编译器》函数体和流程控制语句的编译过程

前言

在自制编译器的Part1和Part2中,只想说明一下编译器的实现的大体逻辑和有趣的、值得探讨地方,不想对代码本身进行分析,所以对于代码的分析流程就放到了这里。

这篇文章主要是分析一下生成输出的语法树节点的过程。

函数体和流程语句被编译成中间代码的过程:

compileFunctionBody() :

  public List<Stmt> compileFunctionBody(DefinedFunction f) {
        stmts = new ArrayList<>();
        scopeStack = new LinkedList<>();
        breakStack = new LinkedList<>();
        continueStack = new LinkedList<>();
        jumpMap = new HashMap<>();
        transformStmt(f.body());
        checkJumpLinks(jumpMap);
        return stmts;
    }

刨去对于函数体作用域(ScopeStack)的压栈过程等等,剩下的逻辑可以用这张图表示:


虽然transformstmt(f.body()),中的f.body()是一个StmtNode 但是这个StmtNode包含了很多内容,所以各种类型的节点还是会被Visitor访问到。

举例

在Part1中说过,因为流程控制语句只能定义在函数体中,所以了解了一个流程控制语句的算法就可以了解函数体的编译过程。通过一个实际的例子 来说明一下编译后的中间节点代码是如何处理流程控制语句的。

void fun(int a, char b) {
        if (a == 5) {
            printf("%d\n",a);
        }else{
            printf("%d\n",b);
        }

        while(b < 100) {
            b ++;
            if (b == 50)
                break;
            else
                continue;
        }
    }


int main(int argc, char ** argv){

}

这个代码在Cflat编译器中会编译为如下的抽象语法树和中间代码语法树,两者可以对比起来看看:

抽象语法树

variables:
functions:
    <<DefinedFunction>> (test.cb:3)
    name: "fun"
    isPrivate: false
    params:
        parameters:
            <<CBCParameter>> (test.cb:3)
            name: "a"
            typeNode: int
            <<CBCParameter>> (test.cb:3)
            name: "b"
            typeNode: char
    body:
        <<BlockNode>> (test.cb:3)
        variables:
        stmts:
            <<IfNode>> (test.cb:4)
            cond:
                <<BinaryOpNode>> (test.cb:4)
                operator: "=="
                left:
                    <<VariableNode>> (test.cb:4)
                    name: "a"
                right:
                    <<IntegerLiteralNode>> (test.cb:4)
                    typeNode: int
                    value: 5
            thenBody:
                <<BlockNode>> (test.cb:4)
                variables:
                stmts:
                    <<ExprStmtNode>> (test.cb:5)
                    expr:
                        <<FuncallNode>> (test.cb:5)
                        expr:
                            <<VariableNode>> (test.cb:5)
                            name: "printf"
                        args:
                            <<StringLiteralNode>> (test.cb:5)
                            value: "%d\n"
                            <<VariableNode>> (test.cb:5)
                            name: "a"
            elseBody:
                <<BlockNode>> (test.cb:6)
                variables:
                stmts:
                    <<ExprStmtNode>> (test.cb:7)
                    expr:
                        <<FuncallNode>> (test.cb:7)
                        expr:
                            <<VariableNode>> (test.cb:7)
                            name: "printf"
                        args:
                            <<StringLiteralNode>> (test.cb:7)
                            value: "%d\n"
                            <<VariableNode>> (test.cb:7)
                            name: "b"
            <<WhileNode>> (test.cb:10)
            cond:
                <<BinaryOpNode>> (test.cb:10)
                operator: "<"
                left:
                    <<VariableNode>> (test.cb:10)
                    name: "b"
                right:
                    <<IntegerLiteralNode>> (test.cb:10)
                    typeNode: int
                    value: 100
            body:
                <<BlockNode>> (test.cb:10)
                variables:
                stmts:
                    <<ExprStmtNode>> (test.cb:11)
                    expr:
                        <<SuffixOpNode>> (test.cb:11)
                        operator: "++"
                        expr:
                            <<VariableNode>> (test.cb:11)
                            name: "b"
                    <<IfNode>> (test.cb:12)
                    cond:
                        <<BinaryOpNode>> (test.cb:12)
                        operator: "=="
                        left:
                            <<VariableNode>> (test.cb:12)
                            name: "b"
                        right:
                            <<IntegerLiteralNode>> (test.cb:12)
                            typeNode: int
                            value: 50
                    thenBody:
                        <<BreakNode>> (test.cb:13)
                    elseBody:
                        <<ContinueNode>> (test.cb:15)
    <<DefinedFunction>> (test.cb:20)
    name: "main"
    isPrivate: false
    params:
        parameters:
            <<CBCParameter>> (test.cb:20)
            name: "argc"
            typeNode: int
            <<CBCParameter>> (test.cb:20)
            name: "argv"
            typeNode: char**
    body:
        <<BlockNode>> (test.cb:20)
        variables:
        stmts:

中间代码:

variables:
functions:
    <<DefinedFunction>> (test.cb:3)
    name: fun
    isPrivate: false
    type: void(int, char)
    body:
        <<CJump>> (test.cb:4)
        cond:
            <<Bin>>
            type: INT32
            op: EQ
            left:
                <<Var>>
                type: INT32
                entity: a
            right:
                <<Int>>
                type: INT32
                value: 5
        thenLabel: 26f0a63f
        elseLabel: 4361bd48
        <<LabelStmt>> (null)
        label: 26f0a63f
        <<ExprStmt>> (test.cb:5)
        expr:
            <<Call>>
            type: INT32
            expr:
                <<Addr>>
                type: INT32
                entity: printf
            args:
                <<Str>>
                type: INT32
                entry: net.loveruby.cflat.entity.ConstantEntry@53bd815b
                <<Var>>
                type: INT32
                entity: a
        <<Jump>> (null)
        label: 2401f4c3
        <<LabelStmt>> (null)
        label: 4361bd48
        <<ExprStmt>> (test.cb:7)
        expr:
            <<Call>>
            type: INT32
            expr:
                <<Addr>>
                type: INT32
                entity: printf
            args:
                <<Str>>
                type: INT32
                entry: net.loveruby.cflat.entity.ConstantEntry@53bd815b
                <<Uni>>
                type: INT32
                op: S_CAST
                expr:
                    <<Var>>
                    type: INT8
                    entity: b
        <<LabelStmt>> (null)
        label: 2401f4c3
        <<LabelStmt>> (null)

        //WhileNode 的 begLabel
        label: 7637f22

        <<CJump>> (test.cb:10)
        cond:
            <<Bin>>
            type: INT32
            op: S_LT
            left:
                <<Var>>
                type: INT8
                entity: b
            right:
                <<Int>>
                type: INT32
                value: 100
        thenLabel: 4926097b
        elseLabel: 762efe5d
        <<LabelStmt>> (null)
        label: 4926097b
        <<Assign>> (test.cb:11)
        lhs:
            <<Addr>>
            type: INT32
            entity: b
        rhs:
            <<Bin>>
            type: INT8
            op: ADD
            left:
                <<Var>>
                type: INT8
                entity: b
            right:
                <<Int>>
                type: INT32
                value: 1
        <<CJump>> (test.cb:12)
        cond:
            <<Bin>>
            type: INT32
            op: EQ
            left:
                <<Var>>
                type: INT8
                entity: b
            right:
                <<Int>>
                type: INT32
                value: 50
        thenLabel: 5d22bbb7
        elseLabel: 41a4555e
        <<LabelStmt>> (null)

        // 正确的话就jump break 退出到endLabel
        label: 5d22bbb7
        <<Jump>> (test.cb:13)
        label: 762efe5d

        <<Jump>> (null)
        label: 3830f1c0
        <<LabelStmt>> (null)

        //else 就跳回到begLabel
        label: 41a4555e
        <<Jump>> (test.cb:15)
        label: 7637f22

        <<LabelStmt>> (null)
        label: 3830f1c0
        <<Jump>> (null)
        label: 7637f22

        <<LabelStmt>> (null)
        //endLabel
        label: 762efe5d
    <<DefinedFunction>> (test.cb:20)
    name: main
    isPrivate: false
    type: int(int, char**)
    body:

明显,为了达成中间代码贴近于最后的汇编代码的目的,很多抽象语法树中的节点,比如IfNode都被处理成为了CJump(有条件跳转), Jump(无条件跳转)这样的中间代码节点,这些节点之间的跳转关系是通过条件表达式cond和通过十六进制字符串构成的label确定的。因为WhileNode和IfNode的逻辑比较相似,而且解析他们的算法已经在Part2中讲过了,所以这里只看一下IfNode的Visitor的处理逻辑:

public Void visit(IfNode node) {
        Label thenLabel = new Label();
        Label elseLabel = new Label();
        Label endLabel = new Label();

        Expr cond = transformExpr(node.cond());
        if (node.elseBody() == null) {
            cjump(node.location(), cond, thenLabel, endLabel);
            label(thenLabel);
            transformStmt(node.thenBody());
        } else {
            cjump(node.location(), cond, thenLabel, elseLabel);
            label(thenLabel);
            transformStmt(node.thenBody());
            jump(endLabel);
            label(elseLabel);
            transformStmt(node.elseBody());
        }
        label(endLabel);
        return null;
    }

首先IfNode会判断是否有elseBody然后进行不同的处理方式。不论是调用cjump()还是label(),最终的目的都是向保存了语句结果的List<Stmt>中存放作为中间代码节点的类。

举cjump()为例:

private void cjump(Location loc, Expr cond, Label thenLabel, Label elseLabel) {
        stmts.add(new CJump(loc, cond, thenLabel, elseLabel));
    }

插入了一个CJump类。

中间代码语法树输出节点的过程

像CJump这样的中间代码树的遍历是由下一步的汇编代码生成器(CodeGenerator,这个类实现了IRVisitor)进行遍历的。我们可以发现在上文中的中间代码跳转Label还通过了16进制字符串来标识跳转到了具体的节点。这个16进制的字符串其实就是节点的hashCode():
CJump#_dump()

protected void _dump(Dumper d) {
        d.printMember("cond", cond);
        d.printMember("thenLabel", thenLabel);
        d.printMember("elseLabel", elseLabel);
    }

Dumper#printMember()

public void printMember(String name, Label memb) {
        printPair(name, Integer.toHexString(memb.hashCode()));
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349