part4.AT&T汇编学习

本文介绍寄存器操作的基本写法、不同类型的立即数与符号常数的使用方法,以及如何在GCC中进行内联汇编。包括操作数的长度标识、内存引用格式等内容。

基本写法

寄存器

在寄存器前要加%
e.g %1

操作数顺序

左边是源,右边是目的
e.g. movl %1, %2

立即数

立即数前加 e.g.movl 0x04, %1
data = 0x04
movl $data, %1
意味着将立即数04h装入寄存器1

符号常数

直接引用
e.g. value = .long 0x12a
movl value, %1
将常数0x12a装入寄存器1

引用符号地址在符号前加符号 $
e.g. movl $value也就是将value的地址装入寄存器1

操作数的长度

操作数的长度用加在指令后的符号表示,b(byte,8-bits),w(word,16-bits),l(long,32-bits)
e.g. movb %1, %2

内存引用

displacement(base, index, scale)
base, index为任意的32-bits base和index寄存器
scale可以取值1,2,4,8,如果不指定scale值,默认值为1
e.g. -4(%1)

GCC内联ASM

将汇编嵌入到GCC中
以下内容转载自百度文库

1.基本内联汇编

e.g.
这里写图片描述
基本内联汇编的格式是
这里写图片描述
这里写图片描述
这里写图片描述

当在”Instruction List”中有多条指令的时候,你可以在一对引号中列出全部指令,也可以
将一条或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,你可以将每一
条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符(\n,大多数
情况下\n 后还要跟一个\t,其中\n 是为了换行,\t 是为了空出一个tab 宽度的空格)将它们
分开。

2.带有C/C++表达式的内联汇编

GCC 允许你通过C/C++表达式指定内联汇编中”Instrcuction List”中指令的输入和输出,
你甚至可以不关心到底使用哪个寄存器被使用,完全靠GCC 来安排和指定。这一点可以让
程序员避免去考虑有限的寄存器的使用,也可以提高目标代码的效率。

带有C/C++表达式的内联汇编格式为:
这里写图片描述
这里写图片描述

补充:转载自ibm
这里写图片描述
这里写图片描述
ok~好像差不多了~阅读源代码估计没有障碍了吧~(不是flag,不是flag,不是flag)

### 基于AST生成SysY语言编译器并输出AT&T汇编代码的方法 SysY语言的编译器可以通过对抽象语法树(Abstract Syntax Tree, AST)进行遍历和处理,将高级语言的语义转化为低级的机器指令。以下是实现这一目标的核心方法: #### 1. AST的构建与遍历 在词法分析和语法分析之后,生成的AST表示了程序的结构化形式。对于SysY语言,常见的节点类型包括变量声明、赋值语句、函数定义、条件语句等。通过深度优先搜索(DFS)或广度优先搜索(BFS)遍历AST,可以逐个处理每个节点。 - **函数定义**:当遇到`A_FUNCTION`类型的节点时,需要生成对应的函数前言和后言。例如,在引用中提到的`arm_function_preamble`和`arm_function_postamble`[^1],可以分别对应于AT&T汇编中的`.globl`声明和函数栈帧的初始化/销毁。 ```c case A_FUNCTION: printf(".globl %s\n", Tsym[n->v.id].name); printf("%s:\n", Tsym[n->v.id].name); printf("pushl %%ebp\n"); printf("movl %%esp, %%ebp\n"); code_generator(n->left, NOREG, n->op); // 处理函数体 printf("popl %%ebp\n"); printf("ret\n"); return NOREG; ``` #### 2. 变量与寄存器分配 在生成汇编代码时,需要考虑变量的存储位置(寄存器或栈内存)。SysY语言中的变量通常分为全局变量和局部变量。全局变量可以直接映射到数据段,而局部变量则需要通过栈指针偏移量访问。 - **寄存器分配**:使用寄存器来存储频繁使用的变量可以显著提高性能。例如,`%eax`、`%ebx`等通用寄存器可以在表达式求值过程中临时存储中间结果。 ```c if (is_register_variable(variable)) { printf("movl $%d, %%eax\n", variable->value); } else { printf("movl %d(%%ebp), %%eax\n", variable->stack_offset); } ``` #### 3. 表达式求值 SysY语言中的算术表达式可以通过递归下降的方式处理。对于二元操作符(如加法、减法),需要先计算左右子表达式的值,并将其存储在寄存器中,然后执行相应的指令。 - **加法示例**: ```c case A_ADD: code_generator(n->left, reg1, n->op); // 左子表达式 code_generator(n->right, reg2, n->op); // 右子表达式 printf("addl %%eax, %%ebx\n"); // 将寄存器中的结果相加 return reg1; ``` #### 4. 条件分支与循环 条件分支和循环结构需要生成跳转指令。SysY语言中的`if`语句和`for`循环可以通过标签和跳转指令实现。 - **条件分支**: ```c case A_IF: code_generator(n->condition, NOREG, n->op); // 生成条件表达式的代码 printf("cmpl $0, %%eax\n"); printf("je .Lelse%d\n", label_count); // 跳转到else部分 code_generator(n->then_part, NOREG, n->op); // then部分 printf("jmp .Lend%d\n", label_count); printf(".Lelse%d:\n", label_count++); code_generator(n->else_part, NOREG, n->op); // else部分 printf(".Lend%d:\n", label_count++); return NOREG; ``` #### 5. 函数调用 函数调用需要将参数压入栈中,并跳转到目标函数的地址。返回值通常存储在`%eax`寄存器中。 - **函数调用示例**: ```c case A_CALL: for (int i = 0; i < n->args.count; i++) { // 遍历参数列表 code_generator(n->args[i], NOREG, n->op); printf("pushl %%eax\n"); // 将参数压栈 } printf("call %s\n", Tsym[n->v.id].name); // 调用函数 printf("addl $%d, %%esp\n", n->args.count * 4); // 恢复栈指针 return NOREG; ``` ### 注意事项 - 确保生成的AT&T汇编代码符合目标平台的调用约定(Calling Convention)。 - 对于复杂的数据类型(如数组、结构体),需要额外处理内存布局和访问方式。 - 在生成代码的过程中,注意优化冗余指令以提高运行效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值