背景 Segger链接器对于RISC-V处理器的优化效果: | | | | | R/O | | GNU | GNU | 43176 | 4608 | 47784 | | GNU | SEGGER | 37772 | 3623 | 41395 | | GNU | SEGGER + outlining | 36086 | 3623 | 39709 |
应用程序通常依据逻辑功能被分为多个源文件进行管理。在开发过程中,这样做的好处是,当更改单个C源文件时,不需要重新编译整个应用程序。
单个文件编译的副作用是优化通常不能跨过编译单元的边界。你可以打开 SEGGER工具集支持的链接时优化选项(LTO),但该方式仅适用于源代码模块。如果应用程序含有由芯片供应商提供的第三方目标代码库,则不能通过链接时优化选项优化代码! 但在LTO之外,你仍然有机会减少应用程序的大小。 特征法(Outlining) 减小应用程序大小的方法之一是搜索函数之间、甚至函数内部的通用指令序列。一旦确定,就可以将公共序列提取到子例程中,使用子程序调用来替换该指令序列。 因为子程序替换在机器码层级,而不是高级语言层级上运行的,因此提取的子程序不需要遵守任何ABI强制的调用约定。这意味着搜索的范围可以更广,可以将多条指令,从短到长作为可用作替换的候选指令序列。 由于SEGGER 链接器的特征优化可与任何目标代码(包括第三方目标代码库)一起使用,并且适用于整个应用程序,因此大大拓宽了发现特征序列机会。 SEGGER 链接器会仔细分析应用程序,选择缩减代码体积的最佳方法:采用最长的序列可能不是一个好的策略,而采用较短的序列通常会更好。 示例 这是来自emWin应用程序的真实示例。在产品使用寿命期间内,由于采用的LCD面板缺货,需要采用另一种兼容性较高的面板进行替换。但是,固件需同时支持两种面板产品。为此,固件会在运行时检测安装的面板,并使用合适的面板驱动程序初始化emWin。最终,应用程序包含两个驱动,但应用层代码不需知道实际使用的面板,但是能够在新旧硬件上同样良好地运行。 面板驱动程序非常相似。两个驱动程序之间有一定数量的重复代码。下面是示例代码,其中的差异用红色突出显示: ReadRectCust_16bpp: ReadRectCust_18bpp: ADDI sp,sp,-32 ADDI sp,sp,-32 SW s2,16 (sp) SW s2,16(sp) LW s2,8(a0) LW s2,8(a0) SW s0,24 (sp) SW s0、24(sp) SW s1、20(sp) SW s1、20(sp) LW a6、168(s2) LW a6、168(s2) SW s3、12(sp) SW s3、12( sp) SW s4、8(sp) SW s4、8(sp) SW s5、4(sp) SW s5、4(sp) SW ra,28(sp) SW ra,28(sp) MV a0,s2 MV a0 ,s2 MV s5,a1 MV s5,a1 MV s4,a2 MV s4,a2 MV s1,a3 MV s1,a3 MV s0,a4 MV s0,a4 MV s3,a5 MV s3,a5 JALR a6 JALR a6 SUB a2,s1,s5 SUB a2,s1,s5 SUB s0,s0,s4 SUB s0,s0,s4 ADDI s0,s0,1 ADDI s0,s0, 1 ADDI a2 ,a2,1 ADDI a2,a2,1 MUL a2,a2,s0 MUL a2,a2,s0 LW a4,64(s2) LW a4,64(s2) LW s0,24(sp) LW s0,24(sp) LW t1,228(s2) LW t1,232(s2) LW ra,28(sp) LW ra,28(sp) LW s1,20(sp) LW s1,20(sp) LW s2,16(sp) LW s2、16(sp) LW s4、8(sp) LW s4、8(sp) LW s5、4(sp) LW s5、4(sp) MV a1,s3 MV a1,s3
LW a0,24(a4) LW a0,24(a4) LW s3,12(sp) LW s3,12(sp) ADDI sp,sp,32 ADDI sp,sp,32 JR t1 JR t1
这两个函数之间似乎有很多重复。我们将基于此代码进行优化解析。 RISC-V相关指令 不熟悉RISC-V指令的人员可以阅读下面的内容: |