计算所需的最小寻址模式数是多少?

时间:2016-02-05 10:10:05

标签: assembly x86 cpu-registers cpu-architecture addressing

在x86汇编程序中,假设您有

  • 用于分配号码的立即寻址模式
  • 寄存器的寻址模式
  • 内存地址的直接寻址模式

为什么需要索引和基本指针寻址模式? 据我所知,每个都可以用循环代替。

同样间接模式似乎也不太有用,因为您可以简单地使用直接模式来引用内存地址。首先访问一个寄存器的目的是什么,该寄存器包含一个指向存储器地址的指针?

简而言之,哪些寻址方式确实是必要的?

2 个答案:

答案 0 :(得分:6)

虽然理论上“寻址模式”可用于指代操作数类型,但由于它不涉及地址,因此有点令人困惑。英特尔手册使用“寻址模式”来指代内存寻址,我将使用此定义。

在汇编中,操作数可以是:

  • 即时值
  • 寄存器
  • 内存中的值(这里的操作数是地址)

在x86架构中,“寻址模式”仅适用于最后一类操作数:存储器操作数(地址),并指可用于计算地址的方法。寻址模式可以在单个可配置寻址模式中汇总:

address = REG_base + REG_index*n + offset

REG_baseREG_indexnoffset都是可配置的,并且都可以省略(但显然至少需要一个)。

address = offset称为立即,直接或绝对寻址 address = REG_base称为寄存器间接寻址 address = REG_base + REG_index称为基数加索引寻址 同样,您可以添加偏移量(offset)和比例(n)。

严格来说,您只需要一种模式即可完成所有操作:注册间接寻址(address = REG)。这样,如果需要访问内存,可以在寄存器中计算所需的任何地址,并使用它来进行访问。 它也可以通过使用内存替换直接寄存器操作数,并通过使用算术构造值来立即操作数。但是,对于实际的指令集,您仍然可以立即操作数来有效地加载地址,如果您不想要仅指针寄存器,则需要注册操作数。

除了间接寄存器之外的所有其他寻址模式都是为了方便起见,它们确实非常方便:

  • 如果您只需要访问内存中的固定变量,立即寻址会为您保存一个寄存器。
  • Base + offset对于访问对象成员非常有用:您可以将基址保存在寄存器中,并访问具有固定偏移量的单个成员。无需中间计算或注册以保存会员地址。
  • 同样,索引寻址用于访问数组:您只需更改索引寄存器即可访问数组中的任何值。
  • 使用比例,您可以访问多字节变量(例如:int)数组而无需额外的寄存器或计算。
  • 所有内容的组合可用于访问对象中的数组成员,仍然保留基指针以便可以访问对象中的其他成员。

这些寻址模式不需要CPU的大量计算:只需要添加和移位。考虑到x86可以在每个周期进行乘法,这些操作很简单,但仍然非常方便。

答案 1 :(得分:1)

如果没有寄存器,x86可以做很多事情,所以我不认为你可以摆脱寄存器"寻址模式"。一些非常不同的架构可能不使用寄存器,只有堆栈或内存,内存指令。 IDK如何实现指针;也许这样的架构可以memory[memory](C数组符号)。

计算可能需要立即生效。您可以使用多个寄存器构造任何值。从零(xor eax, eax),inc开始得到1,左移它到你想要的任何位置,inc设置低位,左移,因此,最差2*popcount(N)指令需要N进入寄存器。请注意,即时移位计数仍然不可用,因此重复移位一个显而易见的方法(shl eax,是的,有一个单独的编码用于逐个移位,或者只是使用{{1将仅取决于最高设置位的位置。所以add eax, eax显然是明显的转变和公司。

绝对(你称之为直接)内存寻址不是最有用的寻址模式。我们可以通过使用一系列指令(参见上文)构建地址并使用log2(N) + popcount(N)来模拟它。如果我们试图减少,我们想放弃它。正如杰斯特所指出的那样,保持绝对地址作为我们唯一的形式将是非常不方便(或者可能不可能?)使用。

索引显然可用于性能,而非必要性:您可以使用单独的说明进行移位和添加。

位移也只是为了提高性能,所以我们可以摆脱它们并强制代码手动添加任何位移。请参阅立即段落了解如何。

我相信x86仍然可以通过 [register]register寻址模式进行任意编程。

使用[register]register[register],性能应该比完整的x86差很多。

如果对内存的隐式访问不算作寻址模式,您当然可以使用immediate[register]模拟lodsd,但您不会能够进行原子读 - 修改 - 写操作。但这感觉就像是作弊。

还有堆栈(stosd):我不知道堆栈+注册机器是否是Turing-complete,但它通常不是可编程的。当然,如果您修改push/pop,则可以再次模拟e/rsp,但操作数大小的选择少于[register] / lodsb/w/d/q

如果包含16个ymm寄存器,x86有很多空间可以将内容存储在寄存器中。虽然我不能想出一种在整数寄存器和ymm的高128b之间移动数据而不使用内存或立即操作数(对于stosb/w/d/q)的方法,所以在实践中你更像16 16B矢量-register槽用于存储堆栈以外的本地状态。尽管如此,它的尺寸有限,这可能意味着32位386 ISA中的8个GP寄存器与64位AVX2 ISA中的所有整数/ mmx / ymm寄存器相关,这与机器是否正在进行调整无关 - 完成只有推/弹,寄存器,并且除了push / pop之外没有修改堆栈指针。