From 9f9642347bd62dcb4c562e96894be2d71bb227be Mon Sep 17 00:00:00 2001 From: sudoer777 Date: Sat, 23 Sep 2023 23:15:11 -0500 Subject: [PATCH] Start new LLVM generator using IRBuilder --- README.md | 17 +++--- main/Eval/{Expression.hs => Expr.hs} | 4 +- main/LLVMGen/Expr.hs | 57 +++++++++++++++++++++ main/LLVMGen/{Expression.hs => Expr.hs.old} | 13 +++-- main/Main.hs | 21 ++++---- main/Objects/Expression.hs | 19 ------- main/Parser/{Expression.hs => Expr.hs} | 9 +++- main/Types/Expr.hs | 9 ++++ package.yaml | 4 +- really-bad-compiler-in-haskell.cabal | 10 ++-- stack.yaml | 6 +-- stack.yaml.lock | 24 ++++----- 12 files changed, 126 insertions(+), 67 deletions(-) rename main/Eval/{Expression.hs => Expr.hs} (71%) create mode 100644 main/LLVMGen/Expr.hs rename main/LLVMGen/{Expression.hs => Expr.hs.old} (76%) delete mode 100644 main/Objects/Expression.hs rename main/Parser/{Expression.hs => Expr.hs} (72%) create mode 100644 main/Types/Expr.hs diff --git a/README.md b/README.md index ff890c3..eb406d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Really Bad Compiler in Haskell -A compiler written in Haskell which can currently perform basic arithmetic. Currently using the megaparsec and llvm-hs libraries, but I plan to reimplement parsing and llvm generation myself. Built for the Introduction to Compiler Design class at The University of Texas at Dallas. +A compiler written in Haskell which can currently perform basic arithmetic. Currently using the megaparsec and llvm-hs-\* libraries, but I plan to reimplement parsing and llvm generation myself. Built for the Introduction to Compiler Design class at The University of Texas at Dallas. Main repo: https://git.sudoer.ch/me/really-bad-compiler-in-haskell @@ -27,19 +27,22 @@ Main repo: https://git.sudoer.ch/me/really-bad-compiler-in-haskell ### Learning Resources Used - Introduction to Compiler Design class at The University of Texas at Dallas, taught by Charles Averill -- learnyouahaskell.com (for learning Haskell basics) +- https://learnyouahaskell.com (for learning Haskell basics) - https://akashagrawal.me/2017/01/19/beginners-guide-to-megaparsec.html -- https://markkarpov.com/tutorial/megaparsec.html (for help writing a Haskell equation parser) +- https://markkarpov.com/tutorial/megaparsec.html (for help writing a Haskell equation parser using megaparsec) - https://www.forth.com/starting-forth/1-forth-stacks-dictionary/ (for learning Forth) - https://blog.josephmorag.com/posts/mcc0/ (Haskell compiler tutorial with megaparsec, llvm-hs, and nix) -- https://gh.sudoer.ch/llvm-hs/llvm-hs-examples (for help writing an llvm code generator) +- https://gh.sudoer.ch/llvm-hs/llvm-hs-examples (for help writing an llvm code generator using llvm-hs) +- https://danieljharvey.github.io/posts/2023-02-08-llvm-compiler-part-1.html (for help using llvm-hs-pure) +- https://gh.sudoer.ch/danieljharvey/mimsa/blob/trunk/llvm-calc/src/Calc/Compile/ToLLVM.hs (source code for above resource) +- https://9to5tutorial.com/homebrew-compiler-made-with-haskell-llvm-configuration (for help using llvm-hs-pure) ### Tools - Language: Haskell -- Haskell tools: GHCup, Stack, Cabal -- Libraries: megaparsec, parser-combinators, text, llvm-hs-pure -- Dependencies: llvm, clang +- Haskell tools: GHCup, Stack, Cabal, GHC 9.2 +- Libraries: megaparsec, parser-combinators, text, llvm-hs-pure 15, llvm-hs-pretty-15 +- Dependencies: llvm 15, clang 15 - IDE: VSCodium - Git platform: Forgejo - AI: Phind (GPT-4), ollama (codellama) diff --git a/main/Eval/Expression.hs b/main/Eval/Expr.hs similarity index 71% rename from main/Eval/Expression.hs rename to main/Eval/Expr.hs index 325afdf..c8016fe 100644 --- a/main/Eval/Expression.hs +++ b/main/Eval/Expr.hs @@ -1,6 +1,6 @@ -module Eval.Expression (evalExpr) where +module Eval.Expr (evalExpr) where -import Objects.Expression +import Types.Expr eval :: Expr -> Int eval (Lit x) = x diff --git a/main/LLVMGen/Expr.hs b/main/LLVMGen/Expr.hs new file mode 100644 index 0000000..78d89b8 --- /dev/null +++ b/main/LLVMGen/Expr.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE ImportQualifiedPost #-} +{-# LANGUAGE OverloadedStrings #-} + +-- see https://gh.sudoer.ch/danieljharvey/mimsa/blob/trunk/llvm-calc/src/Calc/Compile/ToLLVM.hs + +module LLVMGen.Expr (getLLVMStr) where + +import Data.Text.Lazy +import LLVM.AST hiding (function) +import LLVM.AST.Type +import LLVM.IRBuilder.Constant +import LLVM.IRBuilder.Instruction +import LLVM.IRBuilder.Module +import LLVM.IRBuilder.Monad +import LLVM.Pretty +import Types.Expr as Expr + +-- charStar :: LLVM.Type +-- charStar = LLVM.ptr LLVM.i8 + +getLLVM :: Expr -> Module +getLLVM expr = + buildModule "program" $ do + -- TODO: better naming + printf <- externVarArgs "printf" [ptr] i32 -- or LLVM.ptr LLVM.i8 + let numFormatStr = globalStringPtr "%d\n" (mkName "str") + function "main" [] i32 $ \_ -> do + ourExpression <- exprToLLVM expr + nfs <- numFormatStr + _ <- call i32 printf [(ConstantOperand nfs, []), (ourExpression, [])] + ret (int32 0) + +exprToLLVM :: + ( MonadIRBuilder m, + MonadModuleBuilder m + ) => + Expr -> + m Operand +exprToLLVM (Lit prim) = pure $ primToLLVM prim +exprToLLVM (Expr.Add a b) = do + lhs <- exprToLLVM a + rhs <- exprToLLVM b + add lhs rhs +exprToLLVM (Expr.Sub a b) = do + lhs <- exprToLLVM a + rhs <- exprToLLVM b + sub lhs rhs +exprToLLVM (Expr.Mul a b) = do + lhs <- exprToLLVM a + rhs <- exprToLLVM b + mul lhs rhs + +primToLLVM :: Int -> Operand +primToLLVM i = int32 (fromIntegral i) + +getLLVMStr :: Expr -> Text +getLLVMStr expr = ppllvm (getLLVM expr) \ No newline at end of file diff --git a/main/LLVMGen/Expression.hs b/main/LLVMGen/Expr.hs.old similarity index 76% rename from main/LLVMGen/Expression.hs rename to main/LLVMGen/Expr.hs.old index f3a0d28..fac2110 100644 --- a/main/LLVMGen/Expression.hs +++ b/main/LLVMGen/Expr.hs.old @@ -1,6 +1,6 @@ -module LLVMGen.Expression (getLLVM) where +module LLVMGen.Expr (getLLVM) where -import qualified Objects.Expression as Expr +import qualified Objects.Expr as Expr import LLVM.AST import qualified LLVM.AST as AST @@ -28,7 +28,13 @@ defMain = GlobalDefinition functionDefaults where body = BasicBlock (Name "entry") - [] + [ Name "calltmp" := + Call + { tailCallKind = Nothing + , function = Right (ConstantOperand (GlobalReference (PointerType (FunctionType int [int, int] False) defaultAddrSpace) (Name "add"))) + , arguments = [ (ConstantOperand (Int 32 10), []), (ConstantOperand (Int 32 20), []) ] + } + ] (Do $ Ret (Just (ConstantOperand (Int 32 0))) []) defAdd :: Definition @@ -52,7 +58,6 @@ defAdd = GlobalDefinition functionDefaults []] (Do $ Ret (Just (LocalReference int (Name "result"))) []) - module_ :: AST.Module module_ = defaultModule { moduleName = "basic" diff --git a/main/Main.hs b/main/Main.hs index b75f999..642cc99 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -1,24 +1,25 @@ +{-# LANGUAGE ImportQualifiedPost #-} +{-# LANGUAGE OverloadedStrings #-} + module Main (main) where -import Control.Monad -import Data.Either -import Eval.Expression -import Objects.Expression -import Parser.Expression +import Data.Text.Lazy.IO qualified as T +import Eval.Expr +import LLVMGen.Expr +import Parser.Expr import System.Environment -import LLVMGen.Expression -import System.IO -import Text.Megaparsec +import Types.Expr getRight :: ParseResult -> Expr getRight (Right r) = r getResult :: String -> Int -getResult str = evalExpr (getRight (parseExpr str)) +getResult str = evalExpr (getRight (parseExpr str)) -- TODO: add error messages +main :: IO () main = do fileName <- fmap head getArgs contents <- readFile fileName let result = getResult contents print result - getLLVM + T.putStrLn (getLLVMStr (getRight (parseExpr contents))) diff --git a/main/Objects/Expression.hs b/main/Objects/Expression.hs deleted file mode 100644 index 932df82..0000000 --- a/main/Objects/Expression.hs +++ /dev/null @@ -1,19 +0,0 @@ -module Objects.Expression - ( Expr - ( Lit, - Add, - Sub, - Mul, - Div - ), - -- solve, - ) -where - -data Expr - = Lit Int - | Add Expr Expr - | Sub Expr Expr - | Mul Expr Expr - | Div Expr Expr - deriving (Show) \ No newline at end of file diff --git a/main/Parser/Expression.hs b/main/Parser/Expr.hs similarity index 72% rename from main/Parser/Expression.hs rename to main/Parser/Expr.hs index 0d323ff..a2f3f4c 100644 --- a/main/Parser/Expression.hs +++ b/main/Parser/Expr.hs @@ -1,11 +1,15 @@ -module Parser.Expression (parseExpr, ParseResult) where +-- see https://markkarpov.com/tutorial/megaparsec.html +{-# LANGUAGE ImportQualifiedPost #-} + +module Parser.Expr (parseExpr, ParseResult) where import Control.Monad.Combinators.Expr +import Data.Functor.Identity qualified import Data.Void (Void) -import Objects.Expression import Text.Megaparsec as MP import Text.Megaparsec.Char as C import Text.Megaparsec.Char.Lexer as L +import Types.Expr type Parser = Parsec Void String @@ -31,6 +35,7 @@ table = ] ] +binaryOp :: String -> (a -> a -> a) -> Operator (ParsecT Void String Data.Functor.Identity.Identity) a binaryOp name f = InfixL (f <$ symbolParser name) expr :: Parser Expr diff --git a/main/Types/Expr.hs b/main/Types/Expr.hs new file mode 100644 index 0000000..f6c3dee --- /dev/null +++ b/main/Types/Expr.hs @@ -0,0 +1,9 @@ +module Types.Expr (Expr (..)) where + +data Expr + = Lit Int + | Add Expr Expr + | Sub Expr Expr + | Mul Expr Expr + | Div Expr Expr + deriving (Show) \ No newline at end of file diff --git a/package.yaml b/package.yaml index edb5381..ff881a5 100644 --- a/package.yaml +++ b/package.yaml @@ -6,9 +6,9 @@ dependencies: - megaparsec >= 9.0.1 && < 10 - parser-combinators - text - - llvm-hs >= 15 && < 16 + # - llvm-hs >= 15 && < 16 - llvm-hs-pure >= 15 && < 16 - # - llvm-hs-pretty >= 15 && < 16 + - llvm-hs-pretty >= 15 && < 16 - bytestring tested-with: GHC == 9.2.8 category: Compilers/Interpreters diff --git a/really-bad-compiler-in-haskell.cabal b/really-bad-compiler-in-haskell.cabal index fc851e2..8d3b947 100644 --- a/really-bad-compiler-in-haskell.cabal +++ b/really-bad-compiler-in-haskell.cabal @@ -16,10 +16,10 @@ tested-with: executable really-bad-compiler-in-haskell main-is: Main.hs other-modules: - Eval.Expression - LLVMGen.Expression - Objects.Expression - Parser.Expression + Eval.Expr + LLVMGen.Expr + Parser.Expr + Types.Expr Paths_really_bad_compiler_in_haskell hs-source-dirs: main @@ -29,7 +29,7 @@ executable really-bad-compiler-in-haskell build-depends: base >=4.14.3 && <5 , bytestring - , llvm-hs ==15.* + , llvm-hs-pretty ==15.* , llvm-hs-pure ==15.* , megaparsec >=9.0.1 && <10 , parser-combinators diff --git a/stack.yaml b/stack.yaml index f5a1103..7070a4e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -3,13 +3,13 @@ resolver: lts-20.26 packages: - . extra-deps: - # - github: hyunsooda/llvm-hs-pretty-15 - # commit: 79283942d1667168ecd65237667aff7fed730303 + - github: hyunsooda/llvm-hs-pretty-15 + commit: 79283942d1667168ecd65237667aff7fed730303 - github: llvm-hs/llvm-hs commit: 5bca2c1a2a3aa98ecfb19181e7a5ebbf3e212b76 subdirs: - llvm-hs-pure - - llvm-hs + # - llvm-hs nix: enable: true packages: [llvm_15, clang_15, libxml2] diff --git a/stack.yaml.lock b/stack.yaml.lock index 0055e8d..f76a1f2 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -4,6 +4,17 @@ # https://docs.haskellstack.org/en/stable/lock_files packages: +- completed: + name: llvm-hs-pretty + pantry-tree: + sha256: 22ac4a47e1833dece52af7a6f8589958021d67292f6cc3de015ecb7c699db62d + size: 5197 + sha256: 8348b72a61807414ad15e0b7e82a7753e4473701e7ab98520c2e9d9a51c5ed7f + size: 43787 + url: https://github.com/hyunsooda/llvm-hs-pretty-15/archive/79283942d1667168ecd65237667aff7fed730303.tar.gz + version: 15.0.0 + original: + url: https://github.com/hyunsooda/llvm-hs-pretty-15/archive/79283942d1667168ecd65237667aff7fed730303.tar.gz - completed: name: llvm-hs-pure pantry-tree: @@ -17,19 +28,6 @@ packages: original: subdir: llvm-hs-pure url: https://github.com/llvm-hs/llvm-hs/archive/5bca2c1a2a3aa98ecfb19181e7a5ebbf3e212b76.tar.gz -- completed: - name: llvm-hs - pantry-tree: - sha256: 21f74a6f51fae6c0a0fcf3e6620a59f341289ac743998b41ed3276fd1b7d8862 - size: 12716 - sha256: 526b67e2da9ce25b3856c221b6772e699a7593dbb5ba38e7ee2436349de70966 - size: 9802209 - subdir: llvm-hs - url: https://github.com/llvm-hs/llvm-hs/archive/5bca2c1a2a3aa98ecfb19181e7a5ebbf3e212b76.tar.gz - version: 15.0.0 - original: - subdir: llvm-hs - url: https://github.com/llvm-hs/llvm-hs/archive/5bca2c1a2a3aa98ecfb19181e7a5ebbf3e212b76.tar.gz snapshots: - completed: sha256: 5a59b2a405b3aba3c00188453be172b85893cab8ebc352b1ef58b0eae5d248a2