【问题标题】:can gcc always generate word access to register?gcc 总是可以生成字访问寄存器吗?
【发布时间】:2017-09-09 01:04:50
【问题描述】:

我在 ARMv7 平台上工作,遇到了寄存器访问问题。 设备模块中的寄存器对访问有很强的WORD要求:

typedef unsigned char u8;
struct reg {
    u8 byte0; u8 byte1; u8 byte2; u8 byte3;
};

当尝试这样的c代码时:reg.byte0 = 0x3,通常gcc会生成类似LDRB r1, [r0]的汇编代码,这个字节操作会导致我平台的未定义行为。 它有一个选项,以便 gcc 将生成代码“读取 reg,掩码 byte0”,然后生成一个双字“LDR r1,[r0]”而不是“LDRB”操作码?

更新:我想访问的目的地是 SOC 上的设备寄存器。它有 4 个字段,我们使用一个表示该寄存器的结构。访问byte0 字段如reg.byte0 = 3 通常会生成字节访问汇编代码。我想知道这种c 代码reg.byte0=3 是否可以组装成字访问(32 位,LDR)代码。 真的很抱歉我的英语不好!

更新:该示例只是对现实世界的简化。并且volatile内存屏障 也用于linux 驱动程序。只是忘记添加示例。这是我正在研究的ARM11。 1) 似乎memcpy 对我不好,因为不同的寄存器有不同的字段,我不能写所有的访问内联函数 2) 使用union 似乎很有效,我会在完成测试时更新结果。

UPDATE2:只需测试union,它仍然无法在我的平台上运行。 我认为更好的方法是使用显式字访问并且不要混淆编译器。

UPDATE3:似乎其他人发布了完全相同的问题,并且已经解决。 Force GCC to access structs with words

谢谢你们!

【问题讨论】:

  • 什么是u8?使用标准的固定宽度类型。而LDR 使用一个字(即 32 位)访问。下降将是 64 位访问,即LDRD。不清楚你的问题是什么。如果您想要 32 位访问,请使用 32 位变量。对于 64 位访问,请使用 64 位整数类型。
  • 抱歉声明不清楚:我的意思是 word 访问(32 位),u8 代表 unsigned char
  • 我相信如果你希望它只作为一个单词被访问,你需要将它声明为volatile int(或者其他一些类似单词的类型,可能是无符号的)。然后,您可以使用辅助函数来加载/存储字节。
  • 在 x86_64 上,如果您删除 volatile int x; struct A { char a,b,c,d; }; void f(){ union { int i; struct A a; } e; e.i=x; e.a.b=3; x=e.i; } 中的 volatile,差异会非常明显。使用 -O3 进行编译时,将提供不带 volatile 的字节访问和带 volatile 的字访问。
  • 如果有可用的标准类型,请不要使用自制类型!这就是stdint.h 的用途!请注意,((un)signed) char 通常不保证为 8 位宽。

标签: c gcc arm


【解决方案1】:

你可以使用内联汇编:

static inline u8 read_reg_b0(const struct reg *rp) __attribute__((always_inline)) {
    struct reg r;
    u32 tmp;
    __asm__("ldr %0, %1" : "=r" (tmp) : "m" (*rp));
    memcpy(&r, &tmp, 4);
    return r.b0;
}
static inline void write_reg_b0(struct reg *rp, u8 b0) __attribute__((always_inline)) {
    struct reg r;
    u32 tmp;
    __asm__("ldr %0, %1" : "=r" (tmp) : "m" (*rp));
    memcpy(&r, &tmp, 4);
    r.b0 = b0;
    memcpy(&tmp, &r, 4);
    __asm__("str %1, %0" : "=m" (*rp) : "r" (tmp));
}

GCC 将优化掉 memcpy 但不能修改汇编指令。

【讨论】:

  • 不需要汇编语言,memcpy 就更少了(反正这里是个坏主意)。
  • @Olaf 'you can go' 似乎表达了“不需要组装”的观点,但每个人都同意组装可以工作(或者什么都不会)。此外,memcpy 明确表示编译器应将其优化为将“字”传输从设备传输到“内存”影子的最有效方式。对于 OPs 糟糕的问题,这似乎是一个可以接受的答案。 它有 4 个字段,我们使用一个结构来表示这个寄存器。严格的内存排序 直接冲突,解决它的一种方法是使用阴影。
  • @Olaf ASM 精确保证使用哪条指令访问寄存器,而 memcpy 纯粹是在局部变量上进行安全类型双关。您不能保证第一个使用 union 或 volatile,第二个是明确定义的行为。
  • @ephemient 1) 您不能使用memcpy 进行类型双关语。 2) 有更简单和正确的方法可以用标准 C 编写它,甚至更快。 memcpy 的开销在嵌入式系统上是不能容忍的,因为此类访问是在中断处理程序等中完成的。
  • 1) 您绝对可以使用 memcpy 进行类型双关语,它在标准 C 中定义良好,不像通过不同的联合字段或指针访问。 stackoverflow.com/q/11639947 2) 在任何优化级别 GCC 都不会调用 memcpy。如果您查看它的汇编器输出,会发现 rtmp 表示内存中的相同字节,它不会生成对其他函数的任何调用,也不会内联 memcpy。
猜你喜欢
  • 2011-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-30
  • 2014-06-27
  • 1970-01-01
  • 2021-05-03
  • 1970-01-01
相关资源
最近更新 更多