获取arm mov的有效imm编号的快速方法

Fast way to get a valid imm num for arm mov?

本文关键字:方法 编号 有效 arm mov 获取 imm      更新时间:2023-10-16

Arm mov有一个限制,即立即数必须是一个8位旋转2的倍数,我们可以写:

mov ip, #0x5000

但我们不能这样写:

mov ip, #0x5001

0x5000可以拆分为0x5000+1,我的意思是,一个有效的立即数和一个小数字的总和。

那么,对于给定的32位数字,如何快速找到最接近的有效立即数?像这样:

uint32 find_imm(uint32 src, bool less_than_src) {
...
}
// x is 0x5000
uint32 x = find_imm(0x5001, true);

很简单,看看它们之间的距离。0x5001=0b101000000000001。15个有效数字,所以它将为您提供两个指令,每个指令的立即数为8位。还记得在测试中旋转,如果有足够的零0x80000001,并且将其旋转到0x88000000或0x00000003左右,这距离一个测量值之间的距离只有两个有效数字。因此,取立即数,在两个之间执行一段距离的类型测试,旋转一步,再次执行测试,然后重复,直到所有可能的(计数器)旋转都发生了,然后使用指令/立即数最少的一个。

gnu as已经做到了这一点,gas是开源的,所以如果你愿意,你可以去获取他们的代码。当你使用加载地址技巧:

ldr rd,=const

如果该常量可以在单个移动立即指令中解析,则它将其编码为

mov rd,#const

如果它不能,那么它会试图找到一个放置单词的位置,并将其编码为pc相对负载:

ldr rd,[pc,#offset]
...
.word const

没有一个简单的规则或函数来寻找构造值的方法。一旦一个值超过了可以从立即值轻松加载的值,通常通过在数据段中定义它并从内存加载它来加载它,而不是从立即值构建它。

如果您确实想从两个立即值构造一个值,则必须考虑各种操作,包括:

  • 添加两个即时消息
  • 减去两个直接数
  • 两个直接数的乘积
  • 更深奥的指令,例如一些将32位寄存器拆分为多个通道的"SIMD"指令

如果必须使用三个直接值,则会有更多的组合。人们可以在减少搜索的可能性中找到一些模式,但其中一部分仍然是"蛮力"搜索。一般来说,使用复杂的指令序列是没有意义的,因为您可以简单地从内存中准备好的位置加载数据。

ARM汇编程序有一个指令表来帮助实现这一点:

LDR Rd, =const

当汇编程序看到这一点时,它将const值放在文字池中,并生成一条从池中加载该值的指令。如果您使用不同的汇编程序,它可能没有相同的指令形式,但您可以手动编写必要的代码。