Как указать непосредственные числа с плавающей запятой при встроенной сборке? - PullRequest
9 голосов
/ 29 июня 2011

Когда я пытаюсь скомпилировать этот код:

#include <stdio.h>

main(int argc, char *argv[]) {
   double y = 0;

   __asm__ ("fldl $150;"
            "fsqrt;"
            "fstl %0;" : : "g" (y) );

   printf("%f\n", y);


   return 0;
}

Я получаю эту ошибку:

sqrt.c: Assembler messages:
sqrt.c:6: Error: suffix or operands invalid for `fld'

Почему это не работает? Почему я не могу поместить число «150» в стек для операций с плавающей запятой?

Ответы [ 5 ]

9 голосов
/ 29 июня 2011

Я не знаю языка ассемблера, который поддерживает литеральные константы с плавающей запятой для немедленного использования.Обычное средство - объявить инициализированное хранилище, содержащее константу с плавающей запятой, и сослаться на нее:

const1:     dq  1.2345
...
     fldl    const1

Для приведенного вами примера это можно сделать более непосредственно:

printf ("%f\n", sqrt (150));

В противном случае это должен быть искусственно сложный проект, возможно, домашнее задание.

5 голосов
/ 29 июня 2011

Попробуйте что-то вроде этого

push $0x????????
push $0x????????
fldl (%esp)
addl $8,%esp

Где ???????? заменяются IEEE представлением двойной константы. Преимущество этого метода состоит в том, что он одинаково хорошо работает в обычном и независимом от позиции (PIC, т.е. совместно используемой библиотеке) коде.

1 голос
/ 20 октября 2018

t ограничение

Согласно документации GCC https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints

t

Вершина стека с плавающей запятой 80387(% st (0)).

Итак, мы можем сделать:

#include <assert.h>

int main(void) {
    double io = 4.0;
    __asm__ (
        "fsqrt"
        : "+t" (io)
        :
        :
    );
    assert(io == 2.0);
    return 0;
}

GitHub upstream .

Переподготовка: + означает, что io будет использоваться как для ввода, так и для вывода.

Протестировано в Ubuntu 19.04.

Сборка GNU GAS ARM поддерживает это

Например, в ARMv8:

main.c

#include <assert.h>

int main(void) {
    float my_float = 1.5;
    __asm__ (
        "fmov s0, 1.0;"
        "fadd %s[my_float], %s[my_float], s0;"
        : [my_float] "+w" (my_float)
        :
        : "s0"
    );
    assert(my_float == 2.5);
}

GitHub upstream .

Скомпилируйте и запустите:

aarch64-linux-gnu-gcc -o main.out -static -std=gnu99 main.c
qemu-aarch64 ./main.out

Модификатор %s упоминается по адресу: ARMv8, встроенная сборка выходных данных с плавающей запятой

Он также работает в ARMv7.

Однако по некоторым причинам он работает только для плавающихточечные инструкции, такие как fmov, например, следующая попытка ARMv7 не удается собрать:

mov r0, 1.5

с ошибкой:

Error: garbage following instruction -- `mov r0,1.5'

предположительно, потому что она использует инструкцию mov, которая действуетна регистры общего назначения вместо плавающих рoint of.

Однако, возможно, это не имеет большого значения, так как по большей части вы просто хотите выполнить свои операции с плавающей запятой в ваших регистрах с плавающей запятой, а затем выполните fcmp, а затем vmrs какin:

vmov s0, 1.5
vmov s1, 2.5
fadds s2, s0, s1
vmov s3, 4.0
/* Compare two floating point registers. Stores results in fpscr:
 * (floating point status and control register).
 */
vcmp.f32 s2, s3
/* Move the nzcv bits from fpscr to apsr */
vmrs apsr_nzcv, fpscr
/* This branch uses the Z bit of apsr, which was set accordingly. */
beq theyre_equal

GitHub upstream .

Меня не перестает удивлять, как GNU GAS имеет слегка различающийся синтаксис для каждой арки!

Iоднако не удалось найти синтаксис с шестнадцатеричным литералом: Как использовать шестнадцатеричные литералы с плавающей точкой в ​​GNU GAS?

Протестировано в Ubuntu 18.04.

1 голос
/ 29 июня 2011

Единственными допустимыми операндами для инструкции fld являются память или регистр стека с плавающей запятой.

(Кроме того, вы указали y в качестве входного операнда для блока asm, в то время как это должен быть выход. хорошо.)

Если вы действительно хотите сделать это с помощью встроенной сборки:

#include <stdio.h>

int main(void)
{
   double y;
   const double k = 150.0;

   __asm__ ("fldl %1;"
            "fsqrt;"
            "fstl %0;" : "=m" (y) : "m" (k) );

   printf("%f\n", y);

   return 0;
}
0 голосов
/ 26 октября 2017

Вы можете обойти многие ассемблеры, отказывающиеся поддерживать литералы с плавающей точкой, если PHP предварительно обработает литерал для вас. (rawSingleHex взят из здесь ). В идеальном мире препроцессора C было бы достаточно, но на данный момент это не так.

<?php
function rawSingleHex($num) {
    return '0x' . strrev(unpack('h*', pack('f', $num))[1]);
}
?>

#include <stdio.h>

int main(int argc, char **argv) {
   float y = 0;

   __asm__ ("pushl $<?php echo rawSingleHex(150);?>\n"
            "fsqrt\n"
            "fstl %0\n" : : "g" (y));

   printf("%f\n", y);


   return 0;
}

запустите php для генерации файла c и запустите компилятор c для компиляции вашей программы: P

...