Щоб надати більш чіткий приклад, на x86_64, складеному з -O
прапором, функцію
pub fn leet(a : i128) -> i128 {
a + 1337
}
компілює до
example::leet:
mov rdx, rsi
mov rax, rdi
add rax, 1337
adc rdx, 0
ret
(У моєму початковому дописі було u128
не те, про що i128
ви просили. Функція комбінує той самий код у будь-якому випадку. Хороша демонстрація того, що додані підписи та без підпису є однаковими в сучасному процесорі.)
Інший перелік видав неоптимізований код. Можна безпечно перейти через налагоджувач, оскільки він гарантує, що ви можете поставити точку перерви в будь-якому місці та перевірити стан будь-якої змінної в будь-якому рядку програми. Це повільніше і важче читати. Оптимізована версія набагато наближається до коду, який фактично працюватиме у виробництві.
Параметр a
цієї функції передається в парі 64-бітових регістрів, rsi: rdi. Результат повертається в іншій парі регістрів, rdx: rax. Перші два рядки коду ініціалізують суму до a
.
Третій рядок додає 1337 до низького слова введення. Якщо це переповнюється, він несе прапор 1 в переносному процесорі. Четвертий рядок додає нуль до високого слова вводу — плюс 1, якщо воно було здійснено.
Ви можете подумати про це як просте додавання одноцифрового числа до двоцифрового числа
a b
+ 0 7
______
але в базі 18,446,744,073,709,551,616. Ви все ще додаєте спочатку найнижчу цифру, можливо, переносите 1 у наступний стовпець, потім додаєте наступну цифру плюс переносити. Віднімання дуже схоже.
Множення повинно використовувати тотожність (2⁶⁴a + b) (2⁶⁴c + d) = 2¹²⁸ac + 2⁶⁴ (ad + bc) + bd, де кожне з цих множень повертає верхню половину продукту в один регістр, а нижню половину товару в інший. Деякі з цих термінів будуть відмінені, оскільки біти вище 128-го не вписуються в а u128
та відкидаються. Незважаючи на це, для цього потрібна низка інструкцій з машини. Відділ також здійснює кілька кроків. Для підписаного значення, множення та ділення додатково потрібно було б перетворити знаки операндів і результат. Ці операції взагалі не дуже ефективні.
В інших архітектурах це стає простіше або складніше. RISC-V визначає 128-розрядне розширення набору інструкцій, хоча, наскільки мені відомо, ніхто не реалізував його в кремнію. Без цього розширення посібник з архітектури RISC-V рекомендує умовну гілку:addi t0, t1, +imm; blt t0, t1, overflow
SPARC має контрольні коди, як прапори керування x86, але для їх встановлення вам потрібно використовувати спеціальну інструкцію add,cc
. MIPS, з іншого боку, вимагає перевірити, чи сума двох непідписаних цілих чисел суворо менша, ніж один із операндів. Якщо так, додаток переповнилося. Принаймні, ви можете встановити інший регістр для значення переносу без умовної гілки.