ARMv8 A64 程序集中的立即数范围
Posted
技术标签:
【中文标题】ARMv8 A64 程序集中的立即数范围【英文标题】:Range of immediate values in ARMv8 A64 assembly 【发布时间】:2015-09-03 11:07:50 【问题描述】:我的理解是 ARMv8 A64 汇编中的立即参数可以是 12 位长。如果是这样的话,为什么这行汇编代码:
AND X12, X10, 0xFEF
产生此错误(使用 gcc 编译时)
Error: immediate out of range at operand 3 -- `AND X12, X10, 0xFEF'
有趣的是,这行汇编代码编译得很好:
ADD X12, X10, 0xFEF
我正在使用 aarch64-linux-gnu-gcc (Linaro GCC 2014.11) 4.9.3 (prerelease)
【问题讨论】:
【参考方案1】:与 A32 的“灵活第二操作数”不同,A64 中没有通用的立即数格式。对于立即操作数数据处理指令(忽略像移位这样无聊和直接的指令),
算术指令(adds
、subs
、cmp
、cmn
)采用 12 位无符号立即数和可选的 12 位左移。
移动指令(movz
、movn
、movk
)采用 16 位立即数,可选择移位到寄存器内的任何 16 位对齐位置。
地址计算 (adr
, adrp
) 采用 21 位有符号立即数,尽管没有直接指定它的实际语法 - 为此,您必须使用汇编器表达式技巧来生成适当的 "标签”。
逻辑指令(ands
、orr
、eor
、tst
)采用“位掩码立即数”,我不确定我能否解释,所以我只引用the mind-bogglingly complicated definition:
这种立即数是 32 位或 64 位模式,被视为大小为 e = 2、4、8、16、32 或 64 位的相同元素的向量。每个元素都包含相同的子模式:单次运行 1 到 e-1 非零位,旋转 0 到 e-1 位。这种机制可以生成 5,334 个唯一的 64 位模式(作为 2,667 对模式及其按位反转)。
【讨论】:
这种解释更有意义:“逻辑立即数指令接受位掩码立即数 bimm32 或 bimm64。这样的立即数由至少一个非零位的单个连续序列组成,并且在至少一个零位,在 2、4、8、16、32 或 64 位的元素中;然后在整个寄存器宽度上复制该元素,或者这样的值的按位反转。" arm 位掩码立即字段为 13 位(据我所知)。有人确切知道这些位是如何解释的(即,将这 13 位转换为 32 位或 64 位值的算法)吗?为什么这个算法不容易找到? 我在这里找到了一些可能有用的代码:llvm.org/docs/doxygen/html/… @Zack 与所有内容一样,完整、权威的定义可以在the ARM ARM 的指令伪代码中找到(免费下载,但您必须注册才能接受许可)。在这种情况下,它是伪代码附录中的DecodeBitMasks()
函数(问题 A.f 中的第 J8-5588 页)。
按位指令的立即数至少概括起来并不难:一种重复模式,其中在一个元素内设置的位必须是连续的。【参考方案2】:
这是一段代码,用于按照 Notlikethat 的答案中引用的机制转储所有合法位掩码立即数。希望它有助于理解生成位掩码立即数的规则是如何工作的。
#include <stdio.h>
#include <stdint.h>
// Dumps all legal bitmask immediates for ARM64
// Total number of unique 64-bit patterns:
// 1*2 + 3*4 + 7*8 + 15*16 + 31*32 + 63*64 = 5334
const char *uint64_to_binary(uint64_t x)
static char b[65];
unsigned i;
for (i = 0; i < 64; i++, x <<= 1)
b[i] = (0x8000000000000000ULL & x)? '1' : '0';
b[64] = '\0';
return b;
int main()
uint64_t result;
unsigned size, length, rotation, e;
for (size = 2; size <= 64; size *= 2)
for (length = 1; length < size; ++length)
result = 0xffffffffffffffffULL >> (64 - length);
for (e = size; e < 64; e *= 2)
result |= result << e;
for (rotation = 0; rotation < size; ++rotation)
printf("0x%016llx %s (size=%u, length=%u, rotation=%u)\n",
(unsigned long long)result, uint64_to_binary(result),
size, length, rotation);
result = (result >> 63) | (result << 1);
return 0;
【讨论】:
你可以通过排序运行这个输出,这样更容易阅读。【参考方案3】:位掩码立即数的另一种解释,现在是早上,我终于理解了“令人难以置信的复杂”的定义。 (见 Notlikethat's answer。)也许有些人会更容易理解。
它是 X>0 个连续的 0,然后是 Y>0 个连续的 1,其中 X+Y 是 2 的幂,重复以填充整个参数,然后任意旋转。
另请注意,其他立即格式的可选移位是按确切位数,而不是“最多”。也就是说,16 位立即数可以完全移动 0、16、32 或 48 位,而 12 位立即数仅移动 0 或 12 位。 p>
【讨论】:
以上是关于ARMv8 A64 程序集中的立即数范围的主要内容,如果未能解决你的问题,请参考以下文章