美文网首页程序员
《自制编译器》函数体和流程控制语句的编译过程

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

作者: kolibreath | 来源:发表于2020-09-01 15:25 被阅读0次

前言

在自制编译器的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()));
    }

相关文章

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

    前言 在自制编译器的Part1和Part2中,只想说明一下编译器的实现的大体逻辑和有趣的、值得探讨地方,不想对代码...

  • Java核心技术 - Java基础语法

    1.2.1 流程控制语句的介绍 1.2.2 Java 编译器执行流程 1.2.3 if 分支结构 if……else...

  • 2019-01-28 内联函数

    通过inline声明,编译器首先在函数调用处使用函数体本身语句替换了函数调用语句,然后编译替换后的代码。因此,通过...

  • SQL存储过程和函数(2)

    存储过程和函数中可以使用流程控制来控制语句的执行。MySQL中可以使用IF语句、CASE语句、LOOP语句、LEA...

  • sql server 存储过程

    存储过程(procedure)是 sql 语句和流程控制语句的预编译集合。 它的作用就是具封装代码,被外部程序调用...

  • c语言入门套路

    基本数据类型 字符型 整数型 数组 结构体 指针 流程控制 分支语句 循环语句 函数

  • c++ 4、函数

    函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。函数由函数头和函数体组成,函数头包含:...

  • Sql Server 存储过程

    存储过程 一组预编译的SQL语句,包含数据操作语句,逻辑控制语句和调用函数等 优点 执行速度快 允许模块化程序设计...

  • 黑猴子的家:mysql 流程控制函数

    流程控制函数 1、if函数 2、case语句一 案例 3、case语句二 案例:查询部门和对应的级别

  • # shell流程控制语句

    shell流程控制语句 任何编程语言都离不开流程控制语句,其实编程基本上就是掌握了流程控制语句,然后加上函数(或者...

网友评论

    本文标题:《自制编译器》函数体和流程控制语句的编译过程

    本文链接:https://www.haomeiwen.com/subject/fhhusktx.html