CPW Part 16 (B): Equality and Relational Expressions in the Grammar

For adapting the implementation of loadExpr() it might be convenient to have two internal functions:

  • Function makeCondJmp() to translate values of type enum ExprKind to values of type enum GenOp.

  • For equality or relational expression nodes function condJmpExpr() can be used to first generate assembly code that compares the values of the nodes and after that a conditional jump instruction. Like in genCondJmp() it should be possible to generate a conditional jumps that takes place if the condition is either true or false.

This leads to the following structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
static enum GenOp
makeCondJmp(enum ExprKind kind)
{
    /* ... */
}

static void
condJmpExpr(const struct Expr *expr, GenReg dest, const char *trueLabel,
            const char *falseLabel)
{
    /* ... */
}

void
loadExpr(const struct Expr *expr, GenReg dest)
{
    /* ... */
}

If you want the challenge then stop reading here ;-)

More About: loadExpr()

The switch statement now also has to handle the new expression kinds (EK_EQUAL, EK_NOT_EQUAL, etc.). For all the same code can be used. We need two labels. For example, one to mark the instruction for loading 1 into the destination register, and another for marking the end of the generated assembly block. With condJmpExpr() code for comparing the values of the child nodes can be generated and also the proper conditional jump instruction. So we basically the new cases in the switch simply can use this code block:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
    const char *loadOne = genGetLabel();
    const char *done = genGetLabel();
    
    condJmpExpr(expr, dest, loadOne, 0);
    genLoadUInt(0, dest);
    genJmp(done);
    genLabelDef(loadOne);
    genLoadUInt(1, dest);
    genLabelDef(done);
}

More About: condJmpExpr()

For now this function will only deal with nodes where the expression kind is EK_EQUAL, EK_NOT_EQUAL, ..., EK_LESS_EQUAL. The left child gets loaded into the destination register. If the right child is constant code for the comparison can be generated by genOp2i with GEN_CMP_I. Otherwise code needs to be generated to load the value into a temporary register. Then genOp2r with GEN_CMP_R can be used to generate code for the comparison. After comparing the values code for the conditional jump can be generated:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
    const struct Expr *left = expr->binary.left;
    const struct Expr *right = expr->binary.right;

    loadExpr(left, dest);
    if (isConstExpr(right)) {
        uint64_t rVal = right->primary.literal.uint;
        genOp2i(GEN_CMP_I, rVal, dest);
    } else {
        GenReg tmp = genGetReg();
        loadExpr(right, tmp);
        genOp2r(GEN_CMP_R, tmp, dest);
        genUngetReg(tmp);
    }
    genCondJmp(makeCondJmp(expr->kind), trueLabel, falseLabel);
}

Printing Expression Trees

Of course printExprNode() has to handle all new expression kinds.