为 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 add
和 sub
。另外,在我看来,编译器确保要制作的副本数是 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 的角度来看,给一个从未使用过其地址的局部变量取别名是不可能的)。
我需要减少 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 add
和 sub
。另外,在我看来,编译器确保要制作的副本数是 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 的角度来看,给一个从未使用过其地址的局部变量取别名是不可能的)。