Aarch64 SUB指令基于AddWithCarry实现的原理疑问
Aarch64 SUB指令的AddWithCarry实现与进位机制
你疑惑的核心点其实是为啥用carry_in=1的AddWithCarry实现SUB,却没出现多减1的情况,本质是补码减法的数学逻辑在硬件上的落地:
在补码体系里,x - y完全等价于x + (-y),而负数-y的补码是~y + 1(对y按位取反后加1)。对应到SUB的实现代码:
- 先对立即数
imm取反得到operand2 = NOT(imm)(也就是~imm) - 再调用
AddWithCarry(operand1, operand2, '1'),这里的进位输入1就是补码转换里必须的那个+1
两者凑在一起,实际计算的就是:
operand1 + ~imm + 1 = operand1 - imm
完全和减法操作等价,自然不会有"额外多减1"的问题。
拿你举的sub sp, sp, #0x20例子来说:
imm是0x20,64位下按位取反后是0xFFFFFFFFFFFFFFDF- 执行
AddWithCarry(sp, 0xFFFFFFFFFFFFFFDF, 1),计算式是sp + 0xFFFFFFFFFFFFFFDF + 1 - 而
0xFFFFFFFFFFFFFFDF + 1正好等于-0x20的64位补码,所以最终结果就是sp - 0x20,和预期完全一致。
再结合AddWithCarry的定义来看,它的unsigned_sum是UInt(x) + UInt(y) + UInt(carry_in),这里y是~imm,carry_in是1,展开后就是:
UInt(x) + (2^N - 1 - UInt(imm)) + 1 = UInt(x) + 2^N - UInt(imm)
因为是N位运算,2^N会被截断掉(相当于模2^N),所以结果就是UInt(x) - UInt(imm),完全符合减法的无符号数计算逻辑。
SUB指令实现代码
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是补码转加法的关键一步,用来完成"取反加1"里的加1操作,让加法运算的结果等价于减法,所以实际执行时不会有额外偏移,结果完全符合预期。
内容的提问来源于stack exchange,提问作者Jorayen
相关产品推荐
相关产品推荐

