计算机16位32位64位整数,Rust的128位整数“ i128”如何在64位系统上工作?

mcarton..

52

编译器会将它们存储在多个寄存器中,并在需要时使用多个指令对这些值进行算术运算。大多数ISA都有x86这样的“adc随身携带”指令,这使得执行扩展精度整数加/减相当有效。

例如,给定

fn main() {

let a = 42u128;

let b = a + 1337;

}

在不进行优化的情况下为x86-64进行编译时,编译器会生成以下内容:(

注释由@PeterCordes添加)

playground::main:

sub rsp, 56

mov qword ptr [rsp + 32], 0

mov qword ptr [rsp + 24], 42 # store 128-bit 0:42 on the stack

# little-endian = low half at lower address

mov rax, qword ptr [rsp + 24]

mov rcx, qword ptr [rsp + 32] # reload it to registers

add rax, 1337 # add 1337 to the low half

adc rcx, 0 # propagate carry to the high half. 1337u128 >> 64 = 0

setb dl # save carry-out (setb is an alias for setc)

mov rsi, rax

test dl, 1 # check carry-out (to detect overflow)

mov qword ptr [rsp + 16], rax # store the low half result

mov qword ptr [rsp + 8], rsi # store another copy of the low half

mov qword ptr [rsp], rcx # store the high half

# These are temporary copies of the halves; probably the high half at lower address isn't intentional

jne .LBB8_2 # jump if 128-bit add overflowed (to another not-shown block of code after the ret, I think)

mov rax, qword ptr [rsp + 16]

mov qword ptr [rsp + 40], rax # copy low half to RSP+40

mov rcx, qword ptr [rsp]

mov qword ptr [rsp + 48], rcx # copy high half to RSP+48

# This is the actual b, in normal little-endian order, forming a u128 at RSP+40

add rsp, 56

ret # with retval in EAX/RAX = low half result

您可以在其中看到该值42存储在rax和中rcx。

(编者注:x86-64 C调用约定在RDX:RAX中返回128位整数。但这main根本不返回值。所有冗余复制纯粹是出于禁用优化的考虑,Rust实际上在调试中检查溢出模式。)

为了进行比较,这是x86-64上Rust 64位整数的asm,其中不需要加载,每个值仅需一个寄存器或堆栈槽。

playground::main:

sub rsp, 24

mov qword ptr [rsp + 8], 42 # store

mov rax, qword ptr [rsp + 8] # reload

add rax, 1337 # add

setb cl

test cl, 1 # check for carry-out (overflow)

mov qword ptr [rsp], rax # store the result

jne .LBB8_2 # branch on non-zero carry-out

mov rax, qword ptr [rsp] # reload the result

mov qword ptr [rsp + 16], rax # and copy it (to b)

add rsp, 24

ret

.LBB8_2:

call panic function because of integer overflow

setb / test仍然是完全多余的:(jc如果CF = 1,则跳转)就可以了。

启用优化功能后,Rust编译器不会检查溢出,因此其+工作方式类似于.wrapping_add()。

@Anush不,rax / rsp / ...是64位寄存器。每个128位数字存储在两个寄存器/内存位置,这导致两个64位相加。 (4认同)

@Anush:不,它使用了很多指令,因为它是在禁用优化的情况下编译的。如果您编译了一个使用两个`u128` args并返回一个值(例如https://godbolt.org/z/6JBza0)的函数,则会看到*更简单的代码(如add / adc)禁用优化以阻止编译器对编译时常数args进行常数传播的问题。 (4认同)

@PeterCordes:具体地说,Rust语言指定了未指定溢出,而rustc(唯一的编译器)指定了两种行为可供选择:Panic或Wrap。理想情况下,默认情况下将使用Panic。实际上,由于次优的代码生成,在Release模式下,默认值为Wrap,并且长期目标是在(如果有的话)代码生成“足够好”以供主流使用时转向Panic。另外,所有Rust整数类型都支持命名操作来选择行为:选中,包装,饱和...,因此您可以基于每个操作覆盖所选行为。 (3认同)

@ CAD97 Release模式*使用*包装算法,但不像调试模式那样检查溢出和紧急情况。此行为由[RFC 560](https://github.com/rust-lang/rfcs/pull/560)定义。不是UB。 (2认同)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值