求斐波那契数列前十位,x86、riscv32、mips架构下的c语言交叉编译

本人菜鸡一个,在csdn上各位大佬的blog 指点下磕磕绊绊完成了computer architecture的实验,前后历时半个来月,无以为报,献上自己的实验报告,由于我大二下上的网课一直在摸鱼...许多知识点掌握的不是很牢,其中可能会有些错误,也有很多不详细的地方,欢迎大佬指正,也希望能对后人有点帮助,毕竟我一开始做这玩意毫无头绪浪费了不少时间

Write a Fibonacci.c storing the first ten sequence numbers. Run the Fibonacci program in your computer, analyze the assembly code for the Fibonacci.c.

由于没有输出函数,生成.EXE时临时加入system(”pause”)以证明运行成功

 

c语言编译为x86汇编语言

 

汇编完整代码如下:

  .file "Fibonacci.c"

  .def  ___main;  .scl  2;  .type 32; .endef

  .text

  .globl  _main

  .def  _main;  .scl  2;  .type 32; .endef

_main:

LFB25:

  .cfi_startproc

  pushl %ebp

  .cfi_def_cfa_offset 8

  .cfi_offset 5, -8

  movl  %esp, %ebp

  .cfi_def_cfa_register 5

  andl  $-16, %esp

  subl  $48, %esp

  call  ___main

  movl  $10, 44(%esp)

  movl  $1, 8(%esp)

  movl  8(%esp), %eax

  movl  %eax, 4(%esp)

  movl  $2, 44(%esp)

  jmp L2

L3:

  movl  44(%esp), %eax

  subl  $1, %eax

  movl  4(%esp,%eax,4), %edx

  movl  44(%esp), %eax

  subl  $2, %eax

  movl  4(%esp,%eax,4), %eax

  addl  %eax, %edx

  movl  44(%esp), %eax

  movl  %edx, 4(%esp,%eax,4)

  addl  $1, 44(%esp)

L2:

  cmpl  $9, 44(%esp)

  jle L3

  movl  $0, %eax

  leave

  .cfi_restore 5

  .cfi_def_cfa 4, 4

  ret

  .cfi_endproc

LFE25:

  .ident  "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"



汇编代码分析

我的cpu为amd的cpu,故格式与intel不同,汇编指令为AT&T格式

第一部分:声明

  .file "Fibonacci.c"

  .def  ___main;  .scl  2;  .type 32; .endef

  .text

  .globl  _main

  .def  _main;  .scl  2;  .type 32; .endef

为对整个程序中函数的声明定义,无实际意义

第二部分:main函数

_main:

LFB25:

  .cfi_startproc  //用在函数开头作为进入主函数的标志

  pushl %ebp     //存入栈底

  .cfi_def_cfa_offset 8

  .cfi_offset 5, -8   //执行完pushl %ebp后SP与CFA偏了8字节(4字节return address,4字节ebp)

  movl  %esp, %ebp

  .cfi_def_cfa_register 5//寄存器不变,偏移量变为5

  andl  $-16, %esp

  //将寄存器里存的4字节地址与0xfffffff0按位&,并将结果存在寄存器%esp中,调整栈指针的地址

  subl  $48, %esp//申请48个字节

  call  ___main

  movl  $10, 44(%esp)//int i=10;

  movl  $1, 8(%esp)//Fibonacci[0]=1;

  movl  8(%esp), %eax//eax暂存数据

  movl  %eax, 4(%esp)//Fibonacci[1]=1;

  movl  $2, 44(%esp)//i=2;

  jmp L2

第三部分:L3(对应for循环)

L3:

  movl  44(%esp), %eax

  subl  $1, %eax

  movl  4(%esp,%eax,4), %edx  //(sep+4*eax+4)放入edx。

  //由该步骤推出上两步作用在于用eax寄存器设定可变偏移量

  movl  44(%esp), %eax

  subl  $2, %eax

  movl  4(%esp,%eax,4), %eax

  //此三步同理

  addl  %eax, %edx

  //L2中上述步骤实现Fibonacci[i]=Fibonacci[i-1]+Fibonacci[i-2];

  movl  44(%esp), %eax//eax重新置入i=2

  movl  %edx, 4(%esp,%eax,4)//edx放入(sep+4*eax+4)

  addl  $1, 44(%esp)//i++;

第四部分:结束与循环判断

L2:

  cmpl  $9, 44(%esp)

  jle L3//44(%esp)处数据小于等于9则跳转回L2,对应for函数

  movl  $0, %eax//结束eax置0

  leave

  .cfi_restore 5

  .cfi_def_cfa 4, 4

  ret

  .cfi_endproc



Observe the memory areas in bytes for the Fibonacci numbers given by the computer where the Fibonacci program running. Analyze what you have seen.

 

Int Fibonacci[10];

EIP寄存器的值增加,增加大小的就是读取指令的字节大小

 

Fibonacci[0]=Fibonacci[1]=1;

movl  %esp, %ebp

  .cfi_def_cfa_register 5//寄存器不变,偏移量变为5

  andl  $-16, %esp

  //将寄存器里存的4字节地址与0xfffffff0按位&,并将结果存在寄存器%esp中,调整栈指针的地址

  subl  $48, %esp//申请48个字节

  call  ___main

  movl  $10, 44(%esp)//int i=10;

  movl  $1, 8(%esp)//Fibonacci[0]=1;

  movl  8(%esp), %eax//eax暂存数据

  movl  %eax, 4(%esp)//Fibonacci[1]=1;

  movl  $2, 44(%esp)//i=2;

  jmp L2

For循环

 

For循环中eax寄存器存放变量i,edx为数组中第i个数,故每轮循环中eax+1,edx呈斐波那契数列。

 

L3:

  movl  44(%esp), %eax

  subl  $1, %eax

  movl  4(%esp,%eax,4), %edx  //(sep+4*eax+4)放入edx。

  //由该步骤推出上两步作用在于用eax寄存器设定可变偏移量

  movl  44(%esp), %eax

  subl  $2, %eax

  movl  4(%esp,%eax,4), %eax

  //此三步同理

  addl  %eax, %edx

  //L2中上述步骤实现Fibonacci[i]=Fibonacci[i-1]+Fibonacci[i-2];

  movl  44(%esp), %eax//eax重新置入i=2

  movl  %edx, 4(%esp,%eax,4)//edx放入(sep+4*eax+4)

  addl  $1, 44(%esp)//i++;

结束

 

L2:

  cmpl  $9, 44(%esp)

  jle L3//44(%esp)处数据小于等于9则跳转回L2,对应for函数

  movl  $0, %eax//结束eax置0

  leave

观察可知,代码运行中寄存器状态变化符合上述对汇编代码观察得到的结论

Cross compile the Fibonacci.c for RISC-V targets, or write the assembly code of Fibonacci.c in RISC-V assembly language, and run it in RISC-V online simulator (www.kvakil.me/venus/). Observe the memory areas in bytes for the Fibonacci numbers given by the RISC-V simulator.

交叉编译平台:Compiler Explorer

 

交叉编译结果:

Machine Code

Basic Code

Original Code

0xfc010113

addi x2 x2 -64

addi sp,sp,-64

0x02812e23

sw x8 60(x2)

sw s0,60(sp)

0x04010413

addi x8 x2 64

addi s0,sp,64

0x00100793

addi x15 x0 1

li a5,1

0xfcf42423

sw x15 -56(x8)

sw a5,-56(s0)

0xfc842783

lw x15 -56(x8)

lw a5,-56(s0)

0xfcf42223

sw x15 -60(x8)

sw a5,-60(s0)

0x00200793

addi x15 x0 2

li a5,2

0xfef42623

sw x15 -20(x8)

sw a5,-20(s0)

0x0580006f

jal x0 88

j .L2

0xfec42783

lw x15 -20(x8)

lw a5,-20(s0)

0xfff78793

addi x15 x15 -1

addi a5,a5,-1

0x00279793

slli x15 x15 2

slli a5,a5,2

0xff078793

addi x15 x15 -16

addi a5,a5,-16

0x008787b3

add x15 x15 x8

add a5,a5,s0

0xfd47a703

lw x14 -44(x15)

lw a4,-44(a5)

0xfec42783

lw x15 -20(x8)

lw a5,-20(s0)

0xffe78793

addi x15 x15 -2

addi a5,a5,-2

0x00279793

slli x15 x15 2

slli a5,a5,2

0xff078793

addi x15 x15 -16

addi a5,a5,-16

0x008787b3

add x15 x15 x8

add a5,a5,s0

0xfd47a783

lw x15 -44(x15)

lw a5,-44(a5)

0x00f70733

add x14 x14 x15

add a4,a4,a5

0xfec42783

lw x15 -20(x8)

lw a5,-20(s0)

0x00279793

slli x15 x15 2

slli a5,a5,2

0xff078793

addi x15 x15 -16

addi a5,a5,-16

0x008787b3

add x15 x15 x8

add a5,a5,s0

0xfce7aa23

sw x14 -44(x15)

sw a4,-44(a5)

0xfec42783

lw x15 -20(x8)

lw a5,-20(s0)

0x00178793

addi x15 x15 1

addi a5,a5,1

0xfef42623

sw x15 -20(x8)

sw a5,-20(s0)

0xfec42703

lw x14 -20(x8)

lw a4,-20(s0)

0x00900793

addi x15 x0 9

li a5,9

0xfae7d2e3

bge x15 x14 -92

ble a4,a5,.L3

0x00000793

addi x15 x0 0

li a5,0

0x00078513

addi x10 x15 0

mv a0,a5

0x03c12403

lw x8 60(x2)

lw s0,60(sp)

0x04010113

addi x2 x2 64

addi sp,sp,64

0x00008067

jalr x0 x1 0

jr ra

运行结果及代码分析(运行平台:www.kvakil.me/venus/

main:

        addi    sp,sp,-64     #sp=sp-64

        sw      s0,60(sp)     #将s0中存放数据存入地址为[(sp+60)]的主存中

        addi    s0,sp,64      #s0=sp+64

        li      a5,10         #a5=10

        sw      a5,-20(s0)    #将a5(#10)中存放数据存入地址为[(s0-60)]的主存中: 对应i=10

        li      a5,1          #a5=1

        sw      a5,-56(s0)    #将a5(#1)中存放数据存入地址为[(s0-56)]的主存中

        lw      a5,-56(s0)    #从主存地址为[(s0-56)]处读取数据写入a5

        sw      a5,-60(s0)    #将a5中存放数据存入地址为[(s0-60)]的主存中

        #以上三步对应Fibonacci[0]=Fibonacci[1]=1;

        li      a5,2          #a5=2

        sw      a5,-20(s0)    #将a5(#2)中存放数据存入地址为[(s0-20)]的主存中: 对应i=2

        j       .L2

.L3:

        lw      a5,-20(s0)    #从主存地址为[s0-20]处读取数据写入a5(i的值)

        addi    a5,a5,-1      #a5=a5+(-1)

        slli    a5,a5,2       #a5左移2位

        addi    a5,a5,-16     #a5=a5+(-16)

        add     a5,a5,s0      #a5=a5+s0

        lw      a4,-44(a5)    #从主存地址为[(a5-44)]处读取数据写入a4

        lw      a5,-20(s0)    #从主存地址为[(s0-20)]处读取数据写入a5

        addi    a5,a5,-2      #a5=a5+(-2)

        slli    a5,a5,2       #a5左移两位

        addi    a5,a5,-16     #a5=a5+(-16)

        add     a5,a5,s0      #a5=a5+s0

        lw      a5,-44(a5)    #从主存地址为[(a5-44)]处读取数据写入a4

        add     a4,a4,a5      #a4=a4+a5

        lw      a5,-20(s0)    #从主存地址为[(s0-20)]处读取数据写入a5

        slli    a5,a5,2       #a5左移两位

        addi    a5,a5,-16     #a5=a5+(-16)

        add     a5,a5,s0      #a5=a5+s0

        sw      a4,-44(a5)    #将a4中存储的数据写入到主存地址[(a5-44)]处

        #写入Fibonacci[i]

        lw      a5,-20(s0)    #从主存地址为[(s0-20)]处读取数据写入a5

        addi    a5,a5,1       #a5++

        sw      a5,-20(s0)    #将a5中存储的数据写入到主存地址[(s0-20)]处

        #即s0-20暂存i值

.L2:

        lw      a4,-20(s0)    #从主存地址为[(s0-20)]处读取数据写入a4

        li      a5,9          #将9存入a5

        ble     a4,a5,.L3     #if(a4 <= a5){goto L3;}

        #以上两步为for循环结束条件,a4为i的数值,a5为结束条件,即i<=9,即i<10

        li      a5,0          #结束a5置0

        mv      a0,a5         ##结束a5置0

        lw      s0,60(sp)

        addi    sp,sp,64

        jr      ra

 

斐波那契数列存在内存0x7fffffb4——0x7fffffd8中

具体运行过程录制为视频“riscv编译运行斐波那契数列.mp4”存于压缩包中

Cross compile the Fibonacci.c for MIPS targets, or write the assembly code of Fibonacci.c in MIPS assembly language, and run it in Mars simulator. Observe the memory areas in bytes for the Fibonacci numbers given by the Mars simulator.

交叉编译平台:Compiler Explorer

 

交叉编译代码:

main:

        addiu   $sp,$sp,-64

        sw      $fp,60($sp)

        move    $fp,$sp

        li      $2,1                        # 0x1

        sw      $2,16($fp)

        lw      $2,16($fp)

        nop                               #空指令

        sw      $2,12($fp)

        li      $2,2                        # 0x2

        sw      $2,8($fp)

        b       $L2

        nop                               #空指令



$L3:

        lw      $2,8($fp)

        nop

        addiu   $2,$2,-1

        sll     $2,$2,2

        addiu   $3,$fp,8

        addu    $2,$3,$2

        lw      $3,4($2)

        lw      $2,8($fp)

        nop

        addiu   $2,$2,-2

        sll     $2,$2,2

        addiu   $4,$fp,8

        addu    $2,$4,$2

        lw      $2,4($2)

        nop

        addu    $3,$3,$2

        lw      $2,8($fp)

        nop

        sll     $2,$2,2

        addiu   $4,$fp,8

        addu    $2,$4,$2

        sw      $3,4($2)

        lw      $2,8($fp)

        nop

        addiu   $2,$2,1

        sw      $2,8($fp)

$L2:

        lw      $2,8($fp)

        nop

        slti     $2,$2,10

        bne     $2,$0,$L3

        nop



        move    $2,$0

        move    $sp,$fp

        lw      $fp,60($sp)

        addiu   $sp,$sp,64

        jr      $31

        nop

代码分析

mips整体与riscv结构类似,下图为两者交叉编译结果比较

 

显然,两者主要区别在于指令集中指令用法不同,但由于两者交叉编译均为32位且均为精简指令集,整体代码思路及结构均相似。通过该程序可以了解到以下代码区别:

Riscv

Mips

运算指令

addi    s0,sp,64

addiu   $sp,$sp,-64

数据传输指令

sw     a5,-20(s0)

sw      $2,16($fp)

移位运算

slli    a5,a5,2

sll     $2,$2,2

跳转指令

J .L2

b $L2

条件分支指令

ble     a4,a5,.L3

bne     $2,$0,$L3

总结:

Riscv的指令集中涉及寄存器的指令直接记以寄存器的名称,涉及Mips的指令集中以寄存器代码前加‘$’表示,sp、fp等栈指针前直接加‘$’。

运行结果见视频      

write the assembly code of Fibonacci.c in MIPS assembly language, and run it in WinMIPS64 simulator. Observe the memory areas in bytes for the Fibonacci numbers given by the WinMIPS simulator.

参考文章:

WinMIPS64工具进行MIPS指令集实验(一)_SweeNeil的博客-CSDN博客

WinMIps64指令集实验_Ryan-S的博客-CSDN博客_winmips64

MIPS64寄存器与指令集_Wo_der的博客-CSDN博客_mips64 寄存器

WinMIPS64工具进行MIPS指令集实验(二)_SweeNeil的博客-CSDN博客

代码

.data

a: .word 1,1;存入初始数据



.text

;initialize registers

daddi r1,r0,a



daddi r4,r0,10;循环次数

Loop:

  lw r5,0(r1)

  lw r6,8(r1)

  dadd r8,r5,r6 ; a[i]=a[i-1]+a[i-2]

  sw r8,16(r1) ; store value in a[i]

  daddi r1,r1,8 ; increment memory pointers

  daddi r4,r4,-1 ; i--

  bnez r4,Loop

end: halt

参考博客上进行a[i]=a[i]+b[i]+c[i]运算的程序,自己编写计算斐波那契数列前十位代码,与之前最大的不同的是winmips64须有程序段的声明,结束使用halt表示结束

       该代码先存入初始的两个值1,1,然后利用我使用lw r5,0(r1),lw r6,8(r1)始终读取两个数据放入r5,r6中,再使r8=r5+r6,将r8存入r1偏移16位的地址。以上实现a[i]=a[i-1]+a[i-2]。而后再利用daddi r1,r1,8实现指针偏移;

daddi r4,r4,-1和bnez r4,Loop实现计数判断并循环

实验过程

Asm.exe检验编写结果

 

打开文件

 

运行结果

 

过程分析

 

该图为指令的流水线,红色部分为正在执行的指令,黄色为取指过程,蓝色为译码过程,绿色为访存过程,粉色为写回过程。

以上图为例,dadd r8,r5,r6指令正在执行,上一条执行的指令lw r6,8(r1)正在访存,再上一条指令lw r5,0(r1)正在写回数据,而与此对应的,register模块中R5寄存器高亮,表示正在写入。

再往下看,它的下一条指令sw r8,16(r1)正在译码,而daddi r1,r1,8,正在进行取指。

Conclusion

       这次实验前前后后做了半个来月,磕磕绊绊看着网上的blog一边学一边操作,中间遇到过不少问题,也解决了不少问题,还是简单记录一下

       第一个大门槛是.c文件交叉编译为riscv指令的.s文件,交叉编译的结果在venus网站上跑一直会显示伪指令不识别,最后咨询老师了解到这个网站支持的是32位的格式,于是重新编译为32位的riscv指令,最终成功运行。

       其次便是与riscv和mips之间的区别相比,和x86的指令之间格式差距巨大,参考指令集手册,才勉强弄懂了指令含义。

       不过感觉其实是越往后越简单,精简指令集大多都是相通的,只有指令格式在细节略有区别,做到winMIPS64时交叉编译的无法正确运行,我也就干脆放弃了交叉编译的方法(后来才看到是让自己写,还好还好),在网上简单查了几篇blog,弄懂了指令的格式,没费多长时间就完成了最后一个实验。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

younger-_-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值