571
社区成员
发帖
与我相关
我的任务
分享将一个简单的 C 程序汇编成 LoongArch 或 RISC-V 汇编代码,并逐步分析程序的执行过程,深入理解存储程序计算机和函数调用堆栈框架在执行过程中所起的作用。
编写 C 语言程序
#include<stdio.h>
int div(int a, int b) {
if(b == 0) return -1;
return a / b;
}
int main() {
int a = 2024;
int b = 2;
return div(a, b);
}
使用如下指令将其汇编成RISC-V汇编代码
riscv64-linux-gnu-gcc -S -o hello.s hello.c
.file "test.c" // 文件名
.option pic // 选择代码生成选项
.text // 代码段开始
.align 1 // 数据对齐到 2 的 1 次方,也就是 1 个字节
.globl div // 全局符号定义
.type div, @function // 定义函数类型为 @function
div: // 定义函数名
addi sp,sp,-32 // 分配栈空间
sd s0,24(sp) // 保存 s0 到栈中
addi s0,sp,32 // 设置 s0 为当前栈顶地址
mv a5,a0 // 将第一个参数 a0 复制到 a5 中
mv a4,a1 // 将第二个参数 a1 复制到 a4 中
sw a5,-20(s0) // 将 a5 中的值存储在 s0 帧中的 -20 位置
mv a5,a4 // 将 a4 中的值移动到 a5 中
sw a5,-24(s0) // 将 a5 中的值存储在 s0 帧中的 -24 位置
lw a5,-24(s0) // 从 s0 帧的 -24 位置加载一个 32 位值到 a5 中
sext.w a5,a5 // 把 a5 中的 32 位值符号扩展为 64 位,并将结果存储回 a5
bne a5,zero,.L2 // 如果 a5 不等于 0,跳转到 .L2 标签
li a5,-1 // 如果 a5 等于 0,将 -1 存储到 a5 中
j .L3 // 无条件跳转到 .L3 标签
.L2:
lw a5,-20(s0) // 从 s0 帧的 -20 位置加载一个 32 位值到 a5 中
mv a4,a5 // 将 a5 中的值移动到 a4 中
lw a5,-24(s0) // 从 s0 帧的 -24 位置加载一个 32 位值到 a5 中
divw a5,a4,a5 // 将 a4 除以 a5,并将结果存储回 a5
sext.w a5,a5 // 把 a5 中的 32 位值符号扩展为 64 位,并将结果存储回 a5
.L3:
mv a0,a5 // 将 a5 中的值移动到 a0 中
ld s0,24(sp) // 从栈中恢复 s0
addi sp,sp,32 // 回收栈空间
jr ra // 返回地址
.size div, .-div // 设置 div 函数的大小
.align 1 // 数据对齐到 2 的 1 次方,也就是 1 个字节
.globl main // 全局符号定义
.type main, @function // 定义函数类型为 @function
main:
addi sp,sp,-32 // 分配栈空间
sd ra,24(sp) // 保存返回地址
sd s0,16(sp) // 保存旧的栈底指针
addi s0,sp,32 // 更新栈底指针
li a5,2024 // 将2024加载到a5寄存器中
sw a5,-24(s0) // 将a5寄存器中的值存储到栈帧中的位置-24处
li a5,2 // 将2加载到a5寄存器中
sw a5,-20(s0) // 将a5寄存器中的值存储到栈帧中的位置-20处
lw a4,-20(s0) // 将栈帧中的位置-20处的值加载到a4寄存器中
lw a5,-24(s0) // 将栈帧中的位置-24处的值加载到a5寄存器中
mv a1,a4 // 将a4寄存器中的值移动到a1寄存器中,作为div函数的第二个参数
mv a0,a5 // 将a5寄存器中的值移动到a0寄存器中,作为div函数的第一个参数
call div // 调用div函数
mv a5,a0 // 将div函数的返回值移动到a5寄存器中
mv a0,a5 // 将a5寄存器中的值移动到a0寄存器中,作为main函数的返回值
ld ra,24(sp) // 恢复返回地址
ld s0,16(sp) // 恢复旧的栈底指针
addi sp,sp,32 // 释放栈空间
jr ra // 返回到调用者
.size main, .-main // 定义main函数的大小