访问者模式和编译器代码生成,如何处理赋值?

时间:2016-04-22 18:25:53

标签: design-patterns compilation compiler-construction code-generation visitor-pattern

对于我的编程语言中的代码生成,我使用的是访问者模式,我想找到一种更好的方法来处理赋值语句。

我的虚拟机是基于注册的,每个表达式节点只访问一个寄存器编号到全局堆栈中,所以当我访问二进制表达式节点时,我会执行如下代码:

static void visit_binary_expr (gvisitor_t *self, gnode_binary_expr_t *node) {
    DECLARE_CODE();

    bool is_assignment = (node->op == TOK_OP_ASSIGN);
    if (is_assignment) {
        // assignment is right associative
        visit(node->right);
        visit(node->left);
    } else {
        // visiting binary operation from left to right
        visit(node->left);
        visit(node->right);
    }

    if (!is_assignment) {
        uint32_t r3 = ircode_register_pop(code);
        uint32_t r2 = ircode_register_pop(code);
        uint32_t r1 = ircode_register_push_temp(code);

        opcode_t op = token2opcode(node->op);
        ircode_add(code, op, r1, r2, r3);
    }
}

使用此代码,我可以处理如下指令:a + b 假设寄存器1中的变量a和寄存器2中的变量b生成的代码为:

ADD 3 1 2

问题是分配需要一组不同的指令,并且只有堆栈上的寄存器号是不够的。例如,为了访问(读取)全局变量,我应该使用GLOAD指令,而在存储(写入)全局变量时,我应该使用GSTORE指令。

我目前正在通过在每个节点中存储一个布尔值is_assignment值来解决这个问题,所以我可以递归地检查要生成哪条指令,但这需要将大量逻辑分配到每个被访问节点中,我真的想知道更多优雅的方式,只有visit_binary_expr函数可以决定生成的最佳指令是什么。

1 个答案:

答案 0 :(得分:1)

因为赋值与其他二进制操作完全不同(它具有更改左侧操作数的副作用),所以将其作为完全独立的操作进行处理是有意义的,根本不涉及二进制操作。在这种情况下,你会得到visit_assignment之类的东西,其中第二个参数类型相应。

然后,您可以避免当前代码中存在的所有检查。此外,根据您的语言允许的目标类型,分配目标的处理可以使用不同的遍历函数集,不同的访问者或具有指示正在处理目标的标志的同一访问者,而不是正则表达式。决定哪种方法更好取决于您需要生成的语言和代码。