assembly RISC结构中的小数直接算术运算

1qczuiv0  于 2023-08-06  发布在  其他
关注(0)|答案(3)|浏览(73)

是否有任何RISC架构,允许算术运算单独应用于字节,半字和其他数据单元,其大小小于CPU通用寄存器的大小?
在Intel x86(IA-32)和x86-64(称为EM 64 T或AMD 64)处理器中,不仅整个寄存器可用,而且其较小部分也可操作。Intel伊萨允许在整个寄存器上执行所有算术运算,它是半个,四分之一个和一个字节(更精确地说,寄存器中有两个字节可用,例如RAX中的AL和AH)。在执行操作后,我们可以进行溢出检查,如果在前一次操作中发生了溢出,则可以轻松处理。无论我们是对整个字(IA-32为32位宽,EM 64 T为64位宽)进行操作,还是对较小大小的数据(半字,四分之一字或一个字节)执行算术指令,如果结果超过所选数据单元的大小,则相应的标志(OF或CF)将被设置为1。因此,在英特尔架构中,不需要模拟处理此类错误,这些错误发生在小数据的操作中,具有分析结果的高位的指令链。
问题是,是否存在任何RISC架构,其中对小数据的直接算术运算是可能的,这些运算通过处理器硬件来实现(不需要软件仿真来执行它们),并且溢出、进位和借用发生在这样的字节、半字等运算中。由处理器设备跟踪,不应以软件方式检查。或者这种方法与整个RISC哲学相矛盾,并且在现在和过去都没有RISC处理器实现过它?

mpbci0fu

mpbci0fu1#

TL:DR:否,AFAIK不存在具有窄于32位的标志设置部分寄存器操作的RISC ISA。但是许多64位RISC ISA(如AArch 64)都有FLAGS,可以从32位运算的结果中设置它们。
参见最后一节:这是因为通常缺乏对软件整数溢出检查的需求或鸡/蛋问题。**通常你只需要比较/分支16位的值,你可以把它们的零或符号扩展到32或64位。
只有寄存器宽度为8或16位的RISC才能从该操作数大小设置标志。例如,AVR 8位RISC,具有32个寄存器和16位指令字。它需要扩展精度加法/ADC来实现16位int
这基本上是一个历史事件:x86具有16位操作数大小,因为它是从仅16位的286演变而来的。在设计80386时,重要的是它能够全速运行仅16位代码,并且他们提供了将32位操作增量添加到16位代码的方法。并使用相同的机制在32位代码中允许16位操作。
x86的8位低/高寄存器填充(AX=AH:AL)也部分是由于8086被设计为8080的继承者,并使移植变得容易(甚至可以自动化)。(也因为同时拥有8个1字节寄存器 * 和4个2字节寄存器 * 非常有用。)
相关:Which 2's complement integer operations can be used without zeroing high bits in the inputs, if only the low part of the result is wanted?对于许多计算,不必在每次运算后将高位重新归零即可获得相同的结果。因此,*缺少8位/ 16位操作数大小并不妨碍有效实现大多数代码,这些代码在逻辑上将其结果 Package 为8位或16位。
64-位RISC机器通常至少有一些重要指令(如add)的32位版本,因此您可以免费获得零扩展add结果,而无需单独截断它,例如。使用uint32_t i和64位指针使array[i++]这样的代码更高效。但在我听说过的任何RISC上,部分寄存器操作数的大小都不会小于32位
DEC Alpha很有趣,因为它是一个新的设计,64位从头开始,
而不是 * 一个64位扩展到现有的伊萨的方式MIPS 64。这个table of Alpha mnemonics显示了add/sub/穆尔/div在32位和64位形式下都是可用的,但是移位和比较却不是。(也有字节操作指令,基本上是64位整数寄存器中的SIMD shuffle/mask/insert/extract,以及SIMD packged-compare用于有效的字符串填充。
根据official MIPS64 ISA doc(第4.3节CPU寄存器)。
MIPS 64处理器总是产生64位结果,即使对于那些在架构上定义为在32位上操作的指令也是如此。这样的指令通常将它们的32位结果符号扩展成64位。这样做,32位程序就可以按预期工作,尽管寄存器实际上是64位宽而不是32位宽。
(You对完整的64位寄存器使用特殊指令,如DADDU(双字加无符号),而不是ADDU。请注意,adddadd的非U版本会在2的补码有符号溢出时捕获(具有32位或64位操作数大小),因此您必须使用U版本来 Package 有符号数学。(mips.com上的伊萨参考链接)。无论如何,MIPS没有32位的特殊模式,但是操作系统需要关心32位程序与64位,因为32位将假定所有指针都在虚拟地址空间的低32位。
在RISC加载/存储机器上,通常只使用零扩展(或符号扩展)字节/半字加载。完成后,您将使用字节/半字存储来获得截断的结果。(对于无符号base 2,或2的补码有符号,通常是你想要的。)这是编译器(或人类)如何实现使用shortuint8_t的C源代码。
半相关:C的整数提升规则自动将所有小于int的整数提升到int,当用作二元运算符(如+)的操作数时,因此它基本上很好地Map到这种计算方式。(即如果a、b和c都是uint8_t,则C中的unsigned result = (a+b) * c不必在乘法之前将a+b结果截断回uint8_t。但是uint16_t升级为有符号的int是非常糟糕的,所以uint16_t a,b; unsigned c = a * b冒着有符号溢出UB升级为有符号的int的风险。)无论如何,C的升级规则看起来有点像是为不完全支持窄操作数大小的机器设计的,因为这在很多硬件中很常见。

但你问的是narrow ops的溢出检查/标志设置。
不是所有的RISC机器甚至 * 有 * 一个标志寄存器。ARM有,但MIPS和Alpha没有。ARM不会在每条指令上设置标志:你必须显式地使用指令的标志设置形式。

没有FLAGS的CPU通常有一些简单的比较和分支指令(通常针对零,如MIPS bltz),以及其他比较两个输入并将0 / 1结果写入另一个整数寄存器的指令(例如MIPS bltz)。MIPS SLTIU--设置为小于立即无符号)。您可以使用Set指令+带零的bne来创建更复杂的分支条件。

硬件和软件支持有效的溢出检查通常是一个问题。在每个x86指令之后放一个jcc也很糟糕。

但部分是因为大多数语言不容易编写需要在每条指令后进行溢出检查的代码,CPU架构师没有在硬件中提供它,特别是对于窄操作数大小。
MIPS对捕获add的有符号溢出很有意思。
有效实现它的方法可能包括使用“粘性”标志,FPU异常标志的粘性方式:无效标志在除以零(并产生NaN)之后保持置位;其他FP指令不清除它。因此,您可以在一系列计算结束时或循环后检查异常标志。这使得它足够便宜,可以在实践中实际使用,如果有一个软件框架。
使用FP代码,通常不需要查看标志,因为NaN本身是“粘性”或“传染性”的。如果任一输入为NaN,则大多数二元运算符产生NaN。但是无符号和2的补码整数表示没有任何备用位模式:它们都代表特定的数字。(1的补码为负0……)
有关伊萨设计的更多信息,可以使溢出检查成为可能,请查看Agner Fog关于新ISA的建议的讨论,该建议结合了x86(代码密度,每条指令的大量工作)和RISC(易于解码)的最佳功能,以实现高性能的纸架构。一些有趣的SIMD想法,包括使未来对vector width的扩展透明,这样您就不必重新编译以更快地运行更宽的vector。

gfttwv5a

gfttwv5a2#

有没有。。
你只谈论市场上的商用CPU,还是也谈论大学等的学生项目?
我自己设计了一个用于教育目的的RISC CPU,可以进行8位,16位和32位操作。这表明至少有可能做到这一点。
64-位嵌入式PowerPC架构也有类似的东西:它们可以在64位寄存器的低32位中执行32位操作。
此架构没有8位和16位操作。但是,CISC CPU也不支持其他具有较小宽度的计算机所支持的所有宽度:
x86既不支持4位操作,也不支持12位操作,尽管有CPU(Intel 4004和DEC PDP-8)使用这些宽度。
在执行操作后,我们可以进行溢出检查,如果在前一次操作中发生了溢出,则可以轻松处理。
64位SPARC架构在这里很有趣:
为了使32位软件能够在64位CPU上执行,有一些特殊的功能。
其中之一是所有标志(进位,零,...)都是重复的:一次用于低32位,一次用于整个64位。
因此,在执行“ADD”操作(只能在64位上执行)后,您可以检查64位标志或32位标志。

ldioqlga

ldioqlga3#

大多数64位RISC体系结构也支持您所期望的有限形式,即具有对32位或64位字进行操作的指令。许多也支持位域的操作,尽管我不确定是否有一个允许你直接在位域上做算术
但是有一种名为Blackfin的不规则RISC架构,其中数据寄存器可以作为一个整体访问,也可以作为多个单独的部分使用。从它的documentation(为了便于阅读,我将其格式化为项目符号):

  • 蓄电池:一组40位寄存器A1A0,通常包含要处理的数据。每个累加器可以通过五种方式访问:
  • 作为一个40位寄存器
  • 作为一个32位寄存器(指定为A1.WA0.W
  • 作为两个16位寄存器,类似于数据寄存器(指定为A1.HA1.LA0.HA0.L
  • 并且作为一个8位寄存器(指定为A1.XA0.X)用于延伸超过位31的位。
  • 数据寄存器:一组32位寄存器(R0R1R2R3R4R5R6R7),通常包含用于操作的数据。缩写D寄存器或Dreg。
  • 数据寄存器可通过以下方式访问
  • 32位寄存器
  • 或者可选地作为两个独立的16位寄存器。
  • 每个寄存器的最低有效16位称为“低”半位,并在寄存器名称后用“.L”表示。最高有效的16位被称为“高”半位,并在名称后面用“.H”表示。示例:R7.Lr2.hr4.LR0.h

x1c 0d1x的数据
它甚至在 * 算术状态 *(ASTAT)寄存器中有多个独立的进位和溢出标志,因此更容易混合算术运算
另一个有趣的例子是SuperHSH-5,它在通用寄存器中执行SIMD操作,即使它有一组单独的64个浮点寄存器。所以你可以对真实的的字节/字/双字进行算术运算。换句话说,它在硬件上做了SWAR technique



OpenRISC还在GPR中执行SIMD(甚至浮点)操作

4.4 General-Purpose Registers (GPRs)

32个通用寄存器被标记为R 0-R31,并且在32位实现中为32位宽,在64位实现中为64位宽。它们保存标量整数数据、浮点数据、向量或内存指针。表4-3包含通用寄存器列表。GPR可以作为源寄存器和目的地寄存器两者由ORBIS、ORVDX和ORFPX指令访问。
比如说
lv.add.h:向量半字元素加有符号
通用寄存器rA的半字元素被加到通用寄存器rB的半字元素以形成结果元素。结果元素被放入通用寄存器rD中
Intel i960也有其独特之处。它是唯一一个有32个寄存器但没有零寄存器的奇怪RISC架构,它有instructions to compare bytes and shorts,尽管它仍然不能对字节进行其他算术运算

cmpi    Compare Integer
cmpib   Compare Integer Byte
cmpis   Compare Integer Short
cmpo    Compare Ordinal
cmpob   Compare Ordinal Byte
cmpos   Compare Ordinal Short
concmpi Conditional Compare Integer
concmpo Conditional Compare Ordinal

字符串

相关问题