【发布时间】:2015-12-20 20:34:31
【问题描述】:
考虑以下代码:
int bn_div(bn_t *bn1, bn_t *bn2, bn_t *bnr)
{
uint32 q, m; /* Division Result */
uint32 i; /* Loop Counter */
uint32 j; /* Loop Counter */
/* Check Input */
if (bn1 == NULL) return(EFAULT);
if (bn1->dat == NULL) return(EFAULT);
if (bn2 == NULL) return(EFAULT);
if (bn2->dat == NULL) return(EFAULT);
if (bnr == NULL) return(EFAULT);
if (bnr->dat == NULL) return(EFAULT);
#if defined(__i386__) || defined(__amd64__)
__asm__ (".intel_syntax noprefix");
__asm__ ("pushl %eax");
__asm__ ("pushl %edx");
__asm__ ("pushf");
__asm__ ("movl %eax, (bn1->dat[i])");
__asm__ ("xorl %edx, %edx");
__asm__ ("divl (bn2->dat[j])");
__asm__ ("movl (q), %eax");
__asm__ ("movl (m), %edx");
__asm__ ("popf");
__asm__ ("popl %edx");
__asm__ ("popl %eax");
#else
q = bn->dat[i] / bn->dat[j];
m = bn->dat[i] % bn->dat[j];
#endif
/* Return */
return(0);
}
数据类型 uint32 基本上是 unsigned long int 或 uint32_t 无符号 32 位整数。 bnint 类型是 unsigned short int (uint16_t) 或 uint32_t,具体取决于 64 位数据类型是否可用。如果 64 位可用,则 bnint 为 uint32,否则为 uint16。这样做是为了在代码的其他部分捕获进位/溢出。结构体bn_t定义如下:
typedef struct bn_data_t bn_t;
struct bn_data_t
{
uint32 sz1; /* Bit Size */
uint32 sz8; /* Byte Size */
uint32 szw; /* Word Count */
bnint *dat; /* Data Array */
uint32 flags; /* Operational Flags */
};
该函数从我的源代码的第 300 行开始。所以当我尝试编译/制作它时,我收到以下错误:
system:/home/user/c/m3/bn 1036 $$$ ->make
clang -I. -I/home/user/c/m3/bn/.. -I/home/user/c/m3/bn/../include -std=c99 -pedantic -Wall -Wextra -Wshadow -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wwrite-strings -Wfloat-equal -Winline -Wunknown-pragmas -Wundef -Wendif-labels -c /home/user/c/m3/bn/bn.c
/home/user/c/m3/bn/bn.c:302:12: warning: unused variable 'q' [-Wunused-variable]
uint32 q, m; /* Division Result */
^
/home/user/c/m3/bn/bn.c:302:15: warning: unused variable 'm' [-Wunused-variable]
uint32 q, m; /* Division Result */
^
/home/user/c/m3/bn/bn.c:303:12: warning: unused variable 'i' [-Wunused-variable]
uint32 i; /* Loop Counter */
^
/home/user/c/m3/bn/bn.c:304:12: warning: unused variable 'j' [-Wunused-variable]
uint32 j; /* Loop Counter */
^
/home/user/c/m3/bn/bn.c:320:14: error: unknown token in expression
__asm__ ("movl %eax, (bn1->dat[i])");
^
<inline asm>:1:18: note: instantiated into assembly here
movl %eax, (bn1->dat[i])
^
/home/user/c/m3/bn/bn.c:322:14: error: unknown token in expression
__asm__ ("divl (bn2->dat[j])");
^
<inline asm>:1:12: note: instantiated into assembly here
divl (bn2->dat[j])
^
4 warnings and 2 errors generated.
*** [bn.o] Error code 1
Stop in /home/user/c/m3/bn.
system:/home/user/c/m3/bn 1037 $$$ ->
我所知道的:
我认为自己相当精通 x86 汇编程序(从我上面编写的代码中可以看出)。然而,我最后一次混合高级语言和汇编程序是在大约 15 到 20 年前为游戏编写图形驱动程序时使用 Borland Pascal(Windows 95 之前的时代)。我熟悉 Intel 语法。
我不知道的:
如何从 asm 访问 bn_t 的成员(尤其是 *dat)?由于 *dat 是指向 uint32 的指针,因此我将元素作为数组访问(例如 bn1->dat[i])。
如何访问在堆栈上声明的局部变量?
我正在使用 push/pop 将破坏的寄存器恢复到以前的值,以免扰乱编译器。但是,我是否也需要在局部变量中包含 volatile 关键字?
或者,有没有我不知道的更好的方法?由于调用开销,我不想将它放在单独的函数调用中,因为此函数对性能至关重要。
补充:
现在,我刚刚开始编写这个函数,所以它还没有完成。缺少循环和其他此类支持/粘合代码。但是,主要的要点是访问局部变量/结构元素。
编辑 1:
我使用的语法似乎是 clang 唯一支持的语法。我尝试了以下代码,clang 给了我各种错误:
__asm__ ("pushl %%eax",
"pushl %%edx",
"pushf",
"movl (bn1->dat[i]), %%eax",
"xorl %%edx, %%edx",
"divl ($0x0c + bn2 + j)",
"movl %%eax, (q)",
"movl %%edx, (m)",
"popf",
"popl %%edx",
"popl %%eax"
);
它希望我在第一行加上右括号,替换逗号。我改用 %% 而不是 % 因为我在某处读到内联汇编需要 %% 来表示 CPU 寄存器,而 clang 告诉我我使用了无效的转义序列。
【问题讨论】:
-
您是否知道编译器可能会相对于其他语句重新排序
__asm__语句?我非常确信这是不需要的,因此请使用 single__asm__声明。 -
"数据类型 uint32 基本上是一个 unsigned long int" 不,不是。它基本上是一个保证为 32 位宽的无符号整数类型。
-
我尝试使用单个 asm 语句,编译器将其扔回给我。我会再试一次。
-
请阅读文档。我不知道 clang,但是对于 gcc,您必须使用附加参数指定 C 参数(和 afaik clang 类似)。基本上,字符串通过一些文本替换(如果您指定 C 参数)传递给汇编器,而汇编器显然不知道 C 构造。
-
gcc inline assembly(也被 clang 使用)不检查汇编语句。有一个很好的教程here。
标签: c x86 clang freebsd inline-assembly