是否有任何RISC架构允许算术运算单独应用于字节,半字和其他数据单元,其大小小于CPU通用寄存器的大小?
在Intel x86(IA-32)和x86-64(称为EM64T或AMD64)处理器中,不仅整个寄存器可用,而且其较小的部分也可以运行。 Intel ISA允许对整个寄存器执行所有算术运算,它的一半,四分之一和一个字节(更准确地说,寄存器中的两个字节可用,例如RAX中的AL和AH)。执行操作后,我们可以进行溢出检查,如果在上一次操作期间发生溢出,则可以轻松处理。无论我们是否对整个字进行操作(IA-32为32位宽,EM64T为64位宽),或者对较小尺寸的数据执行算术指令(半字,四分之一字)或者一个字节),如果结果超过所选数据单元的大小,相应的标志(OF或CF)将被设置为1.因此在英特尔架构中,不需要模拟处理此类错误,这些错误发生在操作中小型数据,带有一系列指令,用于分析结果的较高位。
问题是有没有任何RISC架构可以对小数据进行直接算术运算,这些操作是通过处理器硬件实现的(不需要软件仿真来执行),溢出,携带和借用在处理器设备跟踪具有字节,半字等的这种操作中,不应以软件方式检查它们。或许这种方法与整个RISC理念相矛盾,现在和过去都没有RISC处理器实现过它?
答案 0 :(得分:1)
TL:DR:不,AFAIK没有RISC ISA,其标志设置部分寄存器操作比32位窄。但是许多具有FLAGS的64位RISC ISA(如AArch64)可以根据32位操作的结果设置它们。
参见最后一节:这是因为对软件整数溢出检查或鸡/蛋问题的需求普遍不足。 通常你只需要在16位值上进行比较/分支,你可以用零或符号扩展到32或64位就可以了。
只有寄存器宽度为8或16位的RISC才能设置该操作数大小的标志。例如AVR 8位RISC,具有32个寄存器和16位指令字。它需要扩展精度的add / adc才能实现16位int
。
这主要是历史性的事情:x86对于所有内容都有16位操作数大小,因为它从16位仅286进化而来。当设计80386时,重要的是它能够运行16-全速的只有位代码,它们提供了将16位操作逐步添加到16位代码的方法。并使用相同的机制允许32位代码中的16位操作。
x86 8位低/高寄存器(AX = AH:AL)再次部分归因于8086如何被设计为8080的后继并且使移植变得容易(甚至可以自动化)见{{3 }}。 (还因为同时有8个1字节寄存器和 4个2字节寄存器非常有用。)
相关:Why are first four x86 GPRs named in such unintuitive order?对于许多计算,您不必在每次操作后将高位重新归零以获得相同的结果。因此,缺少8位/ 16位操作数大小并不是高效实现大多数代码的障碍,这些代码在逻辑上将其结果包装为8位或16位。
64位RISC计算机通常至少有一些重要指令(如add
的32位版本),因此您可以免费获得零扩展add
结果,而无需单独截断它,例如使用array[i++]
和64位指针使代码uint32_t i
高效。 但在我听说过的任何RISC上,绝不会将部分寄存器操作数大小缩小到32位。
根据此table of Alpha mnemonics(第4.3节CPU寄存器)。
MIPS64处理器总是产生64位结果,即使对于那些也是如此 结构上定义为在32位上运行的指令。 这些指令通常将其32位结果符号扩展为64 位。通过这样做,32位程序按预期工作,尽管如此 寄存器实际上是64位宽而不是32位。
(您对完整的64位寄存器使用特殊指令,例如DADDU
(双字添加无符号)而不是ADDU
。请注意{U}版本的add
和{ {1}}陷阱2补码有符号溢出(32位或64位操作数大小),所以你必须使用U版本来包装带符号数学。({3}上的ISA参考链接无论如何,MIPS没有针对32位的特殊模式,但操作系统需要关注32位程序而不是64位,因为32位将假设所有指针都处于低位32虚拟地址空间。
在RISC加载/存储计算机上,您通常只使用零扩展(或符号扩展)字节/半字加载。完成后,您将使用字节/半字存储来获取截断的结果。 (对于无符号的base2或者2的补码,通常是你想要的。)这就是编译器(或人)实现使用dadd
或short
的C源的方式。 / p>
半相关:当用作uint8_t
这样的二元运算符的操作数时,C&#39}的整数提升规则会自动将比int
更窄的所有内容推广到int
,所以它大多数地映射到这种计算方式。 (即,如果a,b和c都是+
,则C中的unsigned result = (a+b) * c
不必在乘法之前将a+b
结果截断回uint8_t
。但是uint8_t
促进签名uint16_t
非常糟糕,因此int
冒着签名溢出UB的风险从促销到签名uint16_t a,b; unsigned c = a * b
进行倍增。)无论如何, C&C的推广规则有点像是为没有完全支持窄操作数大小的机器而设计的,因为这对许多硬件来说都是常见的。
但您要求从窄操作询问溢出检查/标记设置。
并非所有RISC计算机都拥有 FLAGS寄存器。 ARM确实如此,但例如MIPS和Alpha不做。 ARM不会在每条指令上设置标志:您必须明确使用指令的标志设置形式。
没有FLAGS的CPU通常有一些简单的比较和分支指令(通常为零,如official MIPS64 ISA doc),以及其他比较两个输入并将0/1结果写入另一个整数寄存器的指令(例如MIPS { {1}} - 设置为低于立即无符号)。您可以使用Set instructions + a int
和零来创建更复杂的分支条件。
有效溢出检查的硬件和软件支持通常是一个问题。在每个x86指令之后放置SLTIU
也非常糟糕。
但部分原因是大多数语言都不能轻易编写需要在每条指令后进行溢出检查的代码,CPU架构师不会在硬件中提供它,特别是对于窄操作数大小。
MIPS对于捕获bne
签名溢出很有意思。
有效实施它的方法可能包括:"粘性"标志,FPU异常标志是粘滞的方式:无效标志在除以零之后保持设置(并产生NaN);其他FP指令不清除它。因此,您可以在一系列计算结束时或循环之后检查异常标志。如果有一个软件框架,这使得实际使用它足够便宜。
使用FP代码,通常你不需要查看标志,因为NaN本身就是"粘性"或"传染性"。如果任一输入是NaN,则大多数二元运算符产生NaN。但是无符号和2的补码整数表示没有任何备用位模式:它们都代表特定的数字。 (1'补码有负零......)
有关可以进行溢出检查的ISA设计的更多信息,请查看mips.com Agner Fog关于结合x86最佳功能的新ISA的建议(代码密度,每个工作量大)指令)和RISC(易于解码)的高性能纸张架构。一些有趣的SIMD想法,包括将未来扩展到矢量宽度透明,因此您不必重新编译以使用更宽的矢量更快地运行。
答案 1 :(得分:0)
有没有...
您是否只谈论市场上的商业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架构非常有趣:
要在64位CPU上执行32位软件,有一些特殊功能。
其中一个是所有标志(进位,零,......)都是重复的:一次是低32位,一次是整个64位。
因此,在执行“ADD”操作(只能执行64位)之后,您可以检查64位标志还是32位标志。
答案 2 :(得分:0)
在Blackfin中,数据寄存器可以整体访问,也可以用作多个单独的部分。来自documentation(我为方便阅读而将其格式化为项目符号)
- 累加器:40位寄存器
A1
和A0
的集合,通常包含被操纵的数据。可以通过五种方式访问每个累加器:
- 作为一个40位寄存器
- 作为一个32位寄存器(指定为
A1.W
或A0.W
)- 作为两个类似于数据寄存器的16位寄存器(分别指定为
A1.H
,A1.L
,A0.H
或A0.L
)- 并作为一个8位寄存器(指定为
A1.X
或A0.X
),用于扩展到超过31位的位。数据寄存器:一组32位寄存器(
R0
,R1
,R2
,R3
,R4
,{{1 }},R5
和R6
)通常包含要处理的数据。 D寄存器或Dreg的缩写。数据寄存器的访问方式为
- 32位寄存器
- 或作为两个独立的16位寄存器。
每个寄存器的最低有效16位被称为“低”半部分,并在寄存器名称后用“ .L”指定。最高的16位被称为“高”半部分,并在名称后用“ .H”指定。例如:
R7
,R7.L
,r2.h
,r4.L
。
它还具有一个算术状态(ASTAT)寄存器,用于存储各种进位和溢出状态