汇编代码 fsqrt 和 fmul 说明
Assembly code fsqrt and fmul instructions
我正在尝试使用内联汇编在此函数中计算 1.34 *sqrt(lght)
,但出现如下错误:
'_asm' undeclared (first use in this function)
each undeclared identifier is reported only once for each func tion it appears in
expected ';' before '{' token
我一直在研究如何解决这个问题,但找不到太多信息。有人可以建议一种使它起作用的方法吗?
我的代码是:
double hullSpeed(double lgth) {
_asm {
global _start
fld lght; //load lght
fld st(0); //duplicate lght on Top of stack
fsqrt;
square root of lght
fld st(0); //load square result on top of stack
fld 1.34; //load 1.34 on top of stack
fld st(i);
duplicate 1.34 on TOS
fmulp st(0), st(i); //multiply them
fst z;
save result in z
}
return z; // return result of [ 1.34 *sqrt(lght) ]
}
我正在使用 Ubuntu 所以 GCC,32 位
看起来您正在尝试做类似的事情:
#include <stdio.h>
double hullSpeed(double lgth)
{
double result;
__asm__(
"fldl %1\n\t" //st(0)=>st(1), st(0)=lgth . FLDL means load double float
"fsqrt\n\t" //st(0) = square root st(0)
"fmulp\n\t" //Multiplies st(0) and st(1) (1.34). Result in st(0)
: "=&t" (result) : "m" (lgth), "0" (1.34));
return result;
}
int main()
{
printf ("%f\n", hullSpeed(64.0));
}
我使用的模板可以简化,但为了演示目的就足够了。我们使用 "=&t"
约束,因为我们在 st(0)
中返回浮点堆栈顶部的结果,我们使用 ampersand 来表示早期破坏(我们'将使用浮点堆栈的顶部传递 1.34)。我们通过约束 "m" (lgth)
传递带有内存引用的 lgth
的地址,而 "0"(1.34)
约束表示我们将在与参数 0 相同的寄存器中传递 1.34,在本例中为浮点堆栈的顶部。这些是我们的汇编程序将覆盖但不会显示为输入或输出约束的寄存器(或内存)。
用内联汇编器学习汇编语言是一种非常难学的方法。特定于 x86 的机器约束可以在 here under x86 family. Information on the constraint modifiers can be found here, and information on GCC extended assembler templates can be found here.
中找到
我只是给你一个起点,因为 GCC 的内联汇编程序的使用可能相当复杂,而且任何答案对于 Whosebug 的答案来说都可能过于宽泛。事实上,您使用的是带 x87 浮点数的内联汇编器,这使得它变得更加复杂。
一旦你掌握了约束和修饰符,另一种可以由编译器产生更好的汇编代码的机制是:
__asm__(
"fsqrt\n\t" // st(0) = square root st(0)
"fmulp\n\t" // Multiplies st(0) and st(1) (1.34). Result in st(0)
: "=t"(result) : "0"(lgth), "u" (1.34) : "st(1)");
提示:约束 "u"
在 x87 浮点寄存器 st(1)
中放置一个值。汇编程序模板约束有效地将 lgth
置于 st(0)
中,将 1.34 置于 st(1)
中。 st(1)
在内联汇编完成后无效,因此我们将其列为破坏者。我们使用约束为我们将我们的值放在浮点堆栈上。这具有减少我们必须在汇编代码内部完成的工作的效果。
如果您正在开发 64 位应用程序,我强烈建议您至少使用 SSE/SSE2 进行基本的浮点计算。上面的代码应该适用于 32 位和 64 位。在 64 位代码中,x87 浮点指令通常不如 SSE/SSE2 高效,但它们可以工作。
使用内联汇编和 x87 舍入
如果您尝试基于 x87 上的 4 种舍入模式之一进行舍入,您可以使用如下代码:
#include <stdint.h>
#include <stdio.h>
#define RND_CTL_BIT_SHIFT 10
typedef enum {
ROUND_NEAREST_EVEN = 0 << RND_CTL_BIT_SHIFT,
ROUND_MINUS_INF = 1 << RND_CTL_BIT_SHIFT,
ROUND_PLUS_INF = 2 << RND_CTL_BIT_SHIFT,
ROUND_TOWARD_ZERO = 3 << RND_CTL_BIT_SHIFT
} RoundingMode;
double roundd (double n, RoundingMode mode)
{
uint16_t cw; /* Storage for the current x87 control register */
uint16_t newcw; /* Storage for the new value of the control register */
uint16_t dummyreg; /* Temporary dummy register used in the template */
__asm__ __volatile__ (
"fstcw %w[cw] \n\t" /* Read current x87 control register into cw*/
"fwait \n\t" /* Do an fwait after an fstcw instruction */
"mov %w[cw],%w[treg] \n\t" /* ax = value in cw variable*/
"and [=12=]xf3ff,%w[treg] \n\t" /* Set rounding mode bits 10 and 11 of control
register to zero*/
"or %w[rmode],%w[treg] \n\t" /* Set the rounding mode bits */
"mov %w[treg],%w[newcw]\n\t" /* newcw = value for new control reg value*/
"fldcw %w[newcw] \n\t" /* Set control register to newcw */
"frndint \n\t" /* st(0) = round(st(0)) */
"fldcw %w[cw] \n\t" /* restore control reg to orig value in cw*/
: [cw]"=m"(cw),
[newcw]"=m"(newcw),
[treg]"=&r"(dummyreg), /* Register constraint with dummy variable
allows compiler to choose available register */
[n]"+t"(n) /* +t constraint passes `n` through
top of FPU stack (st0) for both input&output*/
: [rmode]"rmi"((uint16_t)mode)); /* "g" constraint same as "rmi" */
return n;
}
double hullSpeed(double lgth)
{
double result;
__asm__(
"fsqrt\n\t" // st(0) = square root st(0)
"fmulp\n\t" // Multiplies st(0) and st(1) (1.34). Result in st(0)
: "=t"(result) : "0"(lgth), "u" (1.34) : "st(1)");
return result;
}
int main()
{
double dbHullSpeed = hullSpeed(64.0);
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_NEAREST_EVEN));
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_MINUS_INF));
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_PLUS_INF));
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_TOWARD_ZERO));
return 0;
}
正如您在评论中指出的那样,此 Whosebug answer 中有等效代码,但它使用了多个 __asm__
语句,您很好奇如何对单个 __asm__
语句进行编码.
舍入模式 (0,1,2,3) 可以在 Intel Architecture Document:
中找到
Rounding Mode RC Field
00B Rounded result is the closest to the infinitely precise result. If two values are equally close, the result is the even value (that is, the one with the least-significant bit of zero). Default Round down (toward −∞)
01B Rounded result is closest to but no greater than the infinitely precise result. Round up (toward +∞)
10B Rounded result is closest to but no less than the infinitely precise result.Round toward zero (Truncate)
11B Rounded result is closest to but no greater in absolute value than the infinitely precise result.
在8.1.5节(8.1.5.3节具体描述的舍入模式)中有对字段的描述。 4 种舍入模式在 4.8.4 节的图 4-8 中定义。
我正在尝试使用内联汇编在此函数中计算 1.34 *sqrt(lght)
,但出现如下错误:
'_asm' undeclared (first use in this function) each undeclared identifier is reported only once for each func tion it appears in expected ';' before '{' token
我一直在研究如何解决这个问题,但找不到太多信息。有人可以建议一种使它起作用的方法吗?
我的代码是:
double hullSpeed(double lgth) {
_asm {
global _start
fld lght; //load lght
fld st(0); //duplicate lght on Top of stack
fsqrt;
square root of lght
fld st(0); //load square result on top of stack
fld 1.34; //load 1.34 on top of stack
fld st(i);
duplicate 1.34 on TOS
fmulp st(0), st(i); //multiply them
fst z;
save result in z
}
return z; // return result of [ 1.34 *sqrt(lght) ]
}
我正在使用 Ubuntu 所以 GCC,32 位
看起来您正在尝试做类似的事情:
#include <stdio.h>
double hullSpeed(double lgth)
{
double result;
__asm__(
"fldl %1\n\t" //st(0)=>st(1), st(0)=lgth . FLDL means load double float
"fsqrt\n\t" //st(0) = square root st(0)
"fmulp\n\t" //Multiplies st(0) and st(1) (1.34). Result in st(0)
: "=&t" (result) : "m" (lgth), "0" (1.34));
return result;
}
int main()
{
printf ("%f\n", hullSpeed(64.0));
}
我使用的模板可以简化,但为了演示目的就足够了。我们使用 "=&t"
约束,因为我们在 st(0)
中返回浮点堆栈顶部的结果,我们使用 ampersand 来表示早期破坏(我们'将使用浮点堆栈的顶部传递 1.34)。我们通过约束 "m" (lgth)
传递带有内存引用的 lgth
的地址,而 "0"(1.34)
约束表示我们将在与参数 0 相同的寄存器中传递 1.34,在本例中为浮点堆栈的顶部。这些是我们的汇编程序将覆盖但不会显示为输入或输出约束的寄存器(或内存)。
用内联汇编器学习汇编语言是一种非常难学的方法。特定于 x86 的机器约束可以在 here under x86 family. Information on the constraint modifiers can be found here, and information on GCC extended assembler templates can be found here.
中找到我只是给你一个起点,因为 GCC 的内联汇编程序的使用可能相当复杂,而且任何答案对于 Whosebug 的答案来说都可能过于宽泛。事实上,您使用的是带 x87 浮点数的内联汇编器,这使得它变得更加复杂。
一旦你掌握了约束和修饰符,另一种可以由编译器产生更好的汇编代码的机制是:
__asm__(
"fsqrt\n\t" // st(0) = square root st(0)
"fmulp\n\t" // Multiplies st(0) and st(1) (1.34). Result in st(0)
: "=t"(result) : "0"(lgth), "u" (1.34) : "st(1)");
提示:约束 "u"
在 x87 浮点寄存器 st(1)
中放置一个值。汇编程序模板约束有效地将 lgth
置于 st(0)
中,将 1.34 置于 st(1)
中。 st(1)
在内联汇编完成后无效,因此我们将其列为破坏者。我们使用约束为我们将我们的值放在浮点堆栈上。这具有减少我们必须在汇编代码内部完成的工作的效果。
如果您正在开发 64 位应用程序,我强烈建议您至少使用 SSE/SSE2 进行基本的浮点计算。上面的代码应该适用于 32 位和 64 位。在 64 位代码中,x87 浮点指令通常不如 SSE/SSE2 高效,但它们可以工作。
使用内联汇编和 x87 舍入
如果您尝试基于 x87 上的 4 种舍入模式之一进行舍入,您可以使用如下代码:
#include <stdint.h>
#include <stdio.h>
#define RND_CTL_BIT_SHIFT 10
typedef enum {
ROUND_NEAREST_EVEN = 0 << RND_CTL_BIT_SHIFT,
ROUND_MINUS_INF = 1 << RND_CTL_BIT_SHIFT,
ROUND_PLUS_INF = 2 << RND_CTL_BIT_SHIFT,
ROUND_TOWARD_ZERO = 3 << RND_CTL_BIT_SHIFT
} RoundingMode;
double roundd (double n, RoundingMode mode)
{
uint16_t cw; /* Storage for the current x87 control register */
uint16_t newcw; /* Storage for the new value of the control register */
uint16_t dummyreg; /* Temporary dummy register used in the template */
__asm__ __volatile__ (
"fstcw %w[cw] \n\t" /* Read current x87 control register into cw*/
"fwait \n\t" /* Do an fwait after an fstcw instruction */
"mov %w[cw],%w[treg] \n\t" /* ax = value in cw variable*/
"and [=12=]xf3ff,%w[treg] \n\t" /* Set rounding mode bits 10 and 11 of control
register to zero*/
"or %w[rmode],%w[treg] \n\t" /* Set the rounding mode bits */
"mov %w[treg],%w[newcw]\n\t" /* newcw = value for new control reg value*/
"fldcw %w[newcw] \n\t" /* Set control register to newcw */
"frndint \n\t" /* st(0) = round(st(0)) */
"fldcw %w[cw] \n\t" /* restore control reg to orig value in cw*/
: [cw]"=m"(cw),
[newcw]"=m"(newcw),
[treg]"=&r"(dummyreg), /* Register constraint with dummy variable
allows compiler to choose available register */
[n]"+t"(n) /* +t constraint passes `n` through
top of FPU stack (st0) for both input&output*/
: [rmode]"rmi"((uint16_t)mode)); /* "g" constraint same as "rmi" */
return n;
}
double hullSpeed(double lgth)
{
double result;
__asm__(
"fsqrt\n\t" // st(0) = square root st(0)
"fmulp\n\t" // Multiplies st(0) and st(1) (1.34). Result in st(0)
: "=t"(result) : "0"(lgth), "u" (1.34) : "st(1)");
return result;
}
int main()
{
double dbHullSpeed = hullSpeed(64.0);
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_NEAREST_EVEN));
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_MINUS_INF));
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_PLUS_INF));
printf ("%f, %f\n", dbHullSpeed, roundd(dbHullSpeed, ROUND_TOWARD_ZERO));
return 0;
}
正如您在评论中指出的那样,此 Whosebug answer 中有等效代码,但它使用了多个 __asm__
语句,您很好奇如何对单个 __asm__
语句进行编码.
舍入模式 (0,1,2,3) 可以在 Intel Architecture Document:
中找到Rounding Mode RC Field
00B Rounded result is the closest to the infinitely precise result. If two values are equally close, the result is the even value (that is, the one with the least-significant bit of zero). Default Round down (toward −∞)
01B Rounded result is closest to but no greater than the infinitely precise result. Round up (toward +∞)
10B Rounded result is closest to but no less than the infinitely precise result.Round toward zero (Truncate)
11B Rounded result is closest to but no greater in absolute value than the infinitely precise result.
在8.1.5节(8.1.5.3节具体描述的舍入模式)中有对字段的描述。 4 种舍入模式在 4.8.4 节的图 4-8 中定义。