问题描述
问题描述
我正在尝试将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
。显然,由于dst
和ldm %0!
指令的影响,stm %1!
和src
在离开switch语句时包含不同的地址。
如何解决?
我不知道如何通知GCC,dst
和src
中使用的寄存器在最后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在不同的寄存器中复制了src
和dst
,并在最后一个UNPACK
宏之后正确使用了它们。两个说明:
- 我使用
lsrs
是因为它编译为2字节的Cortex-M3本机lsrs
。如果我使用未修改的lsr
版本的标志,则它将编译为4个字节的mov.w r3,lsr #16
->默认情况下,16位Thumb 2lsr
带有's'。如果没有's',则必须使用32位Thumb 2(我必须对其进行检查)。无论如何,在这种情况下,我应该在Clobbers中添加“ cc”。 - 在上面的代码中,我删除了nmb8byteBlocks值范围检查以使其清楚。但是,当然,您的最后一句话不仅对所有C程序员都是一个好主意。