为 Cortex-M0 优化 C 或汇编代码的大小

Optimize C or assembly code in size for Cortex-M0

我需要减少 Cortex-M0 微处理器的代码膨胀。

启动时必须将ROM数据复制到RAM数据一次。因此我有这段代码:

void __startup( void ){
  extern unsigned int __data_init_start;
  extern unsigned int __data_start;
  extern unsigned int __data_end;

  // copy .data section from flash to ram
  s = & __data_init_start;
  d = & __data_start;
  e = & __data_end;
  while( d != e ){ 
    *d++ = *s++;
  }
}

编译器生成的汇编代码如下所示:

  ldr   r1, .L10+8
  ldr   r2, .L10+12
  sub   r0, r1, r2
  lsr   r3, r0, #2
  add   r3, r3, #1
  lsl   r1, r3, #2
  mov   r3, #0
.L4:
  add   r3, r3, #4
  cmp   r3, r1
  beq   .L9
.L5:
  ldr   r4, .L10+16
  add   r0, r2, r3
  add   r4, r3, r4
  sub   r4, r4, #4
  ldr   r4, [r4]
  sub   r0, r0, #4
  str   r4, [r0]
  b .L4

如何优化此代码以使代码大小最小?

编译器(或您!)没有意识到要复制的范围是 end - start。似乎正在进行一些不必要的数据改组——循环中的 2 addsub。另外,在我看来,编译器确保要制作的副本数是 4 的倍数。那么,一个明显的优化是确保它是提前的!下面我假设它是(如果不是,bne 将失败并愉快地继续复制和践踏你的记忆)。

使用我已有十年历史的 ARM 汇编程序知识(是的,这是一个主要的免责声明)和 post-递增,我认为可以将其浓缩为以下简短片段。从 18 条指令减少到 8 条,还不错。如果有效。

  ldr   r1, __data_init_start
  ldr   r2, __data_start
  ldr   r3, __data_end
  sub   r4, r3, r2
.L1:
  ldr   r3, [r1], #4  ; safe to re-use r3 here
  str   r3, [r2], #4
  subs  r4, r4, #4
  bne   L1

可能是该平台保证写入 unsigned int * 您可以更改 unsigned int * 值(即它不利用类型不匹配别名规则)。

那么代码效率低下,因为e是一个全局变量,生成的代码逻辑必须考虑到写入*d可能会改变e的值。

至少创建 e 个局部变量应该可以解决这个问题(大多数编译器都知道,从 C 的角度来看,给一个从未使用过其地址的局部变量取别名是不可能的)。