Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Segfault in Haskell LLVM-General code generation

Tags:

haskell

llvm

I'm trying to follow along with the LLVM bindings tutorial here, and running into a segfault. The following code works in the sense that it prints a module header to output.ll, but it also segfaults somewhere.

module Main where

import Control.Monad.Error
import LLVM.General.Module
import LLVM.General.Context
import qualified LLVM.General.AST as AST

--Create and write out an empty LLVM module
main :: IO ()
main = writeModule (AST.defaultModule { AST.moduleName = "myModule" })

outputFile :: File
outputFile = File "output.ll"

writeModule :: AST.Module -> IO ()
writeModule mod = withContext $ (\context ->
                    liftError $ withModuleFromAST context mod (\m ->
                      liftError $ writeLLVMAssemblyToFile outputFile m))

--perform the action, or fail on an error
liftError :: ErrorT String IO a -> IO a
liftError = runErrorT >=> either fail return

I suspect this is related to the following hint from the linked tutorial:

It is very important to remember not to pass or attempt to use resources outside of the bracket as this will lead to undefined behavior and/or segfaults.

I think in this context the "bracket" is implemented by the withContext function, which makes it seem like everything should be handled.

If I change the definition of writeModule to

writeModule mod = do assembly <- (withContext $ (\context ->
                       liftError $ withModuleFromAST context mod moduleLLVMAssembly))
                    putStrLn assembly

that is, instead of writing to a file I just print out the string representation of the LLVM assembly, no segfault is thrown.

Does anyone have experience with these bindings? I'm also interested to know about the failure cases for the warning I quoted. That is, how would one "forget" not to use resources outside the bracket? All of the functions that seem to require a Context, well, require one. Isn't this kind of resource scoping issue exactly what Haskell is good at handling for you?

Version information:

  • llvm-general-3.4.3.0
  • LLVM version 3.4
  • Default target: x86_64-apple-darwin13.2.0
like image 324
Cardano Avatar asked Oct 20 '22 06:10

Cardano


1 Answers

It would help if you shared your LLVM and cabal environment, LLVM is notorious for being backwards incompatible with itself so there might be an issue with using the latest versions of the bindings.

Behind the scenes writeLLVMAssemblyToFile is using a C++ call to do the file IO operation and I speculate that it's holding a reference to the LLVM module as a result of finalizing the file resource.

Try rendering the module to a String using moduleString and then only lifting into the IO monad to call writeFile from Haskell instead of going through C++ to the write.

import LLVM.General.Context
import LLVM.General.Module as Mod
import qualified LLVM.General.AST as AST

import Control.Monad.Error

main :: IO ()
main = do
  writeModule (AST.defaultModule { AST.moduleName = "myModule" })
  return ()

writeModule :: AST.Module -> IO (Either String ())
writeModule ast =
  withContext $ \ctx ->
    runErrorT $ withModuleFromAST ctx ast $ \m -> do
      asm <- moduleString m
      liftIO $ writeFile "output.ll" asm

The bindings can still rather brittle in my experience, you should ask on the issue tracker if the problem persists.

EDIT: This is a workaround for an old version that has been subsequently fixed. See: https://github.com/bscarlet/llvm-general/issues/109

like image 72
Stephen Diehl Avatar answered Oct 23 '22 09:10

Stephen Diehl