为什么16位指令不能访问通用寄存器的高位寄存器

Why can't 16-bit instructions access the high registers of the general purpose registers

所以现在我看了《ARM权威指南Cortex-M3/M4》一书,不明白为什么16位指令不能访问高级通用寄存器R8-R12。

它说他们中很少有人能真正访问这些寄存器,但大多数都不能。

16bits表示机器码指令只有16位来编码信息。 8 个寄存器占用 3 位进行编码。 12 个寄存器需要 4 位来编码。然后我们需要 space 作为操作码和其他选项,这意味着额外的位可能有点太多了。

编码8个寄存器的地址需要3位。编码12个寄存器的地址需要4位。

如果你有一个 3-register-instruction,你将需要 12 位来编码 3 个寄存器,这只剩下 4 位用于指令。您最多只能有 16 条指令。

这些核心的唯一实际权威指南是 arm 文档。在阅读任何其他内容或学习指令集之前,您需要拥有文档;在这种情况下,ARMV7-m 的 ARM 体系结构参考手册以及 cortex-m3 或 cortex-m4 技术参考手册。如果你的书做出了这样的陈述但没有解释它,那它远非权威。

这在技术上是可行的,但它会花费很多 space 并且可能会破坏尝试 16 位指令的目的(并不意味着 16 位处理器或寄存器或类似的东西,指令本身是 16 位)。如果您查看编译后的代码以及编译器的工作方式,则生成的大量代码使用较低的寄存器或可以这样制作。因此,这是指令集的丰富性与指令数量的权衡,这与您拥有多少寄存器无关。

寄存器的大小无关紧要,寄存器的数量才是最重要的。例如为了

add r1,r2,r3

你必须在某个地方编码哪个寄存器是哪个。注释当然是特定于 old/original 16 位指令而不是 thumb2 扩展。

所以你需要一个模式,自然会有:

000 r0
001 r1
010 r2
011 r3
100 r4
101 r5
110 r6
111 r7

如果您将自己限制为 8 个寄存器,那么每个寄存器只需要三位来描述每个字段使用哪个寄存器。

如果你想在指令中使用 16 个寄存器,那么你需要 4 位:

0000 r0
0001 r1
0010 r2
....
0111 r7
1000 r8
1001 r9
....
1110 r14
1111 r15

如果你想在你的指令集中使用 32 个寄存器(通常不是 ARM thumb)那么五位:

00000 r0
00001 r1
10000 r16
11111 r31

备注

2 to the power 0 is 1
2 to the power 1 is 2
2 to the power 2 is 4
2 to the power 3 is 8
2 to the power 4 is 16
2 to the power 5 is 32
2 to the power 16 is 65536

等等。

如果你想描述8个东西需要3位如果你想描述16个东西需要4位

16 位意味着可能有 65536 条独特的指令。 ARM 的文档很好地展示了它如何布局其指令集,与 MIPS 相比,它倾向于最大化指令的数量和可能的特性,而 MIPS 首先倾向于解码逻辑的简单性,然后才是特性(设计 trade-offs ).

向前跳过,例如指令的前 6 位是操作码

00xxxx shift, add, subtract, move, and compare
010000 data processing instructions
010001 special data
01001x ...

等等。根据您下载的 ARM ARM 以各种方式显示。

他们不必执行任何三个寄存器指令,但他们选择了执行。如果这些指令支持每个操作数的 r0 到 r15,那么这意味着需要 12 位,剩下 4 位用于编码,包括其中一个或多个以指示指令的 class 和其余的操作数,您可能会擦除至少有一半可能的指令(一位专门用于指示这是否是三寄存器指令)留下 7 个可能的操作,或者指令的四分之一 space(两位)留下 2 位来选择哪个操作数使它不是很 feature-rich。相反,他们实际做的是占用指令的 1/4 space,但至少为操作数 and/or 其他功能(立即等)保留 5 位。

所以 add 的 T1 编码是

0001100mmmnnnddd

前两位表示这是哪一组指令,如果你看你会看到第10位表示立即数或寄存器mmm位是一个寄存器或0到7之间的立即数,有很多次程序员想要增加 1 或 2 或一些小数字并且必须强制执行一条指令并刻录一个寄存器来放置那个小数字与 trade-off 刻录一些指令 space 在这里是一个平衡。

反正你看到这里有rm、rn和rd寄存器三位用于这个指令编码。 (添加 rd、rn、rm),这表明 r0-r7 对于这些字段中的任何一个都是可能的。

为了让它更完整,有一个 mov 高寄存器指令,允许你移动 high/low、low/high 或 high/high,技术上 low/low 但他们更喜欢你使用记录的不同编码。其中之一可以添加与上述三个寄存器相关的 rd,rn,#0。这就是你看到的 gnu 汇编程序所做的

.thumb
mov r1,r2
mov r10,r2
mov r1,r11
mov r10,r11
    
00000000 <.text>:
   0:   1c11        adds    r1, r2, #0
   2:   4692        mov r10, r2
   4:   4659        mov r1, r11
   6:   46da        mov r10, r11

要点是有一种方法可以进出高位寄存器,这比 to/from 堆栈(无内存循环)更快,因此编译器仍然可以选择使用 one/some 高位寄存器(理解 push/pop 由于显而易见的原因(阅读文档)而受到限制,因此保存和恢复它们需要付出代价,这是一种权衡。

所以你应该坚持对某事的实际权威指南,而不是声称是某事的指南。

除非你当时恰好在 brain/cube/office/conference 房间里,否则你几乎永远找不到的一件事是设计师实现了指令集或特定指令,那就是“为什么“他们做到了吗。 (为什么没有三个 register and,xor 等)因此,尽管有其他答案、评论和上述内容,但这是因为他们想要这样做。如果你需要了解更多,你也许可以在 arm 找到一份工作,也许他们还在那里可能会花时间和你在一起,但我希望他们退休。

很长一段时间以来,有关各方的回答可能会因谁在多年前说了什么以及什么时候说的而有所不同。但是当尝试采用 32 位指令集并将一个子集映射到 16 位指令以使每个单元的指令数加倍时 space,同时尝试足够丰富以不显着增加每个任务的指令数,限制对于像这样的体系结构,大多数低级通用寄存器的指令似乎是一个明显的设计选择。我将不得不重新访问与 thumb 等效的 MIPS,以了解他们做了什么我希望他们做同样的事情。同样,我还必须重新访问 risc-v 和任何其他做过同样事情的人。

了解这是一个设计选择,而不是任何方面的要求。