程序集 - 内联 asm - 从一个数组复制到另一个数组?
Assembly - inline asm - copy from one array to another?
上下文:
Linux64.美国电话电报公司
GCC 4.8.2(带 -O3 -march=native)
我左手下的x86_64 abi,在第21页打开。
预期的 C 代码:
为了明确意图,这里是想法:
int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};
for (int32_t x = 0; x < 4; ++x) {
res[x] = primo[x];
}
printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);
错误的C代码:
检测到错误:
Error: `(%rsp,%esi,4)' is not a valid base/index expression
代码:
int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};
int32_t counter = 0;
__asm__ volatile(
"start_loop:\n\t"
"movl (%1,%2,4), (%0,%2,4)\n\t"
"addl , %2\n\t"
"cmp , %2\n\t"
"jne start_loop"
: "=&r"(res)
: "r"(primo),"r"(counter)
:"cc"
);
printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);
汇编代码(问题所在的行):
...
start_loop:
movl (%rsp,%edx,4), (%si,%edx,4)
addl , %edx
cmp , %edx
jne start_loop
...
问题:
如何表达正确的代码?我哪里做错了?
谢谢
更新:
将流水线改为
时
movl (%rsp,%rdx,4), (%rsi,%rdx,4)
我明白了
Error: too many memory references for 'mov'
什么??
更新 2
对于读者来说,我的系统似乎很特别,它不会放入正确的指令大小。
我必须以 int64_t
为例手动键入我的变量,以强制 r*x 事情发生。如果 c11/c++11 使用 uintptr_t
类型。
否则,gcc 会坚持使用导致无效 base/index 错误的 32 位版本。
它咬了我好几次。我希望它现在不会适合你。
现在关闭ABI doc,打开intel手册,基础架构和指令集参考当然:->
首先,mov
不接受两个内存操作数,你必须通过寄存器或使用专门的字符串移动movs
。其次,在有效地址中不能混合使用 16、32 和 64 位寄存器。鉴于您的代码片段中的类型,编译器为您替换了 16 位寄存器非常可疑。此外,由于不可能的约束,它甚至不应该编译,res
是一个数组,你不能将它用作输出。此外,您正在更改 counter
变量,但您没有告诉编译器。
gcc内联汇编是一个很复杂的东西。如果可能最好避免它,特别是如果你是初学者,否则你将与编译器作斗争而不是学习汇编。
固定版本可能如下所示:
#include <stdint.h>
#include <stdio.h>
int main()
{
int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};
int32_t counter = 0;
int32_t tmp;
__asm__ volatile(
"start_loop:\n\t"
"movl (%3, %q1, 4), %0\n\t"
"movl %0, (%2, %q1, 4)\n\t"
"addl , %1\n\t"
"cmp , %1\n\t"
"jne start_loop"
: "=&r" (tmp), "+r" (counter)
: "r" (res), "r"(primo)
: "cc", "memory"
);
printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);
return 0;
}
上下文:
Linux64.美国电话电报公司
GCC 4.8.2(带 -O3 -march=native)
我左手下的x86_64 abi,在第21页打开。
预期的 C 代码:
为了明确意图,这里是想法:
int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};
for (int32_t x = 0; x < 4; ++x) {
res[x] = primo[x];
}
printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);
错误的C代码:
检测到错误:
Error: `(%rsp,%esi,4)' is not a valid base/index expression
代码:
int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};
int32_t counter = 0;
__asm__ volatile(
"start_loop:\n\t"
"movl (%1,%2,4), (%0,%2,4)\n\t"
"addl , %2\n\t"
"cmp , %2\n\t"
"jne start_loop"
: "=&r"(res)
: "r"(primo),"r"(counter)
:"cc"
);
printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);
汇编代码(问题所在的行):
...
start_loop:
movl (%rsp,%edx,4), (%si,%edx,4)
addl , %edx
cmp , %edx
jne start_loop
...
问题:
如何表达正确的代码?我哪里做错了?
谢谢
更新:
将流水线改为
时movl (%rsp,%rdx,4), (%rsi,%rdx,4)
我明白了
Error: too many memory references for 'mov'
什么??
更新 2
对于读者来说,我的系统似乎很特别,它不会放入正确的指令大小。
我必须以 int64_t
为例手动键入我的变量,以强制 r*x 事情发生。如果 c11/c++11 使用 uintptr_t
类型。
否则,gcc 会坚持使用导致无效 base/index 错误的 32 位版本。
它咬了我好几次。我希望它现在不会适合你。
现在关闭ABI doc,打开intel手册,基础架构和指令集参考当然:->
首先,mov
不接受两个内存操作数,你必须通过寄存器或使用专门的字符串移动movs
。其次,在有效地址中不能混合使用 16、32 和 64 位寄存器。鉴于您的代码片段中的类型,编译器为您替换了 16 位寄存器非常可疑。此外,由于不可能的约束,它甚至不应该编译,res
是一个数组,你不能将它用作输出。此外,您正在更改 counter
变量,但您没有告诉编译器。
gcc内联汇编是一个很复杂的东西。如果可能最好避免它,特别是如果你是初学者,否则你将与编译器作斗争而不是学习汇编。
固定版本可能如下所示:
#include <stdint.h>
#include <stdio.h>
int main()
{
int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};
int32_t counter = 0;
int32_t tmp;
__asm__ volatile(
"start_loop:\n\t"
"movl (%3, %q1, 4), %0\n\t"
"movl %0, (%2, %q1, 4)\n\t"
"addl , %1\n\t"
"cmp , %1\n\t"
"jne start_loop"
: "=&r" (tmp), "+r" (counter)
: "r" (res), "r"(primo)
: "cc", "memory"
);
printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);
return 0;
}