简体   繁体   English

如何使用内联汇编指定立即浮点数?

[英]How do I specify immediate floating point numbers with inline assembly?

When I try to compile this code:当我尝试编译此代码时:

#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;
}

I get this error:我收到此错误:

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

Why doesn't this work?为什么这不起作用? Why can't I push the number "150" onto the stack for floating point operations?为什么我不能将数字“150”压入堆栈以进行浮点运算?

I do not know of an assembly language which supports literal floating point constants for immediate use.我不知道有一种汇编语言支持立即使用的文字浮点常量。 The usual means is to declare initialized storage containing the floating point constant and referencing it:通常的方法是声明包含浮点常量的初始化存储并引用它:

const1:     dq  1.2345
...
     fldl    const1

For the example you give, it is possible to do this more directly:对于您给出的示例,可以更直接地执行此操作:

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

Otherwise, this must be an artificially complicated project, perhaps homework.否则,这一定是一个人为的复杂项目,也许是功课。

Try something like this尝试这样的事情

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

Where the????????'s are replaced by the IEEE representation of the double constant.其中?????????的被双常数的IEEE表示所取代。 This method has the advantage that it works equally well in normal and position-independent (PIC, ie shared library) code.这种方法的优点是它在正常和位置无关(PIC,即共享库)代码中同样有效。

t constraint t约束

According to the GCC docs https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints根据 GCC 文档https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints

t

Top of 80387 floating-point stack (%st(0)). 80387 浮点堆栈的顶部 (%st(0))。

So we can do:所以我们可以这样做:

#include <assert.h>

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

GitHub upstream . GitHub 上游

Refresher: + means that io will be used both as input and output.复习: +表示io将用作输入和 output。

Tested in Ubuntu 19.04.在 Ubuntu 19.04 中测试。

GNU GAS ARM assembly supports it GNU GAS ARM 组件支持它

Eg in ARMv8:例如在 ARMv8 中:

main.c 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 . GitHub 上游

Compile and run:编译并运行:

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

The %s modifier is mentioned at: ARMv8 floating point output inline assembly %s修饰符在: ARMv8 floating point output inline assembly

It also works on ARMv7.它也适用于 ARMv7。

However, for some reason, it only works for floating point instructions such as fmov , eg the following ARMv7 attempt fails to assemble:但是,由于某种原因,它仅适用于fmov等浮点指令,例如以下 ARMv7 尝试无法汇编:

mov r0, 1.5

with error:有错误:

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

presumably because it uses the mov instruction, which acts on general purpose registers instead of floating point ones.大概是因为它使用了mov指令,该指令作用于通用寄存器而不是浮点寄存器。

However perhaps this doesn't matter much, as for the most part you just want to do your floating operations on your floating point registers, and then do an fcmp followed by vmrs as in:然而,也许这并不重要,因为在大多数情况下,您只想对浮点寄存器进行浮点运算,然后执行fcmp ,然后执行vmrs ,如下所示:

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 . GitHub 上游

It never ceases to amuse me how GNU GAS has subtly different syntax for every arch! GNU GAS 对每个拱门都有微妙的不同语法,这总是让我感到好笑!

I could not however find a hex float literal syntax: How to use hexadecimal floating point literals in GNU GAS?但是我找不到十六进制浮点文字语法: How to use hexadecimal floating point literals in GNU GAS?

Tested on Ubuntu 18.04.在 Ubuntu 18.04 上测试。

The only valid operands for the fld instruction are memory or a floating-point stack register. fld指令的唯一有效操作数是 memory 或浮点堆栈寄存器。

(Also, you have specified y as an input operand for the asm block, whereas it should be an output. Probably safer to constrain that to being memory ( "m" , rather than "g" ) as well.) (此外,您已将y指定为asm块的输入操作数,而它应该是 output。将其限制为 memory ( "m" ,而不是"g" )可能更安全。)

If you really want to do this with inline assembly:如果你真的想用内联汇编来做到这一点:

#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;
}

You can bypass many assemblers refusing to support float literals by having PHP preprocess the literal for you.您可以通过让 PHP 为您预处理文字来绕过许多拒绝支持浮点文字的汇编程序。 (rawSingleHex taken from here ). (rawSingleHex 取自这里)。 In a perfect world C preprocessor would be enough, but that's not true at the moment.在一个完美的世界中,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;
}

run php to generate the c file, and run c compiler to compile your program:P运行 php 生成 c 文件,然后运行 c 编译器来编译您的程序:P

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM