打印
[单片机芯片]

请问CH32V103打开-flto选项后如何避免_write函数被优化掉?

[复制链接]
3171|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

使用特权

评论回复
5
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容量。

使用特权

评论回复
6
ufbycd| | 2021-12-11 21:00 | 只看该作者
多60%应该有点离谱了,不单单是指令集密度的问题了吧。另外,newlibc库里的printf是重量级实现,随便手写个功能差不多的printf都要减小不少体积。

使用特权

评论回复
7
imdx|  楼主 | 2021-12-11 22:09 | 只看该作者
ufbycd 发表于 2021-12-11 21:00
多60%应该有点离谱了,不单单是指令集密度的问题了吧。另外,newlibc库里的printf是重量级实现,随便手写个 ...

实测还真就是多用了60%的flash,当然没开-flto,因为开了功能不正常。

使用特权

评论回复
8
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



使用特权

评论回复
9
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函数。


使用特权

评论回复
10
WCHTech37| | 2021-12-13 13:05 | 只看该作者
您好,可通过在函数前增加__attribute__((used))语句来避免该函数被优化:

使用特权

评论回复
11
WCHTech37| | 2021-12-13 13:08 | 只看该作者
本帖最后由 WCHTech37 于 2021-12-13 13:25 编辑

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

typedef struct _struct_test2 {
    UINT8 data1;
    UINT16 data2;
    UINT32 data3;
} struct_test2, *Pstruct_test2;

void  test1()
{
    Pstruct_test1 p = (Pstruct_test1)0x40000000;
    p->data3 = 0x87654321;
}
void  test2()
{
    Pstruct_test2 p = (Pstruct_test2)0x80000000;
    p->data3 = 0x12345678;
}

int main(void)
{
......
test1();
test2();
......
}

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文件,看哪些函数变得特别大,然后再分析相应代码的汇编代码。




使用特权

评论回复
12
zyw123456789| | 2021-12-13 14:17 | 只看该作者
https://carrv.github.io/2020/papers/CARRV2020_paper_12_Perotti.pdf
参考上面文档3.2.1,将-msave-restore选项加上。

使用特权

评论回复
13
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. ...

你是不是很挑呀,没兴趣接受别人的方案就不要想着优化文件大小了,好么。
嫌编译器版本低就自己拉编译器的新源码来编译吧,不要浪费大家的时间了!

使用特权

评论回复
14
imdx|  楼主 | 2021-12-13 17:16 | 只看该作者
ufbycd 发表于 2021-12-13 16:30
你是不是很挑呀,没兴趣接受别人的方案就不要想着优化文件大小了,好么。
嫌编译器版本低就自己拉编译器的 ...

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


使用特权

评论回复
15
imdx|  楼主 | 2021-12-13 17:17 | 只看该作者
WCHTech37 发表于 2021-12-13 13:05
您好,可通过在函数前增加__attribute__((used))语句来避免该函数被优化:

感谢,增加__attribute__((used))选项以后,使用-flto可以正常编译了。

使用特权

评论回复
16
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%

使用特权

评论回复
17
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

使用特权

评论回复
18
imdx|  楼主 | 2021-12-14 09:08 | 只看该作者
ufbycd 发表于 2021-12-13 16:30
你是不是很挑呀,没兴趣接受别人的方案就不要想着优化文件大小了,好么。
嫌编译器版本低就自己拉编译器的 ...

你说对了,我就是很挑,所以才用标准库的printf,通常标准库中的函数完成度会更高,实现也更好,测试也更充分。别人的方案我会自己判断是否更好,更好的才会接受。至于编译编译器这种无聊的事情虽然我能做但我是不会做的。

使用特权

评论回复
19
发呆二极管| | 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增加了快速中断等功能,确实是挺好用。

使用特权

评论回复
20
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就是真香。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

116

主题

890

帖子

7

粉丝