Skip to content

Commit bb19d90

Browse files
authored
Merge pull request #5 from 0xpantera/refactor/compiler-driver-pipeline
Refactor compiler driver pipeline
2 parents b47c367 + ae89513 commit bb19d90

File tree

19 files changed

+333
-136
lines changed

19 files changed

+333
-136
lines changed

CHANGELOG.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,22 @@
7070
- New assembly instructions for binary operations (add, sub, imul, idiv)
7171
- Proper handling of division and remainder with EAX/EDX registers
7272
- Sign extension support using cdq instruction
73-
- Updated instruction fix-up pass for binary operation constraints
73+
- Updated instruction fix-up pass for binary operation constraints
74+
75+
## 0.4.0.0 -- 2024-12-03
76+
77+
### Added
78+
- Hierarchical modules for better code organization:
79+
- Halcyon.Core
80+
- Halcyon.Frontend
81+
- Halcyon.Backend
82+
- Halcyon.Driver
83+
84+
### Changed
85+
- Split Driver.Pipeline into focused modules:
86+
- Driver.Stages for pure compilation stages
87+
- Driver.External for GCC interactions
88+
- Driver.Output for handling stage results
89+
- Driver.Pipeline remains as high-level orchestration
90+
- Reorganized module exports in cabal file
91+
- Improved module documentation and organization

README.md

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,34 @@ The compiler currently handles C programs with unary operators and integer const
88

99
```c
1010
int main(void) {
11-
return ~(-42);
11+
return -((42 * 10) / (2 + 3)); // Returns -84
1212
}
1313
```
1414
1515
### Compilation Pipeline
1616
17-
The compiler processes source code through the following stages:
17+
The compiler is organized into several major subsystems:
1818
19-
1. **Lexical Analysis**: Breaks source code into a sequence of tokens
20-
2. **Parsing**: Converts tokens into an Abstract Syntax Tree (AST)
21-
3. **TACKY Generation**: Transforms AST into TACKY intermediate representation
22-
4. **Code Generation**: Transforms AST into x86_64 assembly
23-
5. **Code Emission**: Outputs the assembly code to an executable
19+
- **Frontend** - Parsing and analysis
20+
- Lexical analysis (breaks source into tokens)
21+
- Parsing (converts tokens to AST)
22+
- Token definitions
23+
24+
- **Core** - Core data types and compiler infrastructure
25+
- AST, Assembly, and TACKY intermediate representations
26+
- Compiler monad and error handling
27+
28+
- **Backend** - Code generation
29+
- TACKY to assembly conversion
30+
- Register allocation
31+
- Assembly output
32+
33+
- **Driver** - Pipeline coordination
34+
- Command line interface
35+
- Compilation pipeline stages
36+
- External tool integration (GCC for preprocessing and linking)
37+
38+
Each subsystem is organized as a hierarchical module that provides a clean interface to its functionality while hiding implementation details.
2439
2540
### Internal Representations
2641
@@ -85,20 +100,24 @@ Programs are represented internally using a series of increasingly lower-level d
85100
├── lib/ # Main library code
86101
│ ├── Halcyon.hs # Library entry point
87102
│ └── Halcyon/ # Core modules
103+
│ ├── Backend.hs # Backend subsystem interface
88104
│ ├── Backend/ # Code generation and emission
89105
│ │ ├── Codegen.hs # TACKY to Assembly conversion
90106
│ │ ├── Emit.hs # Assembly to text output
91107
│ │ └── ReplacePseudos.hs # Register/stack allocation
108+
│ ├── Core.hs # Core subsystem interface
92109
│ ├── Core/ # Core data types and utilities
93110
│ │ ├── Assembly.hs # Assembly representation
94111
│ │ ├── Ast.hs # C language AST
95112
│ │ ├── Monad.hs # Compiler monad stack
96113
│ │ ├── Settings.hs # Compiler settings and types
97114
│ │ ├── Tacky.hs # TACKY IR definition
98115
│ │ └── TackyGen.hs # AST to TACKY transformation
116+
│ ├── Driver.hs # Driver subsystem interface
99117
│ ├── Driver/ # Compiler driver
100118
│ │ ├── Cli.hs # Command line interface
101119
│ │ └── Pipeline.hs # Compilation pipeline
120+
│ ├── Frontend.hs # Frontend subsystem interface
102121
│ └── Frontend/ # Parsing and analysis
103122
│ ├── Lexer.hs # Lexical analysis
104123
│ ├── Parse.hs # Parsing

halcyon.cabal

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 3.0
22
name: halcyon
3-
version: 0.3.0.0
3+
version: 0.4.0.0
44
-- synopsis:
55
-- description:
66
license: BSD-3-Clause
@@ -19,22 +19,36 @@ common warnings
1919
library
2020
import: warnings
2121
exposed-modules:
22-
Halcyon
23-
, Halcyon.Core.Settings
24-
, Halcyon.Core.Ast
22+
-- Main module
23+
Halcyon
24+
-- Hierarchical modules
25+
, Halcyon.Core
26+
, Halcyon.Frontend
27+
, Halcyon.Backend
28+
, Halcyon.Driver
29+
-- Core types (needed for qualified imports)
2530
, Halcyon.Core.Assembly
31+
, Halcyon.Core.Ast
32+
, Halcyon.Core.Tacky
33+
other-modules:
34+
-- Core internals
35+
Halcyon.Core.Settings
2636
, Halcyon.Core.Monad
27-
, Halcyon.Core.Tacky
2837
, Halcyon.Core.TackyGen
38+
-- Frontend internals
2939
, Halcyon.Frontend.Lexer
3040
, Halcyon.Frontend.Parse
3141
, Halcyon.Frontend.Tokens
42+
-- Backend internals
3243
, Halcyon.Backend.Codegen
3344
, Halcyon.Backend.Emit
3445
, Halcyon.Backend.ReplacePseudos
46+
-- Driver internals
3547
, Halcyon.Driver.Cli
3648
, Halcyon.Driver.Pipeline
37-
other-modules:
49+
, Halcyon.Driver.External
50+
, Halcyon.Driver.Output
51+
, Halcyon.Driver.Stages
3852
-- other-extensions:
3953
build-depends: base ^>=4.20.0.0
4054
, bytestring
@@ -71,6 +85,7 @@ test-suite halcyon-test
7185
, Test.Tacky
7286
, Test.Assembly
7387
, Test.Pipeline
88+
, Test.Common
7489
-- other-extensions:
7590
type: exitcode-stdio-1.0
7691
hs-source-dirs: test

lib/Halcyon.hs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@ module Halcyon
77
, Target(..)
88
, Program(..)
99
-- * Compiler Pipeline
10-
, module Halcyon.Driver.Pipeline
11-
-- * Command Line Interface
12-
, module Halcyon.Driver.Cli
10+
, module Halcyon.Driver
1311
) where
1412

1513
import Halcyon.Core.Settings
1614
import Halcyon.Core.Ast (Program(..))
17-
import Halcyon.Driver.Pipeline
18-
import Halcyon.Driver.Cli
19-
import Halcyon.Frontend.Parse ( parseTokens )
15+
import Halcyon.Driver
16+
import Halcyon.Frontend ( parseTokens )

lib/Halcyon/Backend.hs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Halcyon.Backend
3+
( -- * Code Generation
4+
gen -- from Codegen
5+
-- * Assembly Output
6+
, emitProgram -- from Emit
7+
-- * Register/Stack Management
8+
, replacePseudos
9+
, fixupProgram
10+
, ReplacePseudosError(..)
11+
, PseudoM
12+
) where
13+
14+
import Halcyon.Backend.Codegen (gen)
15+
import Halcyon.Backend.Emit (emitProgram)
16+
import Halcyon.Backend.ReplacePseudos
17+
( replacePseudos
18+
, fixupProgram
19+
, ReplacePseudosError(..)
20+
, PseudoM
21+
)

lib/Halcyon/Core.hs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Halcyon.Core
3+
( -- * IR Generation
4+
module Halcyon.Core.TackyGen
5+
-- * Compiler Infrastructure
6+
, module Halcyon.Core.Monad
7+
, module Halcyon.Core.Settings
8+
) where
9+
10+
import Halcyon.Core.Monad
11+
import Halcyon.Core.Settings
12+
import Halcyon.Core.TackyGen

lib/Halcyon/Driver.hs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Halcyon.Driver
3+
( -- * Command Line Interface
4+
module Halcyon.Driver.Cli
5+
-- * Main Pipeline
6+
, processFile
7+
, StageResult(..)
8+
-- * Pipeline Stages
9+
, module Halcyon.Driver.Stages
10+
-- * External Tools
11+
, module Halcyon.Driver.External
12+
-- * Stage Output
13+
, module Halcyon.Driver.Output
14+
) where
15+
16+
import Halcyon.Driver.Cli
17+
import Halcyon.Driver.External
18+
import Halcyon.Driver.Output
19+
import Halcyon.Driver.Pipeline (processFile)
20+
import Halcyon.Driver.Stages
21+
import Halcyon.Core.Settings (StageResult(..))

lib/Halcyon/Driver/External.hs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE RecordWildCards #-}
3+
module Halcyon.Driver.External
4+
( -- * GCC Interface
5+
preprocess
6+
, compileToExecutable
7+
) where
8+
9+
import Control.Monad (unless)
10+
import Control.Monad.IO.Class (liftIO)
11+
import Data.Text (Text)
12+
import Data.Text qualified as T
13+
import Data.Text.IO qualified as TIO
14+
import System.Exit (ExitCode(..))
15+
import System.FilePath (replaceExtension, takeDirectory, takeFileName, dropExtension, (</>))
16+
import System.Directory (makeAbsolute)
17+
import System.Process.Typed (proc, readProcess)
18+
19+
import Halcyon.Core.Monad (MonadCompiler, CompilerError(..), throwError)
20+
import Halcyon.Driver.Cli (AppOptions(..))
21+
22+
-- | Preprocess source file using GCC
23+
preprocess :: MonadCompiler m => AppOptions -> m FilePath
24+
preprocess AppOptions{..} = do
25+
absFile <- liftIO $ makeAbsolute file
26+
let fileDir = takeDirectory absFile
27+
fileName = takeFileName absFile
28+
outputFileName = replaceExtension fileName ".i"
29+
output = fileDir </> outputFileName
30+
processConfig = proc "gcc" ["-E", "-P", absFile, "-o", output]
31+
32+
(exitCode, _, stderr) <- liftIO $ readProcess processConfig
33+
if exitCode == ExitSuccess
34+
then return output
35+
else throwError $ SystemError $ "GCC preprocessing failed: " <> T.pack (show stderr)
36+
37+
-- | Compile assembly to executable using GCC
38+
compileToExecutable :: MonadCompiler m => AppOptions -> Text -> m ()
39+
compileToExecutable AppOptions{..} assembly = do
40+
inputFileAbs <- liftIO $ makeAbsolute file
41+
let inputDir = takeDirectory inputFileAbs
42+
baseName = dropExtension $ takeFileName inputFileAbs
43+
asmFilePath = inputDir </> (baseName ++ ".s")
44+
outputFilePath = inputDir </> baseName
45+
46+
liftIO $ TIO.writeFile asmFilePath assembly
47+
48+
let processConfig = proc "gcc" [asmFilePath, "-o", outputFilePath]
49+
(exitCode, _, stderr) <- liftIO $ readProcess processConfig
50+
51+
unless (exitCode == ExitSuccess) $
52+
throwError $ SystemError $ "GCC compilation failed: " <> T.pack (show stderr)

lib/Halcyon/Driver/Output.hs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Halcyon.Driver.Output
3+
( handleStageResult
4+
) where
5+
6+
import Control.Monad.IO.Class (liftIO)
7+
import qualified Data.Text.IO as TIO
8+
9+
import Halcyon.Core.Monad (MonadCompiler)
10+
import Halcyon.Core.Settings (StageResult(..))
11+
import Halcyon.Driver.Cli (AppOptions(..))
12+
import Halcyon.Driver.External (compileToExecutable)
13+
14+
-- | Handle the result of a compilation stage
15+
handleStageResult :: MonadCompiler m => AppOptions -> StageResult -> m ()
16+
handleStageResult opts@AppOptions{} = \case
17+
StageResultTokens tokens ->
18+
liftIO $ print tokens
19+
StageResultAST ast ->
20+
liftIO $ print ast
21+
StageResultTacky tacky ->
22+
liftIO $ print tacky
23+
StageResultAsm asm ->
24+
liftIO $ print asm
25+
StageResultAssembly assembly ->
26+
liftIO $ TIO.putStrLn assembly
27+
StageResultExecutable assembly ->
28+
compileToExecutable opts assembly

0 commit comments

Comments
 (0)