[单片机芯片] 请问CH32V103打开-flto选项后如何避免_write函数被优化掉?

[复制链接]
5159|31
 楼主| imdx 发表于 2021-12-11 17:57 | 显示全部楼层 |阅读模式
由于RISC-V代码密度低,想尽量压缩代码体积。采取了以下措施:
优化等级-Os体积优化,打开压缩指令RVC,链接器增加了以下选项
--gc-sections --specs=nano.specs --specs=nosys.specs
效果还是不理想,使用-flto选项打开Link-time optimizer以后代码体积减少不少,不过报_write函数找不到。
libg_nano.a(lib_a-writer.o): in function `.L0 ':writer.c:(.text._write_r+0x12): undefined reference to `_write'
看起来是_write函数被优化掉了,这个_write函数是为了使用printf函数实现的,代码中有,不加-flto选项正常。
其它项目都是重写fputc,WCH是重写_write函数,如何在打开-flto选项后如何避免_write函数被优化掉呢?
或者是否还有其它方法可以用来减小代码体积?
同样功能的源代码在ARM上面即使使用速度优化,FLASH空间也十分充足,RISC-V的代码密度还是太拉跨了。
ufbycd 发表于 2021-12-11 19:35 | 显示全部楼层
本帖最后由 ufbycd 于 2021-12-11 19:36 编辑

方法一:在 _write 上指明优化等级 __attribute__((optimize(0)))
方法二:自己实现printf,网上的很多现成的源码
方法三:更换使用更高版的GCC,我就试过:在低版的GCC上开LTO后有些符号被错误地去掉,换高版的之后就没问题了
 楼主| imdx 发表于 2021-12-11 19:42 | 显示全部楼层
本帖最后由 imdx 于 2021-12-11 19:49 编辑
ufbycd 发表于 2021-12-11 19:35
方法一:在 _write 上指明优化等级 __attribute__((optimize(0)))
方法二:自己实现printf,网上的很多现成 ...

你的gcc是什么版本?我的是MounRiver 1.42自带的8.2.0,之所以用MounRiver 1.42,是因为这个版本更像Eclipse一点。方法1我测试不行,这个_write函数似乎在别的地方声明过了,加上会报错。我自己有用xprintf库,不过还是希望能用自带库的实现版本,这样各个项目代码容易统一。
ufbycd 发表于 2021-12-11 19:47 | 显示全部楼层
我用的是ARM内核,GCC用Linux系统官方仓库的,版本是11.2.0
 楼主| imdx 发表于 2021-12-11 19:51 | 显示全部楼层
ufbycd 发表于 2021-12-11 19:47
我用的是ARM内核,GCC用Linux系统官方仓库的,版本是11.2.0

ARM内核用AC6编译器,完全没有这些问题。
同样功能,RISC-V要多用60%的FLASH。我之前同样功能的代码在ARM上随便编译优化都不会超FLASH容量。
ufbycd 发表于 2021-12-11 21:00 | 显示全部楼层
多60%应该有点离谱了,不单单是指令集密度的问题了吧。另外,newlibc库里的printf是重量级实现,随便手写个功能差不多的printf都要减小不少体积。
 楼主| imdx 发表于 2021-12-11 22:09 来自手机 | 显示全部楼层
ufbycd 发表于 2021-12-11 21:00
多60%应该有点离谱了,不单单是指令集密度的问题了吧。另外,newlibc库里的printf是重量级实现,随便手写个 ...

实测还真就是多用了60%的flash,当然没开-flto,因为开了功能不正常。
ufbycd 发表于 2021-12-13 10:29 | 显示全部楼层
本帖最后由 ufbycd 于 2021-12-13 10:34 编辑
imdx 发表于 2021-12-11 19:42
你的gcc是什么版本?我的是MounRiver 1.42自带的8.2.0,之所以用MounRiver 1.42,是因为这个版本更像Eclip ...

之前没仔细看这个回复,但为何叫希望“代码容易统一”?你直接实现printf这个函数就行啦,链接时会先链接.o文件再到.a库文件。
也就是在你源码里实现的printf会替换标准库的printf,因为链接.o文件时已经知道printf这个符号的地址了也就不会去.a标准库里找了。
我的实现方法:https://gitee.com/ufbycd/static_module/blob/master/APP/Core/Src/mstdio.c



 楼主| imdx 发表于 2021-12-13 12:45 | 显示全部楼层
本帖最后由 imdx 于 2021-12-13 12:48 编辑
ufbycd 发表于 2021-12-13 10:29
之前没仔细看这个回复,但为何叫希望“代码容易统一”?你直接实现printf这个函数就行啦,链接时会先链接. ...

https://github.com/tmk/elm-chan_xprintf/blob/master/xprintf.c
http://elm-chan.org/fsw/strf/xprintf.html
我用的是elm-chan写的xprintf,他的函数名字叫做xprintf。
printf是C标准库里面实现的函数,用标准库里面实现的printf换到别的环境下也是printf,不必更换API,这个叫做代码统一。我并没有兴趣自己去实现一个完整的printf函数。


WCHTech37 发表于 2021-12-13 13:05 | 显示全部楼层
您好,可通过在函数前增加__attribute__((used))语句来避免该函数被优化:
opt.png
WCHTech37 发表于 2021-12-13 13:08 | 显示全部楼层
本帖最后由 WCHTech37 于 2021-12-13 13:25 编辑

代码占用空间异常增大现象,可能与定义结构体时加了__attribute__((packed))属性有关非必要时可选择不添加该语句。
如下面代码:
  1. typedef struct __PACKED _struct_test1 {
  2.     UINT8 data1;
  3.     UINT16 data2;
  4.     UINT32 data3;
  5. } struct_test1, *Pstruct_test1;

  6. typedef struct _struct_test2 {
  7.     UINT8 data1;
  8.     UINT16 data2;
  9.     UINT32 data3;
  10. } struct_test2, *Pstruct_test2;

  11. void  test1()
  12. {
  13.     Pstruct_test1 p = (Pstruct_test1)0x40000000;
  14.     p->data3 = 0x87654321;
  15. }
  16. void  test2()
  17. {
  18.     Pstruct_test2 p = (Pstruct_test2)0x80000000;
  19.     p->data3 = 0x12345678;
  20. }

  21. int main(void)
  22. {
  23. ......
  24. test1();
  25. test2();
  26. ......
  27. }

test1和test2汇编代码如下:
test1():
    p->data3 = 0x87654321;
     21c:400007b7           lui a5,0x40000
     220:02100713           li a4,33
     
224: 00e781a3          sb a4,3(a5) # 40000003 <_eusrstack+0x1fffb003>
     228:04300713           li a4,67
     
22c:00e78223           sb a4,4(a5)
     230:06500713           li a4,101
   
234:00e782a3           sb a4,5(a5)
     238: f8700713          li a4,-121
     
23c:00e78323           sb a4,6(a5)
test1函数的总大小是0x23c-0x21c=0x20,32字节

test2():
    p->data3 = 0x12345678;
     240:123457b7           lui a5,0x12345
     244:80000737           lui a4,0x80000
     248:67878793           addi a5,a5,1656 # 12345678 <_data_lma+0x12343c30>
     
24c:c35c                sw a5,4(a4)

test2函数的总大小是0x24c-0x240=0xc,12字节

test1()对成员变量data3访问时以字节的方式,test2()对成员变量data3访问时以4字节的方式.
armcc对test1也是一样的处理以字节的方式,所不同的是ARM提供了sb的16位的压缩指令,但是RISC-V没有,RISC-V只提供sw的16位压缩指令。
如果要进一步分析代码变大的原因,需要比较ARM和RISC-V两个版本的MAP文件,看哪些函数变得特别大,然后再分析相应代码的汇编代码。




zyw123456789 发表于 2021-12-13 14:17 | 显示全部楼层
https://carrv.github.io/2020/papers/CARRV2020_paper_12_Perotti.pdf
参考上面文档3.2.1,将-msave-restore选项加上。
ufbycd 发表于 2021-12-13 16:30 | 显示全部楼层
imdx 发表于 2021-12-13 12:45
https://github.com/tmk/elm-chan_xprintf/blob/master/xprintf.c
http://elm-chan.org/fsw/strf/xprintf. ...

你是不是很挑呀,没兴趣接受别人的方案就不要想着优化文件大小了,好么。
嫌编译器版本低就自己拉编译器的新源码来编译吧,不要浪费大家的时间了!
 楼主| imdx 发表于 2021-12-13 17:16 | 显示全部楼层
ufbycd 发表于 2021-12-13 16:30
你是不是很挑呀,没兴趣接受别人的方案就不要想着优化文件大小了,好么。
嫌编译器版本低就自己拉编译器的 ...

关于printf,正确答案在这里:用wch版本的printf。
GCC自带的printf实现确实体积很大,用wch版本的printf可以减少大约2kB代码尺寸,在MCU上非常可观了。
感谢WCH技术人员。
2021-12-13_171430.jpg

 楼主| imdx 发表于 2021-12-13 17:17 | 显示全部楼层
WCHTech37 发表于 2021-12-13 13:05
您好,可通过在函数前增加__attribute__((used))语句来避免该函数被优化:

感谢,增加__attribute__((used))选项以后,使用-flto可以正常编译了。
 楼主| imdx 发表于 2021-12-13 17:19 | 显示全部楼层
zyw123456789 发表于 2021-12-13 14:17
https://carrv.github.io/2020/papers/CARRV2020_paper_12_Perotti.pdf
参考上面文档3.2.1,将-msave-resto ...

-msave-restore选项可以将固件体积减小大约2%
 楼主| imdx 发表于 2021-12-13 17:30 | 显示全部楼层
总结一下提高RISC-V代码密度的优化选项,欢迎补充
全局优化选项:-Os,面积优化。
-ffunction-sections
-fdata-sections
--gc-sections
--specs=nano.specs Use newlib-nano
--specs=nosys.specs
-lprintf Use wchprint
-msave-restore
-flto Link-time optimizer
 楼主| imdx 发表于 2021-12-14 09:08 | 显示全部楼层
ufbycd 发表于 2021-12-13 16:30
你是不是很挑呀,没兴趣接受别人的方案就不要想着优化文件大小了,好么。
嫌编译器版本低就自己拉编译器的 ...

你说对了,我就是很挑,所以才用标准库的printf,通常标准库中的函数完成度会更高,实现也更好,测试也更充分。别人的方案我会自己判断是否更好,更好的才会接受。至于编译编译器这种无聊的事情虽然我能做但我是不会做的。
发呆二极管 发表于 2021-12-22 10:21 | 显示全部楼层
感谢楼主分享心得,学习了~周末我也再试试~

我就是因为risc-v现在的gcc编译器编译效果太拉胯了所以才没继续用risc-v的……
什么时候risc-v能有个llvm的编译器就好了……当然risc-v现在的问题不是wch的锅,并没有要指责wch的意思。
反正前两天我也测试过代码密度和执行效率,就是移植的coremark测试的。
risc-v的gcc编译后的代码执行效率与ac5同开-o3的效率几乎一样,但是体积前者是后者的3倍……
risc-v开-o3编译后大概是34k,ac5开-o3是大概11k……差太多了。
如果不用-o3用别的优化选项,执行效率又会低一些。
不得不说ac6换成llvm后简直是质变,用oz还是os的来着,体积又小,执行效率又高……

顺便提一句,wch自己优化后的risc-v gcc增加了快速中断等功能,确实是挺好用。
 楼主| imdx 发表于 2021-12-22 10:33 | 显示全部楼层
发呆二极管 发表于 2021-12-22 10:21
感谢楼主分享心得,学习了~周末我也再试试~

我就是因为risc-v现在的gcc编译器编译效果太拉胯了所以才没继 ...

目前的risc-v,至少要多准备50%的FLASH,2倍最佳。当然只要价格合适,一切都不是问题。比如同规格ARM卖10块,risc-v卖5块,那risc-v就是真香。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

123

主题

905

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部