LZ的数据是经过了连接(ld)后的最终执行程序的大小,不能真实地反映“编译器”的水平,LZ的testa.c经过gcc编译所产生的代码只有12字节,下面是dump的代码: int main () { unsigned int a,b,c; unsigned char buf; DDRD = 0xff; 0: 8f ef ldi r24, 0xFF ; 255 2: 81 bb out 0x11, r24 ; 17 a = 100; b = 200; c = 300; buf = 0; 4: 80 e0 ldi r24, 0x00 ; 0 while (1) { buf++; 6: 8f 5f subi r24, 0xFF ; 255 PORTD = buf; 8: 82 bb out 0x12, r24 ; 18 a: 00 c0 rjmp .+0 }
希望LZ提供iar“编译”后的代码,再次PK一下。
什么原因造成了连接后的代码偏大呢?这要归咎于libc,其中的crt(就是所谓的启动代码)占用了相当大的空间。启动代码dump如下: 00000000 <__vectors>: 0: 19 c0 rjmp .+50 ; 0x34 <__ctors_end> 2: 32 c0 rjmp .+100 ; 0x68 <__bad_interrupt> 4: 31 c0 rjmp .+98 ; 0x68 <__bad_interrupt> 6: 30 c0 rjmp .+96 ; 0x68 <__bad_interrupt> 8: 2f c0 rjmp .+94 ; 0x68 <__bad_interrupt> a: 2e c0 rjmp .+92 ; 0x68 <__bad_interrupt> c: 2d c0 rjmp .+90 ; 0x68 <__bad_interrupt> e: 2c c0 rjmp .+88 ; 0x68 <__bad_interrupt> 10: 2b c0 rjmp .+86 ; 0x68 <__bad_interrupt> 12: 2a c0 rjmp .+84 ; 0x68 <__bad_interrupt> 14: 29 c0 rjmp .+82 ; 0x68 <__bad_interrupt> 16: 28 c0 rjmp .+80 ; 0x68 <__bad_interrupt> 18: 27 c0 rjmp .+78 ; 0x68 <__bad_interrupt> 1a: 26 c0 rjmp .+76 ; 0x68 <__bad_interrupt> 1c: 25 c0 rjmp .+74 ; 0x68 <__bad_interrupt> 1e: 24 c0 rjmp .+72 ; 0x68 <__bad_interrupt> 20: 23 c0 rjmp .+70 ; 0x68 <__bad_interrupt> 22: 22 c0 rjmp .+68 ; 0x68 <__bad_interrupt> 24: 21 c0 rjmp .+66 ; 0x68 <__bad_interrupt> 26: 20 c0 rjmp .+64 ; 0x68 <__bad_interrupt> 28: 1f c0 rjmp .+62 ; 0x68 <__bad_interrupt> 2a: 1e c0 rjmp .+60 ; 0x68 <__bad_interrupt> 2c: 1d c0 rjmp .+58 ; 0x68 <__bad_interrupt> 2e: 1c c0 rjmp .+56 ; 0x68 <__bad_interrupt> 30: 1b c0 rjmp .+54 ; 0x68 <__bad_interrupt> 32: 1a c0 rjmp .+52 ; 0x68 <__bad_interrupt>
00000034 <__ctors_end>: 34: 11 24 eor r1, r1 36: 1f be out 0x3f, r1 ; 63 38: cf ef ldi r28, 0xFF ; 255 3a: d2 e0 ldi r29, 0x02 ; 2 3c: de bf out 0x3e, r29 ; 62 3e: cd bf out 0x3d, r28 ; 61
00000040 <__do_copy_data>: 40: 11 e0 ldi r17, 0x01 ; 1 42: a0 e0 ldi r26, 0x00 ; 0 44: b1 e0 ldi r27, 0x01 ; 1 46: e6 e7 ldi r30, 0x76 ; 118 48: f0 e0 ldi r31, 0x00 ; 0 4a: 02 c0 rjmp .+4 ; 0x50 <.do_copy_data_start>
0000004c <.do_copy_data_loop>: 4c: 05 90 lpm r0, Z+ 4e: 0d 92 st X+, r0
00000050 <.do_copy_data_start>: 50: a0 30 cpi r26, 0x00 ; 0 52: b1 07 cpc r27, r17 54: d9 f7 brne .-10 ; 0x4c <.do_copy_data_loop>
00000056 <__do_clear_bss>: 56: 11 e0 ldi r17, 0x01 ; 1 58: a0 e0 ldi r26, 0x00 ; 0 5a: b1 e0 ldi r27, 0x01 ; 1 5c: 01 c0 rjmp .+2 ; 0x60 <.do_clear_bss_start>
0000005e <.do_clear_bss_loop>: 5e: 1d 92 st X+, r1
00000060 <.do_clear_bss_start>: 60: a0 30 cpi r26, 0x00 ; 0 62: b1 07 cpc r27, r17 64: e1 f7 brne .-8 ; 0x5e <.do_clear_bss_loop> 66: 01 c0 rjmp .+2 ; 0x6a <main>
00000068 <__bad_interrupt>: 68: cb cf rjmp .-106 ; 0x0 <__heap_end>
大家可以看出,启动代码基本有4个内容: 1、中断向量表 2、设置栈指针 3、复制已初始化数据区 4、清除未初始化数据区
在一个稍大些的,有实用价值的程序中,这4项基本上是都有的,而LZ提供的程序属于比较极端的情况:无中断处理,无已初始化数据,无未初始化数据。在这种情况下,crt中大部分代码属于无用的。我没有用过iar,LZ能不能把用iar生成的程序dump一下,让大家分析分析,或许是iar在连接时发现此等特殊情况而省略了crt中对应部分的代码?如果真是这样,代码应该是如此才对:
crt部分:(省略1、3、4) 0: cf ef ldi r28, 0xFF ; 255 2: d2 e0 ldi r29, 0x02 ; 2 4: de bf out 0x3e, r29 ; 62 6: cd bf out 0x3d, r28 ; 61
用户程序部分: int main () { unsigned int a,b,c; unsigned char buf; DDRD = 0xff; 8: 8f ef ldi r24, 0xFF ; 255 a: 81 bb out 0x11, r24 ; 17 a = 100; b = 200; c = 300; buf = 0; c: 80 e0 ldi r24, 0x00 ; 0 while (1) { buf++; e: 8f 5f subi r24, 0xFF ; 255 PORTD = buf; 10: 82 bb out 0x12, r24 ; 18 12: 00 c0 rjmp .-6
哈哈,只有20字节!在这个程序中,还有个极端情况:除main外无其它函数!那么连栈设置都可以省略了,最终就剩下12字节。不知iar能否生成此等最优的代码?或者那位再提供一个有中断,有初始化数据和未初始化数据的程序,我们再来PK一下。 |