Ошибка встроенной сборки C: операнд «asm» имеет невозможные ограничения - PullRequest
1 голос
/ 12 июня 2019

Когда я встраивал ассемблер в язык C, я встретил следующую ошибку при компиляции этого кода с помощью команды оболочки в ubuntu linux 14.04.

    IFR_temp_measure.cpp: In function ‘void BlockTempClc(char*, char*, 
     int, int, char, int, int, int, int*, int, int*, int)’:
     IFR_temp_measure.cpp:1843:6: error: ‘asm’ operand has impossible 
    constraints);
    ^
    &make: *** [IFR_temp_measure.o] Error 1

или позиция строки кода ошибки 1842,1843 соответствуетк коду

    :"cc", "memory","q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q10", "q11", "q12", "q13", "q14", "q15","r0", "r1", "r3", "r4", "r5","r6","r8", "r9", "r10", "r12"
            );

Я пытался решить эту проблему, но в Интернете доступно несколько ссылок , есть компоновщик: Встроенная сборка Gcc, что означает, что операнд «asm» имеет невозможные ограничения »означает? и http://www.ethernut.de/en/documents/arm-inline-asm.html но не помогло.Мой код выглядит следующим образом:

    void BlockTempClc(char* src1,char* src2,int StrideDist,int height,char temp_comp1,int numofiterations,int temp_comp2,int temp_comp3,int *dstData,int width,int *dstSum,int step)
{

            volatile char array1[16] = {0,0,0,0,0,0,0,0,
                                       0,0,0,0,0,0,0,0};
            volatile char array2[16] = {0,0,1,0,2,0,3,0,
                                       4,0,5,0,6,0,7,0};
            asm volatile(   
            "mov        r0, %0; " //image[0]    
            "mov        r1, %1; "  //image[1] 
            "mov        r12,%11; " //m
            "mov        r3, %4; " //n
            "mov        r4, %2; " //store data
            "mov        r8, %12; " //step down for loading next line of image
            "mov        r5, %6; " //numofiterations
            "mov        r6, %3; " //out

            "mov.8 r9,%5;"//isp_temp_comp
            "mov.8 r10,%7;"//led_temp_comp
            "mov.8 r11,%8;"//fac_temp_comp


            "vdup.8 d20,r9;"//copy arm register value isp_temp_comp to neon  register
            "VMOV.S16 q9, d20; " //isp_temp_comp transfer to signed short type

            "VLD1.8     {d16,d17}, [%9];"//q8  array1 sum
            "VLD1.8     {d6,d7}, [%10];"//q3  array2

            "VMOV.S16   q0, #256; "
            "VMOV.S16   q1, #2730; " //Assign immediate number 2730 to each 16 bits of d1       

            ".loop:;"           

            "vdup.8 d21,r10;"//copy arm register value led_temp_comp to neon  register 
            "vdup.8 d22,r11;"//copy arm register value fac_temp_comp to neon  register 

            "VLD1.8    d14, [r1],r8; "    // q7  *(image[1] + tmp + n)  Load: Load Picture Pixels   r6:move step  ?
            "VLD1.8    d15, [r0],r8 "    // *(image[0] + tmp + n)  Load: Load Picture Pixels            

            "PLD        [r1]; " //Preload: one line in cache
            "PLD        [r0]; "  //?

            "VMOV.S16  q5, d14; " //q5    8*16  transfer to signed short type:*(image[1] + tmp + n) 
            "VMOV.S16  q6, d15; " //q6    8*16  transfer to signed short type : *(image[0] + tmp + n) 

            "VADD.S16  q12,q6, q9;"//*(image[0] + tmp + n) + isp_temp_comp              
            "VMOV.S16  q6, d21; " //led_temp_comp
            "VADD.S16  q13,q12, q6;"//*(image[0] + tmp + n) + isp_temp_comp+ + led_temp_comp
            "VMOV.S16  q6, d22; " //fac_temp_comp
            "VADD.S16  q14,q13, q6;"//*(image[0] + tmp + n) + isp_temp_comp+ + led_temp_comp+ fac_temp_comp
            "VSUB.S16  q15,q14, q1;"//*(image[0] + tmp + n) + isp_temp_comp+ + led_temp_comp+ fac_temp_comp-2730
            "VMLA.S16   q15, q5, q0;"//img_temp[m][n]=*(image[0] + tmp + n) + isp_temp_comp+ + led_temp_comp+ fac_temp_comp-2730+*(image[1] + tmp + n) *256 


            "VADD.S16  q2,q15, q8;"//sum                
            "VMOV.S16    q8, q2; " //q8


            "vdup.8 d20,r3;"//n 
            "vdup.8 d21,r12;"//m

            "VMOV.S16  q11, d20; " //n
            "VMOV.S16  q10, d21; " //m

            "VADD.S16  q4,q3, q11;"//(n,n+1,n+2,n+3,n+4,n+5,n+6,n+7)
            "VADD.S16  q7,q3, q10;"//(m,m+1,m+2,m+3,m+4,m+5,m+6,m+7)  q7


            "VST1.16     {d30[0]}, [r4]!; "//restore img_temp[m][n] to pointer data
            "VST1.16     {d14[0]}, [r4]!; "//restore m
            "VST1.16     {d8[0]}, [r4]!; "  //restore n

            "VST1.16     {d30[1]}, [r4]!; "     
            "VST1.16     {d14[1]}, [r4]!; "
            "VST1.16     {d8[1]}, [r4]!; "

            "VST1.16     {d30[2]}, [r4]!; "     
            "VST1.16     {d14[2]}, [r4]!; "
            "VST1.16     {d8[2]}, [r4]!; "

            "VST1.16     {d30[3]}, [r4]!; "     
            "VST1.16     {d14[3]}, [r4]!; "
            "VST1.16     {d8[3]}, [r4]!; "//response to array

            "subs        r5, r5, #1; "   // decrement: numofinteration -= 1;
            "bne        .loop; "        // Branch If Not Zero; to .loop
            "VST1.16     {d4[0]}, [r6]!; "//q2 refer to sum restore the final result to pointer out
            "VST1.16     {d4[1]}, [r6]!; "
            "VST1.16     {d4[2]}, [r6]!; "
            "VST1.16     {d4[3]}, [r6]!; "
            "VST1.16     {d5[0]}, [r6]!; "
            "VST1.16     {d5[1]}, [r6]!; "
            "VST1.16     {d5[2]}, [r6]!; "
            "VST1.16     {d5[3]}, [r6]!; "

            :"+r"(src1),"+r"(src2),"+r"(dstData),"+r"(dstSum),"+r"(height)
            :"r"(temp_comp1),"r"(numofiterations),"r"(temp_comp2),"r"(temp_comp3),
                "r"(array1),"r"(array2), "r"(width),"r"(step)
            :"cc", "memory","q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q10", "q11", "q12", "q13", "q14", "q15","r0", "r1", "r3", "r4", "r5","r6","r8", "r9", "r10", "r12"
            );
}

Я полагаю, проблема может быть в выводе списков операндов или выводе списков операндов.Что является причиной ошибки моего кода? И как ее решить?

1 Ответ

3 голосов
/ 12 июня 2019

Вы объявляете клобберы в большинстве целочисленных регистров, но затем запрашиваете 13 различных входных переменных.32-битный ARM имеет только 16 регистров, и 2 из них - это ПК и SP, оставляя только 14 в лучшем случае регистров действительно общего назначения.

Мы можем проверить, что проблема заключается в слишком большом количестве операндов с клобберами +, удалив все клобберына r0 .. r12;это позволяет компилировать (в неправильный код !!).https://godbolt.org/z/Z6x78N Это , а не решение, потому что оно приводит к огромным ошибкам, я просто подтвердил, что это проблема.

Каждый раз, когда запускается встроенный шаблон asmс mov для копирования из операнда входного регистра в жестко запрограммированный регистр, вы обычно делаете это неправильно. Даже если у вас достаточно регистров, компилятору придется выдавать код, чтобы получить переменную врегистр, тогда ваш рукописный ассм использует другой mov, чтобы скопировать его без причины.

См. https://stackoverflow.com/tags/inline-assembly/info для получения дополнительных руководств.

Вместо этого попросите компилятор длясначала введите в этот регистр register int foo asm("r0"), или лучше , чтобы компилятор выполнил распределение регистров , используя %0 или эквивалентный именованный операнд, например %[src1], вместо жестко кодированного r0везде внутри вашего шаблона asm.Синтаксис для именования операндов: [name] "r" (C_var_name).У них нет , но они не обязательно должны быть уникальными;часто удобно использовать то же имя операнда asm, что и имя переменной C.

Затем вы можете удалить клобберы в большинстве регистров GP.Вам нужно сообщить компилятору о любых входных регистрах, которые вы изменяете , например, используя ограничение "+r" вместо "r" (и затем не используйте эту переменную C после того, как asm ее модифицирует).Или используйте выходное ограничение "=r" и соответствующее входное ограничение, например "0" (var), чтобы поместить этот вход в тот же регистр, что и выходной операнд 0. "+r" намного проще в функции-обертке, где переменная C впоследствии не используется в любом случае.

Вы можете удалить клобберы в векторных регистрах, если вы используете фиктивные выходные операнды, чтобы заставить компилятор выполнять распределение регистров, но в принципе хорошо, если вы просто оставите эти жестко запрограммированные.

asm(  // "mov        r0, %[src1]; "   // remove this and just use %[src1] instead of r0

      "... \n\t"
      "VST1.16     {d30[0]}, [%[dstData]]!   \n\t"  //restore img_temp[m][n] to pointer data
      "... \n\t"

    : [src1]"+&r"(src1), [src2]"+&r"(src2), [dstData]"+&r"(dstData),
      [dstSum]"+&r"(dstSum), [height]"+&r"(height)

    : [temp_comp1] "r"(temp_comp1),  [niter] "r"(numofiterations),
      [temp_comp2] "r"(temp_comp2), [temp_comp3] "r"(temp_comp3),
      ...
    : "memory", "cc", all the q and d regs you use.  // but not r0..r13
   );

Вы можете посмотреть на вывод asm компилятора, чтобы увидеть, как он заполнил операнды %0 и %[name] в шаблоне asm, который вы ему дали.Используйте "instruction \n\t", чтобы сделать это читабельным, ; помещает все инструкции в одну строку в выводе asm.(Конкатенация строковых литералов C не вводит новые строки).

Объявления ранних клобберов для операндов чтения / записи гарантируют, что ни один из операндов только для ввода не делит с ними регистр, даже если они имеюткомпилятор знает, что temp_comp1 == height например.Поскольку исходное значение temp_comp1 все еще должно быть читаемым из регистра %[temp_comp1], даже после того, как что-то изменилось %[height].Так что они не могут быть, к примеру, r4.В противном случае, без & в "+&r", компилятор может выбрать его для повышения эффективности, если выходные данные записываются только после того, как все входные данные прочитаны.(например, при переносе одной инструкции, например, встроенный asm GNU C предназначен для эффективной работы).


примечание: char array1[16] и 2 не обязательно должны быть volatile;"memory" clobber в операторе asm достаточно, даже если вы просто передаете им указатели, а не используете их как "m" входные операнды.

...