Сборка AT&T: как умножить двойное на 2 без локальной переменной. Сдвиг двойной?

[домашнее задание]: попробуйте сделать именно то, что написано в коде C.
Полный код функций с C-версией прокомментирован:

/*
double f(double x) 
{
    return x * 2.0;
}

double foo (int a[], double b[], int n) 
{
    int *pint;
    double *pdouble;

double sum = 0.0;

for (pint = a, pdouble = b; n-- ; pint++, pdouble++) 
{
    *pdouble = f((double)*pint);
    sum += *pdouble;
}

return sum;
}
*/

.text

.globl f
f:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ebx

/*fldl  8(%ebp)
fstpl   8(%ebp)*/
/*movl  8(%ebp), %ebx*/
shll    $2, 8(%ebp)
fldl    8(%ebp)

popl    %ebx
movl    %ebp, %esp
popl    %ebp
ret

.globl foo
foo:    
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ebx
    pushl   %esi


subl    $16, %esp   /* for the local variables          */

fldz            /* 0 to the top of the FPU's stack      */
fstpl   (%esp)      /* double sum = 0               */

movl    8(%ebp), %ebx   /* > pint                   */
movl    %ebx, -4(%ebp)  /*           = a <  | INIT          */
movl    12(%ebp), %esi  /* > pdouble                    */
movl    %esi, -8(%ebp)  /*       = b <      | INIT          */
L1: cmpl    $0, 16(%ebp)
js  outfor


pushl   %eax
pushl   %ecx
pushl   %edx

movl    -4(%ebp), %edi  
fildl   (%edi)      /* *pint to the top of FPU'S stack      */
subl    $8, %esp    /* > pushdouble                 */
fstpl   (%esp)      /*              *pint <             */
call    f       /* f((double)*pint) to the top of FPU'S stack   */
addl    $8, %esp    /* clears double pint from stack        */

popl    %edx
popl    %ecx
popl    %eax


movl    -8(%ebp), %eax
fldl    (%eax)      /* *pdouble to the top of FPU's stack       */ 

faddl   -16(%ebp)   /* sum += *pdouble (stored in FPU'S stack)  */
fstpl   -16(%ebp)   /* sum += *pdouble (stored in sum)      */

decl    16(%ebp)

addl    $4, -4(%ebp)
addl    $8, -8(%ebp)
jmp     L1

outfor: fldl    -16(%ebp)

popl    %esi
popl    %ebx
movl    %ebp, %esp
popl    %ebp

foo позвоню f в той же точке. И моя проблема там. Как мне сообщить компилятору об угрозе xкак double? Моя лучшая ставка fldl 8(%ebp) fstpl 8(%ebp) дает мне плохие номера. Любой другой суффикс l Инструкция угрожает ему как 4 байта длиной, очевидно.

И тогда, как только я получаю правильное толкование x, как умножить, если на 2.0 (читать хорошо, а не целое число) без использования локальной переменной? Я пытался использовать shllно, похоже, не существует сдвига для double значение. Я не могу fldl $2 или.

3 ответа

Вы не можете сдвинуть двойную левую сторону, чтобы умножить ее на 2, но вы можете рассматривать двойное как 64-битное целое и увеличивать поле экспоненты (переполнение может быть проблемой). Непонятно, что подразумевается под локальной переменной. В сборке X86 вы можете использовать немедленное добавление в память для увеличения поля экспоненты, но функция возвращает значение, которое можно рассматривать как локальную переменную.

Двойное число с плавающей запятой имеет несколько разных данных в своих битах. Так что нельзя просто сдвинуть его, чтобы удвоить. Как уже упоминалось, вы можете добавить его самостоятельно. Значение с плавающей запятой должно быть возвращено в регистр FPU ST0 (Соглашение о вызове cdecl). Взгляните на следующую программу:

# Name:     double_the_double.s
# Compile:  gcc -m32 double_the_double.s
# Run:      ./a.out

.globl main

.section .data
    dubbel: .double 50.0
    result: .double 0.0
    fmt: .asciz "%f\n"

.section .text

f:
    fldl 4(%esp)
    fadd %st(0),%st(0)
    ret

main:

    pushl (dubbel+4)
    pushl (dubbel)
    push $fmt
    call printf
    add $12, %esp

    pushl (dubbel+4)
    pushl (dubbel)
    call f
    add $8, %esp
    fstpl (result)

    pushl (result+4)
    pushl (result)
    push $fmt
    call printf
    add $12, %esp

    # Exit from _main
    ret

Я вижу, что у вас есть *pint, который является целым числом, и, таким образом, вы можете использовать shl $1, %reg для умножения на 2 (а не shl $2, %reg, который умножается на 4.)

Если вы хотите умножить удвоение на 2, вы должны добавить его к себе. Вероятно, лучший / самый быстрый способ справиться с этим.

Если вы действительно хотите сделать это вручную, вам нужно найти определение двойного, его знак, его показатель степени и его мантиссу. Умножение на 2 означает добавление 1 к показателю степени:

+----+-----------+-----------+
| 63 | 62 ... 52 | 51 ... 00 |
+----+-----------+-----------+
|  X | x x x x x | x x x x x |
+----+-----------+-----------+

( Узнайте больше о форматах IEEE с плавающей запятой)

Из-за знака проще всего было бы что-то вроде этого:

mov (%r1), %r2         ; Load the double
roll $1, %r2           ; move the sign out of the way (bit 0)
mov $(1 << 53), %r3    ; add 1 to exponent (shifted by 1 to the left)
add %r3, %r2           ; do the add now
rolr $1, %r2           ; restore the sign in bit 63

Тем не менее, это не заботится о переполнениях... и это, вероятно, медленнее, чем добавление двойного к себе с помощью FPU.

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