[英]ALU Design - Should a left shift cause overflow for signed numbers?
My understanding is an overflow can happen when all of the following conditions are met:我的理解是当满足以下所有条件时可能会发生溢出:
Ex: for 4-bit numbers 4 10 + 5 10 = 0100 2 + 0101 2 = 1001 2 = (-7) 10例如:对于 4 位数字 4 10 + 5 10 = 0100 2 + 0101 2 = 1001 2 = (-7) 10
But what about a left shift operator on a signed number?但是有符号数字上的左移运算符呢? Should that be considered an overflow as well?
这也应该被视为溢出吗?
Ex: 5 10 << 1 = 0101 2 << 1 = 1010 2 = (-6) 10例如:5 10 << 1 = 0101 2 << 1 = 1010 2 = (-6) 10
Or does it make sense to ignore the overflow for shifting left?或者忽略左移的溢出有意义吗?
1 #include <stdio.h>
2
3 int a = 0x01;
4
5 int main(int argc,char *argv[])
6 {
7 for(int i = 0 ; i < 32 ; i++){
8 printf("%p : %d \n", a, a);
9 a<<=1;
10 }
11 return 0;
12 }
Of course.当然。 Left shift operator also occur overflow
左移运算符也会发生溢出
below is result.下面是结果。
0x1 : 1
0x2 : 2
0x4 : 4
0x8 : 8
0x10 : 16
0x20 : 32
0x40 : 64
0x80 : 128
0x100 : 256
0x200 : 512
0x400 : 1024
0x800 : 2048
0x1000 : 4096
0x2000 : 8192
0x4000 : 16384
0x8000 : 32768
0x10000 : 65536
0x20000 : 131072
0x40000 : 262144
0x80000 : 524288
0x100000 : 1048576
0x200000 : 2097152
0x400000 : 4194304
0x800000 : 8388608
0x1000000 : 16777216
0x2000000 : 33554432
0x4000000 : 67108864
0x8000000 : 134217728
0x10000000 : 268435456
0x20000000 : 536870912
0x40000000 : 1073741824
0x80000000 : -2147483648
It makes sense to indicate when left-shift has overflow, at least for 1-bit shifts.指示左移何时溢出是有意义的,至少对于 1 位移位。 You might as well produce some output for n-bit shifts, too.
您也可以为 n 位移位生成一些输出。 (Although ARM apparently chooses to always produce overflow=0, so that's a valid choice. Most software written in high-level languages never looks at signed-overflow flags except as part of a signed less or greater compare condition.)
(虽然 ARM 显然选择总是产生溢出 = 0,所以这是一个有效的选择。大多数用高级语言编写的软件从不查看有符号溢出标志,除非作为有符号的较小或较大比较条件的一部分。)
For shift count != 1, or variable (which are logically equivalent to repeated x *= 2
), you can overflow along the way and then get back to the same sign as the input.对于移位计数 != 1 或变量(逻辑上等同于重复的
x *= 2
),您可以沿途溢出,然后返回与输入相同的符号。 You might as well still produce an overflow signal according to input_sign ^ output_sign
, because you can do that cheaply even with a barrel shifter ALU.您最好仍然根据
input_sign ^ output_sign
产生溢出信号,因为即使使用桶形移位器 ALU 也可以廉价地做到这一点。 Maybe some users will be interested in that output even though overflow=0 wouldn't mean no overflow.也许有些用户会对该输出感兴趣,即使 overflow=0 并不意味着没有溢出。
(Detecting if any of the bits shifted out were different from the final high bit is probably more expensive for a barrel shifter, although probably still cheap to accumulate for an iterative shifter. But if you're designing an ISA for an implementation that currently uses an iterative shifter, keep in mind you're imposing extra cost on a possible future implementation that wants an O(1) barrel shifter if it still has to match the behaviour of your overflow signal.) (检测移出的任何位是否与最终高位不同对于桶形移位器来说可能更昂贵,尽管对于迭代移位器来说累积起来可能仍然便宜。但是如果您正在为当前使用的实现设计 ISA一个迭代移位器,请记住,如果它仍然必须与溢出信号的行为相匹配,那么您正在对未来可能需要 O(1) 桶形移位器的可能实现施加额外成本。)
You don't need separate shift instructions for signed vs. unsigned left shift.对于有符号和无符号左移,您不需要单独的移位指令。 If your ISA has flags / condition-code output from the ALU accessible to later instructions, then yes you can have the left-shift instruction affect the signed-overflow flag (in some way).
如果您的 ISA 有来自 ALU 的标志/条件代码输出,以后的指令可以访问,那么是的,您可以让左移指令影响有符号溢出标志(以某种方式)。 Code that's shifting unsigned numbers can ignore that flag output.
移动无符号数字的代码可以忽略该标志输出。
All of this is of course assuming you use 2's complement signed integers, not for example sign/magnitude.当然,所有这些都是假设您使用 2 的补码有符号整数,而不是例如符号/大小。 Then you might need separate instructions, since sign/mag shifts would leave the sign bit in place and just shift the low n-1 bits.
然后您可能需要单独的指令,因为符号/磁力移位会将符号位留在原位并只移动低 n-1 位。
The only reason you might not want to signal overflow is if your CPU traps by default in that case (like MIPS add
), meaning that you would need separate instructions for non-trapping left shift (like MIPS addu
for addition; MIPS only has logical left-shift).您可能不想发出溢出信号的唯一原因是,如果您的 CPU 在这种情况下默认捕获(例如 MIPS
add
),这意味着您需要单独的指令进行非捕获左移(例如 MIPS addu
用于加法;MIPS 只有逻辑左移)。 Note that MIPS doesn't have a FLAGS register or any equivalent;请注意,MIPS 没有 FLAGS 寄存器或任何等效项; if you want the carry output of an add, you need
addu
/ sltu
to compare-into-register producing a 0 or 1 result for sum = a+b;
如果你想一个附加的进位输出,则需要
addu
/ sltu
比较-进入-寄存器产生0或1的结果为sum = a+b;
carry = sum < a;
(Semi-related: GNU Cdefines the behaviour of left shifts that have signed overflow, ISO C doesn't . Of course, there's no reason to build a CPU that defines as little about arithmetic as ISO C does! That would be horrible.) (半相关:GNU C定义了有符号溢出的左移行为, ISO C 没有。当然,没有理由构建一个像 ISO C 那样定义的算术很少的 CPU!那太可怕了。 )
ARM and AArch64 apparently don't ever set the oVerflow flag for left shifts. ARM 和 AArch64显然从未为左移设置 oVerflow 标志。
V
. V
。lsl
doesn't mention its effect on any flags, but I'm pretty sure I read somewhere else that the last bit shifted out goes in the Carry flag.lsl
列表没有提到它对任何标志的影响,但我很确定我在其他地方读到最后一位移出进入进位标志。 x86 does set OF according to what happens when left shifting: x86确实根据左移时发生的情况设置 OF:
https://www.felixcloutier.com/x86/sal:sar:shl:shr#flags-affected
https://www.felixcloutier.com/x86/sal:sar:shl:shr#flags-affected
The CF flag contains the value of the last bit shifted out of the destination operand;
CF 标志包含移出目标操作数的最后一位的值; it is undefined for SHL and SHR instructions where the count is greater than or equal to the size (in bits) of the destination operand.
对于计数大于或等于目标操作数的大小(以位为单位)的 SHL 和 SHR 指令,它是未定义的。 The OF flag is affected only for 1-bit shifts (see “Description” above);
OF 标志仅在 1 位移位时受到影响(参见上面的“说明”); otherwise, it is undefined.
否则,它是未定义的。 The SF, ZF, and PF flags are set according to the result.
根据结果设置 SF、ZF 和 PF 标志。 If the count is 0, the flags are not affected.
如果计数为 0,则标志不受影响。 For a non-zero count, the AF flag is undefined.
对于非零计数,AF 标志未定义。
But x86 is weird.但是 x86 很奇怪。 Modern x86 CPUs always write OF, even for shifts with larger counts, because it would be inconvenient to preserve the old value.
现代 x86 CPU 总是写入 OF,即使对于计数较大的班次也是如此,因为保留旧值会很不方便。 (Then the old FLAGS would be an input / dependency for out-of-order exec.)
(那么旧的 FLAGS 将是乱序 exec 的输入/依赖项。)
I haven't looked at what value they choose to write, perhaps just XOR of the original sign and the new sign.我没有看他们选择写什么值,也许只是原始符号和新符号的异或。 (So they'd miss overflows that happened along the way as bits got shifted out.)
(因此,当位移出时,他们会错过沿途发生的溢出。)
x86 has separate mnemonics for shl
(logical left) and sal
(arithmetic left), but they both assemble to the same opcode. x86 对
shl
(逻辑左)和sal
(算术左)有不同的助记符,但它们都组装成相同的操作码。 Ie they're source-level synonyms.即它们是源级同义词。 x86 machine code only has one kind of left shift.
x86 机器码只有一种左移。 (Or two if you count rotates, and of course there are different opcodes for 8-bit operand-size or immediate vs. variable vs. implicit-1 count...)
(或者两个,如果你计算旋转,当然 8 位操作数大小或立即数与变量与隐式 1 计数有不同的操作码......)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.