assembly Aarch64带进位的加法运算

q5iwbnjs  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(124)

我很难理解一些指令,如sub指令,根据手册定义为AddWithCarry操作,其中进位设置为硬编码值1

bits(datasize) result;
bits(datasize) operand1 = if n == 31 then SP[]<datasize-1:0> else X[n, datasize];
bits(datasize) operand2;
operand2 = NOT(imm);
(result, -) = AddWithCarry(operand1, operand2, '1');
if d == 31 then
    SP[] = ZeroExtend(result, 64);
else
    X[d, datasize] = result;

字符串
AddWithCarry操作定义如下:

(bits(N), bits(4)) AddWithCarry(bits(N) x, bits(N) y, bit carry_in)
    integer unsigned_sum = UInt(x) + UInt(y) + UInt(carry_in);
    integer signed_sum = SInt(x) + SInt(y) + UInt(carry_in);
    bits(N) result = unsigned_sum<N-1:0>; // same value as signed_sum<N-1:0>
    bit n = result<N-1>;
    bit z = if IsZero(result) then '1' else '0';
    bit c = if UInt(result) == unsigned_sum then '0' else '1';
    bit v = if SInt(result) == signed_sum then '0' else '1';
    return (result, n:z:c:v);


如果一直将1作为进位传递,并根据AddWithCarry的定义进行查找,那么减法运算是否也会从所有运算中减去1
我知道当我们写这样的东西:

sub sp, sp, #0x20


我们实际上只从sp中减去32个字节,那么这个操作中的进位位是怎么回事呢?

bgtovc5b

bgtovc5b1#

你可能已经看到了一个解释,大多数ALU把a-b当作a + (~b + 1),因为它们已经有了一个二进制加法器,而按位非在硬件上是非常便宜的。-b = ~b + 1two's complement identities). * How does the CPU do subtraction? * 是一个很好的例子,沿着Wikipedia的binary Adder–subtractor文章。
其中一些解释忽略的是,+ 1部分是通过进位到二进制加法器(如adc)来完成的,因此它仍然只是一个加法,这对性能和获得有符号的oVerflow和无符号Carry out的有用标志结果都很重要。
这就是这里发生的事情。注意operand2 = NOT(imm);而不是NEG-imm。额外的+1进位需要得到正确的答案。
在ARM和AARch 64中,减法输出的进位标志是ALU的加法器的原始输出,加法器以这种方式进行减法。这使得它成为一个非借位标志,如果x < y,则在x - y之后为false。
其他一些ISA,特别是x86,将ALU输出的进位标志反转,使其成为借位标志,这就是为什么x86的减法版本adcsbb(带借位减法,对于无借位情况,也必须在输入上反转CF以获得1的进位),与ARM/AArch 64的sbc(带进位的减法,直接在C中输入以获得无借位的1,否则0在有借位时使输出低1,通常来自bigint的较低有效块。
无论哪种方式,x86 sbb/ ARM sbc都是软件将64位ALU链接到一个更宽的加法器/ADC中的一种方式,就像纹波进位加法器中全加器之间的正常进位传播一样。(在每个64位块中,ALU可能正在做一些有趣的事情,比如超前进位或进位选择,以将延迟降低到足够小的门延迟,以适应一个时钟周期,但通常不值得在软件中尝试这样做。)
还涉及:

eivgtgni

eivgtgni2#

一开始我也很困惑。关键是前面的一行:它不是你所期望的operand2 = -imm,而是operand2 = NOT(imm),即按位不是(一的补码)。在二的补码算术中,你可以很容易地检查到NOT(imm) = -imm - 1。所以进位被设置为1实际上计算出x + (-imm - 1) + 1,它确实是x - imm
这是一种伪代码语言的人工制品:它们的整数类型是能够表示任何数字的纯数学整数,并且算术运算符仅在这样的类型上定义。但是在这里,操作数的类型是bits(n),简单地说是位串,所以写operand2 = -imm不会是格式良好的,他们必须说像operand2 = (-SInt(imm))<n-1:0>这样的东西,这会更令人困惑。
这也可能反映了一种简单的ALU可以实现加法指令的方式。而不是需要单独的单元来执行ADD,ADC,ADC等,你只需要一个执行加法和进位的单元。所以ADC将把这个单元的进位输入连接到NZCV寄存器的实际C位; ADD将把它连接到地。ADD将把第一个输入端通过一个反相器,并将进位输入连接到VDD。SBC对反相器进行相同的操作,并将进位输入连接到C标志。(注意,这会导致减法将C标志视为真进位,而不是x86,在x86中,C将进位标志的意义反转,使其表现得像借位。

相关问题