汇编程序说:"重复符号"用于内联汇编代码标签

时间:2016-12-30 22:00:18

标签: assembly avr

我正在尝试采用一段代码,其中有一个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

2 个答案:

答案 0 :(得分:1)

void update_line(args){ body }不仅仅是一个声明。它是一个全局定义编译器无法省略,即使它决定将它内联到此编译单元中的每个调用者(这个.c文件)。

因此编译器必须发出函数的独立定义。

它还必须逐字复制到函数体decides to inline到调用者的所有地方。这就是内联函数的意义。如果您不想要,请使用__attribute__((noinline))。 gcc没有&#34;阅读&#34;你的asm,所以它没有意识到有很多代码。我认为它只是一条指令(行为与约束条件相同),因此它看起来像一个小函数,它应该在任何地方内联使用。

如果你使用static void update_line,编译器会知道这个定义在编译单元外部是不可见的,并且省略了独立的定义。

但它仍然可以内联到两个或更多呼叫者,因此您应该使用%=作为标签名称的一部分。 (或编号标签,并使用1f1b向前/向后引用它们。

或者更好,仅对I / O指令使用内联asm。在C语言中编写分支逻辑,这样您就不需要庞大的asm块,编译器可以将该代码优化到调用者中。

或者如果你真的想在asm中编写整个函数(所以你可以使用NOP来表示延迟?),你可以把它写成一个你从C调用的独立函数。

答案 1 :(得分:1)

GCC提供了一种机制,以function chkbox(id,name) { alert(id,name); } 的形式抵消这种情况。

根据GCC's documentation(在特殊格式字符串下):

  

“%=”

     

输出一个对asm语句的每个实例都唯一的数字   在整个编译中。创建本地时,此选项很有用   标签并在单个模板中多次引用它们   生成多个汇编指令。

只需将此附加到标签的名称(包括引用标签的说明中)即可解决重复问题。

编译器执行此操作(如引用的引用中所述)将数字与“粘贴”汇编代码的每个实例相关联,并将%=替换为该数字。

(我可以验证这是有效的,因为我在更小的范围内遇到了完全相同的问题,这就是我最初的结果。)