在嵌入式开发领域,属性(Attributes)是编译器指令的重要扩展机制,它允许开发者直接干预编译器的代码生成行为。ARM C语言扩展(ACLE)基于GCC的__attribute__语法,针对ARM架构进行了专项优化。属性语法的一般规则遵循GCC规范,其基本结构如下:
c复制A int B x C, D y E;
在这个声明中:
重要提示:当使用属性时,建议保持声明简单明了。除非特别说明,所有属性参数必须是编译时常量。
ARM架构的AArch32过程调用标准(PCS)定义了基础规范及多个变体。在支持硬件浮点的目标平台上,AAPCS允许函数调用选择使用整数寄存器或浮点寄存器传递参数和返回结果。ACLE通过以下属性实现调用约定的灵活选择:
c复制__attribute__((pcs("aapcs"))) // 选择软件(整数)浮点调用约定
__attribute__((pcs("aapcs-vfp"))) // 选择硬件浮点调用约定
这些属性仅适用于函数和函数类型。值得注意的是,AArch64的PCS标准变体不改变参数传递方式,因此不支持PCS属性。
ACLE提供以下目标选择属性来控制代码生成:
c复制__attribute__((target("arm"))) // 强制生成A32状态代码
__attribute__((target("thumb"))) // 强制生成T32状态代码
实现必须生成所需状态的代码,除非确实无法实现。例如,在仅支持A32状态的Armv5/6目标上,如果强制函数使用T32状态,则必须通过库函数或编译器生成的函数来实现仅A32可用的浮点操作。
ACLE提供了两种对齐语法:C11/C++11标准语法和传统属性语法。对齐属性可应用于数据、函数、类型和字段:
c复制__attribute__((aligned(N))) // N必须是2的幂次方
对齐属性不会改变类型本身。例如:
c复制char x __attribute__((aligned(8))); // &x的类型仍是char*
int y __attribute__((aligned(1))); // &y的类型仍是int*
等效的C11/C++11语法:
c复制#include <stdalign.h>
alignas(16) struct S x; // C11
alignas(16) struct S x; // C++11
宏__ARM_ALIGN_MAX_PWR以2的指数形式表示静态数据的最大可用对齐(例如4表示16字节对齐)。因此以下写法总是有效的:
c复制int x __attribute__((aligned(1 << __ARM_ALIGN_MAX_PWR)));
静态对象的对齐原则上没有限制,但支持位置无关动态对象或覆盖的实现可能需要对其对齐要求施加限制。
局部对象的对齐必须满足AAPCS的要求:
宏__ARM_ALIGN_MAX_STACK_PWR表示栈支持的最大对齐(如3表示8字节对齐)。如果程序请求的对齐超出实现支持的范围,编译器应警告但不报错。
ARM架构提供三种内存屏障指令:
屏障的作用域和访问类型通过数值参数指定:
| 参数值 | 助记符 | 作用域 | 有序访问类型 |
|---|---|---|---|
| 15 | SY | 全系统 | 任意-任意 |
| 14 | ST | 全系统 | 存储-存储 |
| 13 | LD | 全系统 | 加载-加载/加载-存储 |
| 11 | ISH | 内部可共享 | 任意-任意 |
| 10 | ISHST | 内部可共享 | 存储-存储 |
| ... | ... | ... | ... |
生产者-消费者模式示例:
c复制// 生产者
value = x;
__dmb(14); // 确保value写入先于flag更新
flag = true;
// 消费者
while (!flag) {}
__dmb(15); // 确保flag读取先于value使用
use(value);
工作队列示例:
c复制// 生产者
work = new WorkItem;
work->payload = x;
__dmb(14); // 确保payload写入先于队列更新
queue_head = work;
// 消费者
while (!(work = queue_head)) {}
// 无需屏障:payload加载数据依赖于work指针
use(work->payload);
ACLE支持多种原子操作方式:
__sync内置函数__swp接口(已过时)__swp的实现示例:
c复制uint32_t __swp(uint32_t x, volatile uint32_t *p) {
uint32_t v;
do v = __ldrex(p); while (__strex(x, p));
return v;
}
强烈建议使用C++
<atomic>头文件提供的标准原子操作,__swp仅用于替换内联汇编中的SWP指令。
ACLE提供以下硬件提示指令:
__wfi():等待中断,可能进入低功耗状态__wfe():等待事件,可能进入低功耗状态__sev():发送全局事件(多核系统有效)__sevl():发送本地事件(仅当前核有效)__yield():提示当前处于可换出的任务(如自旋锁)c复制void __pld(void const volatile *addr); // 预取数据到最内层缓存
void __pldx(unsigned int access_kind, // 访问类型(读/写)
unsigned int cache_level, // 缓存层级(L1/L2/L3)
unsigned int retention_policy, // 保留策略
void const volatile *addr);
访问类型参数:
c复制void __pli(void *addr); // 预取指令到统一缓存
void __plix(unsigned int cache_level,
unsigned int retention_policy,
void *addr);
c复制uint32_t __ror(uint32_t x, uint32_t y); // 循环右移
uint32_t __rev(uint32_t x); // 字节序反转
uint32_t __rbit(uint32_t x); // 位序反转(仅支持RBIT指令的目标)
c复制unsigned int __clz(uint32_t x); // 计算前导零数量
unsigned int __cls(uint32_t x); // 计算前导符号位数量
c复制int32_t __smulbb(int32_t, int32_t); // 低半字×低半字
int32_t __smulbt(int32_t, int32_t); // 低半字×高半字
int32_t __smulwb(int32_t, int32_t); // 32位×16位(返回高32位)
这些指令在DSP算法中特别有用,可以显著提升性能。
内存屏障使用原则:
对齐优化技巧:
__attribute__((aligned(64)))减少缓存行冲突posix_memalign原子操作选择:
__atomic_内置函数atomic_flag或C++ std::atomic__swp预取使用建议:
在嵌入式开发实践中,合理使用这些ACLE特性可以显著提升系统性能和可靠性。特别是在多核通信、设备驱动开发等场景下,正确的内存屏障使用是避免竞态条件的关键。