我正在尝试采用一段代码,其中有一个C例程的声明;它主要由内联汇编代码组成。然而 当我编译/链接它时,如果我从main调用C例程,我得到汇编/链接器错误
> C:\Documents and Settings\ge\Skrivbord\LED strip\GITHUB code>avr-gcc -g -Os -mmcu=atmega8 -c ws2.c
C:\DOCUME~1\ge\LOKALA~1\Temp/ccVFmJez.s: Assembler messages:
C:\DOCUME~1\ge\LOKALA~1\Temp/ccVFmJez.s:136: Error: symbol `pr_byte' is already defined
C:\DOCUME~1\ge\LOKALA~1\Temp/ccVFmJez.s:146: Error: symbol `one_bit' is already defined
C:\DOCUME~1\ge\LOKALA~1\Temp/ccVFmJez.s:154: Error: symbol `both_low' is already defined
C:\DOCUME~1\ge\LOKALA~1\Temp/ccVFmJez.s:163: Error: symbol `nxt_byte' is already defined
C:\DOCUME~1\ge\LOKALA~1\Temp/ccVFmJez.s:169: Error: symbol `done' is already defined
#include <avr/io.h>
#define F_CPU 8000000 // 8MHz
void update_line( const void *values, uint16_t array_size, uint8_t output_bit){
asm volatile(
" LD __tmp_reg__, %a[dataptr]+ \n"
" LDI %[bits], 8 \n" // load no of bits in byte count
"pr_byte: \n"
" LSL __tmp_reg__ \n" // Load next bit into carry flag.
" OUT %[portout], %[upreg] \n" // Go high, start of bit-symbol,
" BRCS one_bit \n" // bit value is in carry flag
" NOP \n"
" OUT %[portout], %[downreg] \n" // go low for 0-bit-symbol (3clc) (carry low)
" NOP \n"
" NOP \n"
" NOP \n"
" rjmp both_low \n" //
"one_bit: \n" //
" NOP \n"
" NOP \n"
" NOP \n"
" NOP \n"
" NOP \n"
" NOP \n"
" OUT %[portout], %[downreg] \n" // go low for the 1-bit-symbol
"both_low: \n" // both low; time to initiate next bit
" SUBI %[bits], 1 \n" // bit countdown for byte
" BREQ nxt_byte \n" //
" NOP \n" // 5nop still bits left to process
" NOP \n"
" NOP \n"
" NOP \n"
" NOP \n"
" rjmp pr_byte \n" // take next bit
// end of previous byte, pick new
"nxt_byte: \n" // 3used/10
" SBIW %[bytes], 1 \n" // byte countdown
" BREQ done \n"
" LD __tmp_reg__, %a[dataptr]+ \n" // Load next byte
" LDI %[bits], 7 \n" // load bit-in-byte count
// fill time before next bit-symbol, none left, kritical ?
"rjmp pr_byte \n" // take next
"done: NOP \n" // program should end in low note, som contents will be displayed
: /* no output */
: /* inputs */
[dataptr] "e" (values),
[upreg] "r" (set_obit_high),
[downreg] "r" (set_obit_low),
[bytes] "w" (size),
[bits] "d" (bitcount),
[portout] "I" (_SFR_IO_ADDR(WS2811_PORT))
);
} /* update_line() */
int main () {
uint8_t LED_rgb;
struct rgb LED_string[48]; // a continuous string of 3 bytes(rgb)
int LED_len = 48;
//update_line( LED_string, 48*3, 1); //<*************
} /*end main*/
`
因此,有趣的事情似乎已经生成了标签 对于&#34; update_line&#34;例程在宣布例程时, 然后在调用例程时也是如此!所以在某种程度上它似乎 整个例程变成&#34; inline&#34; (从某种意义上说,它的代码放在 呼叫本身的位置)。 我有点不知所措,无论发生了什么以及可以做些什么 (似乎有可能使用%= /生成新标签但仍然如此 为什么代码应该成倍增加)tnx Georg
答案 0 :(得分:1)
void update_line(args){ body }
不仅仅是一个声明。它是一个全局定义编译器无法省略,即使它决定将它内联到此编译单元中的每个调用者(这个.c
文件)。
因此编译器必须发出函数的独立定义。
它还必须逐字复制到函数体decides to inline到调用者的所有地方。这就是内联函数的意义。如果您不想要,请使用__attribute__((noinline))
。 gcc没有&#34;阅读&#34;你的asm,所以它没有意识到有很多代码。我认为它只是一条指令(行为与约束条件相同),因此它看起来像一个小函数,它应该在任何地方内联使用。
如果你使用static void update_line
,编译器会知道这个定义在编译单元外部是不可见的,并且省略了独立的定义。
但它仍然可以内联到两个或更多呼叫者,因此您应该使用%=
作为标签名称的一部分。 (或编号标签,并使用1f
或1b
向前/向后引用它们。
或者更好,仅对I / O指令使用内联asm。在C语言中编写分支逻辑,这样您就不需要庞大的asm块,编译器可以将该代码优化到调用者中。
或者如果你真的想在asm中编写整个函数(所以你可以使用NOP来表示延迟?),你可以把它写成一个你从C调用的独立函数。
答案 1 :(得分:1)
GCC提供了一种机制,以function chkbox(id,name) {
alert(id,name);
}
的形式抵消这种情况。
根据GCC's documentation(在特殊格式字符串下):
“%=”
输出一个对asm语句的每个实例都唯一的数字 在整个编译中。创建本地时,此选项很有用 标签并在单个模板中多次引用它们 生成多个汇编指令。
只需将此附加到标签的名称(包括引用标签的说明中)即可解决重复问题。
编译器执行此操作(如引用的引用中所述)将数字与“粘贴”汇编代码的每个实例相关联,并将%=
替换为该数字。
(我可以验证这是有效的,因为我在更小的范围内遇到了完全相同的问题,这就是我最初的结果。)