
本文介绍MRS编译参数中的Small Prologue/Epilogue(-msave-restore)选项用于在嵌入式开发中优化代码体积。MRS的工程配置页中有一个默认选中的勾选项Small Prologue/Epilogue(-msave-restore)该选项是一个专用于 RISC-V 架构的 GCC 编译器选项通过将函数的 Prologue序言和 Epilogue尾声提取为共享的库函数调用从而显著减小代码体积。在理解 -msave-restore 之前需要先了解函数调用中的Prologue序言和 Epilogue尾声。Prologue序言是函数入口处的一段固定代码负责设置函数的执行环境保存那些按照调用约定需要由本函数保护的寄存器的值。主要完成以下工作• 分配栈帧空间调整 sp 寄存器• 保存被调用者保存寄存器callee-saved registers到栈上• 保存返回地址寄存器 ra对于非叶子函数• 设置帧指针 fp/s0可选Epilogue尾声是函数返回前的一段固定代码负责恢复执行现场• 从栈上恢复之前保存的被调用者保存寄存器• 恢复返回地址 ra• 释放栈帧空间恢复 sp• 通过 ret 指令返回使用MRS分别在勾选与取消-msave-restore选项两种情况下编译同一段代码在lst文件中查看生成的对应汇编代码。未勾选msave-restore时编译器会在每个需要保存寄存器的函数内部直接生成相应的汇编指令来完成保存和恢复操作。下图所示为函数demo的汇编语句明显看到在函数首尾生成了一系列的保存/恢复指令作为函数体的一部分。这样的好处是执行的效率较高但每个函数都要执行自己的指令当函数较多时空间占用较大。勾选msave-restore时编译器会通过调用 libgcc 库中的特定函数如 __riscv_save 和 __riscv_restore来完成寄存器的保存和恢复工作。例如下图demo函数首尾分别调用__riscv_save_4和__riscv_restore_4。当程序中包含一定数量的函数时利用特定函数调用取代重复生成Prologue/Epilogue 代码可以一定程度上减少代码大小尽管每次函数调用都需要额外进行两次库函数调用会引入少量的性能损耗但相较之下优化代码体积在嵌入式领域有更大的优势。总结嵌入式代码使用-msave-restore可以优化代码体积建议勾选。