我在ARM上进行c编程,其中内存占用和速度都有一个非常严格的约束。我正在使用GSL-2.1库,它几乎所有功能都是双重的,但我的硬件在硬件上没有浮点,因此它都是用软件完成的。因此,它会产生额外的代码大小并降低执行速度。我的处理器有180KB SRAM和1MB闪存。现在我想提高速度和内存占用,所以我查看了IDE编译器设置并获得了以下设置。
我已经阅读了有关GCC优化级别的some thread,但仍然有一些我没有得到很好的设置。您能否为ARM Cortex-M处理器的GCC详细说明每个设置。
更新: 我已经随机选中/取消选中了一些我在代码大小上没有任何差异的方框。
答案 0 :(得分:1)
当使用gcc为嵌入式系统编写代码时,重要的是要注意,与许多嵌入式系统不同,允许存储的编译器可以写成一种类型并作为另一种类型读取,并且将以至少在某种程度上可预测的方式处理整数溢出除非使用-fno-strict-aliasing
和-fwrapv
标志进行编译,否则在使用gcc编译时,依赖于此类行为的代码很容易中断。例如,虽然C标准的作者会预期该函数
// Assume usmall is an unsigned value half the size of unsigned int
unsigned multiply(usmall x, usmall y) { return x*y; }
在两个补码硬件平台上应该是安全的,在溢出时进行无声环绕,他们没有要求编译器以这种方式实现它(我想他们可能没想到任何人都在写除非模拟其他平台,否则这样一个平台的编译器会如此愚蠢。但是,使用gcc编译时,该函数可能会产生意外的副作用。
同样,在许多编译器上,例如。
struct widget_header {uint16_t length; uint8_t type_id;};
struct acme_widget {uint16_t length; uint8_t type_id; uint8_t dat[5];};
struct beta_widget {uint16_t length; uint8_t type_id; uint32_t foo;};
可以将指向任何这些类型的指针强制转换为widget_header;码 然后可以查看type_id字段并转换为更具体的类型。 然而,这些技术并不总是适用于gcc;即使是工会 包含所有三种类型的声明都在范围内,gcc将假设 访问其中一种类型的字段不可能修改 任何其他领域的相应领域。
一个更具体的例子来说明gcc如何处理别名:
struct s1 { int x; };
struct s2 { int x; };
union { struct s1 v1; struct s2 v2; } u;
static int peek(void *p)
{
struct s1 *p1 = (struct s1*)p;
return *(int*)&p1->x;
}
static void setToFive(void *p)
{
struct s2 *p2 = (struct s2*)p;
*(int*)(&p2->x) = 5;
}
static int test1a(void *p, void *q)
{
struct s1 *p1 = (struct s1*)p;
if (peek(p)!=23) return 0;
setToFive(q);
return peek(p);
}
int test1(void)
{
struct s2 v2 = {23};
u.v2 = v2;
return test1a(&u.v1, &u.v2);
}
ARM gcc 4.8.2生成
test1:
movw r3, #:lower16:u
movt r3, #:upper16:u
movs r2, #5
movs r0, #23
str r2, [r3]
bx lr
将5存储到“u”然后返回23(假设对peek
的第二次调用将返回与第一次相同的值,尽管所有指针类型转换都应该给出非常明确的指示编译器某些东西可能在别处有别名。)