Введите проблему с CodeGenFunction / CodeGenModule с помощью LLVM / Haskell

#haskell #types #llvm #monads

#haskell #типы #llvm #монады

Вопрос:

Я, в течение некоторого времени, экспериментирую с LLVM, просто потому, что. Однако это отнимает у меня больше времени, чем я думал.

Я пробовал с привязками C , но я оставил C много лет назад по очевидным причинам. Я пробовал привязки OCaml, но мне не понравился OCaml в основном из-за громоздкой системы сборки, среди прочих проблем.

Итак, я застрял с модулем LLVM для Haskell, который вполне типобезопасен и очень похож на Haskell. Но документации несколько не хватает, поскольку единственными примерами являются те, что в каком-то блоге.

Короче говоря, я начал делать простой компилятор для PL / 0. И я, всего после двух дней самоотдачи, разобрался с этим (с синтаксическим анализатором проблем не было, Parsec — мой приятель):

Модуль AST

 module AST where

data Statement
    = Assign String Expression
    | Call String
    | Write String
    | If Condition Statement
    | While Condition Statement
    | Begin [Statement]
    deriving (Show, Eq)

data Condition
    = Odd Expression
    | Eq Expression Expression
    | Ne Expression Expression
    | Gt Expression Expression
    | Lt Expression Expression
    | Ge Expression Expression
    | Le Expression Expression
    deriving (Show, Eq)

data Expression
    = Identifier String
    | Number Integer
    | Plus Expression Expression
    | Minus Expression Expression
    | Multiply Expression Expression
    | Divide Expression Expression
    deriving (Show, Eq)

data Block = Block {
        blockConsts :: [(String, Integer)],
        blockVars :: [String],
        blockProcs :: [Procedure],
        blockStatement :: Statement
    }
    deriving (Show, Eq)

data Procedure = Procedure String Block
    deriving (Show, Eq)
  

Модуль Codegen:

 module Codegen {-(writeModule)-} where

import LLVM.Core
import AST
import Data.Int (Int64)
import Data.Word (Word8, Word32)

codegenExpr :: [(String, Value (Ptr Int64))] -> Expression -> CodeGenFunction r (Value Int64)
codegenExpr ls (Identifier s) = case lookup s ls of
    Nothing -> error $ "unknown identifier: "    s
    (Just v) -> load v
codegenExpr _ (Number n) = return $ valueOf $ fromIntegral n
codegenExpr ls (Plus e1 e2)     = arith ls e1 e2 iadd
codegenExpr ls (Minus e1 e2)    = arith ls e1 e2 isub
codegenExpr ls (Multiply e1 e2) = arith ls e1 e2 imul
codegenExpr ls (Divide e1 e2)   = arith ls e1 e2 idiv

arith ls e1 e2 f = do
    lhs <- codegenExpr ls e1
    rhs <- codegenExpr ls e2
    f lhs rhs

codegenCond :: [(String, Value (Ptr Int64))] -> Condition -> CodeGenFunction r (Value Bool)
codegenCond ls (Eq e1 e2) = cnd ls e1 e2 CmpEQ
codegenCond ls (Ne e1 e2) = cnd ls e1 e2 CmpNE
codegenCond ls (Gt e1 e2) = cnd ls e1 e2 CmpGT
codegenCond ls (Lt e1 e2) = cnd ls e1 e2 CmpLT
codegenCond ls (Ge e1 e2) = cnd ls e1 e2 CmpGE
codegenCond ls (Le e1 e2) = cnd ls e1 e2 CmpLE

cnd ls e1 e2 f = do
    lhs <- codegenExpr ls e1
    rhs <- codegenExpr ls e2
    cmp f lhs rhs

codegenStatement :: [(String, Value (Ptr Int64))] -> Statement -> CodeGenFunction () ()
codegenStatement ls (Assign id e) = case lookup id ls of
    Nothing -> error $ "unknown identifier: "    id
    (Just v) -> do
        val <- codegenExpr ls e
        store val v
codegenStatement ls (Begin stmts) = mapM_ (codegenStatement ls) stmts
codegenStatement ls (If cond s1) = do
    ifbl <- newBasicBlock
    thenbl <- newBasicBlock

    cnd <- codegenCond ls cond
    condBr cnd ifbl thenbl

    defineBasicBlock ifbl
    codegenStatement ls s1
    ret ()

    defineBasicBlock thenbl
    ret ()
codegenStatement ls (While cond s) = do
    exit <- newBasicBlock
    while <- newBasicBlock

    defineBasicBlock while
    cnd <- codegenCond ls cond
    codegenStatement ls s
    condBr cnd while exit

    defineBasicBlock exit
    ret ()

codegenBlock :: [(String, Value (Ptr Int64))] -> Block -> CodeGenModule (Function ())
codegenBlock vls (Block _ vars _ stmt) = do
    -- And here is the type error
    func <- createFunction ExternalLinkage $ do
        ls <- mapM named vars
        codegenStatement (vls    ls) stmt
        mapM_ (free . snd) ls
    return func
    where
        named n = do
            v <- alloca
            return (n, v)

writeModule bl file = do
    m <- newModule
    defineModule m $ codegenBlock [] bl
    writeBitcodeToFile file m
  

Так что да, много кода, но завершенного. Ошибка типа, которую я получаю, заключается в следующем:

 GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
[1 of 2] Compiling AST              ( AST.hs, interpreted )
[2 of 2] Compiling Codegen          ( Codegen.hs, interpreted )

Codegen.hs:71:13:
    No instances for (IsFunction (),
                      FunctionArgs () (CodeGenFunction () ()) (CodeGenFunction r0 ()))
      arising from a use of `createFunction'
    Possible fix:
      add instance declarations for
      (IsFunction (),
       FunctionArgs () (CodeGenFunction () ()) (CodeGenFunction r0 ()))
    In the expression: createFunction ExternalLinkage
    In a stmt of a 'do' expression:
        func <- createFunction ExternalLinkage
              $ do { ls <- mapM named vars;
                     codegenStatement (vls    ls) stmt;
                     mapM_ (free . snd) ls }
    In the expression:
      do { func <- createFunction ExternalLinkage
                 $ do { ls <- mapM named vars;
                        codegenStatement (vls    ls) stmt;
                        .... };

       return func }
  

Как я уже сказал, я взял большинство примеров из упомянутого блока, и там это было написано именно так. Я почему-то понятия не имею, как это исправить.

Как всегда, я трачу больше времени на проверку типов, чем на создание нового кода.

Комментарии:

1. На какую строку указывает сообщение об ошибке?

2. @FUZxxl Я добавил комментарий к строке, это просто в codegenBlock функции, строка 79 в моем файле.

3. Несколько слов о модуле LLVM. По какой-то причине в Haddock отсутствует множество функций, поэтому вам действительно нужно посмотреть на исходный код, чтобы узнать обо всем (например, о векторах). Кроме того, пакет synthesizer-llvm ( haskell.org/haskellwiki/Synthesizer ), вероятно, является самым большим примером LLVM, который доступен на hackage, поэтому вы, возможно, захотите посмотреть и там.

4. @Lohn L: Спасибо за совет, я просмотрел исходный код, но я пропустил функции для получения функции из модуля. Означает ли это, что я должен переносить Function значения вокруг себя, если я хочу вызвать их в Haskell?

Ответ №1:

Я совсем не знаком с LLVM, поэтому я не знаю, имеет ли это какой-либо смысл, но изменение подписи типа codegenBlock из

 [(String, Value (Ptr Int64))] -> Block -> CodeGenModule (Function ())
  

Для

 [(String, Value (Ptr Int64))] -> Block -> CodeGenModule (Function (IO ()))
  

удовлетворяет проверку типов, поскольку экземпляра нет IsFunction () , но есть один для IsFunction (IO a) .

Комментарии:

1. Большое спасибо. Я забыл, что LLVM основан на C, поэтому любая функция является своего рода нечистой.

Ответ №2:

Как говорит Хаммар, ваш вызов isFunction имеет неправильный тип. IsFunction определяется для:

 class IsType a => IsFunction a where
  llvm-0.9.1.0:LLVM.Core.Type.funcType :: [TypeDesc] -> a -> TypeDesc
    -- Defined in llvm-0.9.1.0:LLVM.Core.Type

instance [incoherent] IsFirstClass a => IsFunction (VarArgs a)
  -- Defined in llvm-0.9.1.0:LLVM.Core.Type

instance [incoherent] IsFirstClass a => IsFunction (IO a)
  -- Defined in llvm-0.9.1.0:LLVM.Core.Type

instance [incoherent] (IsFirstClass a, IsFunction b) =>
                      IsFunction (a -> b)
  

то есть для переменных, для ввода-вывода и для типов функций. Не для () типов. Итак, я подозреваю, что вы имеете в виду использовать IO () в типе:

 codegenBlock :: [(String, Value (Ptr Int64))] -> Block -> CodeGenModule (Function (IO ()))    
  

?