GCC 扩展 asm 区分文字与寄存器
GCC extended asm differentiating literal vs register
我发现自己广泛使用内联 asm,并且经常希望能够在给定的 asm 位中使用寄存器 或 文字,但是我不知道如何使用 Microchip XC16 套件使其成为一个选项。
据我所知,您需要手动对文字符号 #
进行编码,这是与前缀不兼容的。这意味着以下代码无法编译:
asm("MOV %1, %0" : "=r" (res) : "i" (1));
Invalid operands specified ('mov 1,w0').
但以下内容确实如此:
asm("MOV #%1, %0" : "=r" (res) : "i" (1));
这当然与寄存器不兼容:
asm("MOV #%1, %0" : "=r" (res) : "ri" (x));
Invalid operands specified ('mov #w0,w0').
所以在我看来,Microchip 没有遵循 GCC 约定,我认为文字符号应该嵌入到操作数中,这使得它特别难以使用。
我想知道机会.. 有没有人对如何解决这个问题有什么好主意?
现在我将 __builtin_constant_p
作为一个附加参数传递,然后我在 asm 中 .if
如下所示,但是说它变得笨拙是一种轻描淡写的说法。
asm(".if %[isk] \n"
"MOV #%1, %0 \n"
".elseif \n"
"MOV %1, %0 \n"
".endif \n"
: "=r" (res)
: "ri" (x), [isk] "i" (__builtin_constant_p(x));
而且我什至不相信 GCC 保证如果 isk
为真,%1
将是一个文字,这意味着必须 if-then-else 将其全部阻止在 C 端。 ..叹息。
澄清 MOV
只是一个示例说明。这些处理器 (dsPIC33E) 具有零开销的单指令和多指令循环,需要使用 asm,其语法如下所示:
/* this code demonstrates compilation failure if cnt is a constant
* as there is no # prefix before the %[cnt] */
asm("REPEAT %[cnt] \n"
" MOV [%0++], [%1++] \n"
: "+r" (src), "+r" (dst), "=m" (*dst)
: "m" (*src), [cnt] "ri" (cnt));
这个 memcpy 循环需要 cnt+1
个周期来执行,由于流水线实际上比完全展开循环快两倍,比每次迭代分支快 6 倍。连同它们的多指令 DO 循环变体,它们对于充分利用这些处理器非常重要。
我找到了一种在 asm 中检测给定参数是否为文字的方法,它远非理想但它似乎有效。
首先在一个asm头文件中,为每个寄存器标记一个符号:
.irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
.set _IS_REG_w&r&, 1 ; mark all registers as "REGs"
.set _IS_REG_W&r&, 1
.endr
然后使用:
.ifdecl _IS_REG_%0
REPEAT %0
.else
REPEAT #%0
.endif
可以用 asm 宏来包装:
.macro REPEATN cnt
.ifdecl _IS_REG_&cnt&
REPEAT \cnt
.else
REPEAT #\cnt
.endif
.endm
为了便于嵌入内联 asm:
void DelayCycles(int count)
{
asm("REPEATN %[cnt] \n"
" NOP \n"
:
: [cnt] "ri" (count));
}
我发现自己广泛使用内联 asm,并且经常希望能够在给定的 asm 位中使用寄存器 或 文字,但是我不知道如何使用 Microchip XC16 套件使其成为一个选项。
据我所知,您需要手动对文字符号 #
进行编码,这是与前缀不兼容的。这意味着以下代码无法编译:
asm("MOV %1, %0" : "=r" (res) : "i" (1));
Invalid operands specified ('mov 1,w0').
但以下内容确实如此:
asm("MOV #%1, %0" : "=r" (res) : "i" (1));
这当然与寄存器不兼容:
asm("MOV #%1, %0" : "=r" (res) : "ri" (x));
Invalid operands specified ('mov #w0,w0').
所以在我看来,Microchip 没有遵循 GCC 约定,我认为文字符号应该嵌入到操作数中,这使得它特别难以使用。
我想知道机会.. 有没有人对如何解决这个问题有什么好主意?
现在我将 __builtin_constant_p
作为一个附加参数传递,然后我在 asm 中 .if
如下所示,但是说它变得笨拙是一种轻描淡写的说法。
asm(".if %[isk] \n"
"MOV #%1, %0 \n"
".elseif \n"
"MOV %1, %0 \n"
".endif \n"
: "=r" (res)
: "ri" (x), [isk] "i" (__builtin_constant_p(x));
而且我什至不相信 GCC 保证如果 isk
为真,%1
将是一个文字,这意味着必须 if-then-else 将其全部阻止在 C 端。 ..叹息。
澄清 MOV
只是一个示例说明。这些处理器 (dsPIC33E) 具有零开销的单指令和多指令循环,需要使用 asm,其语法如下所示:
/* this code demonstrates compilation failure if cnt is a constant
* as there is no # prefix before the %[cnt] */
asm("REPEAT %[cnt] \n"
" MOV [%0++], [%1++] \n"
: "+r" (src), "+r" (dst), "=m" (*dst)
: "m" (*src), [cnt] "ri" (cnt));
这个 memcpy 循环需要 cnt+1
个周期来执行,由于流水线实际上比完全展开循环快两倍,比每次迭代分支快 6 倍。连同它们的多指令 DO 循环变体,它们对于充分利用这些处理器非常重要。
我找到了一种在 asm 中检测给定参数是否为文字的方法,它远非理想但它似乎有效。
首先在一个asm头文件中,为每个寄存器标记一个符号:
.irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
.set _IS_REG_w&r&, 1 ; mark all registers as "REGs"
.set _IS_REG_W&r&, 1
.endr
然后使用:
.ifdecl _IS_REG_%0
REPEAT %0
.else
REPEAT #%0
.endif
可以用 asm 宏来包装:
.macro REPEATN cnt
.ifdecl _IS_REG_&cnt&
REPEAT \cnt
.else
REPEAT #\cnt
.endif
.endm
为了便于嵌入内联 asm:
void DelayCycles(int count)
{
asm("REPEATN %[cnt] \n"
" NOP \n"
:
: [cnt] "ri" (count));
}