Clause
← All dokumentation

Det engelska originalet har ändrats sedan översättningen gjordes, så delar av den kan vara inaktuella. Läs på engelska

Översatt från det engelska originalet · 1e3a39c8-dirty · 2026-10-26

ABI för genererad kod (revision 21)

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, fun-deskriptorer i en privat <prefix>.funs-tabell (funs), de kodade modulliteralerna och literalcachen (områden), de skrivbara atom- och fun-cacher som genererad kod läser inline, samt code_convention för dess kroppar. 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 kanalen innan resultatet används eller nästa argument evalueras: den läser ordet failed i kontextens FrameState, som runtime håller lika med CLAUSE_call_failed_v2(context). 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}), som bygger resultatet i en enda heapreservation; listgeneratorer testar och läser cons-celler inline; 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 tar MapOperation::entries (11, {Map}) en gång: en cell med nycklarna och värdena på orden 1 + 2i och 2 + 2i i kanonisk nyckelordning (en klassisk map själv, eller en tupelformad kopia av posterna i en map utanför heapen som endast genererad kod läser) och läser varje post inline; key_at/value_at ({Map, Position}) finns kvar för andra anropare. En zips återstående indata visas med MapOperation::iterator, OTP:s kedja {K, V, Next} som slutar i none. MapOperation::update_owned (10) tar operanderna till update för en map-uppdatering vars källa kompilatorn har bevisat inte läses efteråt: en källmap utanför heapen uppdateras på plats och dess stubbe lämnas förbrukad (tom). 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; 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 abi::v1::Code, void(void *context, TermWord x0, ...) med abi::v1::register_arguments termparametrar; med en runtime byggd med Clang på x86-64 och AArch64 använder kropparna och varje överföring konventionen preserve_none, liksom abi::v1::Code gör där: kroppar returnerar aldrig normalt, så de sparar inga register som den anropade ska bevara; registreringen avvisar en modul vars code_convention (0 ingen, 1 C, 2 preserve_none) inte är runtimens. 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 (för andra användare; genererad kod testar och läser tupel- och cons-celler inline), 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.

Inline-former (codegen/lowering_inline.cpp): typtester (is_atom, is_integer, is_number, is_boolean, is_tuple, is_list, is_bitstring, is_float, is_map, is_pid, is_port, is_reference, is_function/1, native is_record/1, is_binary/1 via bitlängdsordet) läser taggar och, efter ett test av boxad tagg, huvudets sort (abi::v1::HeaderKind); de anropar aldrig en tjänst. Jämförelser av två omedelbara värden (==, /=) eller två små heltal (<, =<, >, >=), element/2, tuple_size/1, bit_size/1 och byte_size/1 (från bitlängdsordet, så länge storleken är ett litet heltal), hd/1, tl/1, de booleska operatorerna, band, bor, bxor, bsr, bsl (så länge resultatet förblir litet) och små div/rem körs inline; self/0 läser FrameState::self. De körs inline där operanderna tillåter det och anropar annars CLAUSE_immediate_v1, som behåller varje fel- och bignum-fall. setelement/3 med ett litet index inom en boxad tupel kopierar tupeln till heapblockets fria ord och ersätter platsen inline; annars, eller utan plats, går den in i den inbyggda bryggfunktionen.

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).

En direkt inbyggd funktion (BuiltinCall::direct: rena konverteringar som integer_to_list/1, atom_to_list/1, list_to_tuple/1, setelement/3, phash2/1) trappar aldrig, väntar aldrig och verkar aldrig på en annan process. Genererad kod anropar den med CLAUSE_builtin_call_v1(context, builtin, arguments), som kör dess kropp direkt och returnerar resultatet, utan ramöverföring, utan återupptagningspunkt och utan safepoint vid ingången; den förbrukar en reduktion, och ett fel går till den kontrollerade kanalen som för en tjänst. En snabb inbyggd funktion (BuiltinCall::quick: length/1, binary_to_list/1, list_to_binary/1, iolist_to_binary/1) anropas först på samma sätt; dess begränsade snabba väg returnerar 0 när den avböjer (längre indata, andra former, varje fel och alltid när anrop spåras), och den genererade koden går då in i den inbyggda funktionens ram som för vilken inbyggd bryggfunktion som helst.

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
9Varje kontext börjar med abi::v1::FrameState; inline-överföringar
10Inledande X-register skickas som kodparametrar
11Fasta ord för false och true
12Inline-heapfälten heap_top/heap_limit i FrameState
13Fasta undefined, ok, error; ModuleDescriptor::atom_cache och FrameState::runtime
14Literalområden för moduler: ModuleDescriptor::literals, literal_words
15FrameState::self, kontextens pid-ord
16Code använder preserve_none på x86-64 och AArch64 med en runtime byggd med Clang; ModuleDescriptor::code_convention
17Sex X-register som parametrar till Code
18sub_binary-celler (HeaderKind::sub_binary, sub_*_word) och inline-extraktion av Rest
19Slut- och gränsord i refc_binary-celler (refc_append_*_word, 8 nyttolastord) och inline-tillägg
20MapOperation::entries och inline map-generatorer
21ModuleDescriptor::fun_cache och inline-closures
22Direkta inbyggda funktioner: BuiltinName::direct, CLAUSE_builtin_call_v1
23Snabba inbyggda funktioner: BuiltinName::call (BuiltinCall), 0 från CLAUSE_builtin_call_v1 avböjer
24RecordDescriptor::definition (fältlistan som External Term Format-byte, för records:get_definition/2) och ModuleDescriptor::features (atomplatser för de aktiverade funktionerna, för erl_features:used/1)
25Kodade modulliteraler och literalheapen: ModuleDescriptor::literals innehåller kodade byte (literal_bytes), literal_cache, literal_count; CLAUSE_literal_v1