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

Exekveringsmodell: ramar och fortsättningar

Hur genererade Erlang-funktioner anropar, returnerar, suspenderas och misslyckas. Modellen valdes efter prototypjämförelsen i jämförda alternativ; den genererade kodens sida av dess ABI finns i ramar och överföringar.

Beslut

Varje Erlang-process körs på sin egen platta stack av explicita ramar, och genererad kod rör sig mellan funktioner endast genom garanterade svansöverföringar (LLVM musttail). Ett anrop som inte är ett svansanrop lagrar anroparens fortsättning (continuation) i anroparens ram och hoppar till den anropade; en retur hoppar tillbaka till den fortsättningen. Den inbyggda (native) stacken förblir därför ett anrop djup ovanför schemaläggaren, oavsett Erlang-rekursionens djup, och en process kan stanna vid vilken överföring som helst och återupptas senare på vilken tråd som helst.

Processtillstånd

abi::v1::FrameState inleder varje kontext, så att genererad kod läser och uppdaterar det inline:

FältBetydelse
stack, top, limitStackblocket (flyttas när det växer), dess första lediga ord och de ord som en push får nå utan att växa eller passera stackgränsen
frameOrdoffset för det aktuella ramhuvudet
registers, liveDe 256 argument-/resultatregistren; de första live orden är rötter vid en överföring
reductionsÅterstående funktionsingångar i tidsskivan
slow, failedBitar som skickar överföringar till tjänsterna (slow_collect, slow_profile); skilt från noll medan felkanalen innehåller ett fel

heap_top/heap_limit, runtime och self betjänar inline-allokering, atomladdningar och self/0 (ramar och överföringar). En suspenderad process registrerar ingången där den fortsätter i ProcessStack::resume_.

Ramar

En ram är ett huvud på 4 ord följt av funktionens platser (BEAM:s Y-register): termplatser, som är rötter, och sedan råa platser för spillda native-värden.

HuvudordBetydelse
previousOffset för anroparens huvud (ramar länkas med offset, aldrig pekare)
functionFunktionens FrameDescriptor
resumeFortsättningsindex som kroppen väljer på när kontrollen återvänder hit
handlerHanterarindex; alltid 0, eftersom fel returnerar genom anropare

Deskriptorn <symbol>.frame anger modul, funktion och aritet och innehåller kroppens kodpekare och antalet platser (abi). Huvudord är inte termer; en genomlöpare följer previous och läser antalet platser från deskriptorer. En bottenram som ägs av runtimen ligger under det första anropet i varje process och i varje värdanrop: att returnera in i den lagrar resultatet i det första registret och avslutar native-anropet.

Operationer

Varje anrop pushar en ram; lövfunktioner hoppar inte över den, så varje funktion behåller sin post i stackspårningen.

Synlighet för rötter

Vid varje överföring och varje safepoint är en process rötter: termplatserna i alla ramar på dess stack, x[0..live) samt felkanalens nyttolast, argumentlista och stackterm (rötter).

Värden överlever aldrig en överföring i native-register eller SSA-värden, utom parametrarna för själva överföringen. Varje kropp laddar om sin ramadress från stack + frame efter att man gått in i den och läser levande värden från platser. Inom en fortsättning gör en tjänst som kan pusha en ram eller flytta heapen varje platspekare och heappekare som hålls i SSA-värden ogiltig; omladdningsregeln för skräpsamlingar fastställs i omladdningsregeln.

Stackblocket växer genom fördubbling och flytt så länge gränsen för hela runtimen tillåter det, krymper aldrig under körning och är skilt från heapblocket. Det har ingen gräns för antalet ramar; stackord räknas mot runtimens minnesgräns.

Mål

musttail med en enhetlig kodsignatur accepteras av varje obligatoriskt mål vid O0 och O2 (prototyp nedan, clang 23.1.2):

MålOrdResultat
x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu64svanshopp
i686-pc-windows-msvc, i686-unknown-linux-gnu32svanshopp
aarch64-unknown-linux-gnu, arm64-apple-macosx14.064svanshopp
armv7-unknown-linux-gnueabihf32svanshopp

Backend rapporterar ett fel när den inte kan uppfylla ett musttail-anrop, så en lyckad kompilering är garantin. Reserv för ett framtida mål som avvisar det: en studsmatta (trampoline), där varje kodstycke returnerar nästa kodpekare till schemaläggarloopen i stället för att hoppa; ramar, rötter och fortsättningsindex är oförändrade. Inget obligatoriskt mål behöver den.

Implementation

Jämförda alternativ

tests/prototypes/execution_model sänker samma Erlang-funktioner för hand (sum/1 kroppsrekursion, loop/2 svansrekursion, fail/1 som kastar på djup N, catcher/1 som fångar det) på tre sätt och kompilerar dem för varje mål ovan vid O0 och O2 (python tests/prototypes/execution_model/run.py). På Windows x64 vid O2 körde den valda modellen kroppsrekursion med djup 1M på 17 ms med 5 ord per ram och 10M svansanrop med konstant stack; dess studsmattevariant klarade samma körningar. Avvisade:

Accepterade kostnader: ett indirekt hopp per retur plus en switch-dispatch; värden som lever över anrop laddas om från platser; bakåtspårningar i native-debuggern visar endast den aktuella funktionen, medan Erlang-stackspårningar kommer från ramkedjan (felsökning).