一个const引入的优化问题

今天定位了一个const引入的优化问题, 问题本身挺简单的, 但是自己太菜, 写代码时竟然没注意, 所以记录一下, 引起警戒.

问题背景与定位

测试团队反馈修改newlib系统调用以后跑coremark报错bad alloc, 于是就看了了下sbrk()的修改.
因为之前在修改newlib时需要通过系统调用初始化一些数据, 这些数据对newlib而言是只读的, 于是我写了这样一段代码.

typedef struct mem_s {
  int i1;
  int i2;
} mem_t;
static const mem_t meminfo;

extern int __syscall(void *p);
int __attribute__((constructor(0))) wrapper() {
  return __syscall(&meminfo);
}

void get_info(int *p1, int *p2) {
  *p1 = meminfo.i1;
  *p2 = meminfo.i2;
}

meminfo保存了一些全局的数据信息, 它在constructor函数wrapper()中通过系统调用__syscall()被初始化. 外部程序通过调用get_info()获取这些数据.
由于meminfo在运行期间本身不会被修改, 为了模块化和扩展性就将meminfo定义为static const, 保证变量仅本文件可见并且不会误修改.
而我修改后的sbrk()正是通过get_info()获取信息并做对应处理的, 那么第一怀疑点是get_info()返回值有误. 尝试打印一下返回值, 发现都是0.
于是迷之自信的怀疑是系统调用返回出错, 把问题推给仿真器团队. 但是仿真器团队不肯接锅, 于是写个测试接口证明给它们看.

void dump_info() {
  int fd = __open("1.log", O_RDWR | O_CREAT, 0644);
  __write(fd, &meminfo, sizeof(meminfo));
}

这里直接把数据写入文件是因为printf()会调用sbrk()导致递归调用. 加了打印接口以后发现仿真器返回的系统调用值是正确的. 那问题出在哪里呢?
重新review一遍代码, 突然想到是编译优化导致的! 因为是const变量所以默认值是恒定的, compiler直接将其localize成初始值.
而编译器本身无法感知constructor函数中修改内存的行为, 所以get_info()中p1与p2直接被赋值为0.
为阻止优化给meminfo加上volatile修饰符即可, 添加volatile之后coremark正确执行.

小结

const/volatile的作用随便找个人都能说上来, 但是自己写代码时候还是考虑不周.
说到底是写代码时候没有考虑清楚每个变量的设置与使用导致的, 所以古话说谋定而后动, 一定自己想明白了再写代码.
最重要的是想清楚了再甩锅, 否则小心怼在铁板上.

posted @ 2020-06-17 22:27  Five100Miles  阅读(218)  评论(0编辑  收藏  举报