У меня есть встроенная сборка x86 (32-разрядная) в коде C ++, который компилируется для 32-разрядной Linux с помощью g cc 9.3.0. В конце вызова asm
я получаю ошибку 'asm' has impossible constraints
. Он не сообщает мне , какие ограничения являются «невозможными» и почему.
Я понимаю синтаксис оператора __asm__(...)
, как описано здесь , и я потратил некоторое время, пытаясь доказать, почему каждое ограничение является правильным, но я не могу понять, какие ограничения использовать добавить или удалить сборку, нарушающую ограничение.
Вот код:
extern int view_pitch;
static int scale;
static int rle_remainder;
static int height;
static short *color_table;
static PIXEL *RLE_palette;
static int repeat;
static PIXEL *dest;
static UCHAR *src;
void RLE_blit(void)
{
__asm__ __volatile__ (
" mov %2, %%edi\n"
" push %%ebp\n"
" mov %3, %%edx\n"
"mloop:\n"
" xor %%ecx, %%ecx\n"
" mov (%%edx), %%cl\n"
" add $0x2, %%edx\n"
" imul %4, %%ecx\n"
" add %0, %%ecx\n"
" xor %%eax, %%eax\n"
" mov %%ecx, %%ebx\n"
" and $0xffff, %%ecx\n"
" shr $0x10, %%ebx\n"
" mov %%ecx, %0\n"
" cmp $0x0, %%ebx\n"
" jbe mloop\n"
// color region code
" mov -0x1(%%edx), %%al\n"
" mov %%eax, %%ecx\n" // save orginal pixel in eax
" and $0x1f, %%eax\n" // now modulo original pixel by 32 to get offset
" mov %5, %%esi\n" // pointer to color table
" jz bypass\n"
" shr $0x5, %%ecx\n" // divide pixel by 32 to get region
" mov (%%esi,%%ecx,2), %%cx\n" // get region number from region array into cx
" shl $0x5, %%ecx\n" // multiply by 32 to get start of region
" mov %6, %%esi\n"
" add %%ecx, %%eax\n" // add offset
" mov (%%esi,%%eax,2), %%ax\n"
" cmp %1, %%ebx\n" // is run length <= than remaining column height
" jbe no_run_len_adjust\n" // yes: don't adjust
" mov %1, %%ebx\n" // no : set run length to height
"no_run_len_adjust:\n"
" sub %%ebx, %1\n"
" mov %7, %%ebp\n"
" mov %8, %%esi\n"
"run_len_loop:\n" // BEGIN outer run length loop
" mov %%ebp, %%ecx\n"
"col_loop:\n" // BEGIN inner column repeat loop
" dec %%ecx\n"
" mov %%ax, (%%edi,%%ecx,2)\n"
" jnz col_loop\n" // END column repeat loop
" add %%esi, %%edi\n"
" dec %%ebx\n"
" jnz run_len_loop\n" // END run length loop
" cmp $0x0, %1\n"
" jg mloop\n"
" jmp exit1\n"
"bypass:\n"
" mov %%ebx, %%eax\n"
" imul %8, %%eax\n"
" add %%eax, %%edi\n"
" sub %%ebx, %1\n"
" cmp $0x0, %1\n"
" jg mloop\n"
"exit1:\n"
" pop %%ebp\n"
:"+m"(rle_remainder), "+m"(height)
:"m"(dest), "m"(src), "m"(scale), "m"(color_table), "m"(RLE_palette), "m"(repeat), "m"(view_pitch)
:"esi", "edi", "eax", "ebx", "ecx", "edx", "memory"
);
}
Вот краткое изложение того, почему я думаю то, что у меня есть справа:
- Выходы:
rle_remainder
и height
читаются и записываются, поэтому +m
подходит. - Входные данные:
dest
, src
, scale
, color_table
, RLE_palette
, repeat
и view_pitch
отображаются только для чтения; они не находятся в позиции «назначения» ни в одной инструкции. Таким образом, m
означает, что они (только) считываются из. - Список закрытых регистров (
"esi", "edi", "eax", "ebx", "ecx", "edx"
) содержит все регистры, которые были изменены. Поскольку некоторые из модифицированных регистров являются 8- или 16-разрядными «частями» этих регистров, например ax
, эти части регистров не нужно явно указывать, потому что весь 32-разрядный регистр является частью of уже указан как clobbered. "memory"
указан как clobbered, чтобы обеспечить барьер чтения / записи для адресов памяти, в которые выполняется запись, а не только для ячеек памяти самих выходов. Например, mov %%ax, (%%edi,%%ecx,2)
уничтожает память. - Я не использовал ключевое слово
goto
, потому что не используются метки C - единственные метки, на которые выполняется переход - это метки внутри этого блока встроенной сборки. - Указатель кадра
ebp
изменяется в ходе выполнения кода, но восстанавливается в конце, поэтому я не указал его как затертый. Если я его перечислю, g cc выдаст ошибку, потому что вам не разрешено сообщать g cc, что вы больше не смываете указатель кадра, я не думаю. В любом случае, код не может быть скомпилирован с или без ebp
, названного как затертый. - Предположение: может ли он жаловаться, что
ebp
вообще изменяется ? Даже если мы восстановим его значение обратно к исходному в конце кода - гарантированно - если программа не выйдет из строя? В документации не было ясно, является ли правило «вы не можете изменить ebp
вообще , даже временно» или «если вы изменяете ebp
, значение в конце вашего встроенного asm должен быть восстановлен до оригинала ". Если это первое, то мой код неверен, потому что ebp
изменяется во время выполнения этого кода.
Ясно, что мне что-то не хватает в ожиданиях g cc в отношении ограничений или сборка, но я не могу определить, что это.