Builtins
Plan 11 step 36 (2026-10-07): die produktive Builtin-Brücke. Builtins sind
erlang-Funktionen, die die Runtime in C++ implementiert. Die Runtime
registriert sie nach Modul, Funktion und Stelligkeit; generierter Code erreicht
sie direkt, über dynamische Aufrufe und als Fun-Werte.
Welche Builtins es gibt
Der Brückenkatalog abi::v1::bridge_builtins
(builtins.hpp) führt jedes Builtin
auf, das sowohl der Compiler als auch die Runtime kennen:
- die Guard-BIFs: Typtests (
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; - die Operatoren als Funktionen: Vergleiche,
not/1,and/2,or/2,xor/2, arithmetische und bitweise Operatoren (erlang:'+'/1,2…); display/1,halt/0,1,error/1,2,3,exit/1,throw/1,raise/3undfunction_exported/3;- Term-Zugriff (plan step 37):
setelement/3,make_tuple/2,3,tuple_to_list/1,list_to_tuple/1und die Listenoperatoren'++'/2und'--'/2(A ++ B,A -- Bwerden per Lowering auf sie abgebildet); - Konvertierungen (plan 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/1ist nicht ausgewählt); - Konsolenausgabe (plan step 40):
io:format/1,2undio:put_chars/1, die ersten Builtins eines anderen Moduls (io); - Prozessidentitäten (plan step 42):
self/0,make_ref/0,pid_to_list/1undref_to_list/1(Pids und Referenzen); - Prozesse (plan step 43):
spawn/1,3undis_process_alive/1(Prozesse); Links und Exit-Signale (step 48):spawn_link/1,3,link/1,unlink/1,exit/2,exit_signal/2,process_flag/2(Links); Monitore (step 49):spawn_monitor/1,3,monitor/2,demonitor/1,2(Monitore); registrierte Namen (step 50):register/2,unregister/1,whereis/1,registered/0(registrierte Namen); - Nachrichten (plan step 45):
'!'/2(der Operator!) undsend/2(Nachrichten).
Einträge werden nur angehängt: Der Index eines Eintrags ist die Nummer, die
generierter Code an den Brückendienst übergibt. Ein qualifizierter Aufruf eines
Katalog-Builtins eines anderen Moduls (io:format(F, A)) ruft ebenfalls die
Brücke auf. Andere erlang-Funktionen behalten ihre Diagnosen:
ein direkter Aufruf einer unbekannten ergibt unknown module erlang;
fun erlang:F/A oder fun F/A eines Guard-BIF außerhalb des Katalogs
(node/0) sowie fun erlang:apply/2,3 melden die nicht verfügbare Fähigkeit
dynamic calls.
Wie Aufrufe sie erreichen
| Quelle | Weg |
|---|---|
abs(X), X + Y, erlang:display(X), halt(), error(R) | Inline-Dienste, wie vor der Brücke |
erlang:function_exported(M, F, A), setelement(I, T, V), A ++ B, A -- B, length(L) in einem Rumpf (Katalog-Builtins ohne Inline-Dienst) | Wie eine Funktion betreten: CLAUSE_builtin_frame_v1(context, index) liefert den Frame des Builtins, die Argumente kommen in die Register (Portionen) |
fun abs/1, fun erlang:'+'/2 | Externe Fun erlang:F/A; die Registrierung bindet sie an das Builtin |
M:F(Args), apply(M, F, Args), fun M:F/A zur Laufzeit | Der Code-Server sucht zuerst einen Export des Moduls, dann ein Builtin |
fun F/Aeines automatisch importierten Builtins, das das Modul weder definiert noch unterdrückt (-compile({no_auto_import, ...})), ist die externe Funerlang:F/A, wie in OTP:fun abs/1 =:= fun erlang:abs/1, und sie wird alsfun erlang:abs/1ausgegeben.halt/0,1,setelement/3,tuple_to_list/1,list_to_tuple/1, die Konvertierungen, die Prozess-Builtins und die Port-Builtinsopen_port/2,port_close/1,port_command/2,3,port_connect/2,port_control/3,port_to_list/1,list_to_port/1werden wie in OTP automatisch importiert;display/1,raise/3,function_exported/3,make_tuple/2,3,port_info/1,2,port_call/2,3undports/0brauchen das Präfixerlang:(Ports).- Ein Builtin hat einen
FrameDescriptormit leerem Rumpf (BuiltinFrame). Sein Betreten (CLAUSE_enter_v1,CLAUSE_tail_v1) legt keinen Frame an: Das Builtin läuft auf den Registern, und sein Ergebnis kehrt in den Rumpf des Aufrufers zurück, sodass ein Builtin in Endposition zum Aufrufer des Aufrufers zurückkehrt. - Fehler sind dieselben, die das Inline-Lowering in einem Rumpf auslöst:
badarg,badarithfür Arithmetik,system_limit,{badmap, M},{badkey, K};raise/3mit ungültiger Klasse oder ungültigem Stack gibtbadargzurück. Sie laufen wie jeder Dienstfehler über den geprüften Fehlerkanal. - Term-Zugriff folgt den
badarg-Regeln von OTP:setelement/3braucht einen kleinen ganzzahligen Index innerhalb des Tupels;make_tuple/2,3eine kleine Größe in 0..16.777.215, undmake_tuple/3eine echte Liste von{Index, Value}-Paaren innerhalb der Größe (spätere Paare gewinnen);list_to_tuple/1eine echte Liste;A ++ Beine echte ListeA([] ++ BistBfür jedesB, einB, das keine Liste ist, beendet das Ergebnis);A -- Bzwei echte Listen, wobei jedes Element vonBdas erste exakt gleiche (=:=) Element vonAentfernt, in O((n + m) log m). - Konvertierungen folgen OTP:
list_to_atom/1nimmt eine echte Liste von Codepoints (keine Surrogate); ein 256. Zeichen istsystem_limit, bevor es geprüft wird. Eine volle Atomtabelle (--max-atoms) beendet das Programm als Runtime-Fehler (resource_limit, Exit-Code 70).integer_to_list/2undlist_to_integer/2nehmen eine kleine Basis in 2..36; Ziffern werden in Großbuchstaben ausgegeben und in beiden Schreibweisen gelesen.list_to_integerakzeptiert ein optionales Vorzeichen, überspringt führende Nullen, braucht eine Ziffer und löstsystem_limitaus bei mehr als 1.262.611 signifikanten Dezimalziffern (oder 4.194.304 in beliebiger Basis), sobald seine ersten Ziffern gültig sind, und bei einem Wert jenseits der Ganzzahlgrenze.float_to_list/1ist"%.20e"; Optionen von/2gelten der Reihe nach, das letzte Format gewinnt:{scientific, D}("%.*e", negatives D ist 6),{decimals, D}(D >= 0; Festkomma mit OTPs eigener Rundung unter 2^53 und 19 Dezimalstellen,compactentfernt nachgestellte Nullen, auch bei einer Ganzzahl mit{decimals, 0}über 2^53),short(kürzeste verlustfreie Ziffern in OTPs Wahl zwischen Festkomma und wissenschaftlicher Notation). Text von 256 Bytes oder mehr istbadarg.binary_to_list/1braucht ein Binary;list_to_binary/1eine Liste undiolist_to_binary/1eine Liste oder ein Binary aus Bytes, Binaries und verschachtelten Listen, wobei jede Liste auf[]oder ein Binary endet.
function_exported(M, F, A)löstbadargaus, sofernMundFnicht Atome sind undAkeine kleine Ganzzahl ist; es ist wahr, wenn ein Modul des ProgrammsM:F/Aexportiert oderM:F/Aein registriertes Builtin ist.
Portionen
Plan 11 step 43A (2026-10-08): Builtins, deren Arbeit mit einem Listen- oder Binary-Argument wächst, laufen in begrenzten Portionen, wie die trappenden BIFs von OTP, sodass ein langes Builtin andere Prozesse nicht am Laufen hindern kann (Prozesse).
- Jedes Brücken-Builtin, das ein Rumpf aufruft, wird wie eine Funktion betreten
und verbraucht eine Reduktion. Eine Portion darf
WORK_PER_REDUCTION(16) Arbeitseinheiten pro verbleibender Reduktion der Zeitscheibe leisten (mindestens den Gegenwert einer Reduktion): durchlaufene oder gebaute Listenzellen, Vergleiche, Bytes. Sie bezahlt diese aus der Zeitscheibe. - Ein Builtin mit verbleibender Arbeit trappt:
ProcessStack::trapbenennt einen Fortsetzungs-Frame und legt die Zustandsterme des Builtins in die Register, die Wurzeln bleiben; nativer Zustand (Bytes, Sortierpositionen, als Wurzeln gehaltene Termwörter) lebt imTrapStatedes Prozess-Stacks. Der Prozess gibt ab und setzt in einer späteren Zeitscheibe an der Fortsetzung fort. Speicherbereinigungen zwischen Portionen schreiben die Register und Zustandswörter wie andere Wurzeln um. - Ergebnisse, Fehler und Auswertungsreihenfolge sind dieselben wie bei einem Durchlauf bis zum Ende. Ein spät gefundener Fehler (ein unechter Rest) wird ausgelöst, wenn der Durchlauf ihn erreicht.
- Host-Aufrufe (
call_builtin) setzen jeden Trap sofort fort.
| Builtin | Portionen |
|---|---|
length/1 in einem Rumpf | Zählt Zellen; in einem Guard bleibt es der Inline-Dienst, da das Guard-BIF von OTP nicht trappt |
A ++ B | Sammelt die Elemente von A und baut dann die Kopie von ihrem Ende her auf B auf |
A -- B | Sammelt B, sortiert es nach exakter Ordnung (Bottom-up-Mergesort), durchsucht A mit einer binären Suche pro Element und baut dann die behaltenen Elemente; wird nichts entfernt, ist das Ergebnis A selbst |
binary_to_list/1 | Baut die Liste vom Ende des Binary her |
list_to_binary/1, iolist_to_binary/1 | Durchläuft die Iolist in Tiefensuche und erzeugt dann das Binary auf einmal |
Andere Builtins laufen bis zum Ende. Die Tupel-Builtins (setelement/3,
make_tuple/2,3, tuple_to_list/1, list_to_tuple/1) verhalten sich wie in
OTP: Ihre Arbeit ist durch die Grenze der Tupelstelligkeit beschränkt. Die
übrigen Konvertierungen lesen Eingaben, die durch die Grenzen für Atome,
Ganzzahlen und Gleitkommazahlen beschränkt sind. Formatieren mit
io:format/1,2 läuft bis zum Ende (Unterschiede).
Inline-Dienste von Schleifen (das abschließende Umkehren einer Comprehension)
laufen als Teil der Schleife bis zum Ende.
Typisierte Builtins
Plan 11 step 41 (2026-10-07): Builtins, die ihre eigenen Argumente prüfen, sind
C++-Funktionen mit typisierten Parametern
(typed.hpp),
Result Function(ProcessContext &, Parameters...), registriert mit
typed_entry<Function>(module, name) (die Stelligkeit ist die Parameteranzahl).
- Der Adapter lässt jedes Argumentwort der Reihe nach zu (ein Wort, das diesem
Prozess nicht gehört, ist der Fehler, der es ist, nie
badarg) und konvertiert dann jedes in seinen Parametertyp; eine Abweichung löstbadargaus, und die Funktion läuft nicht. - Parametertypen:
Term(beliebiger Term, der generische Rückfall),std::int64_t(eine kleine Ganzzahl),detail::Integer(eine beliebige Ganzzahl),double(eine Gleitkommazahl),ListArgument(eine echte Liste und ihre Elemente),TupleArgument,BinaryArgument(Bytes eines Binary),AtomArgument(seine Schreibweise). - Ergebnisse:
Term,TermResult<Term>(eine fehlgeschlagene Konstruktion ist ein Runtime-Fehler),BuiltinResult<Term>(std::expectedmit einemBuiltinFailure) oder ein rohesWord, das die Funktion selbst veröffentlicht hat. Eine Funktion darf auchBuiltinFailurewerfen (einen Erlang-Fehler wiebadargodersystem_limitoder einen Fehler beim Term-Zugriff);call_builtinmacht aus jeder anderen C++-Ausnahmeout_of_memoryoderinternal_error, sodass keine die ABI des generierten Codes überquert. - Die Familien Term-Zugriff, Konvertierung und io,
binary_part/2undfunction_exported/3sind typisiert. Die anderenerlang-Builtins reichen ihre Argumentwörter unkonvertiert an die Inline-Dienste weiter, die auch generierter Code aufruft und die sie zulassen; sie bleibenBuiltinBody-Adapter auf Wortebene.
Registrierung
BuiltinRegistry(builtin_registry.hpp), im Besitz desCodeServer, bildet exakte Modul/Funktion/Stelligkeit auf einenBuiltinFrameab.addnimmt einen Batch vonBuiltinEntrys und registriert alle oder keinen: ein leerer Name, mehr als 255 Argumente, eine fehlende Implementierung oder ein bereits registrierter (oder im Batch wiederholter) Name weist den Batch ab, ohne etwas zu behalten.- Der Start der Runtime registriert jede Tabelle von
production_builtins()(erlang_builtins(),term_access_builtins(),conversion_builtins(),io_builtins()), deren Einträge größtenteils mittyped_entryerstellt sind und die zusammen den Katalog abdecken; spätere Familien fügen eigene Tabellen hinzu. - Ein Rumpf liest genau so viele Argumentwörter wie seine Stelligkeit und
vermerkt Fehler im geprüften Kanal; Host-Ausnahmen werden zu Fehlern
out_of_memoryoderinternal_error. - Der ältere Host-Pfad
abi::v1::dispatch_builtin(vom Host registrierte native Module nach Namen, Runtime) ist getrennt und unverändert.
Clause