Попытка преобразовать этот ассемблерный код в C-код
# x at %ebp+8, n at %ebp+12
movl 8(%ebp), %esi
movl 12(%ebp), %ebx
movl $-1, %edi
movl $1, %edx
.L2:
movl %edx, %eax
andl %esi, %eax
xorl %eax, %edi
movl %ebx, %ecx
sall %cl, %edx
testl %edx, %edx
jne .L2
movl %edi, %eax
Я преобразовал приведенный выше код в приведенный ниже код, но я не совсем уверен, что он правильный.
int loop(int x, int n){
int result = -1;
for (mask = 1; mask >= result; mask = x&1) {
result ^= n;
}
return result;
}
x и n - это два целых числа, которые хранятся в памяти%ebp и перемещаются в реестр%esi и%ebx. Result и Mask имеют значения -1 и 1, и это из первой части кода, который я думаю после .L2:
цикл начинается, и вот где я запутался. В конце возвращается результат movl %edi, %eax
1 ответ
Ваш код полностью неверен. Вы должны были сделать некоторые тесты самостоятельно, прежде чем отправлять вопросы.
Прежде всего, mask
в вашем коде не объявлено в вашей функции.
Затем, после объявления mask
как int
, функция loop
попадет в бесконечный цикл, когда result
не станет положительным через result ^= n;
, С другой стороны, ассемблерный код не попадет в бесконечный цикл, если только n
кратно 32 (включая ноль).
Чтобы преобразовать код в сборку:
1. Я сделал прямое преобразование из сборки в C.
Обратите внимание, что я использовал неподписанный тип uint32_t
так как
- Используйте беззнаковый тип, потому что операция сдвига влево на целое число со знаком приведет к неопределенному поведению, когда происходит переполнение или значение, которое должно быть смещено, является отрицательным.
- использование
uint32_t
потому что размерunsigned int
зависит от окружения и может иметь длину менее 32 бит, в то время как регистры используются здесь (за исключением%cl
) 32-битные
Цитата из N1570 6.5.7 Операции побитового сдвига:
4 Результат
E1 << E2
являетсяE1
сдвинуты влевоE2
битовые позиции; освобожденные биты заполнены нулями. ЕслиE1
имеет тип без знака, значение результатаE1
× 2E2
, уменьшено по модулю на единицу больше максимального значения, представляемого в типе результата. ЕслиE1
имеет тип со знаком и неотрицательное значение, иE1
× 2E2
представимо в типе результата, то есть полученное значение; в противном случае поведение не определено.
Также обратите внимание, что stdint.h
или же inttypes.h
должен быть включен для использования uint32_t
,
Ширина для сдвига маскируется до 5-битной длины в процессорах x86, то есть 80286 или более поздней.
uint32_t loop(uint32_t x, uint32_t n) {
uint32_t esi = x; /* movl 8(%ebp), %esi */
uint32_t ebx = n; /* movl 12(%ebp), %ebx */
uint32_t edi = -1; /* movl $-1, %edi */
uint32_t edx = 1; /* movl $1, %edx */
uint32_t eax, ecx;
do { /* .L2: */
eax = edx; /* movl %edx, %eax */
eax &= esi; /* andl %esi, %eax */
edi ^= eax; /* xorl %eax, %edi */
ecx = ebx; /* movl %ebx, %ecx */
edx <<= (ecx & 0xff) & 31; /* sall %cl, %edx */
} while (edx != 0); /* testl %edx, %edx ; jne .L2 */
eax = edi; /* movl %edi, %eax */
return eax;
}
2. Я ввел имена переменных, чтобы прояснить их роли.
uint32_t loop(uint32_t x, uint32_t n) {
uint32_t result = -1;
uint32_t mask = 1;
uint32_t eax, ecx;
do {
eax = mask;
eax &= x;
result ^= eax;
ecx = n;
mask <<= (ecx & 0xff) & 31;
} while (mask != 0);
return result;
}
3. Я слил некоторые выражения.
uint32_t loop(uint32_t x, uint32_t n) {
uint32_t result = -1;
uint32_t mask = 1;
do {
result ^= mask & x;
mask <<= n & 31;
} while (mask != 0);
return result;
}
4. Я изменился do
цикл до for
цикл, потому что ваша попытка использует его.
uint32_t loop(uint32_t x, uint32_t n) {
uint32_t result = -1;
uint32_t mask;
for (mask = 1; mask != 0; mask <<= n & 31) {
result ^= mask & x;
}
return result;
}
Полный код для тестирования и демонстрации:
#include <stdio.h>
#include <inttypes.h>
#include <limits.h>
__asm__ (
/* support both environments that does and doesn't add underscore before function name */
"loop_asm:\n"
"_loop_asm:\n"
"push %ebp\n"
"mov %esp, %ebp\n"
"push %esi\n"
"push %edi\n"
"push %ebx\n"
"# x at %ebp+8, n at %ebp+12\n"
"movl 8(%ebp), %esi\n"
"movl 12(%ebp), %ebx\n"
"movl $-1, %edi\n"
"movl $1, %edx\n"
".L2_test:\n" /* rename .L2 to .L2_test to avoid collision */
"movl %edx, %eax\n"
"andl %esi, %eax\n"
"xorl %eax, %edi\n"
"movl %ebx, %ecx\n"
"sall %cl, %edx\n"
"testl %edx, %edx\n"
"jne .L2_test\n"
"movl %edi, %eax\n"
"pop %ebx\n"
"pop %edi\n"
"pop %esi\n"
"leave\n"
"ret\n"
);
uint32_t loop_asm(uint32_t, uint32_t);
uint32_t loop_convert(uint32_t x, uint32_t n) {
uint32_t result = -1;
uint32_t mask;
for (mask = 1; mask != 0; mask <<= n & 31) {
result ^= mask & x;
}
return result;
}
int mask;
int loop(int x, int n){
int result = -1;
for (mask = 1; mask >= result; mask = x&1) {
result ^= n;
}
return result;
}
int main(void) {
int x, n;
uint32_t raw, test, conv;
int miss_count = 0;
/* search for mismatch in some range */
for (n = 1; n < 32; n++) {
uint32_t x_test;
for (x_test = 0; x_test < UINT32_C(100000); x_test++) {
if (loop_asm(x, n) != loop_convert(x, n)) {
printf("mismatch at x=%"PRIu32", n=%d\n", x_test, n);
if (miss_count < INT_MAX) miss_count++;
}
}
}
printf("%d mismatch(es) found.\n", miss_count);
/* print some examples */
x = 100;
n = 5;
raw = loop_asm(x, n);
conv = loop_convert(x, n);
printf("loop_asm(%d, %d) = %"PRIu32"\n", x, n, raw);
printf("loop_convert(%d, %d) = %"PRIu32"\n", x, n, conv);
fflush(stdout);
test = loop(x, n);
printf("loop(%d, %d) = %"PRIu32"\n", x, n, test);
return 0;
}