我试图将一些现有的内联x86_64程序集转换为AArch64兼容版本。我在编译时遇到以下错误:
/tmp/ccSvqF1I.s:72547: Error: operand 1 should be an integer register -- `str [0x4,x1],#0x43e00000'
/tmp/ccSvqF1I.s:72548: Error: operand 1 should be an integer register -- `str [20,x1],2'
下面的x86_64代码,如果原始代码和AARch64代码是我尝试移植它。
x86_64汇编:
__asm__(
"incq (%0)\n\t"
"jno 0f\n\t"
"movl $0x0, (%0)\n\t"
"movl $0x43e00000, 0x4(%0)\n\t"
"movb %1, %c2(%0)\n"
"0:"
:
: "r"(&op1->value),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
: "cc");
AArch64程序集
__asm__(
"add %0, %0, #1\n\t"
"bvc 0f\n\t"
"mov %0, #0x0\n\t"
"str [0x4, %0], #0x43e00000\n\t"
"str [%c2, %0], %1\n\t"
"0:"
:
: "r"(&op1->value),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
: "cc");
编辑:更新了新的尝试和错误消息
答案 0 :(得分:0)
当你想要做某事时,不能用一条指令来描述,你需要把它分成几条指令。
问题是AArch64没有将立即值存储到内存的指令。您需要将立即值移动到寄存器,然后将寄存器存储到内存中,如:
movz w2, #0x43e0, lsl #16 // move #0x43e00000 to a register
str w2, [x1, #20] // store to address [x1, #20]
orr w2, wzr, #0x2 // move #0x2 to a register
str w2, [x1, #4] // store to address [x1, #4]
ARM指令是RISC(精简指令集计算机)之类的指令。好处是指令都非常简单且固定长度。 X86有更复杂的指令。但是,如果没有指令支持您的行为,您仍需要将您的行为分成几条指令。 您可以在http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488d/CIHGGBGB.html中找到有关AArch64的更多信息。
答案 1 :(得分:0)
更好的解决方案可能是使用ldr=
伪操作,这会使隐藏的movz
用法更容易阅读:
ldr w2, =0x43e00000
str w2, [x1, #20]
..等等。你的汇编程序应该足够聪明,可以意识到它可以使用movz
或其他一些指令来生成w2
中的立即数,但在最坏的情况下,它会以文字池加载的形式出现。在学习的过程中,ldr=
是一个福音,你不应该尝试和汇编汇编程序,直到你知道它已经破坏或者不像你那样有效。
答案 2 :(得分:0)
不幸的是,几乎没有一个装配是正确的。 A64指令集使用加载/存储体系结构。这意味着存在从存储器向寄存器加载和从寄存器存储值的特定指令。没有其他指令访问内存。因此,例如ADD指令不能访问存储器。这意味着您的add %0, %0, #1
语句不会递增op1->value
,它会递增保存op1->value
地址的寄存器。它基本上与你在C中op1++
代替op1->value++
。
zend_long temp;
static zend_long const overflow = 0x43e0000000000000;
asm("ldr %[temp], %[value]\n\t"
"adds %[temp], %[temp], #1\n\t"
"str %[temp], %[value]\n\t"
"b.vc 0f\n\t"
"mov %[temp], #%[overflow]\n\t"
"str %[temp], %[value]\n\t"
"mov %w[temp], #%[is_double]\n\t"
"str %w[temp], %[type_info]\n\t"
"0:\n\t"
:
[temp] "=&r" (temp),
[value] "+m" (op1->value.lval),
[type_info] "=m" (op1->u1.type_info)
:
[overflow] "N" (overflow),
[is_double] "M" (IS_DOUBLE)
: "cc");
您会注意到x86 INC指令变为三个单独的指令。第一个将值加载到寄存器中,第二个将一个加到寄存器中,最后第三个将它存储回寄存器。
两个常量0x43e000000000000
和IS_DOUBLE
碰巧是可以用单个指令加载到寄存器中的常量。使用MOV伪指令允许汇编器找出哪一个。否则,必须使用LDR =伪指令从内存加载常量。无论哪种方式,正如克莱莫尔在他的回答中所说,你不能将直接价值直接存储在记忆中。
最后,asm语句使用ADDS指令而不是ADD指令。前者根据结果设置条件标志,后者不是。 asm声明的这一点。它应该通过检查条件标志来使签名溢出检测更有效。