Вбудовані функції
План 11, step 36 (2026-10-07): робочий міст вбудованих функцій (builtins).
Вбудовані функції — це функції erlang, які runtime реалізує мовою C++.
Runtime реєструє їх за модулем, функцією та арністю; згенерований код досягає
їх безпосередньо, через динамічні виклики та як значення fun.
Які вбудовані функції існують
Каталог мосту abi::v1::bridge_builtins
(builtins.hpp) перелічує кожну
вбудовану функцію, яку знають і компілятор, і runtime:
- guard BIF: перевірки типів (
is_atom/1…is_tuple/1,is_function/1,2),abs/1,bit_size/1,byte_size/1,ceil/1,element/2,float/1,floor/1,hd/1,length/1,map_get/2,map_size/1,is_map_key/2,max/2,min/2,round/1,size/1,tl/1,trunc/1,tuple_size/1,binary_part/2,3; - оператори як функції: порівняння,
not/1,and/2,or/2,xor/2, арифметичні та побітові оператори (erlang:'+'/1,2…); display/1,halt/0,1,error/1,2,3,exit/1,throw/1,raise/3іfunction_exported/3;- доступ до термів (step 37 плану):
setelement/3,make_tuple/2,3,tuple_to_list/1,list_to_tuple/1і спискові оператори'++'/2та'--'/2(A ++ B,A -- Bпонижуються до них); - перетворення (step 38 плану):
atom_to_list/1,list_to_atom/1,integer_to_list/1,2,list_to_integer/1,2,float_to_list/1,2,binary_to_list/1,list_to_binary/1,iolist_to_binary/1(term_to_binary/1не вибрано); - консольний вивід (step 40 плану):
io:format/1,2іio:put_chars/1, перші вбудовані функції іншого модуля (io); - ідентичності процесів (step 42 плану):
self/0,make_ref/0,pid_to_list/1іref_to_list/1(pid і посилання); - процеси (step 43 плану):
spawn/1,3іis_process_alive/1(процеси); зв'язки та сигнали виходу (step 48):spawn_link/1,3,link/1,unlink/1,exit/2,exit_signal/2,process_flag/2(зв'язки); монітори (step 49):spawn_monitor/1,3,monitor/2,demonitor/1,2(монітори); зареєстровані імена (step 50):register/2,unregister/1,whereis/1,registered/0(зареєстровані імена); - повідомлення (step 45 плану):
'!'/2(оператор!) іsend/2(повідомлення).
Записи лише дописуються в кінець: індекс запису — це число, яке згенерований
код передає сервісу мосту. Кваліфікований виклик вбудованої функції з
каталогу, що належить іншому модулю (io:format(F, A)), також викликає міст.
Інші функції erlang зберігають свою діагностику:
прямий виклик невідомої дає unknown module erlang, а fun erlang:F/A або
fun F/A для guard BIF поза каталогом (node/0) і
fun erlang:apply/2,3 повідомляють про недоступну можливість dynamic calls.
Як виклики їх досягають
| Вихідний код | Шлях |
|---|---|
abs(X), X + Y, erlang:display(X), halt(), error(R) | Вбудовані сервіси, як і до появи мосту |
erlang:function_exported(M, F, A), setelement(I, T, V), A ++ B, A -- B, length(L) у тілі (вбудовані функції каталогу без вбудованого сервісу) | Вхід як у функцію: CLAUSE_builtin_frame_v1(context, index) дає фрейм вбудованої функції, аргументи передаються в регістрах (порції) |
fun abs/1, fun erlang:'+'/2 | Зовнішній fun erlang:F/A; реєстрація прив'язує його до вбудованої функції |
M:F(Args), apply(M, F, Args), fun M:F/A під час виконання | Сервер коду спершу шукає експорт модуля, потім вбудовану функцію |
fun F/Aдля автоімпортованої вбудованої функції, яку модуль не визначає і не приглушує (-compile({no_auto_import, ...})), — це зовнішній funerlang:F/A, як в OTP:fun abs/1 =:= fun erlang:abs/1, і він друкується якfun erlang:abs/1.halt/0,1,setelement/3,tuple_to_list/1,list_to_tuple/1, перетворення, вбудовані функції процесів і вбудовані функції портівopen_port/2,port_close/1,port_command/2,3,port_connect/2,port_control/3,port_to_list/1,list_to_port/1автоімпортуються, як в OTP;display/1,raise/3,function_exported/3,make_tuple/2,3,port_info/1,2,port_call/2,3іports/0потребують префіксаerlang:(порти).- Вбудована функція має
FrameDescriptorз порожнім (null) тілом (BuiltinFrame). Вхід у неї (CLAUSE_enter_v1,CLAUSE_tail_v1) не додає фрейму: вбудована функція працює з регістрами, а її результат повертається в тіло викликача, тож вбудована функція у хвостовій позиції повертає керування викликачеві викликача. - Помилки ті самі, що їх викликає вбудоване пониження в тілі:
badarg,badarithдля арифметики,system_limit,{badmap, M},{badkey, K};raise/3з недійсним класом або стеком повертаєbadarg. Вони проходять через перевірений канал невдач, як і кожна помилка сервісу. - Доступ до термів дотримується правил
badargз OTP:setelement/3потребує малого цілого індексу в межах кортежу;make_tuple/2,3— малого розміру в діапазоні 0..16 777 215, аmake_tuple/3— правильного списку пар{Index, Value}у межах розміру (пізніші пари мають перевагу);list_to_tuple/1— правильного списку;A ++ B— правильного спискуA([] ++ BдаєBдля будь-якогоB, не-списокBзавершує результат);A -- B— двох правильних списків, де кожен елементBвилучає перший точно рівний (=:=) елементA, за O((n + m) log m). - Перетворення дотримуються OTP:
list_to_atom/1приймає правильний список кодових точок (без сурогатів); 256-й символ даєsystem_limitще до перевірки. Заповнена таблиця атомів (--max-atoms) зупиняє програму як невдача runtime (resource_limit, код виходу 70).integer_to_list/2іlist_to_integer/2приймають малу основу в діапазоні 2..36; цифри друкуються у верхньому регістрі й розбираються в будь-якому.list_to_integerприймає один необов'язковий знак, пропускає початкові нулі, потребує хоча б однієї цифри і викликаєsystem_limitдля понад 1 262 611 значущих десяткових цифр (або 4 194 304 у будь-якій основі), щойно його перші цифри дійсні, а також для значення понад межу цілих чисел.float_to_list/1— це"%.20e"; параметри/2застосовуються по порядку, і перемагає останній формат:{scientific, D}("%.*e", від'ємне D дає 6),{decimals, D}(D >= 0; фіксований формат із власним округленням OTP нижче 2^53 і 19 десяткових знаків,compactобрізає кінцеві нулі, також для цілого числа з{decimals, 0}понад 2^53),short(найкоротші цифри для точного відновлення з вибором OTP між фіксованим і науковим форматом). Текст завдовжки 256 байтів або більше даєbadarg.binary_to_list/1потребує binary;list_to_binary/1— списку, аiolist_to_binary/1— списку або binary з байтів, binary і вкладених списків, кожен з яких закінчується[]або binary.
function_exported(M, F, A)викликаєbadarg, якщоMіFне атоми абоAне мале ціле число; вона істинна, коли модуль програми експортуєM:F/AабоM:F/Aє зареєстрованою вбудованою функцією.
Порції
План 11, step 43A (2026-10-08): вбудовані функції, робота яких зростає зі списковим або binary аргументом, виконуються обмеженими порціями, як переривані (trapping) BIF в OTP, тож довга вбудована функція не може завадити виконанню інших процесів (процеси).
- Кожна вбудована функція мосту, яку викликає тіло, викликається як функція і
витрачає одну редукцію. Порція може виконати
WORK_PER_REDUCTION(16) одиниць роботи на кожну редукцію, що лишилася в кванті часу (щонайменше обсяг однієї редукції): пройдені або побудовані комірки списку, порівняння, байти. Вона оплачує їх із кванта. - Вбудована функція, у якої лишилася робота, переривається:
ProcessStack::trapназиває фрейм продовження і кладе терми стану вбудованої функції в регістри, які лишаються коренями; нативний стан (байти, позиції сортування, слова термів, що зберігаються як корені) живе вTrapStateстека процесу. Процес поступається виконанням і відновлюється в продовженні в пізнішому кванті. Збирання сміття між порціями переписують регістри і слова стану, як і інші корені. - Результати, помилки й порядок обчислення такі самі, як при виконанні до кінця. Помилка, виявлена пізно (неправильний хвіст), викликається, коли обхід до неї доходить.
- Виклики з хоста (
call_builtin) одразу продовжують кожне переривання.
| Вбудована функція | Порції |
|---|---|
length/1 у тілі | Лічить комірки; у guard лишається вбудованим сервісом, бо guard BIF в OTP не переривається |
A ++ B | Збирає елементи A, потім будує копію поверх B з її кінця |
A -- B | Збирає B, сортує за точним порядком (висхідне сортування злиттям), переглядає A з двійковим пошуком для кожного елемента, потім будує збережені елементи; якщо нічого не вилучено, результатом є сам A |
binary_to_list/1 | Будує список із кінця binary |
list_to_binary/1, iolist_to_binary/1 | Обходить iolist у глибину, потім створює binary одразу |
Інші вбудовані функції виконуються до кінця. Кортежні вбудовані функції
(setelement/3, make_tuple/2,3, tuple_to_list/1, list_to_tuple/1)
поводяться як в OTP: їхню роботу обмежує межа арності кортежу. Решта
перетворень читає вхідні дані, обмежені межами атомів, цілих чисел і чисел із
рухомою комою. Форматування через io:format/1,2 виконується до кінця
(відмінності). Вбудовані сервіси циклів (кінцеве обертання
списку в comprehension) виконуються до кінця як частина циклу.
Типізовані вбудовані функції
План 11, step 41 (2026-10-07): вбудовані функції, що самі перевіряють свої
аргументи, — це функції C++ із типізованими параметрами
(typed.hpp),
Result Function(ProcessContext &, Parameters...), зареєстровані через
typed_entry<Function>(module, name) (арність дорівнює кількості параметрів).
- Адаптер допускає кожне слово аргументу по порядку (слово, яким цей процес не
володіє, дає відповідну невдачу, а не
badarg), потім перетворює кожне на тип свого параметра; невідповідність викликаєbadarg, і функція не виконується. - Типи параметрів:
Term(будь-який терм, загальний запасний варіант),std::int64_t(мале ціле число),detail::Integer(будь-яке ціле число),double(число з рухомою комою),ListArgument(правильний список і його елементи),TupleArgument,BinaryArgument(байти binary),AtomArgument(його написання). - Результати:
Term,TermResult<Term>(невдале конструювання є невдачею runtime),BuiltinResult<Term>(std::expectedзBuiltinFailure) або сиреWord, яке функція опублікувала сама. Функція також може кинутиBuiltinFailure(помилку Erlang, як-отbadargчиsystem_limit, або невдачу доступу до терма);call_builtinперетворює кожен інший виняток C++ наout_of_memoryабоinternal_error, тож жоден не перетинає ABI згенерованого коду. - Родини доступу до термів, перетворень і io,
binary_part/2іfunction_exported/3типізовані. Інші вбудовані функціїerlangпередають свої слова аргументів без перетворення до вбудованих сервісів, які викликає й згенерований код і які їх допускають; вони лишаються адаптерамиBuiltinBodyна рівні слів.
Реєстрація
BuiltinRegistry(builtin_registry.hpp), яким володієCodeServer, відображає точні модуль/функцію/арність наBuiltinFrame.addприймає пакетBuiltinEntryі реєструє або всі, або жодного: порожня назва, понад 255 аргументів, відсутня реалізація або назва, яку вже зареєстровано (або повторено в пакеті), відхиляє пакет, нічого не зберігаючи.- Під час старту runtime реєструє кожну таблицю з
production_builtins()(erlang_builtins(),term_access_builtins(),conversion_builtins(),io_builtins()), більшість записів яких створено черезtyped_entry, і які разом покривають каталог; пізніші родини додають власні таблиці. - Тіло читає рівно стільки слів аргументів, скільки становить його арність, і
записує помилки в перевірений канал; винятки хоста стають невдачами
out_of_memoryабоinternal_error. - Старіший шлях хоста
abi::v1::dispatch_builtin(нативні модулі, зареєстровані хостом за назвою, runtime) є окремим і не змінився.
Clause