У меня был распайка университета надзора, чтобы написать функцию на ассемблере, которыйКак сборщик linux умножает и делит 64-битные номера?
будет принимать три 32-битовое беззнаковое число, а, б, г и
возвращает результат (а * б)/д
с декларированием этой функции:
unsigned int muldiv(const unsigned int a,
const unsigned int b,
const unsigned int d);
Обратите внимание, что мы хотим быть уверены, что никакое ненужное переполнение или сгущенный не происходит. Например,
, если а = 2^31, Ь = 2^31, д = 2^31,
ответ должен быть 2^31, несмотря на то, что а * Ь будет переполнение. (См. Дополнительные пояснения по этому вопросу ниже)
Теперь я написал простую функцию в c, которая работает, затем скомпилировала ее в машинный код, а затем разобрала обратно на ассемблерный код и, наконец, удалила ненужные инструкции.
Мой последний кусок сборки код:
muldiv:
imulq %rsi, %rax
xorl %edx, %edx
divq %rcx
ret
Который работает при компиляции в исполняемый код и проверить на нескольких тестовых случаев. Однако я не понимаю, что происходит в этом коде.
Следовательно, может кто-нибудь объяснить мне, почему этот код работает (или, возможно, это не делает?), В частности:
- почему DIVQ% RCX инструкция использует только один регистр? Я предполагаю, что это часть разделения, так как он знает, какие два аргумента?
- Как узнать, что, когда я вызываю muldiv из другого места, аргументы a, b и d сохраняются в регистрах% rsi /% rax/e.t.c, а не где-то еще?
Почему xorl% edx,% edx необходим? При удалении я получаю ошибку времени выполнения.
Как это сделать умножение на длинные длинные числа, используя только одну инструкцию, если машина может работать только на 32-битных номерах?
Разъяснение переполнения и опустошения: Эта функция должна возвращать результат, как если бы мы действуем на чисел без знака 64-битных. Код в C выглядит следующим образом:
// NOTE: when compiled to assembly code, I removed A LOT of instructions,
// but it still works
unsigned int muldiv(const unsigned int a,
const unsigned int b,
const unsigned int d) {
const unsigned long long la = a;
const unsigned long long lb = b;
const unsigned long long ld = d;
const unsigned long long ab = la * lb;
const unsigned long long ab_over_d = ab/ld;
return (unsigned int) ab_over_d;
}
И это сработало, когда называют таким образом:
#include "muldiv.h"
int main(void) {
unsigned int a = (1 << 31);
unsigned int b = (1 << 31);
unsigned int d = (1 << 31);
unsigned int result = muldiv(a, b, d);
printf("%u\n", result); // prints (1 << 31), which is correct.
return 0;
}
Вы читали справочник по программистам Intel? Это длинная книга, но действительно интересная ... и ответы на некоторые из ваших вопросов. Вы также захотите понять, какой ABI вы используете. Я найду некоторые ссылки ... (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction- набор-ссылка ручной 325383.pdf) – JCx