1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2module IRTS.CodegenLLVM (codegenLLVM) where
4import IRTS.CodegenCommon
7import qualified Core.TT as TT
8import Core.TT (ArithTy(..), IntTy(..), NativeTy(..), nativeTyWidth)
13import LLVM.General.Context
14import LLVM.General.Diagnostic
15import LLVM.General.AST
16import LLVM.General.AST.AddrSpace
17import LLVM.General.Target
18import LLVM.General.AST.DataLayout
19import LLVM.General.PassManager
20import qualified LLVM.General.Module as M
21import qualified LLVM.General.AST.IntegerPredicate as IPred
22import qualified LLVM.General.AST.Linkage as L
23import qualified LLVM.General.AST.Visibility as V
24import qualified LLVM.General.AST.CallingConvention as CC
25import qualified LLVM.General.AST.Attribute as A
26import qualified LLVM.General.AST.Global as G
27import qualified LLVM.General.AST.Constant as C
28import qualified LLVM.General.AST.Float as F
29import qualified LLVM.General.Relocation as R
30import qualified LLVM.General.CodeModel as CM
31import qualified LLVM.General.CodeGenOpt as CGO
37import qualified Data.Set as S
38import qualified Data.Map as M
39import Control.Applicative
40import Control.Monad.RWS
41import Control.Monad.Writer
42import Control.Monad.State
43import Control.Monad.Error
45import qualified System.Info as SI (arch, os)
47import System.Directory (removeFile)
48import System.FilePath ((</>))
49import System.Process (rawSystem)
50import System.Exit (ExitCode(..))
53data Target = Target { triple :: String, dataLayout :: DataLayout }
55codegenLLVM :: [(TT.Name, SDecl)] ->
56 String -> -- target triple
57 String -> -- target CPU
58 Int -> -- Optimization degree
59 FilePath -> -- output file name
62codegenLLVM defs triple cpu optimize file outty = withContext $ \context -> do
64 (target, _) <- failInIO $ lookupTarget Nothing triple
65 withTargetOptions $ \options ->
66 withTargetMachine target triple cpu S.empty options R.Default CM.Default CGO.Default $ \tm ->
67 do layout <- getTargetMachineDataLayout tm
68 let ast = codegen (Target triple layout) (map snd defs)
69 result <- runErrorT . M.withModuleFromAST context ast $ \m ->
70 do let opts = defaultCuratedPassSetSpec
71 { optLevel = Just optimize
72 , simplifyLibCalls = Just True
73 , useInlinerWithThreshold = Just 225
75 when (optimize /= 0) $ withPassManager opts $ void . flip runPassManager m
76 outputModule tm file outty m
79 Left msg -> ierror msg
81failInIO :: ErrorT String IO a -> IO a
82failInIO = either fail return <=< runErrorT
84outputModule :: TargetMachine -> FilePath -> OutputType -> M.Module -> IO ()
85outputModule _ file Raw m = failInIO $ M.writeBitcodeToFile file m
86outputModule tm file Object m = failInIO $ M.writeObjectToFile tm file m
87outputModule tm file Executable m = withTmpFile $ \obj -> do
88 outputModule tm obj Object m
90 defs <- (</> "llvm" </> "libidris_rts.a") <$> getDataDir
91 exit <- rawSystem cc [obj, defs, "-lm", "-lgmp", "-lgc", "-o", file]
92 when (exit /= ExitSuccess) $ ierror "FAILURE: Linking"
94withTmpFile :: (FilePath -> IO a) -> IO a
96 (path, handle) <- tempfile
103ierror msg = error $ "INTERNAL ERROR: IRTS.CodegenLLVM: " ++ msg
108 { G.returnType = IntegerType 32
110 ([ Parameter (IntegerType 32) (Name "argc") []
111 , Parameter (PointerType (PointerType (IntegerType 8) (AddrSpace 0)) (AddrSpace 0)) (Name "argv") []
113 , G.name = Name "main"
115 [ BasicBlock (UnName 0)
116 [ Do $ simpleCall "GC_init" [] -- Initialize Boehm GC
117 , Do $ simpleCall "__gmp_set_memory_functions"
118 [ ConstantOperand . C.GlobalReference . Name $ "__idris_gmpMalloc"
119 , ConstantOperand . C.GlobalReference . Name $ "__idris_gmpRealloc"
120 , ConstantOperand . C.GlobalReference . Name $ "__idris_gmpFree"
122 , UnName 1 := idrCall "{runMain0}" [] ]
123 (Do $ Ret (Just (ConstantOperand (C.Int 32 0))) [])
126initDefs :: Target -> [Definition]
128 [ TypeDefinition (Name "valTy")
129 (Just $ StructureType False
131 , ArrayType 0 (PointerType valueType (AddrSpace 0))
133 , TypeDefinition (Name "mpz")
134 (Just $ StructureType False
137 , PointerType intPtr (AddrSpace 0)
139 , GlobalDefinition $ globalVariableDefaults
140 { G.name = Name "__idris_intFmtStr"
141 , G.linkage = L.Internal
142 , G.isConstant = True
143 , G.hasUnnamedAddr = True
144 , G.type' = ArrayType 5 (IntegerType 8)
145 , G.initializer = Just $ C.Array (IntegerType 8) (map (C.Int 8 . fromIntegral . fromEnum) "%lld" ++ [C.Int 8 0])
147 , rtsFun "intStr" ptrI8 [IntegerType 64]
148 [ BasicBlock (UnName 0)
149 [ UnName 1 := simpleCall "GC_malloc_atomic" [ConstantOperand (C.Int (tgtWordSize tgt) 21)]
150 , UnName 2 := simpleCall "snprintf"
151 [ LocalReference (UnName 1)
152 , ConstantOperand (C.Int (tgtWordSize tgt) 21)
153 , ConstantOperand $ C.GetElementPtr True (C.GlobalReference . Name $ "__idris_intFmtStr") [C.Int 32 0, C.Int 32 0]
154 , LocalReference (UnName 0)
157 (Do $ Ret (Just (LocalReference (UnName 1))) [])
159 , exfun "llvm.trap" VoidType [] False
160 -- , exfun "llvm.llvm.memcpy.p0i8.p0i8.i32" VoidType [ptrI8, ptrI8, IntegerType 32, IntegerType 32, IntegerType 1] False
161 -- , exfun "llvm.llvm.memcpy.p0i8.p0i8.i64" VoidType [ptrI8, ptrI8, IntegerType 64, IntegerType 32, IntegerType 1] False
162 , exfun "memcpy" ptrI8 [ptrI8, ptrI8, intPtr] False
163 , exfun "snprintf" (IntegerType 32) [ptrI8, intPtr, ptrI8] True
164 , exfun "strcmp" (IntegerType 32) [ptrI8, ptrI8] False
165 , exfun "strlen" intPtr [ptrI8] False
166 , exfun "GC_init" VoidType [] False
167 , exfun "GC_malloc" ptrI8 [intPtr] False
168 , exfun "GC_malloc_atomic" ptrI8 [intPtr] False
169 , exfun "__gmp_set_memory_functions" VoidType
170 [ PointerType (FunctionType ptrI8 [intPtr] False) (AddrSpace 0)
171 , PointerType (FunctionType ptrI8 [ptrI8, intPtr, intPtr] False) (AddrSpace 0)
172 , PointerType (FunctionType VoidType [ptrI8, intPtr] False) (AddrSpace 0)
174 , exfun "__gmpz_init" VoidType [pmpz] False
175 , exfun "__gmpz_init_set_str" (IntegerType 32) [pmpz, ptrI8, IntegerType 32] False
176 , exfun "__gmpz_get_str" ptrI8 [ptrI8, IntegerType 32, pmpz] False
177 , exfun "__gmpz_cmp" (IntegerType 32) [pmpz, pmpz] False
178 , exfun "mpz_get_ull" (IntegerType 64) [pmpz] False
179 , exfun "mpz_init_set_ull" VoidType [pmpz, IntegerType 64] False
180 , exfun "mpz_init_set_sll" VoidType [pmpz, IntegerType 64] False
181 , exfun "__idris_strCons" ptrI8 [IntegerType 8, ptrI8] False
182 , exfun "__idris_readStr" ptrI8 [ptrI8] False -- Actually pointer to FILE, but it's opaque anyway
183 , exfun "__idris_gmpMalloc" ptrI8 [intPtr] False
184 , exfun "__idris_gmpRealloc" ptrI8 [ptrI8, intPtr, intPtr] False
185 , exfun "__idris_gmpFree" VoidType [ptrI8, intPtr] False
186 , exfun "strtoll" (IntegerType 64) [ptrI8, PointerType ptrI8 (AddrSpace 0), IntegerType 32] False
187 , exVar (stdinName tgt) ptrI8
188 , exVar (stdoutName tgt) ptrI8
189 , exVar (stderrName tgt) ptrI8
190 , GlobalDefinition mainDef
191 ] ++ map mpzBinFun ["add", "sub", "mul", "fdiv_q", "fdiv_r", "and", "ior", "xor"]
193 intPtr = IntegerType (tgtWordSize tgt)
194 ptrI8 = PointerType (IntegerType 8) (AddrSpace 0)
195 pmpz = PointerType mpzTy (AddrSpace 0)
197 mpzBinFun n = exfun ("__gmpz_" ++ n) VoidType [pmpz, pmpz, pmpz] False
199 rtsFun :: String -> Type -> [Type] -> [BasicBlock] -> Definition
200 rtsFun name rty argtys def =
201 GlobalDefinition $ functionDefaults
202 { G.linkage = L.Internal
204 , G.parameters = (flip map argtys $ \ty -> Parameter ty (UnName 0) [], False)
205 , G.name = Name $ "__idris_" ++ name
206 , G.basicBlocks = def
209 exfun :: String -> Type -> [Type] -> Bool -> Definition
210 exfun name rty argtys vari =
211 GlobalDefinition $ functionDefaults
214 , G.parameters = (flip map argtys $ \ty -> Parameter ty (UnName 0) [], vari)
216 exVar :: String -> Type -> Definition
217 exVar name ty = GlobalDefinition $ globalVariableDefaults { G.name = Name name, G.type' = ty }
219isApple :: Target -> Bool
220isApple (Target { triple = t }) = isJust . stripPrefix "-apple" $ dropWhile (/= '-') t
222stdinName, stdoutName, stderrName :: Target -> String
223stdinName t | isApple t = "__stdinp"
225stdoutName t | isApple t = "__stdoutp"
226stdoutName _ = "stdout"
227stderrName t | isApple t = "__stderrp"
228stderrName _ = "stderr"
230getStdIn, getStdOut, getStdErr :: Codegen Operand
231getStdIn = ConstantOperand . C.GlobalReference . Name . stdinName <$> asks target
232getStdOut = ConstantOperand . C.GlobalReference . Name . stdoutName <$> asks target
233getStdErr = ConstantOperand . C.GlobalReference . Name . stderrName <$> asks target
235codegen :: Target -> [SDecl] -> Module
236codegen tgt defs = Module "idris" (Just . dataLayout $ tgt) (Just . triple $ tgt) (initDefs tgt ++ globals ++ gendefs)
238 (gendefs, _, globals) = runRWS (mapM cgDef defs) tgt 0
241valueType = NamedTypeReference (Name "valTy")
243nullValue :: C.Constant
244nullValue = C.Null (PointerType valueType (AddrSpace 0))
246primTy :: Type -> Type
247primTy inner = StructureType False [IntegerType 32, inner]
250mpzTy = NamedTypeReference (Name "mpz")
252conType :: Word64 -> Type
253conType nargs = StructureType False
255 , ArrayType nargs (PointerType valueType (AddrSpace 0))
258type Modgen = RWS Target [Definition] Word
260cgDef :: SDecl -> Modgen Definition
261cgDef (SFun name argNames _ expr) = do
264 let (_, CGS { nextGlobalName = nextGlobal' }, (allocas, bbs, globals)) =
265 runRWS (do r <- cgExpr expr
267 Nothing -> terminate $ Unreachable []
268 Just r' -> terminate $ Ret (Just r') [])
269 (CGR tgt (show name))
270 (CGS 0 nextGlobal (Name "begin") [] (map (Just . LocalReference . Name . show) argNames) S.empty)
271 entryTerm = case bbs of
272 [] -> Do $ Ret Nothing []
273 BasicBlock n _ _:_ -> Do $ Br n []
276 return . GlobalDefinition $ functionDefaults
277 { G.linkage = L.Internal
278 , G.callingConvention = CC.Fast
279 , G.name = Name (show name)
280 , G.returnType = PointerType valueType (AddrSpace 0)
281 , G.parameters = (flip map argNames $ \argName ->
282 Parameter (PointerType valueType (AddrSpace 0)) (Name (show argName)) []
285 BasicBlock (Name "entry")
286 (map (\(n, t) -> n := Alloca t Nothing 0 []) allocas)
291type CGW = ([(Name, Type)], [BasicBlock], [Definition])
293type Env = [Maybe Operand]
295data CGS = CGS { nextName :: Word
296 , nextGlobalName :: Word
297 , currentBlockName :: Name
298 , instAccum :: [Named Instruction]
300 , foreignSyms :: Set String
303data CGR = CGR { target :: Target
304 , funcName :: String }
306type Codegen = RWS CGR CGW CGS
308getFuncName :: Codegen String
309getFuncName = asks funcName
311getGlobalUnName :: Codegen Name
313 i <- gets nextGlobalName
314 modify $ \s -> s { nextGlobalName = 1 + i }
317getUnName :: Codegen Name
320 modify $ \s -> s { nextName = 1 + i }
323getName :: String -> Codegen Name
326 modify $ \s -> s { nextName = 1 + i }
327 return (Name $ n ++ show i)
329alloca :: Name -> Type -> Codegen ()
330alloca n t = tell ([(n, t)], [], [])
332terminate :: Terminator -> Codegen ()
334 name <- gets currentBlockName
335 insts <- gets instAccum
336 modify $ \s -> s { instAccum = ierror "Not in a block"
337 , currentBlockName = ierror "Not in a block" }
338 tell ([], [BasicBlock name insts (Do term)], [])
340newBlock :: Name -> Codegen ()
341newBlock name = modify $ \s -> s { instAccum = []
342 , currentBlockName = name
345inst :: Instruction -> Codegen Operand
348 modify $ \s -> s { instAccum = instAccum s ++ [n := i] }
349 return $ LocalReference n
351ninst :: String -> Instruction -> Codegen Operand
354 modify $ \s -> s { instAccum = instAccum s ++ [n := i] }
355 return $ LocalReference n
357inst' :: Instruction -> Codegen ()
358inst' i = modify $ \s -> s { instAccum = instAccum s ++ [Do i] }
360insts :: [Named Instruction] -> Codegen ()
361insts is = modify $ \s -> s { instAccum = instAccum s ++ is }
363var :: LVar -> Codegen (Maybe Operand)
364var (Loc level) = (!! level) <$> gets lexenv
365var (Glob n) = return . Just . ConstantOperand . C.GlobalReference . Name $ show n
367binds :: Env -> Codegen (Maybe Operand) -> Codegen (Maybe Operand)
369 envLen <- length <$> gets lexenv
370 modify $ \s -> s { lexenv = lexenv s ++ vals }
372 modify $ \s -> s { lexenv = take envLen $ lexenv s }
375replaceElt :: Int -> a -> [a] -> [a]
376replaceElt _ val [] = error "replaceElt: Ran out of list"
377replaceElt 0 val (x:xs) = val:xs
378replaceElt n val (x:xs) = x : replaceElt (n-1) val xs
380alloc' :: Operand -> Codegen Operand
381alloc' size = inst $ simpleCall "GC_malloc" [size]
383allocAtomic :: Operand -> Codegen Operand
384allocAtomic size = inst $ simpleCall "GC_malloc_atomic" [size]
386alloc :: Type -> Codegen Operand
390 inst $ BitCast mem (PointerType ty (AddrSpace 0)) []
392sizeOf :: Type -> Codegen Operand
393sizeOf ty = ConstantOperand . C.PtrToInt
394 (C.GetElementPtr True (C.Null (PointerType ty (AddrSpace 0))) [C.Int 32 1])
395 . IntegerType <$> getWordSize
397tgtWordSize :: Target -> Word32
398tgtWordSize (Target { dataLayout = DataLayout { pointerLayouts = l } }) =
399 fst . fromJust $ M.lookup (AddrSpace 0) l
401getWordSize :: Codegen Word32
402getWordSize = tgtWordSize <$> asks target
404cgExpr :: SExp -> Codegen (Maybe Operand)
406cgExpr (SApp tailcall fname args) = do
407 argSlots <- mapM var args
408 case sequence argSlots of
409 Nothing -> return Nothing
411 fn <- var (Glob fname)
412 Just <$> inst ((idrCall (show fname) argVals) { isTailCall = tailcall })
413cgExpr (SLet _ varExpr bodyExpr) = do
414 val <- cgExpr varExpr
415 binds [val] $ cgExpr bodyExpr
416cgExpr (SUpdate (Loc level) expr) = do
418 modify $ \s -> s { lexenv = replaceElt level val (lexenv s) }
420cgExpr (SCon tag name args) = do
421 argSlots <- mapM var args
422 case sequence argSlots of
423 Nothing -> return Nothing
425 con <- alloc . conType . fromIntegral . length $ argVals
426 tagPtr <- inst $ GetElementPtr True con [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 0)] []
427 inst' $ Store False tagPtr (ConstantOperand (C.Int 32 (fromIntegral tag))) Nothing 0 []
428 forM_ (zip argVals [0..]) $ \(arg, i) -> do
429 ptr <- inst $ GetElementPtr True con [ ConstantOperand (C.Int 32 0)
430 , ConstantOperand (C.Int 32 1)
431 , ConstantOperand (C.Int 32 i)] []
432 inst' $ Store False ptr arg Nothing 0 []
433 Just <$> inst (BitCast con (PointerType valueType (AddrSpace 0)) [])
434cgExpr (SCase inspect alts) = do
437 Nothing -> return Nothing
438 Just v -> cgCase v alts
439cgExpr (SChkCase inspect alts) = do
442 Nothing -> return Nothing
444 do endBBN <- getName "endChkCase"
445 notNullBBN <- getName "notNull"
446 originBlock <- gets currentBlockName
447 isNull <- inst $ ICmp IPred.EQ val (ConstantOperand nullValue) []
448 terminate $ CondBr isNull endBBN notNullBBN []
450 ptr <- inst $ BitCast val (PointerType (IntegerType 32) (AddrSpace 0)) []
451 flag <- inst $ Load False ptr Nothing 0 []
452 isVal <- inst $ ICmp IPred.EQ flag (ConstantOperand (C.Int 32 (-1))) []
453 conBBN <- getName "constructor"
454 terminate $ CondBr isVal endBBN conBBN []
456 result <- cgCase val alts
457 caseExitBlock <- gets currentBlockName
460 terminate $ Unreachable []
464 terminate $ Br endBBN []
466 Just <$> inst (Phi (PointerType valueType (AddrSpace 0))
467 [(val, originBlock), (val, notNullBBN), (caseVal, caseExitBlock)] [])
468cgExpr (SProj conVar idx) = do
471 Nothing -> return Nothing
473 do ptr <- inst $ GetElementPtr True v
474 [ ConstantOperand (C.Int 32 0)
475 , ConstantOperand (C.Int 32 1)
476 , ConstantOperand (C.Int 32 (fromIntegral idx))
478 Just <$> inst (Load False ptr Nothing 0 [])
479cgExpr (SConst c) = Just <$> cgConst c
480cgExpr (SForeign LANG_C rty fname args) = do
481 func <- ensureCDecl fname rty (map fst args)
482 argVals <- forM args $ \(fty, v) -> do
485 Just val -> return $ Just (fty, val)
486 Nothing -> return Nothing
487 case sequence argVals of
488 Nothing -> return Nothing
490 argUVals <- mapM (uncurry unbox) argVals'
491 result <- inst Call { isTailCall = False
492 , callingConvention = CC.C
493 , returnAttributes = []
494 , function = Right func
495 , arguments = map (\v -> (v, [])) argUVals
496 , functionAttributes = []
499 Just <$> box rty result
500cgExpr (SOp fn args) = do
501 argVals <- mapM var args
502 case sequence argVals of
503 Just ops -> Just <$> cgOp fn ops
504 Nothing -> return Nothing
505cgExpr SNothing = return . Just . ConstantOperand $ nullValue
506cgExpr (SError msg) = do -- TODO: Print message
507 str <- addGlobal' (ArrayType (2 + fromIntegral (length msg)) (IntegerType 8))
508 (cgConst' (TT.Str (msg ++ "\n")))
509 inst' $ simpleCall "putStr" [ConstantOperand $ C.GetElementPtr True str [ C.Int 32 0
511 inst' Call { isTailCall = True
512 , callingConvention = CC.C
513 , returnAttributes = []
514 , function = Right . ConstantOperand . C.GlobalReference . Name $ "llvm.trap"
516 , functionAttributes = [A.NoReturn]
521cgCase :: Operand -> [SAlt] -> Codegen (Maybe Operand)
523 case find isConstCase alts of
524 Just (SConstCase (TT.BI _) _) -> cgChainCase val defExp constAlts
525 Just (SConstCase (TT.Str _) _) -> cgChainCase val defExp constAlts
526 Just (SConstCase _ _) -> cgPrimCase val defExp constAlts
527 Nothing -> cgConCase val defExp conAlts
529 defExp = getDefExp =<< find isDefCase alts
530 constAlts = filter isConstCase alts
531 conAlts = filter isConCase alts
533 isConstCase (SConstCase {}) = True
534 isConstCase _ = False
536 isConCase (SConCase {}) = True
539 isDefCase (SDefaultCase _) = True
542 getDefExp (SDefaultCase e) = Just e
543 getDefExp _ = Nothing
545cgPrimCase :: Operand -> Maybe SExp -> [SAlt] -> Codegen (Maybe Operand)
546cgPrimCase caseValPtr defExp alts = do
547 let caseTy = case head alts of
548 SConstCase (TT.I _) _ -> IntegerType 32
549 SConstCase (TT.B8 _) _ -> IntegerType 8
550 SConstCase (TT.B16 _) _ -> IntegerType 16
551 SConstCase (TT.B32 _) _ -> IntegerType 32
552 SConstCase (TT.B64 _) _ -> IntegerType 64
553 SConstCase (TT.Fl _) _ -> FloatingPointType 64 IEEE
554 SConstCase (TT.Ch _) _ -> IntegerType 32
555 realPtr <- inst $ BitCast caseValPtr (PointerType (primTy caseTy) (AddrSpace 0)) []
556 valPtr <- inst $ GetElementPtr True realPtr [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 1)] []
557 caseVal <- inst $ Load False valPtr Nothing 0 []
558 defBlockName <- getName "default"
559 exitBlockName <- getName "caseExit"
560 namedAlts <- mapM (\a -> do n <- nameAlt defBlockName a; return (n, a)) alts
561 terminate $ Switch caseVal defBlockName (map (\(n, SConstCase c _) -> (cgConst' c, n)) namedAlts) []
562 initEnv <- gets lexenv
563 defResult <- cgDefaultAlt exitBlockName defBlockName defExp
564 results <- forM namedAlts $ \(name, SConstCase _ exp) -> cgAlt initEnv exitBlockName name Nothing exp
565 finishCase initEnv exitBlockName (defResult:results)
567cgConCase :: Operand -> Maybe SExp -> [SAlt] -> Codegen (Maybe Operand)
568cgConCase con defExp alts = do
569 tagPtr <- inst $ GetElementPtr True con [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 0)] []
570 tag <- inst $ Load False tagPtr Nothing 0 []
571 defBlockName <- getName "default"
572 exitBlockName <- getName "caseExit"
573 namedAlts <- mapM (\a -> do n <- nameAlt defBlockName a; return (n, a)) alts
574 terminate $ Switch tag defBlockName (map (\(n, SConCase _ tag _ _ _) ->
575 (C.Int 32 (fromIntegral tag), n)) namedAlts) []
576 initEnv <- gets lexenv
577 defResult <- cgDefaultAlt exitBlockName defBlockName defExp
578 results <- forM namedAlts $ \(name, SConCase _ _ _ argns exp) ->
579 cgAlt initEnv exitBlockName name (Just (con, map show argns)) exp
580 finishCase initEnv exitBlockName (defResult:results)
582cgChainCase :: Operand -> Maybe SExp -> [SAlt] -> Codegen (Maybe Operand)
583cgChainCase caseValPtr defExp alts = do
584 let (caseTy, comparator) =
586 SConstCase (TT.BI _) _ -> (FArith (ATInt ITBig), "__gmpz_cmp")
587 SConstCase (TT.Str _) _ -> (FString, "strcmp")
588 caseVal <- unbox caseTy caseValPtr
589 defBlockName <- getName "default"
590 exitBlockName <- getName "caseExit"
591 namedAlts <- mapM (\a -> do n <- nameAlt defBlockName a; return (n, a)) alts
592 initEnv <- gets lexenv
593 results <- forM namedAlts $ \(name, SConstCase c e) ->
594 do const <- unbox caseTy =<< cgConst c
595 cmp <- inst $ simpleCall comparator [const, caseVal]
596 cmpResult <- inst $ ICmp IPred.EQ cmp (ConstantOperand (C.Int 32 0)) []
597 elseName <- getName "else"
598 terminate $ CondBr cmpResult name elseName []
599 result <- cgAlt initEnv exitBlockName name Nothing e
602 modify $ \s -> s { lexenv = initEnv }
604 defaultVal <- cgExpr (fromMaybe (SError $ "Inexhaustive case failure in " ++ fname) defExp)
605 defaultBlock <- gets currentBlockName
606 defaultEnv <- gets lexenv
607 defResult <- case defaultVal of
609 terminate $ Br exitBlockName []
610 return $ Just (v, defaultBlock, defaultEnv)
612 terminate $ Unreachable []
614 finishCase initEnv exitBlockName (defResult:results)
616finishCase :: Env -> Name -> [Maybe (Operand, Name, Env)] -> Codegen (Maybe Operand)
617finishCase initEnv exitBlockName results = do
618 let definedResults = mapMaybe id results
619 case definedResults of
620 [] -> do modify $ \s -> s { lexenv = initEnv }
623 newBlock exitBlockName
624 mergeEnvs $ map (\(_, altBlock, altEnv) -> (altBlock, altEnv)) xs
625 Just <$> inst (Phi (PointerType valueType (AddrSpace 0))
626 (map (\(altVal, altBlock, _) -> (altVal, altBlock)) xs) [])
629cgDefaultAlt :: Name -> Name -> Maybe SExp -> Codegen (Maybe (Operand, Name, Env))
630cgDefaultAlt exitName name exp = do
633 val <- cgExpr (fromMaybe (SError $ "Inexhaustive case failure in " ++ fname) exp)
635 block <- gets currentBlockName
638 do terminate $ Br exitName []
639 return $ Just (v, block, env)
641 do terminate $ Unreachable []
644cgAlt :: Env -> Name -> Name -> Maybe (Operand, [String]) -> SExp
645 -> Codegen (Maybe (Operand, Name, Env))
646cgAlt initEnv exitBlockName name destr exp = do
647 modify $ \s -> s { lexenv = initEnv }
649 locals <- case destr of
652 forM (zip argns [0..]) $ \(argName, i) ->
653 do ptr <- inst $ GetElementPtr True con [ ConstantOperand (C.Int 32 0)
654 , ConstantOperand (C.Int 32 1)
655 , ConstantOperand (C.Int 32 i)] []
656 Just <$> ninst argName (Load False ptr Nothing 0 [])
657 altVal <- binds locals $ cgExpr exp
658 altEnv <- gets lexenv
659 altBlock <- gets currentBlockName
662 terminate $ Br exitBlockName []
663 return $ Just (v, altBlock, altEnv)
665 terminate $ Unreachable []
668mergeEnvs :: [(Name, Env)] -> Codegen ()
671 . map (\(block, env) -> map (\x -> (x, block)) env)
673 env <- forM vars $ \var ->
675 [] -> ierror "mergeEnvs: impossible"
678 | all (== v) (map fst vs) -> return v
680 let valid = map (\(a, b) -> (fromJust a, b)) . filter (isJust . fst) $ vs in
681 Just <$> inst (Phi (PointerType valueType (AddrSpace 0)) valid [])
682 modify $ \s -> s { lexenv = env }
684nameAlt :: Name -> SAlt -> Codegen Name
685nameAlt d (SDefaultCase _) = return d
686nameAlt _ (SConCase _ _ name _ _) = getName (show name)
687nameAlt _ (SConstCase const _) = getName (show const)
689box :: FType -> Operand -> Codegen Operand
690box FUnit _ = return $ ConstantOperand nullValue
692 val <- alloc (primTy (ftyToTy fty))
693 tagptr <- inst $ GetElementPtr True val [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 0)] []
694 valptr <- inst $ GetElementPtr True val [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 1)] []
695 inst' $ Store False tagptr (ConstantOperand (C.Int 32 (-1))) Nothing 0 []
696 inst' $ Store False valptr fval Nothing 0 []
697 inst $ BitCast val (PointerType valueType (AddrSpace 0)) []
699unbox :: FType -> Operand -> Codegen Operand
700unbox FUnit x = return x
702 val <- inst $ BitCast bval (PointerType (primTy (ftyToTy fty)) (AddrSpace 0)) []
703 fvalptr <- inst $ GetElementPtr True val [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 1)] []
704 inst $ Load False fvalptr Nothing 0 []
706cgConst' :: TT.Const -> C.Constant
707cgConst' (TT.I i) = C.Int 32 (fromIntegral i)
708cgConst' (TT.B8 i) = C.Int 8 (fromIntegral i)
709cgConst' (TT.B16 i) = C.Int 16 (fromIntegral i)
710cgConst' (TT.B32 i) = C.Int 32 (fromIntegral i)
711cgConst' (TT.B64 i) = C.Int 64 (fromIntegral i)
712cgConst' (TT.BI i) = C.Array (IntegerType 8) (map (C.Int 8 . fromIntegral . fromEnum) (show i) ++ [C.Int 8 0])
713cgConst' (TT.Fl f) = C.Float (F.Double f)
714cgConst' (TT.Ch c) = C.Int 32 . fromIntegral . fromEnum $ c
715cgConst' (TT.Str s) = C.Array (IntegerType 8) (map (C.Int 8 . fromIntegral . fromEnum) s ++ [C.Int 8 0])
716cgConst' x = ierror $ "Unsupported constant: " ++ show x
718cgConst :: TT.Const -> Codegen Operand
719cgConst c@(TT.I _) = box (FArith (ATInt ITNative)) (ConstantOperand $ cgConst' c)
720cgConst c@(TT.B8 _) = box (FArith (ATInt (ITFixed IT8))) (ConstantOperand $ cgConst' c)
721cgConst c@(TT.B16 _) = box (FArith (ATInt (ITFixed IT16))) (ConstantOperand $ cgConst' c)
722cgConst c@(TT.B32 _) = box (FArith (ATInt (ITFixed IT32))) (ConstantOperand $ cgConst' c)
723cgConst c@(TT.B64 _) = box (FArith (ATInt (ITFixed IT64))) (ConstantOperand $ cgConst' c)
724cgConst c@(TT.Fl _) = box (FArith ATFloat) (ConstantOperand $ cgConst' c)
725cgConst c@(TT.Ch _) = box FChar (ConstantOperand $ cgConst' c)
726cgConst c@(TT.Str s) = do
727 str <- addGlobal' (ArrayType (1 + fromIntegral (length s)) (IntegerType 8)) (cgConst' c)
728 box FString (ConstantOperand $ C.GetElementPtr True str [C.Int 32 0, C.Int 32 0])
729cgConst c@(TT.BI i) = do
730 str <- addGlobal' (ArrayType (1 + fromInteger (numDigits 10 i)) (IntegerType 8)) (cgConst' c)
732 inst $ simpleCall "__gmpz_init_set_str" [mpz
733 , ConstantOperand $ C.GetElementPtr True str [ C.Int 32 0, C.Int 32 0]
734 , ConstantOperand $ C.Int 32 10
736 box (FArith (ATInt ITBig)) mpz
737cgConst x = return $ ConstantOperand nullValue
739numDigits :: Integer -> Integer -> Integer
740numDigits b n = 1 + fst (ilog b n) where
743 | otherwise = let (e, r) = ilog (b*b) n
744 in if r < b then (2*e, r) else (2*e+1, r `div` b)
746addGlobal :: Global -> Codegen ()
747addGlobal def = tell ([], [], [GlobalDefinition def])
749addGlobal' :: Type -> C.Constant -> Codegen C.Constant
750addGlobal' ty val = do
751 name <- getGlobalUnName
752 addGlobal $ globalVariableDefaults
754 , G.linkage = L.Internal
755 , G.hasUnnamedAddr = True
756 , G.isConstant = True
758 , G.initializer = Just val
760 return . C.GlobalReference $ name
763ensureCDecl :: String -> FType -> [FType] -> Codegen Operand
764ensureCDecl name rty argtys = do
765 syms <- gets foreignSyms
766 unless (S.member name syms) $
767 do addGlobal (ffunDecl name rty argtys)
768 modify $ \s -> s { foreignSyms = S.insert name (foreignSyms s) }
769 return $ ConstantOperand (C.GlobalReference (Name name))
771ffunDecl :: String -> FType -> [FType] -> Global
772ffunDecl name rty argtys =
774 { G.returnType = ftyToTy rty
776 , G.parameters = (flip map argtys $ \fty ->
777 Parameter (ftyToTy fty) (UnName 0) []
781ftyToTy :: FType -> Type
782ftyToTy (FArith (ATInt ITNative)) = IntegerType 32
783ftyToTy (FArith (ATInt ITBig)) = PointerType mpzTy (AddrSpace 0)
784ftyToTy (FArith (ATInt (ITFixed ty))) = IntegerType (fromIntegral $ nativeTyWidth ty)
785ftyToTy FChar = IntegerType 32
786ftyToTy FString = PointerType (IntegerType 8) (AddrSpace 0)
787ftyToTy FUnit = VoidType
788ftyToTy FPtr = PointerType (IntegerType 8) (AddrSpace 0)
789ftyToTy (FArith ATFloat) = FloatingPointType 64 IEEE
790ftyToTy FAny = valueType
792-- Only use when known not to be ITBig
793itWidth :: IntTy -> Word32
795itWidth (ITFixed x) = fromIntegral $ nativeTyWidth x
797cgOp :: PrimFn -> [Operand] -> Codegen Operand
798cgOp (LTrunc ITBig ity) [x] = do
799 nx <- unbox (FArith (ATInt ITBig)) x
800 val <- inst $ simpleCall "mpz_get_ull" [nx]
802 (ITFixed IT64) -> return val
803 _ -> inst $ Trunc val (ftyToTy (FArith (ATInt ity))) []
804 box (FArith (ATInt ity)) v
805cgOp (LZExt from ITBig) [x] = do
806 nx <- unbox (FArith (ATInt from)) x
808 (ITFixed IT64) -> return nx
809 _ -> inst $ ZExt nx (IntegerType 64) []
811 inst' $ simpleCall "mpz_init_set_ull" [mpz, nx']
812 box (FArith (ATInt ITBig)) mpz
813cgOp (LSExt from ITBig) [x] = do
814 nx <- unbox (FArith (ATInt from)) x
816 (ITFixed IT64) -> return nx
817 _ -> inst $ SExt nx (IntegerType 64) []
819 inst' $ simpleCall "mpz_init_set_sll" [mpz, nx']
820 box (FArith (ATInt ITBig)) mpz
822cgOp (LLt (ATInt ITBig)) [x,y] = mpzCmp IPred.SLT x y
823cgOp (LLe (ATInt ITBig)) [x,y] = mpzCmp IPred.SLE x y
824cgOp (LEq (ATInt ITBig)) [x,y] = mpzCmp IPred.EQ x y
825cgOp (LGe (ATInt ITBig)) [x,y] = mpzCmp IPred.SGE x y
826cgOp (LGt (ATInt ITBig)) [x,y] = mpzCmp IPred.SGT x y
827cgOp (LPlus (ATInt ITBig)) [x,y] = mpzBin "add" x y
828cgOp (LMinus (ATInt ITBig)) [x,y] = mpzBin "sub" x y
829cgOp (LTimes (ATInt ITBig)) [x,y] = mpzBin "mul" x y
830cgOp (LSDiv (ATInt ITBig)) [x,y] = mpzBin "fdiv_q" x y
831cgOp (LSRem (ATInt ITBig)) [x,y] = mpzBin "fdiv_r" x y
832cgOp (LAnd ITBig) [x,y] = mpzBin "and" x y
833cgOp (LOr ITBig) [x,y] = mpzBin "ior" x y
834cgOp (LXOr ITBig) [x,y] = mpzBin "xor" x y
835cgOp (LCompl ITBig) [x] = mpzUn "com" x
837cgOp (LTrunc (ITFixed from) (ITFixed to)) [x]
838 | nativeTyWidth from > nativeTyWidth to = iCoerce Trunc from to x
839cgOp (LZExt (ITFixed from) (ITFixed to)) [x]
840 | nativeTyWidth from < nativeTyWidth to = iCoerce ZExt from to x
841cgOp (LSExt (ITFixed from) (ITFixed to)) [x]
842 | nativeTyWidth from < nativeTyWidth to = iCoerce SExt from to x
844cgOp (LLt (ATInt ity)) [x,y] = iCmp ity IPred.SLT x y
845cgOp (LLe (ATInt ity)) [x,y] = iCmp ity IPred.SLE x y
846cgOp (LEq (ATInt ity)) [x,y] = iCmp ity IPred.EQ x y
847cgOp (LGe (ATInt ity)) [x,y] = iCmp ity IPred.SGE x y
848cgOp (LGt (ATInt ity)) [x,y] = iCmp ity IPred.SGT x y
849cgOp (LPlus (ATInt ity)) [x,y] = ibin ity x y (Add False False)
850cgOp (LMinus (ATInt ity)) [x,y] = ibin ity x y (Sub False False)
851cgOp (LTimes (ATInt ity)) [x,y] = ibin ity x y (Mul False False)
852cgOp (LSDiv (ATInt ity)) [x,y] = ibin ity x y (SDiv False)
853cgOp (LSRem (ATInt ity)) [x,y] = ibin ity x y SRem
854cgOp (LUDiv ity) [x,y] = ibin ity x y (UDiv False)
855cgOp (LURem ity) [x,y] = ibin ity x y URem
856cgOp (LAnd ity) [x,y] = ibin ity x y And
857cgOp (LOr ity) [x,y] = ibin ity x y Or
858cgOp (LXOr ity) [x,y] = ibin ity x y Xor
859cgOp (LCompl ity) [x] = iun ity x (Xor (ConstantOperand (C.Int (itWidth ity) (-1))))
860cgOp (LSHL ity) [x,y] = ibin ity x y (Shl False False)
861cgOp (LLSHR ity) [x,y] = ibin ity x y (LShr False)
862cgOp (LASHR ity) [x,y] = ibin ity x y (AShr False)
864cgOp LNoOp xs = return $ last xs
866cgOp LStrEq [x,y] = do
867 x' <- unbox FString x
868 y' <- unbox FString y
869 cmp <- inst $ simpleCall "strcmp" [x', y']
870 flag <- inst $ ICmp IPred.EQ cmp (ConstantOperand (C.Int 32 0)) []
871 val <- inst $ ZExt flag (IntegerType 32) []
872 box (FArith (ATInt (ITFixed IT32))) val
874cgOp LStrLt [x,y] = do
875 nx <- unbox FString x
876 ny <- unbox FString y
877 cmp <- inst $ simpleCall "strcmp" [nx, ny]
878 flag <- inst $ ICmp IPred.ULT cmp (ConstantOperand (C.Int 32 0)) []
879 val <- inst $ ZExt flag (IntegerType 32) []
880 box (FArith (ATInt (ITFixed IT32))) val
882cgOp (LIntStr ITBig) [x] = do
883 x' <- unbox (FArith (ATInt ITBig)) x
884 ustr <- inst $ simpleCall "__gmpz_get_str"
885 [ ConstantOperand (C.Null (PointerType (IntegerType 8) (AddrSpace 0)))
886 , ConstantOperand (C.Int 32 10)
890cgOp (LIntStr ity) [x] = do
891 x' <- unbox (FArith (ATInt ity)) x
892 x'' <- if itWidth ity < 64
893 then inst $ SExt x' (IntegerType 64) []
895 box FString =<< inst (idrCall "__idris_intStr" [x''])
896cgOp (LStrInt ITBig) [s] = do
897 ns <- unbox FString s
899 inst $ simpleCall "__gmpz_init_set_str" [mpz, ns, ConstantOperand $ C.Int 32 10]
900 box (FArith (ATInt ITBig)) mpz
901cgOp (LStrInt ity) [s] = do
902 ns <- unbox FString s
903 nx <- inst $ simpleCall "strtoll"
905 , ConstantOperand $ C.Null (PointerType (PointerType (IntegerType 8) (AddrSpace 0)) (AddrSpace 0))
906 , ConstantOperand $ C.Int 32 10
909 (ITFixed IT64) -> return nx
910 _ -> inst $ Trunc nx (IntegerType (itWidth ity)) []
911 box (FArith (ATInt ity)) nx'
913cgOp LStrConcat [x,y] = cgStrCat x y
915cgOp LStrCons [c,s] = do
917 ns <- unbox FString s
918 nc' <- inst $ Trunc nc (IntegerType 8) []
919 r <- inst $ simpleCall "__idris_strCons" [nc', ns]
922cgOp LStrHead [c] = do
924 c <- inst $ Load False s Nothing 0 []
925 c' <- inst $ ZExt c (IntegerType 32) []
928cgOp LStrIndex [s, i] = do
929 ns <- unbox FString s
930 ni <- unbox (FArith (ATInt (ITFixed IT32))) i
931 p <- inst $ GetElementPtr True ns [ni] []
932 c <- inst $ Load False p Nothing 0 []
933 c' <- inst $ ZExt c (IntegerType 32) []
936cgOp LStrTail [c] = do
938 c <- inst $ GetElementPtr True s [ConstantOperand $ C.Int 32 1] []
942 ns <- unbox FString s
943 len <- inst $ simpleCall "strlen" [ns]
947 x | x > 32 -> inst $ Trunc len (IntegerType 32) []
948 | x < 32 -> inst $ ZExt len (IntegerType 32) []
949 box (FArith (ATInt (ITFixed IT32))) len'
951cgOp LReadStr [p] = do
953 s <- inst $ simpleCall "__idris_readStr" [np]
958 ptr <- inst $ Load False stdin Nothing 0 []
962 ptr <- inst $ Load False stdout Nothing 0 []
966 ptr <- inst $ Load False stdErr Nothing 0 []
969cgOp prim args = ierror $ "Unimplemented primitive: [" ++ show prim ++ "]("
970 ++ intersperse ',' (take (length args) ['a'..]) ++ ")"
972iCoerce :: (Operand -> Type -> InstructionMetadata -> Instruction) -> NativeTy -> NativeTy -> Operand -> Codegen Operand
973iCoerce operator from to x = do
974 x' <- unbox (FArith (ATInt (ITFixed from))) x
975 x'' <- inst $ operator x' (ftyToTy (FArith (ATInt (ITFixed from)))) []
976 box (FArith (ATInt (ITFixed to))) x''
978cgStrCat :: Operand -> Operand -> Codegen Operand
980 x' <- unbox FString x
981 y' <- unbox FString y
982 xlen <- inst $ simpleCall "strlen" [x']
983 ylen <- inst $ simpleCall "strlen" [y']
984 zlen <- inst $ Add False True xlen ylen []
986 total <- inst $ Add False True zlen (ConstantOperand (C.Int ws 1)) []
987 mem <- allocAtomic total
988 inst $ simpleCall "memcpy" [mem, x', xlen]
989 i <- inst $ PtrToInt mem (IntegerType ws) []
990 offi <- inst $ Add False True i xlen []
991 offp <- inst $ IntToPtr offi (PointerType (IntegerType 8) (AddrSpace 0)) []
992 inst $ simpleCall "memcpy" [offp, y', ylen]
993 j <- inst $ PtrToInt offp (IntegerType ws) []
994 offj <- inst $ Add False True j ylen []
995 end <- inst $ IntToPtr offj (PointerType (IntegerType 8) (AddrSpace 0)) []
996 inst' $ Store False end (ConstantOperand (C.Int 8 0)) Nothing 0 []
999ibin :: IntTy -> Operand -> Operand
1000 -> (Operand -> Operand -> InstructionMetadata -> Instruction) -> Codegen Operand
1001ibin ity x y instCon = do
1002 nx <- unbox (FArith (ATInt ity)) x
1003 ny <- unbox (FArith (ATInt ity)) y
1004 nr <- inst $ instCon nx ny []
1005 box (FArith (ATInt ity)) nr
1007iun :: IntTy -> Operand -> (Operand -> InstructionMetadata -> Instruction) -> Codegen Operand
1008iun ity x instCon = do
1009 nx <- unbox (FArith (ATInt ity)) x
1010 nr <- inst $ instCon nx []
1011 box (FArith (ATInt ity)) nr
1013iCmp :: IntTy -> IPred.IntegerPredicate -> Operand -> Operand -> Codegen Operand
1014iCmp ity pred x y = do
1015 nx <- unbox (FArith (ATInt ity)) x
1016 ny <- unbox (FArith (ATInt ity)) y
1017 nr <- inst $ ICmp pred nx ny []
1018 nr' <- inst $ ZExt nr (IntegerType 32) []
1019 box (FArith (ATInt (ITFixed IT32))) nr'
1021mpzBin :: String -> Operand -> Operand -> Codegen Operand
1023 nx <- unbox (FArith (ATInt ITBig)) x
1024 ny <- unbox (FArith (ATInt ITBig)) y
1026 inst' $ simpleCall "__gmpz_init" [nz]
1027 inst' $ simpleCall ("__gmpz_" ++ name) [nz, nx, ny]
1028 box (FArith (ATInt ITBig)) nz
1030mpzUn :: String -> Operand -> Codegen Operand
1032 nx <- unbox (FArith (ATInt ITBig)) x
1034 inst' $ simpleCall "__gmpz_init" [nz]
1035 inst' $ simpleCall ("__gmpz_" ++ name) [nz, nx]
1036 box (FArith (ATInt ITBig)) nz
1038mpzCmp :: IPred.IntegerPredicate -> Operand -> Operand -> Codegen Operand
1040 nx <- unbox (FArith (ATInt ITBig)) x
1041 ny <- unbox (FArith (ATInt ITBig)) y
1042 cmp <- inst $ simpleCall "__gmpz_cmp" [nx, ny]
1043 result <- inst $ ICmp pred cmp (ConstantOperand (C.Int 32 0)) []
1044 i <- inst $ ZExt result (IntegerType 32) []
1045 box (FArith (ATInt (ITFixed IT32))) i
1047simpleCall :: String -> [Operand] -> Instruction
1048simpleCall name args =
1049 Call { isTailCall = False
1050 , callingConvention = CC.C
1051 , returnAttributes = []
1052 , function = Right . ConstantOperand . C.GlobalReference . Name $ name
1053 , arguments = map (\x -> (x, [])) args
1054 , functionAttributes = []
1058idrCall :: String -> [Operand] -> Instruction
1060 Call { isTailCall = False
1061 , callingConvention = CC.Fast
1062 , returnAttributes = []
1063 , function = Right . ConstantOperand . C.GlobalReference . Name $ name
1064 , arguments = map (\x -> (x, [])) args
1065 , functionAttributes = []
1069assert :: Operand -> String -> Codegen ()
1070assert condition message = do
1071 passed <- getName "assertPassed"
1072 failed <- getName "assertFailed"
1073 terminate $ CondBr condition passed failed []
1075 cgExpr (SError message)
1076 terminate $ Unreachable []
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list