动手实现编译器(七)——比较运算符

上一节中,我们已经实现了全局变量的声明、赋值和计算,本节中我们将会实现计较运算符的计算。

==, !=, <, >, <=, >=

修改词法分析

首先增加新的单词类型

// 单词类型
enum
{
    T_EOF,
    T_ADD, T_SUB, T_MUL, T_DIV, T_MOD, T_EQ, T_NE, T_LT, T_GT, T_LE, T_GE,
    T_INT, T_SEM, T_PRINT, T_EQU, T_IDENT, T_KEYINT
};

我们必须区分 = 和 ==,< 和 <=,> 和 >=。因此我们将需要从输入中读取一个额外的字符,然后在不需要时将其放回原处,修改scan()代码如下:

// 扫描并返回在输入中找到的下一个单词。
// 如果标记有效则返回 1,如果没有标记则返回 0
int scan(struct token *t)
{
    int c, tokentype;
    c = skip_getchr();
    switch (c)
    {
        case EOF:   t->token = T_EOF;   return 0;
        case '+':   t->token = T_ADD;   break;
        case '-':   t->token = T_SUB;   break;
        case '*':   t->token = T_MUL;   break;
        case '/':   t->token = T_DIV;   break;
        case '%':   t->token = T_MOD;   break;
        case ';':   t->token = T_SEM;   break;
        case '=':   
                    if((c = get_nextchr()) == '=')
                    {
                        t->token = T_EQ;
                    }
                    else
                    {
                        t->token = T_EQU;
                        put_backchr(c);
                    }
                    break;
        case '!':   if((c = get_nextchr()) == '=')
                    {
                        t->token = T_NE;
                    }
                    else
                    {
                        fprintf(stderr, "Unrecognised character:%c on line %d\n", c, Line);
                        exit(1);
                    }
                    break;
        case '<':   if((c = get_nextchr()) == '=')
                    {
                        t->token = T_LE;
                    }
                    else
                    {
                        t->token = T_LT;
                        put_backchr(c);
                    }
                    break;
        case '>':   if((c = get_nextchr()) == '=')
                    {
                        t->token = T_GE;
                    }
                    else
                    {
                        t->token = T_GT;
                        put_backchr(c);
                    }
                    break;
        default:    if (isdigit(c)) 
                    {
                        t->intvalue = scan_int(c);
                        t->token = T_INT;
                        break;
                    }
                    else if(isalpha(c) || '_' == c)
                    {
                        scan_ident(c, Text, TEXTLEN);
                        if (tokentype = match_keyword(Text))
                        {
                        	t->token = tokentype;
	                        break;
	                    }
	                    t->token = T_IDENT;
                        break;
                    }
                    printf("Unrecognised character %c on line %d\n", c, Line);
                    exit(1);
    }
    return 1;
}

修改语法分析

增加AST节点类型

// AST节点类型
enum
{
    A_ADD = 1, A_SUB, A_MUL, A_DIV, A_MOD, A_EQ, A_NE, A_LT, A_GT, A_LE, A_GE,
    A_INT, A_IDENT, A_ASSIGN, A_LVIDENT
};

修改单词到AST节点的映射

// 将单词转换为AST操作
int token_op(int tokentype)
{
    if(tokentype > T_EOF && tokentype < T_INT)
    {
        return tokentype;
    }
    fprintf(stderr, "unknown token in token_op() on line %d\n", Line);
    exit(1);
}

注意到,我们简化了映射函数操作,为此,我们在前面的单词类型和AST节点类型要按一定顺序排列
修改AST节点的运算优先级

// 每个AST节点的运算符优先级
int OpPrec[] = {0, 10, 10, 20, 20, 20, 30, 30, 40, 40, 40, 40};

修改代码生成

在代码生成函数code_generator()中加入六种比较运算

// 给定AST,生成汇编代码,返回值为结果所在寄存器号
int code_generator(struct ASTnode *n, int reg)
{
    int leftreg, rightreg;
    if (n->left)    leftreg = code_generator(n->left, -1);
    if (n->right)   rightreg = code_generator(n->right, leftreg);
    switch (n->op)
    {
        case A_ADD:    return (arm_add(leftreg, rightreg));
        case A_SUB:    return (arm_sub(leftreg, rightreg));
        case A_MUL:    return (arm_mul(leftreg, rightreg));
        case A_DIV:    return (arm_div(leftreg, rightreg));
        case A_MOD:    return (arm_mod(leftreg, rightreg));
        case A_EQ:     return (arm_equal(leftreg, rightreg));
        case A_NE:     return (arm_notequal(leftreg, rightreg));
        case A_LT:     return (arm_lessthan(leftreg, rightreg));
        case A_GT:     return (arm_greaterthan(leftreg, rightreg));
        case A_LE:     return (arm_lessequal(leftreg, rightreg));
        case A_GE:     return (arm_greaterequal(leftreg, rightreg));
        case A_INT:    return (arm_load_int(n->v.intvalue));
        case A_IDENT:  return (arm_load_global(n->v.id));
        case A_LVIDENT:return (arm_stor_global(reg, n->v.id));
        case A_ASSIGN: return rightreg;
        default:    fprintf(stderr, "Unknown AST operator %d\n", n->op);
                    exit(1);
    }
}

修改汇编代码
ARM比较需要先调用CMP Rm Rn指令,进行Rm-Rn,并修改CPSR。然后我们调用对应条件的MOV{S} Rd, #<imm8>指令赋值1,再调用相反条件的MOV指令赋值0,最后对赋值寄存器进行字节到字的扩展,因为有条件MOV{S}只能赋值8位立即数。
CPSR(ARM状态寄存器)的最后四位分别是N、Z、C、V。

符号名称作用
N符号标志N=1表示运算结果为负数,N=0表示运算结果为整数
Z全0标志Z=1则表示运算结果为0,否则Z=0
C进位标志加法有进位时C=1(加法运算是数据溢出),无进位时C=0;减法有借位时C=0,无借位时C=1
V数据溢出标志加法运算结果有溢出是V=1,否则V=0

S条件如下:

助记符后缀标志含义符号
EQZ置位相等=
NEZ清零不相等
LTN不等于V带符号数小于<
GTZ清零且(N不等于V)带符号数大于>
LEZ置位或(N不等于V)带符号数小于或等于
GEN等于V带符号数大于或等于
// 比较函数:第一个寄存器减去第二个寄存器并修改CPSR
int arm_compare(int r1, int r2)
{
    fprintf(Outfile, "\tcmp\tr%d, r%d\n", r1, r2);
}

// '=='汇编函数
int arm_equal(int r1, int r2)
{
    arm_compare(r1, r2);
    fprintf(Outfile, "\tmoveq\tr%d, #1\n", r1);
    fprintf(Outfile, "\tmovne\tr%d, #0\n", r1);
    fprintf(Outfile, "\tuxtb\tr%d, r%d\n", r1, r1);
    arm_free_register(r2);
    return r1;
}

// '!='汇编函数
int arm_notequal(int r1, int r2)
{
    arm_compare(r1, r2);
    fprintf(Outfile, "\tmovne\tr%d, #1\n", r1);
    fprintf(Outfile, "\tmoveq\tr%d, #0\n", r1);
    fprintf(Outfile, "\tuxtb\tr%d, r%d\n", r1, r1);
    arm_free_register(r2);
    return r1;
}

// '<'汇编函数
int arm_lessthan(int r1, int r2)
{
    arm_compare(r1, r2);
    fprintf(Outfile, "\tmovlt\tr%d, #1\n", r1);
    fprintf(Outfile, "\tmovge\tr%d, #0\n", r1);
    fprintf(Outfile, "\tuxtb\tr%d, r%d\n", r1, r1);
    arm_free_register(r2);
    return r1;
}

// '>'汇编函数
int arm_greaterthan(int r1, int r2)
{
    arm_compare(r1, r2);
    fprintf(Outfile, "\tmovgt\tr%d, #1\n", r1);
    fprintf(Outfile, "\tmovle\tr%d, #0\n", r1);
    fprintf(Outfile, "\tuxtb\tr%d, r%d\n", r1, r1);
    arm_free_register(r2);
    return r1;
}

// '<='汇编函数
int arm_lessequal(int r1, int r2)
{
    arm_compare(r1, r2);
    fprintf(Outfile, "\tmovle\tr%d, #1\n", r1);
    fprintf(Outfile, "\tmovgt\tr%d, #0\n", r1);
    fprintf(Outfile, "\tuxtb\tr%d, r%d\n", r1, r1);
    arm_free_register(r2);
    return r1;
}

// '>='汇编函数
int arm_greaterequal(int r1, int r2)
{
    arm_compare(r1, r2);
    fprintf(Outfile, "\tmovge\tr%d, #1\n", r1);
    fprintf(Outfile, "\tmovlt\tr%d, #0\n", r1);
    fprintf(Outfile, "\tuxtb\tr%d, r%d\n", r1, r1);
    arm_free_register(r2);
    return r1;
}

结果测试

输入:

int x;
x = 1 < 1;  print x;
x = 1 < 2;  print x;
x = 2 < 1;  print x;
x = 3 <= 3; print x;
x = 3 <= 4; print x;
x = 4 <= 3; print x;
x = 4 != 5; print x;
x = 5 != 5; print x;
x = 6 == 6; print x;
x = 6 == 7; print x;
x = 7 > 7;  print x;
x = 6 > 7;  print x;
x = 7 > 6;  print x;
x = 8 >= 8; print x;
x = 8 >= 9; print x;
x = 9 >= 8; print x;

输出(out.s):

	.text
	.global __aeabi_idiv
	.section	.rodata
	.align  2
.LC0:
	.ascii  "%d\012\000"
	.text
	.align  2
	.global main
	.type   main, %function
main:
	push    {fp, lr}
	add     fp, sp, #4
	.text
	.comm	x,4,4
	mov	r4, #1
	mov	r5, #1
	cmp	r4, r5
	movlt	r4, #1
	movge	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #1
	mov	r5, #2
	cmp	r4, r5
	movlt	r4, #1
	movge	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #2
	mov	r5, #1
	cmp	r4, r5
	movlt	r4, #1
	movge	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #3
	mov	r5, #3
	cmp	r4, r5
	movle	r4, #1
	movgt	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #3
	mov	r5, #4
	cmp	r4, r5
	movle	r4, #1
	movgt	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #4
	mov	r5, #3
	cmp	r4, r5
	movle	r4, #1
	movgt	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #4
	mov	r5, #5
	cmp	r4, r5
	movne	r4, #1
	moveq	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #5
	mov	r5, #5
	cmp	r4, r5
	movne	r4, #1
	moveq	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #6
	mov	r5, #6
	cmp	r4, r5
	moveq	r4, #1
	movne	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #6
	mov	r5, #7
	cmp	r4, r5
	moveq	r4, #1
	movne	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #7
	mov	r5, #7
	cmp	r4, r5
	movgt	r4, #1
	movle	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #6
	mov	r5, #7
	cmp	r4, r5
	movgt	r4, #1
	movle	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #7
	mov	r5, #6
	cmp	r4, r5
	movgt	r4, #1
	movle	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #8
	mov	r5, #8
	cmp	r4, r5
	movge	r4, #1
	movlt	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #8
	mov	r5, #9
	cmp	r4, r5
	movge	r4, #1
	movlt	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #9
	mov	r5, #8
	cmp	r4, r5
	movge	r4, #1
	movlt	r4, #0
	uxtb	r4, r4
	ldr	r3, .L2+0
	str	r4, [r3]
	ldr	r3, .L2+0
	ldr	r4, [r3]
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov     r3, #0
	mov     r0, r3
	pop     {fp, pc}
.L3:
	.word   .LC0
	.size   main, .-main
.L2:
	.word x

输出(out):

0
1
0
1
1
0
1
0
1
0
0
0
1
1
0
1

总结

在这一节中,我们实现了六种比较运算符的运算,在下一节中,我们将利用这些比较运算符实现IF语句。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值