Сборочный перевод

Я пытаюсь перевести следующий код 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 чтобы замаскировать эти биты.)

Мораль этой истории: вы всегда можете взглянуть на вывод компилятора для вдохновения в том, как что-то сделать, и часто увидите трюки, о которых вы изначально не задумывались.

Другие вопросы по тегам