Clause
← All dokumentation

Översatt från det engelska originalet · 06042fa · 2026-10-09 · Läs på engelska

ABI för genererad kod (revision 4)

Privat kontrakt mellan kompilatorns utdata och runtime. Det är projektintern C++23, inte BEAM-kompatibelt och inte ett allmänt FFI. Objekt och runtime måste komma från samma bygge; äldre deskriptorrevisioner avvisas före användning.

Headers: v1.hpp (typer för term/kontext/funktion), term.hpp (kodek för omedelbara heltal), status.hpp, builtins.hpp, startup.hpp (programstart).

Termer

En term är ett teckenlöst ord med målets pekarbredd (32 eller 64 bitar). Layouter härleds från det konfigurerade LLVM-målet, så kod för andra mål använder målets bredder.

Funktioner och symboler

Ingångssignatur (native C-anropskonvention, ingen C-länkning krävs):

TermWord function(ProcessContext *context, const TermWord *arguments);

Argumenten är en lånad, ordjusterad array i källordning (null vid aritet 0). Kontexten är levande och skickas oförändrad genom anrop. Ett returnerat ord är användbart först efter kontroll av felkanalen.

Symboler: clausev1_<hex module>_<hex function>_<arity>, hex med gemener av UTF-8-byte, kanonisk decimal aritet; reversibla och oberoende av värdsystemet. Exporterade ingångar är externa, övriga interna.

Modulregistrering

Varje modul emitterar clausev1_<hex module>__0.descriptor och .register. Deskriptorn innehåller ABI-version, termbredd, exporttabell (namn, aritet, värdingång och den FrameDescriptor som dynamiska anrop går in i), atomstavningar (par av UTF-8-pekare/storlek), deskriptorer för native records (modul-, namn- och fältatomplatser, exportflagga; native records) i en extern <prefix>.records-tabell som andra moduler i en batch refererar till, och fun-deskriptorer i en privat <prefix>.funs-tabell (funs). Registreringen anropar CLAUSE_register_module_v4(Runtime*), som validerar version/bredd och alla exporter, internerar atomer, bygger ett fryst register och publicerar det tillsammans med kodavbilden i en enda transaktion. Dubblerade moduler ersätter aldrig kod; vid varje fel publiceras ingenting (redan internerade atomer stannar i den begränsade tabellen).

Felkanal (revision 2)

Fel kodas inte i termbitar. Efter varje genererat anrop som inte är ett svansanrop kontrollerar anroparen CLAUSE_call_failed_v2(context) innan resultatet används eller nästa argument evalueras. Vid fel returnerar den anropade ett ogiltigt nollord.

UtfallTransport
Mönster som inte matchar, guard som avvisarFortsättning till nästa kandidat; kanalen orörd
Uttömda klausulererror:function_clause; error:{case_clause, Value} med ägd nyttolast för ett case; error:if_clause; error:{try_clause, Value} för ett trys of-klausuler; error:{else_clause, Value} för en maybes else-klausuler
Misslyckad matchning i kropperror:{badmatch, Value} med ägd nyttolast
Comprehensionserror:{bad_generator, Tail}, error:{bad_filter, Value}, error:{bad_generators, Inputs} (ErrorReason 16-18); en strikt generators avvisning är {badmatch, Element}
Åtkomst till records, felaktiga argument, aritmetik, mapsbadrecord, badarg, badarith, badmap/badkey
Fält i native record saknasErrorReason::badfield (20), nyttolast {{Module, Name}, Field}
Anrop av ett värde (F(Args))ErrorReason::badfun (22, nyttolasten är värdet), badarity (23, nyttolast {Fun, Args}), undef (24), registrerade av CLAUSE_apply_v1 (funs)
Dynamiska anrop (M:F(Args), apply/2,3, fun M:F/A med variabler)badarg för en modul eller funktion som inte är en atom, en ofullständig argumentlista eller en ogiltig aritet; undef när ingen modul i programmet exporterar funktionen; badfun/badarity som ovan (funs)
Extern native-konstruktion utan värdeErrorReason::novalue (21), nyttolast {{Module, Name}, Field}
Heltalsresultat över storleksgränsenTjänsteutfall ValueOutcome::system_limit (3): en guard avvisar, en kropp kastar error:system_limit (ErrorReason 19)
Ogiltig lat vänsteroperand{badarg, Value}
Infrastruktur (OOM, gränser, ägarskap, internt)CallError::runtime_failure med exakt Status
erlang:error/1,2,3, exit/1, throw/1, erlang:raise/3raised_error/raised_exit/raised_throw: klassen kommer från ID:t, den ägda nyttolasten är hela orsaken
erlang:halt/0,1CallError::halted med halt_status (och slogan)

Orsaker är typade ID:n som registreras av CLAUSE_raise_v2; de tre raised_*-ID:na väljer klassen exit eller throw (annars error) och bär vilken term som helst som orsak. error/2,3 kastar via CLAUSE_error_v1(context, reason, args), som också behåller en lista args för den översta stackramen. Det första felet vinner; nästlade anrop delar kanalen. GeneratedInvocation är värdens räckvidd: den kontrollerar väntande fel före ingång och efter retur, kopierar resultat eller fel och rensar endast vid den yttersta utgången (även vid C++-undantag). Inga undantag passerar genererade ingångar. Anropare av råa ingångar måste öppna en GeneratedInvocation; vanliga värdar använder ResolvedFunction::call.

catch Expr omdirigerar varje felkontroll och varje kast inuti Expr till ett hanterarblock som anropar CLAUSE_catch_v1(context, slot). För ett väntande Erlang-undantag skriver den catch-värdet till rotplatsen och rensar kanalen: den kastade termen, {'EXIT', Reason} för en exit eller {'EXIT', {Reason, []}} för ett error (typade orsaker blir sina OTP-termer, till exempel {badmatch, V}; stacken beskrivs nedan). Halts och infrastrukturfel förblir väntande, och hanterarens egen kontroll fortsätter till den omgivande hanteraren eller funktionens utgång. Bindningar som görs inuti Expr är osäkra efteråt, så sammanfogningen slår bara ihop värdet.

try Body of ... catch ... end skyddar endast Body på samma sätt. Dess hanterare anropar CLAUSE_exception_v2(context, class_slot, reason_slot, stack_slot), som skriver klassatomen (error, exit eller throw), orsaken och stackspårstermen till rotplatser och rensar kanalen (halts och infrastrukturfel förblir väntande som för catch). Catch-klausuler matchar sedan Class:Reason med vanliga mönster och guards; en utelämnad klass matchar throw, och en namngiven stackvariabel binder stacktermen. När ingen matchar registrerar CLAUSE_reraise_v2(context, class, reason, stack) undantaget igen med en raised_*-orsak och samma stack, vilket rapporteras och fångas exakt som originalet. of-klausuler väljer på kroppens värde och kastar {try_clause, Value} (ErrorReason::try_clause = 14); undantag inuti of-klausuler och hanterare går till den omgivande hanteraren.

maybe behöver ingen tjänst: varje ?= är en vanlig matchning vars kant för icke-matchning lämnar kroppen mot maybes utgång med det omatchade (redan rotade) värdet. Utan else är det värdet resultatet; annars väljer else-klausuler på det som case-klausuler och kastar {else_clause, Value} (ErrorReason::else_clause = 15) när ingen matchar.

Comprehensions använder de befintliga tjänsterna med några få operationer: de ackumulerade elementen vänds av konstruktionen reverse (ContainerConstruction::reverse = 2, värden {List, Tail}); en binary comprehension sammanfogar sina delar med BitOperation::concat ({List}) och en map comprehension bygger sin map med MapOperation::from_list ({Pairs}, senare nycklar vinner). Map-generatorer läser MapOperation::key_at/value_at ({Map, Position} i kanonisk nyckelordning) och visar en zips återstående indata med MapOperation::iterator, OTP:s kedja {K, V, Next} som slutar i none. Bitstring-generatorer använder vanlig mönsterextraktion plus ett avslutande binary/all-segment för resten.

try ... after A end lägger till ett andra skydd runt kroppen och alla of- och catch-klausuler. På den normala vägen körs A efter att det valda värdet har rotats, och dess värde kastas bort. After-hanteraren tar undantaget med CLAUSE_exception_v2, kör en andra kopia av A och kastar om med CLAUSE_reraise_v2; ett undantag eller fel inuti A lämnar i stället via den omgivande hanteraren och ersätter originalet. Halts och infrastrukturfel hoppar över A. Rotplatser tillhör funktionsramen, så varje väg frigör dem vid funktionens utgång.

Stackspår

Varje rotram namnger sin genererade funktion med en privat abi::v1::FrameDescriptor (moduldeskriptor, platser för modul- och funktionsnamnsatomer, aritet). När ett Erlang-undantag registreras kopierar kanalen de 8 innersta namngivna ramarna (BEAM:s standard för backtrace_depth); termen [{Module, Function, Arity, []}, ...] byggs först när en hanterare, catch eller en rapport efterfrågar den. Den översta ramen visar argumentlistan till error/2,3 i stället för ariteten när det argumentet är en lista.

erlang:raise(Class, Reason, Stack) (CLAUSE_reraise_v2) godtar de stackar som BEAM godtar: en korrekt lista av {M, F, A} (kompletterad med platsen []) eller {M, F, A, Location} med atomerna M, F och en lista Location, avkortad till 8 poster; stacken behålls sedan som den gavs och ramar fångas inte längre. En ogiltig klass eller stack registrerar ingenting och anropet evalueras till badarg, som i OTP. erlang:get_stacktrace/0 avvisas med OTP 29:s lint-text om att funktionen är "removed".

Skillnader mot OTP, alla synliga endast i stacktermen:

Ramar och överföringar

Genererade funktioner körs på explicita ramar (exekveringsmodell, step 19; tjänster i frames.hpp). Varje funktion har en <symbol>.frame-deskriptor (FrameDescriptor: moduldeskriptor, platser för modul- och funktionsatomer, aritet, kroppskod, antal platser, antal termplatser; extern för exporterade funktioner) och en intern <symbol>.body av typen void(void *context). En exporterad <symbol> behåller signaturen TermWord(Context *, const TermWord *) som värdingång som anropar CLAUSE_invoke_v1(context, frame, arguments).

Runtime-tjänster

Genererad kod anropar kontrollerade C++-tjänster: CLAUSE_exact_v1 (exakt likhet), CLAUSE_immediate_v1 (predikat/frågor för omedelbara värden), CLAUSE_construct_v1, CLAUSE_inspect_v1, CLAUSE_integer_v1, CLAUSE_float_v1, CLAUSE_map_v1, CLAUSE_bits_v1, CLAUSE_record_v1 (make/get/update/match/test för native records under en RecordCheck; utfall bad_record, bad_field, no_match), CLAUSE_make_fun_v1 (bygger en fun av en FunDescriptor). Var och en returnerar framgång, semantiskt fel (badarg/badarith/...) eller infrastrukturfel och skriver utdata endast vid framgång. CLAUSE_display_v1 (output.hpp) skriver ut en rad med erlang:display/1 och ger true; den har inget semantiskt fel. CLAUSE_halt_v1 lyckas aldrig: den registrerar en begäran om halt (CallError::halted med slutstatusen) eller badarg, så att anroparen avvecklar. Länkarens stavningar följer målets Itanium- eller Microsoft-C++-mangling.

CLAUSE_builtin_frame_v1(context, builtin) (builtins.hpp) returnerar FrameDescriptor för den inbyggda produktionsfunktionen med index builtin i abi::v1::bridge_builtins (endast tillägg); genererad kod går in i den som i en funktion med argumenten i registren, och fel och misslyckanden går till den kontrollerade kanalen (inbyggda funktioner). En FrameDescriptor med null-kropp är en inbyggd funktion: att gå in i den kör den inbyggda funktionen på registren och returnerar till anroparen, eller suspenderar processen vid den inbyggda funktionens fortsättning när den har trappat (portioner).

abi::v1::dispatch_builtin anropar inbyggda funktioner som registrerats av värden via modul-/funktionsbyte, argumentarray och aritet, och returnerar en Status; utdata skrivs endast vid framgång.

StatusVärdeBetydelse
ok0Framgång
not_implemented1Känd uppskjuten funktion, rapporteras en gång
invalid_argument2Ogiltig indata
diagnostic_failure3Leverans av rapport misslyckades
out_of_memory4Allokering misslyckades, tillståndet återställt
busy5Levande kontexter eller pågående dispatch
wrong_owner6Objektet tillhör en annan runtime/kontext
resource_limit7Tak eller budget uttömd
stopped8Ägaren har stängts ned
abi_mismatch9Version eller termbredd skiljer sig
internal_error10Oväntat fel inneslutet
unknown_builtin11Signaturen är inte registrerad eller katalogiserad
erlang_error12Strukturerat Erlang-fel registrerat
output_failure13Standardutdata avvisade en skrivning

Revisioner

RevisionÄndring
1Inledande deskriptorer och ingångar
2Kontrollerad felkanal
3Atomstavningar och platser i deskriptorer
4Obligatoriska genererade rotomfång
5Explicita processramar och överföringar
6Deskriptorer för native records i moduldeskriptorer, CLAUSE_record_v1
7Fun-deskriptorer i moduldeskriptorer, CLAUSE_make_fun_v1, CLAUSE_apply_v1
8Exportdeskriptorer namnger sin FrameDescriptor; tjänster för dynamiska anrop CLAUSE_call_v1, CLAUSE_apply_list_v1, CLAUSE_call_list_v1, CLAUSE_make_external_fun_v1