[Antique-Hackers] C64-kompilatorns middle stage fungerar

John Lorentzson duuqnd at stacken.kth.se
Thu Jun 26 15:00:31 CEST 2025


Jag har nu skrivit klart och kommittat en första version av kompilatorns 
middle stage. Den kompilerar syntaxträdsnoder till en intermediate 
representation (IR) som kan optimeras.

IR har följande struktur:

Programmet startar i en dubbellänkad lista av IBLOCK. Ett IBLOCK pekar 
till den första och sista IR-instruktionen som den innehåller, samt 
nästa och föregående IBLOCK. En IR-instruktion pekar till nästa och 
föregående IR-instruktion, vilken data som IR-instruktionen tar som 
input, och ett objekt data som den kan ge som output. IR-instruktioner 
är subklasser av IR-INST.

Inputs är instanser av IR-DATA, som håller koll på en definition (den 
instruktion vars output datan är) och användare (instruktioner som tar 
datan som input). Ett IR-RESULT har endast en användare och är oftast 
skapat som returvärde eller temporär resultat. En subklass av 
IR-REUSABLE har istället flera användare och är oftast variabler eller 
temporära resultat som en optimering har gjort återanvändbar. 
IR-instruktioner kan ha en output. Dessa outputs är också IR-DATA och 
kan användas som inputs till andra IR-instruktioner. När en 
IR-instruktions inputlista ändras uppdateras även all relevant datas 
användarlistor. När en output ändras sker liknande uppdatering för 
outputens definition. Att IR-DATA håller koll på användare själv gör det 
trivialt att se om data är oanvänd, samt att följa datans flöde.

Kompilering sker primärt genom den generiska funktionen COMPILE-NODE. En 
metod görs som specialiserar med en syntaxträdnod som första parameter 
och tar som andra parameter en "BUILDER". Metoden skapar där passande 
IR-instruktioner som ges till funktionen BUILD-INSERT tillsammans med en 
BUILDER. Den sätter in IR-instruktionen i det IBLOCK som BUILDER just nu 
bygger upp. Returvärdet är, om relevant för noden, en IR-RESULT som ska 
användas som "nodens" outputvärde. IR-RESULT objekt skapas manuellt i 
dessa metoder och ges till IR-instruktionerna genom deras initargs.

När branching ska genereras måste nuvarande IBLOCKet avslutas eftersom 
flödet i ett IBLOCK måste vara linjärt förutom den sista instruktionen, 
som måste vara en speciell typ av instruktion. Vi skapar två eller tre 
nya IBLOCK (beroende på om vi har en "else" eller inte), och sätter 
sedan in en instruktion IR-IF som den sista i IBLOCKet.

Den sista instruktionen i ett IBLOCK får endast vara en IR-TERMINATOR. 
Dessutom får en IR-TERMINATOR endast vara den sista instruktionen i ett 
IBLOCK. Den har en lista med destinationer som den kan hoppa till från 
slutet av sitt IBLOCK. Nuvarande IBLOCKet avslutas med BUILD-INSERT-END 
som tar en IR-TERMINATOR samt en BUILDER. För att branching ska fungera 
så behövs mer än en destination, så ett nytt IBLOCK kan skapas och göras 
det aktiva IBLOCKet av en BUILDER genom BUILD-BEGIN. Efter 
IBLOCK-konstruktion länkas alla nåbara IBLOCK ihop i en linjär ordning 
genom deras sista instruktioners destinationer.

Både vanliga "if"s och loopar implementeras genom IR-IF samt lite extra 
arbete omkring den ("if" behöver ett testuttryck och kan ha en "else" 
som insättning av jumps nödvändigt, loopar behöver inkrementering och 
testning av en variabel).

Optimering görs relativt enkel både genom att data håller koll på sina 
användningar och definitioner, och att det finns simpla funktioner för 
att flytta, radera, och sätta in instruktioner i ett färdigbyggt IBLOCK. 
Det finns just nu optimeringar för deduplicering av funktionsargument 
(endast för konstanter just nu), omordning av argumentladdning för att 
förenkla generering av bra 6502-kod (simpla "ladda konstant", "ladda 
variabel" hamnar närmast anropet, funktionsanrop som argument hamnar 
tidigare), simpel constant folding (att flytta beräkningar med 
konstanter till compile-time) för kommutativa operationer (just nu 
addition och multiplikation), och radering av bieffektfria instruktioner 
vars output inte används.

Vidare optimering kan bli nödvändig eller användbar, och kompilatorn 
borde göras medveten om deklarerade assemblyrutiner så att den kan veta 
om en assemblyrutin som tar konstanta argument kan evalueras vid 
compile-time eller måste anropas. Jag kommer förhoppningsvis påbörja 
kodgenerering av 6502-assembly ikväll.

-duuq-


More information about the Antique-Hackers mailing list