如何在扩展的GCC内联汇编中将输入操作数C寄存器变量标记为混乱的输入?

问题描述

问题描述

我正在尝试将uint32_t元素的C代码解包数组A设计为uint32_t元素的数组B,其中A的每个元素都解包为{{ 1}},因此B包含B[2*i]的低16位,而A[i]包含向右移位的B[2*i + 1]的高16位,即

A[i]

请注意,数组对齐为4,长度可变,但是B[2*i] = A[i] & 0xFFFFul; B[2*i+1] = A[i] >> 16u; 始终包含uint32_t的4的倍数,并且大小为A有足够的空间用于解包,我们在ARM Cortex-M3。

GCC内联汇编中的当前不良解决方

由于GCC在优化此拆包方面并不擅长,因此我编写了展开的C&内联汇编,以使其在可接受的代码大小和寄存器使用情况下进行速度优化。展开的代码如下:

B

其中

static void unpack(uint32_t * src,uint32_t * dst,uint8_t nmb8byteBlocks)
{
    switch(nmb8byteBlocks) {
        case 8:
            UNPACK(src,dst)
        case 7:
            UNPACK(src,dst)
        ...
        case 1:
            UNPACK(src,dst)
        default:;
    }
}

它起作用,直到GCC的优化程序决定内联函数(所需的属性)并在下一个代码中重用寄存器变量#define UNPACK(src,dst) \ asm volatile ( \ "ldm %0!,{r2,r4} \n\t" \ "lsrs r3,r2,#16 \n\t" \ "lsrs r5,r4,#16 \n\t" \ "stm %1!,{r2-r5} \n\t" \ : \ : "r" (src),"r" (dst) \ : "r2","r3","r4","r5" \ ); src。显然,由于dstldm %0!指令的影响,stm %1!src在离开switch语句时包含不同的地址。

如何解决

我不知道如何通知GCC,dstsrc中使用的寄存器在最后dst中的最后一个UNPACK宏之后无效。

我试图将它们作为输出操作数传递给所有或仅最后一个宏(case 1:)或以某种方式(如何)将它们包含在内联asm修饰符中,但是这只会使寄存器再次因错误代码而变得更糟。 / p>

只有一种解决方案是禁用函数内联("=r" (mem),"=r" (pma)),但是在这种情况下,我失去了GCC的优势,它可以在编译时知道nmb8byteBlocks的情况下削减适当数量的宏并将其内联。 (将代码重写为纯汇编代码也有同样的缺点。)

是否有可能在嵌入式组装中解决此问题?

解决方法

认为,您正在寻找+约束修饰符,这意味着“此操作数既可读取也可写入”。 (请参阅GCC内联汇编文档的“ Modifiers”部分。)

您还需要告诉GCC这个asm读写内存; easiest way要做的是将"memory"添加到更新列表。而且您用lsrs破坏了“条件代码”,因此也需要"cc"破坏者。试试这个:

#define UNPACK(src,dst) \
    asm volatile ( \
        "ldm     %0!,{r2,r4} \n\t" \
        "lsrs    r3,r2,#16 \n\t" \
        "lsrs    r5,r4,#16 \n\t" \
        "stm     %1!,{r2-r5} \n\t" \
        : "+r" (src),"+r" (dst) \
        : /* no input-only operands */ \
        : "r2","r3","r4","r5","memory","cc" \
    );

微优化:由于您不使用班次中的条件代码,因此最好使用lsr而不是lsrs,这也使代码易于阅读几个月以后;将来,您将不会想知道是否确实需要在这里使用条件代码。编辑:提醒我,lsrs的编码比{{ 1}}以Thumb格式显示,即使不需要条件代码,也足以使用它。)

(我想说的是,如果让GCC选择暂存寄存器,您会得到更好的寄存器分配器行为,但是我不知道如何告诉它按{要求的特定数字顺序来选择暂存寄存器。 {1}}和lsr,或如何告诉它仅使用2字节Thumb指令可访问的寄存器。)

(可以使用ldm类型的输入和输出操作数来确切指定要读取和写入的内存,但是它很复杂,可能并没有太大的改进。如果您发现此代码有效,但会导致一堆无关的东西不必要地从内存中重新加载到寄存器中,请咨询How can I indicate that the memory *pointed* to by an inline ASM argument may be used?

(如果将其函数签名更改为{p>,则对于嵌入的stm可能会获得更好的代码生成

"m"

我还将尝试将unpack添加为函数的第一行。)

,

非常感谢zwol,这正是我在寻找的东西,但在GCC内联汇编页面中找不到。它完美地解决了这个问题-现在,GCC在不同的寄存器中复制了srcdst,并在最后一个UNPACK宏之后正确使用了它们。两个说明:

  1. 我使用lsrs是因为它编译为2字节的Cortex-M3本机lsrs。如果我使用未修改的lsr版本的标志,则它将编译为4个字节的mov.w r3,lsr #16->默认情况下,16位Thumb 2 lsr带有's'。如果没有's',则必须使用32位Thumb 2(我必须对其进行检查)。无论如何,在这种情况下,我应该在Clobbers中添加“ cc”。
  2. 在上面的代码中,我删除了nmb8byteBlocks值范围检查以使其清楚。但是,当然,您的最后一句话不仅对所有C程序员都是一个好主意。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...