[英]Porting x86_64 Assembly to AArch64
我正在尝试将一些现有的嵌入式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");
编辑:更新了新的尝试和错误消息
当您想做某事无法用一条指令来描述时,您需要将其分成几条指令。
问题在于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的更多信息。
更好的解决方案可能是使用ldr=
伪操作,这将使隐藏的movz
用法更易于阅读:
ldr w2, =0x43e00000
str w2, [x1, #20]
.. 等等。 您的汇编器应该足够聪明,以意识到它可以使用movz
或其他指令在w2
生成立即数,但在最坏的情况下,它将作为文字池加载出现。 学习时, ldr=
是一个福音,在您知道汇编器损坏或效率不如您之前,不应该尝试对汇编器进行游戏。
不幸的是,您的程序集几乎都不正确。 A64指令集使用加载/存储架构。 这意味着有特定的指令可以将值从内存加载和存储到寄存器中以及从寄存器中存储值。 没有其他指令访问内存。 因此,例如ADD指令无法访问内存。 这意味着您add %0, %0, #1
语句不会增加op1->value
,它会增加保存op1->value
地址的op1->value
。 基本上就像您在C中op1->value++
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语句的整个要点。 它应该通过检查条件标志来使带符号的溢出检测更加有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.