Để cung cấp một ví dụ rõ ràng hơn, trên x86_64, được biên dịch với -O
cờ, hàm
pub fn leet(a : i128) -> i128 {
a + 1337
}
biên dịch thành
example::leet:
mov rdx, rsi
mov rax, rdi
add rax, 1337
adc rdx, 0
ret
(Bài viết gốc của tôi có u128
chứ không phải là i128
bạn đã hỏi về. Hàm này biên dịch cùng một mã, một minh chứng tốt cho thấy bổ sung có chữ ký và không dấu là giống nhau trên CPU hiện đại.)
Các danh sách khác sản xuất mã không tối ưu hóa. Sẽ an toàn khi bước qua trình gỡ lỗi, bởi vì nó đảm bảo bạn có thể đặt điểm dừng ở bất kỳ đâu và kiểm tra trạng thái của bất kỳ biến nào tại bất kỳ dòng nào của chương trình. Nó chậm hơn và khó đọc hơn. Phiên bản tối ưu hóa gần hơn với mã sẽ thực sự chạy trong sản xuất.
Tham số a
của hàm này được truyền trong một cặp thanh ghi 64 bit, rsi: rdi. Kết quả được trả về trong một cặp thanh ghi khác, rdx: rax. Hai dòng mã đầu tiên khởi tạo tổng thành a
.
Dòng thứ ba thêm 1337 vào từ thấp của đầu vào. Nếu điều này tràn ra, nó mang số 1 trong cờ mang của CPU. Dòng thứ tư thêm 0 vào từ cao của đầu vào cộng với 1 nếu nó được thực hiện.
Bạn có thể nghĩ về điều này như việc thêm đơn giản một số có một chữ số vào số có hai chữ số
a b
+ 0 7
______
nhưng trong căn cứ 18.446.744.073.709.551.616. Trước tiên, bạn vẫn đang thêm chữ số thấp nhất, có thể mang số 1 vào cột tiếp theo, sau đó thêm chữ số tiếp theo cộng với chữ số mang. Phép trừ rất giống nhau.
Phép nhân phải sử dụng danh tính (2⁶⁴a + b) (2⁶⁴c + d) = 2¹²⁸ac + 2⁶⁴ (quảng cáo + bc) + bd, trong đó mỗi phép nhân này trả về nửa trên của sản phẩm trong một thanh ghi và nửa dưới của sản phẩm trong khác. Một số thuật ngữ đó sẽ bị loại bỏ, bởi vì các bit trên 128 không phù hợp với a u128
và bị loại bỏ. Mặc dù vậy, điều này cần một số hướng dẫn máy. Bộ phận cũng mất vài bước. Đối với một giá trị đã ký, phép nhân và phép chia sẽ cần phải chuyển đổi các dấu của toán hạng và kết quả. Những hoạt động đó không hiệu quả lắm.
Trên các kiến trúc khác, nó trở nên dễ dàng hơn hoặc khó hơn. RISC-V định nghĩa một phần mở rộng tập lệnh 128 bit, mặc dù theo hiểu biết của tôi thì không ai thực hiện nó trong silicon. Không có phần mở rộng này, hướng dẫn kiến trúc RISC-V đề xuất một nhánh có điều kiện:addi t0, t1, +imm; blt t0, t1, overflow
SPARC có các mã điều khiển như các cờ điều khiển của x86, nhưng bạn phải sử dụng một lệnh đặc biệt add,cc
, để đặt chúng. Mặt khác, MIPS yêu cầu bạn kiểm tra xem tổng của hai số nguyên không dấu có đúng hơn một trong các toán hạng hay không. Nếu vậy, bổ sung tràn. Ít nhất bạn có thể đặt một thanh ghi khác thành giá trị của bit carry mà không cần nhánh có điều kiện.