笔者来聊聊编译器的用法
首先来了解一下编译器,其通常分为三个部分:前端+优化器+后端。
再通俗地说编译器的工作就是:源代码->预处理->编译->目标代码->链接->可执行程序。
再来简单看看一些编译器的历史,GCC、LLVM以及Clang等,以及文章介绍的armcc 以及armclang。
arm 公司 开发的一款编译器,在2005年收购 KEIL 公司后,这块编译器就集成在KEIL IDE里面,以及自家开发的ARM DS5,编译器以及IDE相关的文档可以去ARM 公司的官网下载。
下载的文档主要分几个部分:armcc 编译器、armasm 汇编器、armlink 链接器、armar 打包以及fromelf bin文件。
armcc 编译器 主要是编译.c/.cpp源文件文件,生成目标文件,通过各种编译选项 command-line来支持各种特性。接着来罗列几个常见的编译选项。
一般的arm cc的编译器的编译器的语法如下:
armcc [options] [source]
举例如下:
armcc -I ../common/ -I ../driver -g --apcs=interwork --cpu=Cortex-R5 -c ../common/led.c -o ../out/led.o
armcc -c -C -E -I ../common/ -I ../driver -g --apcs=interwork --cpu=Cortex-R5 ../common/led.c -o ../out/led.i
这样之后,可以看到预处理的结果,比如宏替换后的结果,方便分析问题。
-o 指定输出的文件名称
-D 定义宏名称,例如:-DLOG -DUART=1 -U 移除已经定义的宏名称
#define LOG
#define UART 1
在编译器命令行指定上面的宏,相当于在程序里面定义上述代码的定义
armcc -c -M -I ..\SYSTEM\sys -I ... sys.c --no_depend_single_line --md
--diag_error=warning 将err的编译消息视为warning,
--diag_suppress=3017,1256,1148 将编译消息 编码为 3017,1256,1148的诊断消息屏蔽
--diag_warning=1234,5678 屏蔽编码为 1234,5678的warning的诊断消息
--diag_warning=error 将warning视为error
--feedback=unused_section.txt 编译器阶段把没用到的代码和code单独放在一个section,方便链接阶段去除,链接阶段,生成不用的section区
--feedback=image_none 忽略链接阶段的链接脚本,忽略代码布局,则不会生成axf文件
--remove 去除不用的section
--keep memory_alyout.o\(rw\) 可以设置memory_out.o中的rw段不删除
通过feedback,空间从950k -> 800k (双core的bin 所需空间)
-O0
最小优化。关闭大多数优化。启用调试时,此选项提供最佳调试视图,因为生成代码的结构直接对应于源代码。所有干扰调试视图的优化都被禁用。
-O1
受限优化。编译器只执行可以描述为调试信息的优化。删除未使用的内联函数和未使用的静态函数。关掉严重降低调试视图的优化。如果与 –debug 一起使用,此选项会给出总体上令人满意的调试视图且具有良好的代码密度。
调试视图与 –O0 的区别在于:
-O2
高度优化。 如果与 --debug 一起使用,调试视图可能不太令人满意,因为目标代码到源代码的映射并不总是清晰的。 编译器可能会执行调试信息无法描述的优化。
这是默认的优化级别。
调试视图与 –O1 的区别在于:
-O3
最大优化。启用调试后,此选项通常会提供较差的调试视图。 ARM 建议在较低的优化级别进行调试。
如果同时使用 -O3 和 -Otime,编译器会执行更积极的额外优化,例如:
#pragma arm 编译成arm指令
#pragma thumb 编译成thumb指令
#pragam push 保存#pragma 状态
#pragma pop 弹出状态 与上面的可以一起使用
#pragma pack(n) 设置 n字节对齐,对于结构体来说。
__asm return-type function-name(parameter-list)
{
// ARM/Thumb assembly code
instruction{;comment is optional}
...
instruction
}
/*示例1*/
__asm int f(int i)
{
ADD r0, r0, #1
}
/*示例2*/
#include
__asm void my_strcpy(const char *src, char *dst)
{
loop
LDRB r2, [r0], #1
STRB r2, [r1], #1
CMP r2, #0
BNE loop
BX lr
}
int main(void)
{
const char *a = "Hello world!";
char b[20];
my_strcpy (a, b);
printf("Original string: '%s'\n", a);
printf("Copied string: '%s'\n", b);
return 0;
}
内联汇编
int f(int x)
{
__asm
{
STMFD sp!, {r0} // save r0 - illegal: read before write
ADD r0, x, 1
EOR x, r0, x
LDMFD sp!, {r0} // restore r0 - not needed.
}
return x;
}
The function must be written as:
int f(int x)
{
int r0;
__asm
{
ADD r0, x, 1
EOR x, r0, x
}
return x;
}
int foo(int x, int y)
{
__asm
{
SUBS x,x,y
BEQ end
}
return 1;
end:
return 0;
}
汇编选项一般都比较少,只是编译启动文件会用到,例如STM32的等,都不多,选择对应的CPU以及架构,基本就可以。
--cpu Cortex-M3 -g --apcs=interwork --pd "__MICROLIB SETA 1"
-I D:\SoftWare\MDK\system\Packs\Keil\STM32F1xx_DFP\2.3.0\Device\Include
--pd "__UVISION_VERSION SETA 537" --pd "STM32F10X_HD SETA 1" --list ".\Listings\*.lst" --xref -o "*.o" --depend "*.d"
Image结构与视图
链接器优化
链接符号管理
链接脚本语法
链接选项
官网下载