7、ARM_栈

概述

什么是栈:

栈的本质就是一段内存,程序运行时用于保存一些临时数据,如局部变量、函数的参数、返回值、以及程序跳转时需要保护的寄存器等

什么是压栈和出栈:

压栈就是把数据写入栈,就是写内存。

出栈就是把栈中的数据读出来,就是读内存。

什么是增栈和减栈:

增栈和减栈是对于压栈过程中的地址偏移方向的分类,增栈就是新的数据向高地址存,减栈就是新的数据向低地址存。

在下图中,DATA1存放到SP后,下一个数据DATA2存在高地址位置,这就是增栈;下一个数据DATA2存在低地址位置,这就是减栈

增栈与减栈的压栈和出栈过程中的SP指针偏移关系如下:

|------|----------|----------|
| 栈的分类 | 压栈 | 出栈 |
| 增栈 | SP向高地址偏移 | SP向低地址偏移 |
| 减栈 | SP向低地址偏移 | SP向高地址偏移 |

什么是满栈和空栈:

满栈与空栈是对于压栈过程中SP指针指向的位置是否有数据的分类,满栈就是SP指向的空间中就是栈中的数据,空栈就是SP指向的空间是一个没有数据的空间。

在下图中,当DATA2想要压栈时,SP指向的位置是DATA1的位置,这就是满栈;SP指向的位置是DATA1的下一个地址的位置,这就是空栈。究竟下一个地址是高地址还是低地址,这与增栈和减栈有关。

满栈与空栈的压栈过程中SP移动的过程如下:

|------|-----------|-----------|
| 栈的分类 | 压栈 | 出栈 |
| 满栈 | SP先偏移,再压栈 | 先出栈,再SP偏移 |
| 空栈 | 先压栈,再SP偏移 | SP先偏移,再出栈 |

栈的分类总结:

栈的分类就是 "增栈与满栈" 和 "满栈与空栈" 这两者的结合:空增EA、空减ED、满增FA、满减FD

其中:E代表empty、F代表full、A代表ascendant、D代表descendant。ARM中常用FD的栈

栈的类型与压栈出栈(STM、LDM)指令的关系如下:

|----------|-----------|-----------|
| 栈的分类 | 压栈 | 出栈 |
| 空增EA | STMIA | LDMDB |
| 空减ED | STMDA | LDMIB |
| 满增FA | STMIB | LDMDA |
| 满减FD | STMDB | LDMIA |

栈的类型与专门后缀的压栈出栈(STM、LDM)指令的关系如下:

|----------|-----------|-----------|
| 栈的分类 | 压栈 | 出栈 |
| 空增EA | STMEA | LDMEA |
| 空减ED | STMED | LDMED |
| 满增FA | STMFA | LDMFA |
| 满减FD | STMFD | LDMFD |

注意:这些EA、ED、FA、FD并不是新的指令,编译器会把他们编译为相应的指令。如:STMFD最终编译成汇编依旧是STMDB

栈的应用

1、叶子函数调用过程

什么是叶子函数:

叶子函数就是只有一层的函数,即:main中调用了fun函数,fun函数中没有调用其他的函数。

实现功能:

使用汇编实现函数调用,main中计算1+2并调用fun,fun中计算20-10。要求只能使用R1、R2、R3这三个寄存器。

设计思路:

  • 初始化栈指针SP
  • 入栈当前寄存器中的数据,即:压栈保护现场
  • 进行函数相关的操作
  • 出栈数据到寄存器中,即:出栈恢复现场
  • 移动PC到断点下一条指令位置

代码实现:

复制代码
.text 
.global _start
_start:

MOV SP,#0x40000020 	   @初始化栈指针sp

MAIN:
	MOV R1,#1
	MOV R2,#2
	BL FUN
	ADD R3,R1,R2
	B STOP
	
FUN:
	STMFD SP!,{R1,R2}  @入栈保护现场
	MOV R1,#10
	MOV R2,#20
	SUB R3,R2,R1
	LDMFD SP!,{R1,R2}  @出栈恢复现场
	MOV PC,LR          @PC指向断点下一个指令位置

STOP:
	B STOP

.end

2、非叶子函数调用过程

什么是非叶子函数:

非叶子函数就是有多层的函数,即:main中调用了fun函数,fun函数中还调用其他的函数。

实现功能:

使用汇编实现函数调用,main中计算1+2并调用fun,fun中计算20-10并调用fun2,fun2中计算300-200。要求只能使用R1、R2、R3这三个寄存器。

设计思路:

  • 初始化栈指针SP
  • 入栈当前寄存器和LR中的数据,即:压栈保护现场
  • 进行函数相关的操作
  • 出栈数据到寄存器中,即:出栈恢复现场
  • 移动PC到断点下一条指令位置

代码实现:

复制代码
.text 
.global _start
_start:

MOV SP,#0x40000020 	      @初始化栈指针sp

MAIN:
	MOV R1,#1
	MOV R2,#2
	BL FUN
	ADD R3,R1,R2
	B STOP
	
FUN:
	STMFD SP!,{R1,R2,LR}  @入栈保护现场,注意这里入栈了LR
	MOV R1,#10
	MOV R2,#20
	BL FUN2
	SUB R3,R2,R1
	LDMFD SP!,{R1,R2,LR}  @出栈恢复现场
	MOV PC,LR             @PC指向断点下一个指令位置

FUN2:
	STMFD SP!,{R1,R2,LR}  @入栈保护现场,注意这里入栈了LR
	MOV R1,#200
	MOV R2,#300
	SUB R3,R2,R1
	LDMFD SP!,{R1,R2,LR}  @出栈恢复现场
	MOV PC,LR             @PC指向断点下一个指令位置

STOP:
	B STOP

.end

3、局部变量是随机值的原因

出栈发生了什么:

以"叶子函数调用过程"中的代码为例,当程序走到STOP后,栈中的数据已经出栈,但是栈中保存的数据并未被清除。即:出栈只是SP去移动,而不是顺带着把栈中的数据清零

局部变量是随机值的原因:

当另一个函数被调用,局部变量被申请时,局部变量的地址会从栈指针位置去申请,这里就是申请0x000000001C这个空间,所以在不初始化的情况下,该局部变量的值就是2,是上一个函数入栈的数据,因此是一个随机值。

相关推荐
CinzWS19 小时前
A53 FPGA原型验证:从RTL到可运行系统的挑战
arm开发·嵌入式·芯片验证·原型验证·a53
AI服务老曹20 小时前
深度解析:支持 GB28181/RTSP 及异构计算(X86/ARM+GPU/NPU)的 AI 视频管理平台架构方案(附源码交付与 Docker 部署)
arm开发·人工智能·音视频
2302_8138062220 小时前
基础环境篇 – 交叉编译环境搭建与NFS服务配置
arm开发
极创信息20 小时前
信创领域五种主流CPU架构(X86 / ARM / RISC-V / MIPS / LoongArch)
java·arm开发·数据库·spring boot·mysql·软件工程·risc-v
AI服务老曹21 小时前
节省95%开发成本:支持X86/ARM与GPU/NPU异构部署的AI视频云网关架构深度解析
arm开发·人工智能·音视频
徐某人..1 天前
基于i.MX6ULL平台的智能网关系统开发
arm开发·c++·单片机·qt·物联网·学习·arm
2035去旅行1 天前
嵌入式开发,如何选择C标准库
linux·arm开发
进击的小头2 天前
20_第20篇:嵌入式外设驱动开发基础:寄存器级开发与库函数开发对比实战
arm开发·驱动开发·单片机
AI服务老曹2 天前
架构实战:如何构建支持X86/ARM及异构GPU/NPU的跨平台企业级AI视频管理系统?
arm开发·人工智能·架构
ai产品老杨2 天前
【架构深研】如何构建兼容X86/ARM与异构算力的AI视频中台?基于GB28181与边缘计算的源码交付实践
arm开发·人工智能·架构