Сборочный перевод
Я пытаюсь перевести следующий код C в сборку:
void write (int bitpos, unsigned short sample)
{
int pos = bitpos / 16;
int posA = bitpos - pos * 16;
unsigned short write1 = sample >> posA;
}
Я продолжаю получать ошибку в операции смены. Я посмотрел на некоторые примеры в книге, но я не понимаю, что не так. Я думаю, что это может быть потому, что величина, которую я хочу изменить, является переменной. Мне было интересно, как правильно добиться этого?
Вот что я попробовал:
//int pos = bitpos / 16;
mov eax, 0
mov eax, [bitpos] // eax= bitpos
cdq
mov ecx, 16
idiv ecx //ecx = pos
//int posA = bitpos - pos * 16;
mov ebx, ecx //ebx = pos
imul ebx, 16 // ebx = pos*16
sub eax, ebx // eax = posA
//unsigned short write1 = sample >> posA;
mov bx, [sample]
shr bx, eax // This is the part that is not working.
Ошибка говорит: неправильный тип операнда. Код ошибки: C2415
1 ответ
Ваш write()
функция не имеет возвращаемого значения и никаких побочных эффектов (нет записи в какую-либо глобальную переменную, нет системных вызовов, только устанавливаются некоторые локальные объекты, которые отбрасываются при возврате функции). Вы можете и должны оптимизировать его до пустой функции, как это делает gcc.
global write
write:
ret
Давайте представим, что ваша функция возвращает write1
переменная, так что вы должны рассчитать его.
gcc -Og
(оптимизировать для отладки) делает хороший читаемый asm, который не сохраняет / перезагружает из памяти все время. gcc -m32 -Og -fverbose-asm -masm=intel
испускает:
# see the godbolt link for colour-coded mapping of source lines to asm lines
write(int, unsigned short):
mov edx, DWORD PTR [esp+4] # bitpos, bitpos
lea eax, [edx+15] # tmp98,
test edx, edx # bitpos
cmovns eax, edx # tmp98,, bitpos, bitpos
sar eax, 4 # tmp99,
neg eax # tmp101
sal eax, 4 # tmp102,
mov ecx, eax # tmp102, tmp102
add ecx, edx # posA, bitpos
movzx eax, WORD PTR [esp+8] # D.2591, sample
sar eax, cl # D.2591, posA
ret
Обратите внимание, как он загружает функциональные параметры из стека, потому что они являются функциональными параметрами, а не глобальными. (Ваш код ссылки [bitpos]
глобальная, а не первая позиция в стеке после адреса возврата, [esp+4]
.) 64-битный ABI передает аргументы в регистрах, так что вы получите более чистый код.
Код условного перемещения существует потому, что семантика C для целочисленного деления отрицательного числа дает разные результаты от арифметического сдвига вправо (они округляются по-разному). поскольку idiv
Это очень дорого по сравнению со сменой, но все же стоит использовать дополнительные инструкции для настройки смены. Если bitpos
был без знака, он мог просто использовать shr
,
При полной оптимизации gcc находит более эффективный способ выполнения задач и складывает часть арифметики вместе. (т.е. деление на 16, а затем умножение на 16 для округления до ближайшего кратного 16, реализуется с помощью одного and
чтобы замаскировать эти биты.)
Мораль этой истории: вы всегда можете взглянуть на вывод компилятора для вдохновения в том, как что-то сделать, и часто увидите трюки, о которых вы изначально не задумывались.