diff options
Diffstat (limited to 'compiler/llvmGen')
-rw-r--r-- | compiler/llvmGen/Llvm.hs | 11 | ||||
-rw-r--r-- | compiler/llvmGen/Llvm/AbsSyn.hs | 40 | ||||
-rw-r--r-- | compiler/llvmGen/Llvm/MetaData.hs | 84 | ||||
-rw-r--r-- | compiler/llvmGen/Llvm/PpLlvm.hs | 210 | ||||
-rw-r--r-- | compiler/llvmGen/Llvm/Types.hs | 469 | ||||
-rw-r--r-- | compiler/llvmGen/LlvmCodeGen.hs | 252 | ||||
-rw-r--r-- | compiler/llvmGen/LlvmCodeGen/Base.hs | 404 | ||||
-rw-r--r-- | compiler/llvmGen/LlvmCodeGen/CodeGen.hs | 1175 | ||||
-rw-r--r-- | compiler/llvmGen/LlvmCodeGen/Data.hs | 155 | ||||
-rw-r--r-- | compiler/llvmGen/LlvmCodeGen/Ppr.hs | 85 | ||||
-rw-r--r-- | compiler/llvmGen/LlvmCodeGen/Regs.hs | 55 |
11 files changed, 1599 insertions, 1341 deletions
diff --git a/compiler/llvmGen/Llvm.hs b/compiler/llvmGen/Llvm.hs index d69b88ce23..85095997ae 100644 --- a/compiler/llvmGen/Llvm.hs +++ b/compiler/llvmGen/Llvm.hs @@ -32,20 +32,22 @@ module Llvm ( -- * Variables and Type System LlvmVar(..), LlvmStatic(..), LlvmLit(..), LlvmType(..), - LlvmAlias, LMGlobal, LMString, LMSection, LMAlign, + LlvmAlias, LMGlobal(..), LMString, LMSection, LMAlign, + LMConst(..), -- ** Some basic types i64, i32, i16, i8, i1, i8Ptr, llvmWord, llvmWordPtr, -- ** Metadata types - LlvmMetaVal(..), LlvmMetaUnamed(..), LlvmMeta(..), MetaData, + MetaExpr(..), MetaAnnot(..), MetaDecl(..), -- ** Operations on the type system. - isGlobal, getLitType, getLit, getName, getPlainName, getVarType, - getLink, getStatType, getGlobalVar, getGlobalType, pVarLift, pVarLower, + isGlobal, getLitType, getVarType, + getLink, getStatType, pVarLift, pVarLower, pLift, pLower, isInt, isFloat, isPointer, isVector, llvmWidthInBits, -- * Pretty Printing + ppLit, ppName, ppPlainName, ppLlvmModule, ppLlvmComments, ppLlvmComment, ppLlvmGlobals, ppLlvmGlobal, ppLlvmFunctionDecls, ppLlvmFunctionDecl, ppLlvmFunctions, ppLlvmFunction, ppLlvmAlias, ppLlvmAliases, ppLlvmMetas, ppLlvmMeta, @@ -53,6 +55,7 @@ module Llvm ( ) where import Llvm.AbsSyn +import Llvm.MetaData import Llvm.PpLlvm import Llvm.Types diff --git a/compiler/llvmGen/Llvm/AbsSyn.hs b/compiler/llvmGen/Llvm/AbsSyn.hs index f5f5eacdee..f92bd89c5c 100644 --- a/compiler/llvmGen/Llvm/AbsSyn.hs +++ b/compiler/llvmGen/Llvm/AbsSyn.hs @@ -4,6 +4,7 @@ module Llvm.AbsSyn where +import Llvm.MetaData import Llvm.Types import Unique @@ -32,7 +33,7 @@ data LlvmModule = LlvmModule { modAliases :: [LlvmAlias], -- | LLVM meta data. - modMeta :: [LlvmMeta], + modMeta :: [MetaDecl], -- | Global variables to include in the module. modGlobals :: [LMGlobal], @@ -165,11 +166,9 @@ data LlvmStatement {- | A LLVM statement with metadata attached to it. -} - | MetaStmt [MetaData] LlvmStatement + | MetaStmt [MetaAnnot] LlvmStatement - deriving (Show, Eq) - -type MetaData = (LMString, LlvmMetaUnamed) + deriving (Eq) -- | Llvm Expressions @@ -253,6 +252,17 @@ data LlvmExpression | Call LlvmCallType LlvmVar [LlvmVar] [LlvmFuncAttr] {- | + Call a function as above but potentially taking metadata as arguments. + * tailJumps: CallType to signal if the function should be tail called + * fnptrval: An LLVM value containing a pointer to a function to be + invoked. Can be indirect. Should be LMFunction type. + * args: Arguments that may include metadata. + * attrs: A list of function attributes for the call. Only NoReturn, + NoUnwind, ReadOnly and ReadNone are valid here. + -} + | CallM LlvmCallType LlvmVar [MetaExpr] [LlvmFuncAttr] + + {- | Merge variables from different basic blocks which are predecessors of this basic block in a new variable of type tp. * tp: type of the merged variable, must match the types of the @@ -264,21 +274,21 @@ data LlvmExpression {- | Inline assembly expression. Syntax is very similar to the style used by GCC. - * assembly: Actual inline assembly code. - * contraints: Operand constraints. - * return ty: Return type of function. - * vars: Any variables involved in the assembly code. - * sideeffect: Does the expression have side effects not visible from the - constraints list. - * alignstack: Should the stack be conservatively aligned before this - expression is executed. + * assembly: Actual inline assembly code. + * constraints: Operand constraints. + * return ty: Return type of function. + * vars: Any variables involved in the assembly code. + * sideeffect: Does the expression have side effects not visible from the + constraints list. + * alignstack: Should the stack be conservatively aligned before this + expression is executed. -} | Asm LMString LMString LlvmType [LlvmVar] Bool Bool {- | A LLVM expression with metadata attached to it. -} - | MetaExpr [MetaData] LlvmExpression + | MExpr [MetaAnnot] LlvmExpression - deriving (Show, Eq) + deriving (Eq) diff --git a/compiler/llvmGen/Llvm/MetaData.hs b/compiler/llvmGen/Llvm/MetaData.hs new file mode 100644 index 0000000000..dda3ca0c4c --- /dev/null +++ b/compiler/llvmGen/Llvm/MetaData.hs @@ -0,0 +1,84 @@ +-------------------------------------------------------------------------------- +-- | The LLVM Metadata System. +-- +-- The LLVM metadata feature is poorly documented but roughly follows the +-- following design: +-- * Metadata can be constructed in a few different ways (See below). +-- * After which it can either be attached to LLVM statements to pass along +-- extra information to the optimizer and code generator OR specificially named +-- metadata has an affect on the whole module (i.e., linking behaviour). +-- +-- +-- # Constructing metadata +-- Metadata comes largely in three forms: +-- +-- * Metadata expressions -- these are the raw metadata values that encode +-- information. They consist of metadata strings, metadata nodes, regular +-- LLVM values (both literals and references to global variables) and +-- metadata expressions (i.e., recursive data type). Some examples: +-- !{ metadata !"hello", metadata !0, i32 0 } +-- !{ metadata !1, metadata !{ i32 0 } } +-- +-- * Metadata nodes -- global metadata variables that attach a metadata +-- expression to a number. For example: +-- !0 = metadata !{ [<metadata expressions>] !} +-- +-- * Named metadata -- global metadata variables that attach a metadata nodes +-- to a name. Used ONLY to communicated module level information to LLVM +-- through a meaningful name. For example: +-- !llvm.module.linkage = !{ !0, !1 } +-- +-- +-- # Using Metadata +-- Using metadata depends on the form it is in: +-- +-- * Attach to instructions -- metadata can be attached to LLVM instructions +-- using a specific reference as follows: +-- %l = load i32* @glob, !nontemporal !10 +-- %m = load i32* @glob, !nontemporal !{ i32 0, metadata !{ i32 0 } } +-- Only metadata nodes or expressions can be attached, named metadata cannot. +-- Refer to LLVM documentation for which instructions take metadata and its +-- meaning. +-- +-- * As arguments -- llvm functions can take metadata as arguments, for +-- example: +-- call void @llvm.dbg.value(metadata !{ i32 0 }, i64 0, metadata !1) +-- As with instructions, only metadata nodes or expressions can be attached. +-- +-- * As a named metadata -- Here the metadata is simply declared in global +-- scope using a specific name to communicate module level information to LLVM. +-- For example: +-- !llvm.module.linkage = !{ !0, !1 } +-- +module Llvm.MetaData where + +import Llvm.Types + +import Outputable + +-- | LLVM metadata expressions +data MetaExpr = MetaStr LMString + | MetaNode Int + | MetaVar LlvmVar + | MetaStruct [MetaExpr] + deriving (Eq) + +instance Outputable MetaExpr where + ppr (MetaStr s ) = text "metadata !\"" <> ftext s <> char '"' + ppr (MetaNode n ) = text "metadata !" <> int n + ppr (MetaVar v ) = ppr v + ppr (MetaStruct es) = text "metadata !{ " <> ppCommaJoin es <> char '}' + +-- | Associates some metadata with a specific label for attaching to an +-- instruction. +data MetaAnnot = MetaAnnot LMString MetaExpr + deriving (Eq) + +-- | Metadata declarations. Metadata can only be declared in global scope. +data MetaDecl + -- | Named metadata. Only used for communicating module information to + -- LLVM. ('!name = !{ [!<n>] }' form). + = MetaNamed LMString [Int] + -- | Metadata node declaration. + -- ('!0 = metadata !{ <metadata expression> }' form). + | MetaUnamed Int MetaExpr diff --git a/compiler/llvmGen/Llvm/PpLlvm.hs b/compiler/llvmGen/Llvm/PpLlvm.hs index a709a05b7d..fca1a7cd4d 100644 --- a/compiler/llvmGen/Llvm/PpLlvm.hs +++ b/compiler/llvmGen/Llvm/PpLlvm.hs @@ -24,11 +24,13 @@ module Llvm.PpLlvm ( #include "HsVersions.h" import Llvm.AbsSyn +import Llvm.MetaData import Llvm.Types import Data.List ( intersperse ) import Outputable import Unique +import FastString ( sLit ) -------------------------------------------------------------------------------- -- * Top Level Print functions @@ -59,7 +61,7 @@ ppLlvmGlobals ls = vcat $ map ppLlvmGlobal ls -- | Print out a global mutable variable definition ppLlvmGlobal :: LMGlobal -> SDoc -ppLlvmGlobal (var@(LMGlobalVar _ _ link x a c), dat) = +ppLlvmGlobal (LMGlobal var@(LMGlobalVar _ _ link x a c) dat) = let sect = case x of Just x' -> text ", section" <+> doubleQuotes (ftext x') Nothing -> empty @@ -69,15 +71,21 @@ ppLlvmGlobal (var@(LMGlobalVar _ _ link x a c), dat) = Nothing -> empty rhs = case dat of - Just stat -> texts stat - Nothing -> texts (pLower $ getVarType var) + Just stat -> ppr stat + Nothing -> ppr (pLower $ getVarType var) - const' = if c then text "constant" else text "global" + -- Position of linkage is different for aliases. + const_link = case c of + Global -> ppr link <+> text "global" + Constant -> ppr link <+> text "constant" + Alias -> text "alias" <+> ppr link - in ppAssignment var $ texts link <+> const' <+> rhs <> sect <> align + in ppAssignment var $ const_link <+> rhs <> sect <> align $+$ newLine -ppLlvmGlobal oth = error $ "Non Global var ppr as global! " ++ show oth +ppLlvmGlobal (LMGlobal var val) = sdocWithDynFlags $ \dflags -> + error $ "Non Global var ppr as global! " + ++ showSDoc dflags (ppr var) ++ " " ++ showSDoc dflags (ppr val) -- | Print out a list of LLVM type aliases. @@ -87,32 +95,31 @@ ppLlvmAliases tys = vcat $ map ppLlvmAlias tys -- | Print out an LLVM type alias. ppLlvmAlias :: LlvmAlias -> SDoc ppLlvmAlias (name, ty) - = text "%" <> ftext name <+> equals <+> text "type" <+> texts ty + = char '%' <> ftext name <+> equals <+> text "type" <+> ppr ty -- | Print out a list of LLVM metadata. -ppLlvmMetas :: [LlvmMeta] -> SDoc +ppLlvmMetas :: [MetaDecl] -> SDoc ppLlvmMetas metas = vcat $ map ppLlvmMeta metas -- | Print out an LLVM metadata definition. -ppLlvmMeta :: LlvmMeta -> SDoc -ppLlvmMeta (MetaUnamed (LMMetaUnamed u) metas) - = exclamation <> int u <> text " = metadata !{" <> - hcat (intersperse comma $ map ppLlvmMetaVal metas) <> text "}" - -ppLlvmMeta (MetaNamed n metas) - = exclamation <> ftext n <> text " = !{" <> - hcat (intersperse comma $ map pprNode munq) <> text "}" +ppLlvmMeta :: MetaDecl -> SDoc +ppLlvmMeta (MetaUnamed n m) + = exclamation <> int n <> text " = " <> ppLlvmMetaExpr m + +ppLlvmMeta (MetaNamed n m) + = exclamation <> ftext n <> text " = !" <> braces nodes where - munq = map (\(LMMetaUnamed u) -> u) metas + nodes = hcat $ intersperse comma $ map pprNode m pprNode n = exclamation <> int n -- | Print out an LLVM metadata value. -ppLlvmMetaVal :: LlvmMetaVal -> SDoc -ppLlvmMetaVal (MetaStr s) = text "metadata !" <> doubleQuotes (ftext s) -ppLlvmMetaVal (MetaVar v) = texts v -ppLlvmMetaVal (MetaNode (LMMetaUnamed u)) - = text "metadata !" <> int u +ppLlvmMetaExpr :: MetaExpr -> SDoc +ppLlvmMetaExpr (MetaStr s ) = text "metadata !" <> doubleQuotes (ftext s) +ppLlvmMetaExpr (MetaNode n ) = text "metadata !" <> int n +ppLlvmMetaExpr (MetaVar v ) = ppr v +ppLlvmMetaExpr (MetaStruct es) = + text "metadata !{" <> hsep (punctuate comma (map ppLlvmMetaExpr es)) <> char '}' -- | Print out a list of function definitions. @@ -138,17 +145,17 @@ ppLlvmFunction (LlvmFunction dec args attrs sec body) = ppLlvmFunctionHeader :: LlvmFunctionDecl -> [LMString] -> SDoc ppLlvmFunctionHeader (LlvmFunctionDecl n l c r varg p a) args = let varg' = case varg of - VarArgs | null p -> text "..." - | otherwise -> text ", ..." - _otherwise -> empty + VarArgs | null p -> sLit "..." + | otherwise -> sLit ", ..." + _otherwise -> sLit "" align = case a of - Just a' -> text " align" <+> texts a' + Just a' -> text " align " <> ppr a' Nothing -> empty - args' = map (\((ty,p),n) -> texts ty <+> ppSpaceJoin p <+> text "%" + args' = map (\((ty,p),n) -> ppr ty <+> ppSpaceJoin p <+> char '%' <> ftext n) (zip p args) - in texts l <+> texts c <+> texts r <+> text "@" <> ftext n <> lparen <> - (hcat $ intersperse (comma <> space) args') <> varg' <> rparen <> align + in ppr l <+> ppr c <+> ppr r <+> char '@' <> ftext n <> lparen <> + (hsep $ punctuate comma args') <> ptext varg' <> rparen <> align -- | Print out a list of function declaration. ppLlvmFunctionDecls :: LlvmFunctionDecls -> SDoc @@ -160,16 +167,16 @@ ppLlvmFunctionDecls decs = vcat $ map ppLlvmFunctionDecl decs ppLlvmFunctionDecl :: LlvmFunctionDecl -> SDoc ppLlvmFunctionDecl (LlvmFunctionDecl n l c r varg p a) = let varg' = case varg of - VarArgs | null p -> text "..." - | otherwise -> text ", ..." - _otherwise -> empty + VarArgs | null p -> sLit "..." + | otherwise -> sLit ", ..." + _otherwise -> sLit "" align = case a of - Just a' -> text " align" <+> texts a' + Just a' -> text " align" <+> ppr a' Nothing -> empty args = hcat $ intersperse (comma <> space) $ - map (\(t,a) -> texts t <+> ppSpaceJoin a) p - in text "declare" <+> texts l <+> texts c <+> texts r <+> text "@" <> - ftext n <> lparen <> args <> varg' <> rparen <> align $+$ newLine + map (\(t,a) -> ppr t <+> ppSpaceJoin a) p + in text "declare" <+> ppr l <+> ppr c <+> ppr r <+> char '@' <> + ftext n <> lparen <> args <> ptext varg' <> rparen <> align $+$ newLine -- | Print out a list of LLVM blocks. @@ -179,19 +186,14 @@ ppLlvmBlocks blocks = vcat $ map ppLlvmBlock blocks -- | Print out an LLVM block. -- It must be part of a function definition. ppLlvmBlock :: LlvmBlock -> SDoc -ppLlvmBlock (LlvmBlock blockId stmts) - = go blockId stmts - where - lbreak acc [] = (Nothing, reverse acc, []) - lbreak acc (MkLabel id:xs) = (Just id, reverse acc, xs) - lbreak acc (x:xs) = lbreak (x:acc) xs - - go id code = - let (id2, block, rest) = lbreak [] code - ppRest = case id2 of - Just id2' -> go id2' rest - Nothing -> empty - in ppLlvmBlockLabel id +ppLlvmBlock (LlvmBlock blockId stmts) = + let isLabel (MkLabel _) = True + isLabel _ = False + (block, rest) = break isLabel stmts + ppRest = case rest of + MkLabel id:xs -> ppLlvmBlock (LlvmBlock id xs) + _ -> empty + in ppLlvmBlockLabel blockId $+$ (vcat $ map ppLlvmStatement block) $+$ newLine $+$ ppRest @@ -227,7 +229,8 @@ ppLlvmExpression expr = case expr of Alloca tp amount -> ppAlloca tp amount LlvmOp op left right -> ppMachOp op left right - Call tp fp args attrs -> ppCall tp fp args attrs + Call tp fp args attrs -> ppCall tp fp (map MetaVar args) attrs + CallM tp fp args attrs -> ppCall tp fp args attrs Cast op from to -> ppCast op from to Compare op left right -> ppCmpOp op left right Extract vec idx -> ppExtract vec idx @@ -237,7 +240,7 @@ ppLlvmExpression expr Malloc tp amount -> ppMalloc tp amount Phi tp precessors -> ppPhi tp precessors Asm asm c ty v se sk -> ppAsm asm c ty v se sk - MetaExpr meta expr -> ppMetaExpr meta expr + MExpr meta expr -> ppMetaExpr meta expr -------------------------------------------------------------------------------- @@ -246,8 +249,8 @@ ppLlvmExpression expr -- | Should always be a function pointer. So a global var of function type -- (since globals are always pointers) or a local var of pointer function type. -ppCall :: LlvmCallType -> LlvmVar -> [LlvmVar] -> [LlvmFuncAttr] -> SDoc -ppCall ct fptr vals attrs = case fptr of +ppCall :: LlvmCallType -> LlvmVar -> [MetaExpr] -> [LlvmFuncAttr] -> SDoc +ppCall ct fptr args attrs = case fptr of -- -- if local var function pointer, unwrap LMLocalVar _ (LMPointer (LMFunction d)) -> ppCall' d @@ -263,23 +266,22 @@ ppCall ct fptr vals attrs = case fptr of where ppCall' (LlvmFunctionDecl _ _ cc ret argTy params _) = let tc = if ct == TailCall then text "tail " else empty - ppValues = ppCommaJoin vals - ppParams = map (texts . fst) params - ppArgTy = (hcat $ intersperse comma ppParams) <> + ppValues = ppCommaJoin args + ppArgTy = (ppCommaJoin $ map fst params) <> (case argTy of VarArgs -> text ", ..." FixedArgs -> empty) - fnty = space <> lparen <> ppArgTy <> rparen <> text "*" + fnty = space <> lparen <> ppArgTy <> rparen <> char '*' attrDoc = ppSpaceJoin attrs - in tc <> text "call" <+> texts cc <+> texts ret - <> fnty <+> (text $ getName fptr) <> lparen <+> ppValues + in tc <> text "call" <+> ppr cc <+> ppr ret + <> fnty <+> ppName fptr <> lparen <+> ppValues <+> rparen <+> attrDoc ppMachOp :: LlvmMachOp -> LlvmVar -> LlvmVar -> SDoc ppMachOp op left right = - (texts op) <+> (texts (getVarType left)) <+> (text $ getName left) - <> comma <+> (text $ getName right) + (ppr op) <+> (ppr (getVarType left)) <+> ppName left + <> comma <+> ppName right ppCmpOp :: LlvmCmpOp -> LlvmVar -> LlvmVar -> SDoc @@ -293,12 +295,12 @@ ppCmpOp op left right = ++ (show $ getVarType left) ++ ", right = " ++ (show $ getVarType right)) -} - in cmpOp <+> texts op <+> texts (getVarType left) - <+> (text $ getName left) <> comma <+> (text $ getName right) + in cmpOp <+> ppr op <+> ppr (getVarType left) + <+> ppName left <> comma <+> ppName right ppAssignment :: LlvmVar -> SDoc -> SDoc -ppAssignment var expr = (text $ getName var) <+> equals <+> expr +ppAssignment var expr = ppName var <+> equals <+> expr ppFence :: Bool -> LlvmSyncOrdering -> SDoc ppFence st ord = @@ -324,72 +326,71 @@ ppSyncOrdering SyncSeqCst = text "seq_cst" ppLoad :: LlvmVar -> SDoc ppLoad var - | isVecPtrVar var = text "load" <+> texts var <> + | isVecPtrVar var = text "load" <+> ppr var <> comma <+> text "align 1" - | otherwise = text "load" <+> texts var + | otherwise = text "load" <+> ppr var where isVecPtrVar :: LlvmVar -> Bool isVecPtrVar = isVector . pLower . getVarType ppStore :: LlvmVar -> LlvmVar -> SDoc ppStore val dst - | isVecPtrVar dst = text "store" <+> texts val <> comma <+> texts dst <> + | isVecPtrVar dst = text "store" <+> ppr val <> comma <+> ppr dst <> comma <+> text "align 1" - | otherwise = text "store" <+> texts val <> comma <+> texts dst + | otherwise = text "store" <+> ppr val <> comma <+> ppr dst where isVecPtrVar :: LlvmVar -> Bool isVecPtrVar = isVector . pLower . getVarType ppCast :: LlvmCastOp -> LlvmVar -> LlvmType -> SDoc -ppCast op from to = texts op <+> texts from <+> text "to" <+> texts to +ppCast op from to = ppr op <+> ppr from <+> text "to" <+> ppr to ppMalloc :: LlvmType -> Int -> SDoc ppMalloc tp amount = let amount' = LMLitVar $ LMIntLit (toInteger amount) i32 - in text "malloc" <+> texts tp <> comma <+> texts amount' + in text "malloc" <+> ppr tp <> comma <+> ppr amount' ppAlloca :: LlvmType -> Int -> SDoc ppAlloca tp amount = let amount' = LMLitVar $ LMIntLit (toInteger amount) i32 - in text "alloca" <+> texts tp <> comma <+> texts amount' + in text "alloca" <+> ppr tp <> comma <+> ppr amount' ppGetElementPtr :: Bool -> LlvmVar -> [LlvmVar] -> SDoc ppGetElementPtr inb ptr idx = let indexes = comma <+> ppCommaJoin idx inbound = if inb then text "inbounds" else empty - in text "getelementptr" <+> inbound <+> texts ptr <> indexes + in text "getelementptr" <+> inbound <+> ppr ptr <> indexes ppReturn :: Maybe LlvmVar -> SDoc -ppReturn (Just var) = text "ret" <+> texts var -ppReturn Nothing = text "ret" <+> texts LMVoid +ppReturn (Just var) = text "ret" <+> ppr var +ppReturn Nothing = text "ret" <+> ppr LMVoid ppBranch :: LlvmVar -> SDoc -ppBranch var = text "br" <+> texts var +ppBranch var = text "br" <+> ppr var ppBranchIf :: LlvmVar -> LlvmVar -> LlvmVar -> SDoc ppBranchIf cond trueT falseT - = text "br" <+> texts cond <> comma <+> texts trueT <> comma <+> texts falseT + = text "br" <+> ppr cond <> comma <+> ppr trueT <> comma <+> ppr falseT ppPhi :: LlvmType -> [(LlvmVar,LlvmVar)] -> SDoc ppPhi tp preds = - let ppPreds (val, label) = brackets $ (text $ getName val) <> comma - <+> (text $ getName label) - in text "phi" <+> texts tp <+> hcat (intersperse comma $ map ppPreds preds) + let ppPreds (val, label) = brackets $ ppName val <> comma <+> ppName label + in text "phi" <+> ppr tp <+> hsep (punctuate comma $ map ppPreds preds) ppSwitch :: LlvmVar -> LlvmVar -> [(LlvmVar,LlvmVar)] -> SDoc ppSwitch scrut dflt targets = - let ppTarget (val, lab) = texts val <> comma <+> texts lab + let ppTarget (val, lab) = ppr val <> comma <+> ppr lab ppTargets xs = brackets $ vcat (map ppTarget xs) - in text "switch" <+> texts scrut <> comma <+> texts dflt + in text "switch" <+> ppr scrut <> comma <+> ppr dflt <+> ppTargets targets @@ -397,7 +398,7 @@ ppAsm :: LMString -> LMString -> LlvmType -> [LlvmVar] -> Bool -> Bool -> SDoc ppAsm asm constraints rty vars sideeffect alignstack = let asm' = doubleQuotes $ ftext asm cons = doubleQuotes $ ftext constraints - rty' = texts rty + rty' = ppr rty vars' = lparen <+> ppCommaJoin vars <+> rparen side = if sideeffect then text "sideeffect" else empty align = if alignstack then text "alignstack" else empty @@ -407,49 +408,42 @@ ppAsm asm constraints rty vars sideeffect alignstack = ppExtract :: LlvmVar -> LlvmVar -> SDoc ppExtract vec idx = text "extractelement" - <+> texts (getVarType vec) <+> text (getName vec) <> comma - <+> texts idx + <+> ppr (getVarType vec) <+> ppName vec <> comma + <+> ppr idx ppInsert :: LlvmVar -> LlvmVar -> LlvmVar -> SDoc ppInsert vec elt idx = text "insertelement" - <+> texts (getVarType vec) <+> text (getName vec) <> comma - <+> texts (getVarType elt) <+> text (getName elt) <> comma - <+> texts idx - -ppMetaStatement :: [MetaData] -> LlvmStatement -> SDoc -ppMetaStatement meta stmt = ppLlvmStatement stmt <> ppMetas meta + <+> ppr (getVarType vec) <+> ppName vec <> comma + <+> ppr (getVarType elt) <+> ppName elt <> comma + <+> ppr idx -ppMetaExpr :: [MetaData] -> LlvmExpression -> SDoc -ppMetaExpr meta expr = ppLlvmExpression expr <> ppMetas meta +ppMetaStatement :: [MetaAnnot] -> LlvmStatement -> SDoc +ppMetaStatement meta stmt = ppLlvmStatement stmt <> ppMetaAnnots meta +ppMetaExpr :: [MetaAnnot] -> LlvmExpression -> SDoc +ppMetaExpr meta expr = ppLlvmExpression expr <> ppMetaAnnots meta -ppMetas :: [MetaData] -> SDoc -ppMetas meta = hcat $ map ppMeta meta +ppMetaAnnots :: [MetaAnnot] -> SDoc +ppMetaAnnots meta = hcat $ map ppMeta meta where - ppMeta (name, (LMMetaUnamed n)) - = comma <+> exclamation <> ftext name <+> exclamation <> int n + ppMeta (MetaAnnot name e) + = comma <+> exclamation <> ftext name <+> + case e of + MetaNode n -> exclamation <> int n + MetaStruct ms -> exclamation <> braces (ppCommaJoin ms) + other -> exclamation <> braces (ppr other) -- possible? -------------------------------------------------------------------------------- -- * Misc functions -------------------------------------------------------------------------------- -ppCommaJoin :: (Show a) => [a] -> SDoc -ppCommaJoin strs = hcat $ intersperse (comma <> space) (map texts strs) - -ppSpaceJoin :: (Show a) => [a] -> SDoc -ppSpaceJoin strs = hcat $ intersperse space (map texts strs) - --- | Showable to SDoc -texts :: (Show a) => a -> SDoc -texts = (text . show) -- | Blank line. newLine :: SDoc -newLine = text "" +newLine = empty -- | Exclamation point. exclamation :: SDoc -exclamation = text "!" - +exclamation = char '!' diff --git a/compiler/llvmGen/Llvm/Types.hs b/compiler/llvmGen/Llvm/Types.hs index 8b33c0b9dd..6b9c8c181a 100644 --- a/compiler/llvmGen/Llvm/Types.hs +++ b/compiler/llvmGen/Llvm/Types.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + -------------------------------------------------------------------------------- -- | The LLVM Type System. -- @@ -8,12 +10,11 @@ module Llvm.Types where import Data.Char import Data.Int -import Data.List (intercalate) import Numeric import DynFlags import FastString -import Outputable (panic) +import Outputable import Unique -- from NCG @@ -26,7 +27,11 @@ import GHC.Float -- -- | A global mutable variable. Maybe defined or external -type LMGlobal = (LlvmVar, Maybe LlvmStatic) +data LMGlobal = LMGlobal { + getGlobalVar :: LlvmVar, -- ^ Returns the variable of the 'LMGlobal' + getGlobalValue :: Maybe LlvmStatic -- ^ Return the value of the 'LMGlobal' + } + -- | A String in LLVM type LMString = FastString @@ -47,76 +52,49 @@ data LlvmType | LMVoid -- ^ Void type | LMStruct [LlvmType] -- ^ Structure type | LMAlias LlvmAlias -- ^ A type alias + | LMMetadata -- ^ LLVM Metadata -- | Function type, used to create pointers to functions | LMFunction LlvmFunctionDecl deriving (Eq) -instance Show LlvmType where - show (LMInt size ) = "i" ++ show size - show (LMFloat ) = "float" - show (LMDouble ) = "double" - show (LMFloat80 ) = "x86_fp80" - show (LMFloat128 ) = "fp128" - show (LMPointer x ) = show x ++ "*" - show (LMArray nr tp ) = "[" ++ show nr ++ " x " ++ show tp ++ "]" - show (LMVector nr tp ) = "<" ++ show nr ++ " x " ++ show tp ++ ">" - show (LMLabel ) = "label" - show (LMVoid ) = "void" - show (LMStruct tys ) = "<{" ++ (commaCat tys) ++ "}>" - - show (LMFunction (LlvmFunctionDecl _ _ _ r varg p _)) - = let varg' = case varg of - VarArgs | null args -> "..." - | otherwise -> ", ..." - _otherwise -> "" - -- by default we don't print param attributes - args = intercalate ", " $ map (show . fst) p - in show r ++ " (" ++ args ++ varg' ++ ")" - - show (LMAlias (s,_)) = "%" ++ unpackFS s - --- | LLVM metadata values. Used for representing debug and optimization --- information. -data LlvmMetaVal - -- | Metadata string - = MetaStr LMString - -- | Metadata node - | MetaNode LlvmMetaUnamed - -- | Normal value type as metadata - | MetaVar LlvmVar - deriving (Eq) - --- | LLVM metadata nodes. -data LlvmMeta - -- | Unamed metadata - = MetaUnamed LlvmMetaUnamed [LlvmMetaVal] - -- | Named metadata - | MetaNamed LMString [LlvmMetaUnamed] - deriving (Eq) - --- | Unamed metadata variable. -newtype LlvmMetaUnamed = LMMetaUnamed Int - -instance Eq LlvmMetaUnamed where - (==) (LMMetaUnamed n) (LMMetaUnamed m) = n == m - -instance Show LlvmMetaVal where - show (MetaStr s) = "metadata !\"" ++ unpackFS s ++ "\"" - show (MetaNode n) = "metadata " ++ show n - show (MetaVar v) = show v - -instance Show LlvmMetaUnamed where - show (LMMetaUnamed u) = "!" ++ show u - -instance Show LlvmMeta where - show (MetaUnamed m _) = show m - show (MetaNamed m _) = "!" ++ unpackFS m +instance Outputable LlvmType where + ppr (LMInt size ) = char 'i' <> ppr size + ppr (LMFloat ) = text "float" + ppr (LMDouble ) = text "double" + ppr (LMFloat80 ) = text "x86_fp80" + ppr (LMFloat128 ) = text "fp128" + ppr (LMPointer x ) = ppr x <> char '*' + ppr (LMArray nr tp ) = char '[' <> ppr nr <> text " x " <> ppr tp <> char ']' + ppr (LMVector nr tp ) = char '<' <> ppr nr <> text " x " <> ppr tp <> char '>' + ppr (LMLabel ) = text "label" + ppr (LMVoid ) = text "void" + ppr (LMStruct tys ) = text "<{" <> ppCommaJoin tys <> text "}>" + ppr (LMMetadata ) = text "metadata" + + ppr (LMFunction (LlvmFunctionDecl _ _ _ r varg p _)) + = ppr r <+> lparen <> ppParams varg p <> rparen + + ppr (LMAlias (s,_)) = char '%' <> ftext s + +ppParams :: LlvmParameterListType -> [LlvmParameter] -> SDoc +ppParams varg p + = let varg' = case varg of + VarArgs | null args -> sLit "..." + | otherwise -> sLit ", ..." + _otherwise -> sLit "" + -- by default we don't print param attributes + args = map fst p + in ppCommaJoin args <> ptext varg' -- | An LLVM section definition. If Nothing then let LLVM decide the section type LMSection = Maybe LMString type LMAlign = Maybe Int -type LMConst = Bool -- ^ is a variable constant or not + +data LMConst = Global -- ^ Mutable global variable + | Constant -- ^ Constant global variable + | Alias -- ^ Alias of another variable + deriving (Eq) -- | LLVM Variables data LlvmVar @@ -131,9 +109,9 @@ data LlvmVar | LMLitVar LlvmLit deriving (Eq) -instance Show LlvmVar where - show (LMLitVar x) = show x - show (x ) = show (getVarType x) ++ " " ++ getName x +instance Outputable LlvmVar where + ppr (LMLitVar x) = ppr x + ppr (x ) = ppr (getVarType x) <+> ppName x -- | Llvm Literal Data. @@ -152,9 +130,9 @@ data LlvmLit | LMUndefLit LlvmType deriving (Eq) -instance Show LlvmLit where - show l@(LMVectorLit {}) = getLit l - show l = show (getLitType l) ++ " " ++ getLit l +instance Outputable LlvmLit where + ppr l@(LMVectorLit {}) = ppLit l + ppr l = ppr (getLitType l) <+> ppLit l -- | Llvm Static Data. @@ -177,37 +155,33 @@ data LlvmStatic | LMAdd LlvmStatic LlvmStatic -- ^ Constant addition operation | LMSub LlvmStatic LlvmStatic -- ^ Constant subtraction operation -instance Show LlvmStatic where - show (LMComment s) = "; " ++ unpackFS s - show (LMStaticLit l ) = show l - show (LMUninitType t) = show t ++ " undef" - show (LMStaticStr s t) = show t ++ " c\"" ++ unpackFS s ++ "\\00\"" - show (LMStaticArray d t) = show t ++ " [" ++ commaCat d ++ "]" - show (LMStaticStruc d t) = show t ++ "<{" ++ commaCat d ++ "}>" - show (LMStaticPointer v) = show v - show (LMBitc v t) - = show t ++ " bitcast (" ++ show v ++ " to " ++ show t ++ ")" - show (LMPtoI v t) - = show t ++ " ptrtoint (" ++ show v ++ " to " ++ show t ++ ")" - show (LMAdd s1 s2) - = let ty1 = getStatType s1 - op = if isFloat ty1 then " fadd (" else " add (" - in if ty1 == getStatType s2 - then show ty1 ++ op ++ show s1 ++ "," ++ show s2 ++ ")" - else error $ "LMAdd with different types! s1: " - ++ show s1 ++ ", s2: " ++ show s2 - show (LMSub s1 s2) - = let ty1 = getStatType s1 - op = if isFloat ty1 then " fsub (" else " sub (" - in if ty1 == getStatType s2 - then show ty1 ++ op ++ show s1 ++ "," ++ show s2 ++ ")" - else error $ "LMSub with different types! s1: " - ++ show s1 ++ ", s2: " ++ show s2 - - --- | Concatenate an array together, separated by commas -commaCat :: Show a => [a] -> String -commaCat xs = intercalate ", " $ map show xs +instance Outputable LlvmStatic where + ppr (LMComment s) = text "; " <> ftext s + ppr (LMStaticLit l ) = ppr l + ppr (LMUninitType t) = ppr t <> text " undef" + ppr (LMStaticStr s t) = ppr t <> text " c\"" <> ftext s <> text "\\00\"" + ppr (LMStaticArray d t) = ppr t <> text " [" <> ppCommaJoin d <> char ']' + ppr (LMStaticStruc d t) = ppr t <> text "<{" <> ppCommaJoin d <> text "}>" + ppr (LMStaticPointer v) = ppr v + ppr (LMBitc v t) + = ppr t <> text " bitcast (" <> ppr v <> text " to " <> ppr t <> char ')' + ppr (LMPtoI v t) + = ppr t <> text " ptrtoint (" <> ppr v <> text " to " <> ppr t <> char ')' + + ppr (LMAdd s1 s2) + = pprStaticArith s1 s2 (sLit "add") (sLit "fadd") "LMAdd" + ppr (LMSub s1 s2) + = pprStaticArith s1 s2 (sLit "sub") (sLit "fsub") "LMSub" + +pprStaticArith :: LlvmStatic -> LlvmStatic -> LitString -> LitString -> String -> SDoc +pprStaticArith s1 s2 int_op float_op op_name = + let ty1 = getStatType s1 + op = if isFloat ty1 then float_op else int_op + in if ty1 == getStatType s2 + then ppr ty1 <+> ptext op <+> lparen <> ppr s1 <> comma <> ppr s2 <> rparen + else sdocWithDynFlags $ \dflags -> + error $ op_name ++ " with different types! s1: " + ++ showSDoc dflags (ppr s1) ++ ", s2: " ++ showSDoc dflags (ppr s2) -- ----------------------------------------------------------------------------- -- ** Operations on LLVM Basic Types and Variables @@ -215,33 +189,33 @@ commaCat xs = intercalate ", " $ map show xs -- | Return the variable name or value of the 'LlvmVar' -- in Llvm IR textual representation (e.g. @\@x@, @%y@ or @42@). -getName :: LlvmVar -> String -getName v@(LMGlobalVar _ _ _ _ _ _) = "@" ++ getPlainName v -getName v@(LMLocalVar _ _ ) = "%" ++ getPlainName v -getName v@(LMNLocalVar _ _ ) = "%" ++ getPlainName v -getName v@(LMLitVar _ ) = getPlainName v +ppName :: LlvmVar -> SDoc +ppName v@(LMGlobalVar {}) = char '@' <> ppPlainName v +ppName v@(LMLocalVar {}) = char '%' <> ppPlainName v +ppName v@(LMNLocalVar {}) = char '%' <> ppPlainName v +ppName v@(LMLitVar {}) = ppPlainName v -- | Return the variable name or value of the 'LlvmVar' -- in a plain textual representation (e.g. @x@, @y@ or @42@). -getPlainName :: LlvmVar -> String -getPlainName (LMGlobalVar x _ _ _ _ _) = unpackFS x -getPlainName (LMLocalVar x LMLabel ) = show x -getPlainName (LMLocalVar x _ ) = "l" ++ show x -getPlainName (LMNLocalVar x _ ) = unpackFS x -getPlainName (LMLitVar x ) = getLit x +ppPlainName :: LlvmVar -> SDoc +ppPlainName (LMGlobalVar x _ _ _ _ _) = ftext x +ppPlainName (LMLocalVar x LMLabel ) = text (show x) +ppPlainName (LMLocalVar x _ ) = text ('l' : show x) +ppPlainName (LMNLocalVar x _ ) = ftext x +ppPlainName (LMLitVar x ) = ppLit x -- | Print a literal value. No type. -getLit :: LlvmLit -> String -getLit (LMIntLit i (LMInt 32)) = show (fromInteger i :: Int32) -getLit (LMIntLit i (LMInt 64)) = show (fromInteger i :: Int64) -getLit (LMIntLit i _ ) = show (fromInteger i :: Int) --- See Note [LLVM Float Types]. -getLit (LMFloatLit r LMFloat ) = (dToStr . widenFp . narrowFp) r -getLit (LMFloatLit r LMDouble) = dToStr r -getLit f@(LMFloatLit _ _) = error $ "Can't print this float literal!" ++ show f -getLit (LMVectorLit ls ) = "< " ++ commaCat ls ++ " >" -getLit (LMNullLit _ ) = "null" -getLit (LMUndefLit _ ) = "undef" +ppLit :: LlvmLit -> SDoc +ppLit (LMIntLit i (LMInt 32)) = ppr (fromInteger i :: Int32) +ppLit (LMIntLit i (LMInt 64)) = ppr (fromInteger i :: Int64) +ppLit (LMIntLit i _ ) = ppr ((fromInteger i)::Int) +ppLit (LMFloatLit r LMFloat ) = ppFloat $ narrowFp r +ppLit (LMFloatLit r LMDouble) = ppDouble r +ppLit f@(LMFloatLit _ _) = sdocWithDynFlags (\dflags -> + error $ "Can't print this float literal!" ++ showSDoc dflags (ppr f)) +ppLit (LMVectorLit ls ) = char '<' <+> ppCommaJoin ls <+> char '>' +ppLit (LMNullLit _ ) = text "null" +ppLit (LMUndefLit _ ) = text "undef" -- | Return the 'LlvmType' of the 'LlvmVar' getVarType :: LlvmVar -> LlvmType @@ -252,12 +226,12 @@ getVarType (LMLitVar l ) = getLitType l -- | Return the 'LlvmType' of a 'LlvmLit' getLitType :: LlvmLit -> LlvmType -getLitType (LMIntLit _ t) = t -getLitType (LMFloatLit _ t) = t +getLitType (LMIntLit _ t) = t +getLitType (LMFloatLit _ t) = t getLitType (LMVectorLit []) = panic "getLitType" getLitType (LMVectorLit ls) = LMVector (length ls) (getLitType (head ls)) -getLitType (LMNullLit t) = t -getLitType (LMUndefLit t) = t +getLitType (LMNullLit t) = t +getLitType (LMUndefLit t) = t -- | Return the 'LlvmType' of the 'LlvmStatic' getStatType :: LlvmStatic -> LlvmType @@ -273,14 +247,6 @@ getStatType (LMAdd t _) = getStatType t getStatType (LMSub t _) = getStatType t getStatType (LMComment _) = error "Can't call getStatType on LMComment!" --- | Return the 'LlvmType' of the 'LMGlobal' -getGlobalType :: LMGlobal -> LlvmType -getGlobalType (v, _) = getVarType v - --- | Return the 'LlvmVar' part of a 'LMGlobal' -getGlobalVar :: LMGlobal -> LlvmVar -getGlobalVar (v, _) = v - -- | Return the 'LlvmLinkageType' for a 'LlvmVar' getLink :: LlvmVar -> LlvmLinkageType getLink (LMGlobalVar _ _ l _ _ _) = l @@ -289,9 +255,10 @@ getLink _ = Internal -- | Add a pointer indirection to the supplied type. 'LMLabel' and 'LMVoid' -- cannot be lifted. pLift :: LlvmType -> LlvmType -pLift (LMLabel) = error "Labels are unliftable" -pLift (LMVoid) = error "Voids are unliftable" -pLift x = LMPointer x +pLift LMLabel = error "Labels are unliftable" +pLift LMVoid = error "Voids are unliftable" +pLift LMMetadata = error "Metadatas are unliftable" +pLift x = LMPointer x -- | Lower a variable of 'LMPointer' type. pVarLift :: LlvmVar -> LlvmVar @@ -304,7 +271,7 @@ pVarLift (LMLitVar _ ) = error $ "Can't lower a literal type!" -- constructors can be lowered. pLower :: LlvmType -> LlvmType pLower (LMPointer x) = x -pLower x = error $ show x ++ " is a unlowerable type, need a pointer" +pLower x = error $ showSDoc undefined (ppr x) ++ " is a unlowerable type, need a pointer" -- | Lower a variable of 'LMPointer' type. pVarLower :: LlvmVar -> LlvmVar @@ -350,14 +317,17 @@ llvmWidthInBits _ (LMFloat80) = 80 llvmWidthInBits _ (LMFloat128) = 128 -- Could return either a pointer width here or the width of what -- it points to. We will go with the former for now. +-- PMW: At least judging by the way LLVM outputs constants, pointers +-- should use the former, but arrays the latter. llvmWidthInBits dflags (LMPointer _) = llvmWidthInBits dflags (llvmWord dflags) -llvmWidthInBits dflags (LMArray _ _) = llvmWidthInBits dflags (llvmWord dflags) +llvmWidthInBits dflags (LMArray n t) = n * llvmWidthInBits dflags t llvmWidthInBits dflags (LMVector n ty) = n * llvmWidthInBits dflags ty llvmWidthInBits _ LMLabel = 0 llvmWidthInBits _ LMVoid = 0 llvmWidthInBits dflags (LMStruct tys) = sum $ map (llvmWidthInBits dflags) tys llvmWidthInBits _ (LMFunction _) = 0 llvmWidthInBits dflags (LMAlias (_,t)) = llvmWidthInBits dflags t +llvmWidthInBits _ LMMetadata = panic "llvmWidthInBits: Meta-data has no runtime representation!" -- ----------------------------------------------------------------------------- @@ -401,19 +371,13 @@ data LlvmFunctionDecl = LlvmFunctionDecl { } deriving (Eq) -instance Show LlvmFunctionDecl where - show (LlvmFunctionDecl n l c r varg p a) - = let varg' = case varg of - VarArgs | null args -> "..." - | otherwise -> ", ..." - _otherwise -> "" - align = case a of - Just a' -> " align " ++ show a' - Nothing -> "" - -- by default we don't print param attributes - args = intercalate ", " $ map (show . fst) p - in show l ++ " " ++ show c ++ " " ++ show r ++ " @" ++ unpackFS n ++ - "(" ++ args ++ varg' ++ ")" ++ align +instance Outputable LlvmFunctionDecl where + ppr (LlvmFunctionDecl n l c r varg p a) + = let align = case a of + Just a' -> text " align " <> ppr a' + Nothing -> empty + in ppr l <+> ppr c <+> ppr r <+> char '@' <> ftext n <> + lparen <> ppParams varg p <> rparen <> align type LlvmFunctionDecls = [LlvmFunctionDecl] @@ -454,15 +418,15 @@ data LlvmParamAttr | Nest deriving (Eq) -instance Show LlvmParamAttr where - show ZeroExt = "zeroext" - show SignExt = "signext" - show InReg = "inreg" - show ByVal = "byval" - show SRet = "sret" - show NoAlias = "noalias" - show NoCapture = "nocapture" - show Nest = "nest" +instance Outputable LlvmParamAttr where + ppr ZeroExt = text "zeroext" + ppr SignExt = text "signext" + ppr InReg = text "inreg" + ppr ByVal = text "byval" + ppr SRet = text "sret" + ppr NoAlias = text "noalias" + ppr NoCapture = text "nocapture" + ppr Nest = text "nest" -- | Llvm Function Attributes. -- @@ -542,20 +506,20 @@ data LlvmFuncAttr | Naked deriving (Eq) -instance Show LlvmFuncAttr where - show AlwaysInline = "alwaysinline" - show InlineHint = "inlinehint" - show NoInline = "noinline" - show OptSize = "optsize" - show NoReturn = "noreturn" - show NoUnwind = "nounwind" - show ReadNone = "readnon" - show ReadOnly = "readonly" - show Ssp = "ssp" - show SspReq = "ssqreq" - show NoRedZone = "noredzone" - show NoImplicitFloat = "noimplicitfloat" - show Naked = "naked" +instance Outputable LlvmFuncAttr where + ppr AlwaysInline = text "alwaysinline" + ppr InlineHint = text "inlinehint" + ppr NoInline = text "noinline" + ppr OptSize = text "optsize" + ppr NoReturn = text "noreturn" + ppr NoUnwind = text "nounwind" + ppr ReadNone = text "readnon" + ppr ReadOnly = text "readonly" + ppr Ssp = text "ssp" + ppr SspReq = text "ssqreq" + ppr NoRedZone = text "noredzone" + ppr NoImplicitFloat = text "noimplicitfloat" + ppr Naked = text "naked" -- | Different types to call a function. @@ -600,12 +564,12 @@ data LlvmCallConvention | CC_X86_Stdcc deriving (Eq) -instance Show LlvmCallConvention where - show CC_Ccc = "ccc" - show CC_Fastcc = "fastcc" - show CC_Coldcc = "coldcc" - show (CC_Ncc i) = "cc " ++ show i - show CC_X86_Stdcc = "x86_stdcallcc" +instance Outputable LlvmCallConvention where + ppr CC_Ccc = text "ccc" + ppr CC_Fastcc = text "fastcc" + ppr CC_Coldcc = text "coldcc" + ppr (CC_Ncc i) = text "cc " <> ppr i + ppr CC_X86_Stdcc = text "x86_stdcallcc" -- | Functions can have a fixed amount of parameters, or a variable amount. @@ -659,20 +623,22 @@ data LlvmLinkageType -- | Alias for 'ExternallyVisible' but with explicit textual form in LLVM -- assembly. | External + -- | Symbol is private to the module and should not appear in the symbol table + | Private deriving (Eq) -instance Show LlvmLinkageType where - show Internal = "internal" - show LinkOnce = "linkonce" - show Weak = "weak" - show Appending = "appending" - show ExternWeak = "extern_weak" +instance Outputable LlvmLinkageType where + ppr Internal = text "internal" + ppr LinkOnce = text "linkonce" + ppr Weak = text "weak" + ppr Appending = text "appending" + ppr ExternWeak = text "extern_weak" -- ExternallyVisible does not have a textual representation, it is -- the linkage type a function resolves to if no other is specified -- in Llvm. - show ExternallyVisible = "" - show External = "external" - + ppr ExternallyVisible = empty + ppr External = text "external" + ppr Private = text "private" -- ----------------------------------------------------------------------------- -- * LLVM Operations @@ -709,25 +675,25 @@ data LlvmMachOp | LM_MO_Xor -- ^ XOR bitwise logical operation. deriving (Eq) -instance Show LlvmMachOp where - show LM_MO_Add = "add" - show LM_MO_Sub = "sub" - show LM_MO_Mul = "mul" - show LM_MO_UDiv = "udiv" - show LM_MO_SDiv = "sdiv" - show LM_MO_URem = "urem" - show LM_MO_SRem = "srem" - show LM_MO_FAdd = "fadd" - show LM_MO_FSub = "fsub" - show LM_MO_FMul = "fmul" - show LM_MO_FDiv = "fdiv" - show LM_MO_FRem = "frem" - show LM_MO_Shl = "shl" - show LM_MO_LShr = "lshr" - show LM_MO_AShr = "ashr" - show LM_MO_And = "and" - show LM_MO_Or = "or" - show LM_MO_Xor = "xor" +instance Outputable LlvmMachOp where + ppr LM_MO_Add = text "add" + ppr LM_MO_Sub = text "sub" + ppr LM_MO_Mul = text "mul" + ppr LM_MO_UDiv = text "udiv" + ppr LM_MO_SDiv = text "sdiv" + ppr LM_MO_URem = text "urem" + ppr LM_MO_SRem = text "srem" + ppr LM_MO_FAdd = text "fadd" + ppr LM_MO_FSub = text "fsub" + ppr LM_MO_FMul = text "fmul" + ppr LM_MO_FDiv = text "fdiv" + ppr LM_MO_FRem = text "frem" + ppr LM_MO_Shl = text "shl" + ppr LM_MO_LShr = text "lshr" + ppr LM_MO_AShr = text "ashr" + ppr LM_MO_And = text "and" + ppr LM_MO_Or = text "or" + ppr LM_MO_Xor = text "xor" -- | Llvm compare operations. @@ -753,23 +719,23 @@ data LlvmCmpOp | LM_CMP_Fle -- ^ Float less than or equal deriving (Eq) -instance Show LlvmCmpOp where - show LM_CMP_Eq = "eq" - show LM_CMP_Ne = "ne" - show LM_CMP_Ugt = "ugt" - show LM_CMP_Uge = "uge" - show LM_CMP_Ult = "ult" - show LM_CMP_Ule = "ule" - show LM_CMP_Sgt = "sgt" - show LM_CMP_Sge = "sge" - show LM_CMP_Slt = "slt" - show LM_CMP_Sle = "sle" - show LM_CMP_Feq = "oeq" - show LM_CMP_Fne = "une" - show LM_CMP_Fgt = "ogt" - show LM_CMP_Fge = "oge" - show LM_CMP_Flt = "olt" - show LM_CMP_Fle = "ole" +instance Outputable LlvmCmpOp where + ppr LM_CMP_Eq = text "eq" + ppr LM_CMP_Ne = text "ne" + ppr LM_CMP_Ugt = text "ugt" + ppr LM_CMP_Uge = text "uge" + ppr LM_CMP_Ult = text "ult" + ppr LM_CMP_Ule = text "ule" + ppr LM_CMP_Sgt = text "sgt" + ppr LM_CMP_Sge = text "sge" + ppr LM_CMP_Slt = text "slt" + ppr LM_CMP_Sle = text "sle" + ppr LM_CMP_Feq = text "oeq" + ppr LM_CMP_Fne = text "une" + ppr LM_CMP_Fgt = text "ogt" + ppr LM_CMP_Fge = text "oge" + ppr LM_CMP_Flt = text "olt" + ppr LM_CMP_Fle = text "ole" -- | Llvm cast operations. @@ -788,19 +754,19 @@ data LlvmCastOp | LM_Bitcast -- ^ Cast between types where no bit manipulation is needed deriving (Eq) -instance Show LlvmCastOp where - show LM_Trunc = "trunc" - show LM_Zext = "zext" - show LM_Sext = "sext" - show LM_Fptrunc = "fptrunc" - show LM_Fpext = "fpext" - show LM_Fptoui = "fptoui" - show LM_Fptosi = "fptosi" - show LM_Uitofp = "uitofp" - show LM_Sitofp = "sitofp" - show LM_Ptrtoint = "ptrtoint" - show LM_Inttoptr = "inttoptr" - show LM_Bitcast = "bitcast" +instance Outputable LlvmCastOp where + ppr LM_Trunc = text "trunc" + ppr LM_Zext = text "zext" + ppr LM_Sext = text "sext" + ppr LM_Fptrunc = text "fptrunc" + ppr LM_Fpext = text "fpext" + ppr LM_Fptoui = text "fptoui" + ppr LM_Fptosi = text "fptosi" + ppr LM_Uitofp = text "uitofp" + ppr LM_Sitofp = text "sitofp" + ppr LM_Ptrtoint = text "ptrtoint" + ppr LM_Inttoptr = text "inttoptr" + ppr LM_Bitcast = text "bitcast" -- ----------------------------------------------------------------------------- @@ -812,8 +778,8 @@ instance Show LlvmCastOp where -- regardless of underlying architecture. -- -- See Note [LLVM Float Types]. -dToStr :: Double -> String -dToStr d +ppDouble :: Double -> SDoc +ppDouble d = let bs = doubleToBytes d hex d' = case showHex d' "" of [] -> error "dToStr: too few hex digits for float" @@ -821,12 +787,12 @@ dToStr d [x,y] -> [x,y] _ -> error "dToStr: too many hex digits for float" - str = map toUpper $ concat . fixEndian . (map hex) $ bs - in "0x" ++ str + str = map toUpper $ concat $ fixEndian $ map hex bs + in text "0x" <> text str -- Note [LLVM Float Types] -- ~~~~~~~~~~~~~~~~~~~~~~~ --- We use 'dToStr' for both printing Float and Double floating point types. This is +-- We use 'ppDouble' for both printing Float and Double floating point types. This is -- as LLVM expects all floating point constants (single & double) to be in IEEE -- 754 Double precision format. However, for single precision numbers (Float) -- they should be *representable* in IEEE 754 Single precision format. So the @@ -849,6 +815,9 @@ widenFp :: Float -> Double {-# NOINLINE widenFp #-} widenFp = float2Double +ppFloat :: Float -> SDoc +ppFloat = ppDouble . widenFp + -- | Reverse or leave byte data alone to fix endianness on this target. fixEndian :: [a] -> [a] #ifdef WORDS_BIGENDIAN @@ -857,3 +826,13 @@ fixEndian = id fixEndian = reverse #endif + +-------------------------------------------------------------------------------- +-- * Misc functions +-------------------------------------------------------------------------------- + +ppCommaJoin :: (Outputable a) => [a] -> SDoc +ppCommaJoin strs = hsep $ punctuate comma (map ppr strs) + +ppSpaceJoin :: (Outputable a) => [a] -> SDoc +ppSpaceJoin strs = hsep (map ppr strs) diff --git a/compiler/llvmGen/LlvmCodeGen.hs b/compiler/llvmGen/LlvmCodeGen.hs index a157a258fe..d0f343fa92 100644 --- a/compiler/llvmGen/LlvmCodeGen.hs +++ b/compiler/llvmGen/LlvmCodeGen.hs @@ -11,6 +11,7 @@ import LlvmCodeGen.Base import LlvmCodeGen.CodeGen import LlvmCodeGen.Data import LlvmCodeGen.Ppr +import LlvmCodeGen.Regs import LlvmMangler import CgUtils ( fixStgRegisters ) @@ -23,142 +24,173 @@ import DynFlags import ErrUtils import FastString import Outputable -import qualified Pretty as Prt import UniqSupply -import Util import SysTools ( figureLlvmVersion ) +import qualified Stream import Control.Monad ( when ) import Data.IORef ( writeIORef ) -import Data.Maybe ( fromMaybe ) +import Data.Maybe ( fromMaybe, catMaybes ) import System.IO -- ----------------------------------------------------------------------------- -- | Top-level of the LLVM Code generator -- -llvmCodeGen :: DynFlags -> Handle -> UniqSupply -> [RawCmmGroup] -> IO () -llvmCodeGen dflags h us cmms - = let cmm = concat cmms - (cdata,env) = {-# SCC "llvm_split" #-} - foldr split ([], initLlvmEnv dflags) cmm - split (CmmData s d' ) (d,e) = ((s,d'):d,e) - split (CmmProc h l live g) (d,e) = - let lbl = strCLabel_llvm env $ - case mapLookup (g_entry g) h of - Nothing -> l - Just (Statics info_lbl _) -> info_lbl - env' = funInsert lbl (llvmFunTy dflags live) e - in (d,env') - in do - showPass dflags "LlVM CodeGen" - dumpIfSet_dyn dflags Opt_D_dump_llvm "LLVM Code" pprLlvmHeader - bufh <- newBufHandle h - Prt.bufLeftRender bufh $ withPprStyleDoc dflags (mkCodeStyle CStyle) pprLlvmHeader - ver <- getLlvmVersion - env' <- {-# SCC "llvm_datas_gen" #-} - cmmDataLlvmGens dflags bufh (setLlvmVer ver env) cdata [] - {-# SCC "llvm_procs_gen" #-} - cmmProcLlvmGens dflags bufh us env' cmm 1 [] - bFlush bufh - return () - - where - -- | Handle setting up the LLVM version. - getLlvmVersion = do - ver <- (fromMaybe defaultLlvmVersion) `fmap` figureLlvmVersion dflags - -- cache llvm version for later use - writeIORef (llvmVersion dflags) ver - debugTraceMsg dflags 2 - (text "Using LLVM version:" <+> text (show ver)) - let doWarn = wopt Opt_WarnUnsupportedLlvmVersion dflags - when (ver < minSupportLlvmVersion && doWarn) $ - errorMsg dflags (text "You are using an old version of LLVM that" - <> text " isn't supported anymore!" - $+$ text "We will try though...") - when (ver > maxSupportLlvmVersion && doWarn) $ - putMsg dflags (text "You are using a new version of LLVM that" - <> text " hasn't been tested yet!" - $+$ text "We will try though...") - return ver +llvmCodeGen :: DynFlags -> Handle -> UniqSupply + -> Stream.Stream IO RawCmmGroup () + -> IO () +llvmCodeGen dflags h us cmm_stream + = do bufh <- newBufHandle h + + -- Pass header + showPass dflags "LLVM CodeGen" + -- get llvm version, cache for later use + ver <- (fromMaybe defaultLlvmVersion) `fmap` figureLlvmVersion dflags + writeIORef (llvmVersion dflags) ver + + -- warn if unsupported + debugTraceMsg dflags 2 + (text "Using LLVM version:" <+> text (show ver)) + let doWarn = wopt Opt_WarnUnsupportedLlvmVersion dflags + when (ver < minSupportLlvmVersion && doWarn) $ + errorMsg dflags (text "You are using an old version of LLVM that" + <> text " isn't supported anymore!" + $+$ text "We will try though...") + when (ver > maxSupportLlvmVersion && doWarn) $ + putMsg dflags (text "You are using a new version of LLVM that" + <> text " hasn't been tested yet!" + $+$ text "We will try though...") + + -- run code generation + runLlvm dflags ver bufh us $ + llvmCodeGen' (liftStream cmm_stream) + + bFlush bufh + +llvmCodeGen' :: Stream.Stream LlvmM RawCmmGroup () -> LlvmM () +llvmCodeGen' cmm_stream + = do -- Preamble + renderLlvm pprLlvmHeader + ghcInternalFunctions + cmmMetaLlvmPrelude + + -- Procedures + let llvmStream = Stream.mapM llvmGroupLlvmGens cmm_stream + _ <- Stream.collect llvmStream + + -- Declare aliases for forward references + renderLlvm . pprLlvmData =<< generateAliases + + -- Postamble + cmmUsedLlvmGens + +llvmGroupLlvmGens :: RawCmmGroup -> LlvmM () +llvmGroupLlvmGens cmm = do + + -- Insert functions into map, collect data + let split (CmmData s d' ) = return $ Just (s, d') + split (CmmProc h l live g) = do + -- Set function type + let l' = case mapLookup (g_entry g) h of + Nothing -> l + Just (Statics info_lbl _) -> info_lbl + lml <- strCLabel_llvm l' + funInsert lml =<< llvmFunTy live + return Nothing + cdata <- fmap catMaybes $ mapM split cmm + + {-# SCC "llvm_datas_gen" #-} + cmmDataLlvmGens cdata + {-# SCC "llvm_procs_gen" #-} + mapM_ cmmLlvmGen cmm -- ----------------------------------------------------------------------------- -- | Do LLVM code generation on all these Cmms data sections. -- -cmmDataLlvmGens :: DynFlags -> BufHandle -> LlvmEnv -> [(Section,CmmStatics)] - -> [LlvmUnresData] -> IO ( LlvmEnv ) - -cmmDataLlvmGens dflags h env [] lmdata - = let (env', lmdata') = {-# SCC "llvm_resolve" #-} - resolveLlvmDatas env lmdata - lmdoc = {-# SCC "llvm_data_ppr" #-} - vcat $ map pprLlvmData lmdata' - in do - dumpIfSet_dyn dflags Opt_D_dump_llvm "LLVM Code" lmdoc - {-# SCC "llvm_data_out" #-} - Prt.bufLeftRender h $ withPprStyleDoc dflags (mkCodeStyle CStyle) lmdoc - return env' - -cmmDataLlvmGens dflags h env (cmm:cmms) lmdata - = let lm@(l, _, ty, _) = {-# SCC "llvm_data_gen" #-} - genLlvmData env cmm - env' = {-# SCC "llvm_data_insert" #-} - funInsert (strCLabel_llvm env l) ty env - lmdata' = {-# SCC "llvm_data_append" #-} - lm:lmdata - in cmmDataLlvmGens dflags h env' cmms lmdata' +cmmDataLlvmGens :: [(Section,CmmStatics)] -> LlvmM () +cmmDataLlvmGens statics + = do lmdatas <- mapM genLlvmData statics --- ----------------------------------------------------------------------------- --- | Do LLVM code generation on all these Cmms procs. --- -cmmProcLlvmGens :: DynFlags -> BufHandle -> UniqSupply -> LlvmEnv -> [RawCmmDecl] - -> Int -- ^ count, used for generating unique subsections - -> [[LlvmVar]] -- ^ info tables that need to be marked as 'used' - -> IO () - -cmmProcLlvmGens _ _ _ _ [] _ [] - = return () - -cmmProcLlvmGens dflags h _ _ [] _ ivars - = let ivars' = concat ivars - cast x = LMBitc (LMStaticPointer (pVarLift x)) i8Ptr - ty = (LMArray (length ivars') i8Ptr) - usedArray = LMStaticArray (map cast ivars') ty - lmUsed = (LMGlobalVar (fsLit "llvm.used") ty Appending - (Just $ fsLit "llvm.metadata") Nothing False, Just usedArray) - in Prt.bufLeftRender h $ {-# SCC "llvm_used_ppr" #-} - withPprStyleDoc dflags (mkCodeStyle CStyle) $ - pprLlvmData ([lmUsed], []) - -cmmProcLlvmGens dflags h us env ((CmmData _ _) : cmms) count ivars - = cmmProcLlvmGens dflags h us env cmms count ivars - -cmmProcLlvmGens dflags h us env (cmm : cmms) count ivars = do - (us', env', llvm) <- cmmLlvmGen dflags us (clearVars env) cmm - let (docs, ivar) = mapAndUnzip (pprLlvmCmmDecl env' count) llvm - Prt.bufLeftRender h $ {-# SCC "llvm_proc_ppr" #-} - withPprStyleDoc dflags (mkCodeStyle CStyle) $ vcat docs - cmmProcLlvmGens dflags h us' env' cmms (count + 2) (ivar ++ ivars) + let (gss, tss) = unzip lmdatas + let regGlobal (LMGlobal (LMGlobalVar l ty _ _ _ _) _) + = funInsert l ty + regGlobal _ = return () + mapM_ regGlobal (concat gss) + + renderLlvm $ pprLlvmData (concat gss, concat tss) -- | Complete LLVM code generation phase for a single top-level chunk of Cmm. -cmmLlvmGen :: DynFlags -> UniqSupply -> LlvmEnv -> RawCmmDecl - -> IO ( UniqSupply, LlvmEnv, [LlvmCmmDecl] ) -cmmLlvmGen dflags us env cmm = do +cmmLlvmGen ::RawCmmDecl -> LlvmM () +cmmLlvmGen cmm@CmmProc{} = do + -- rewrite assignments to global regs + dflags <- getDynFlag id let fixed_cmm = {-# SCC "llvm_fix_regs" #-} fixStgRegisters dflags cmm - dumpIfSet_dyn dflags Opt_D_dump_opt_cmm "Optimised Cmm" - (pprCmmGroup [fixed_cmm]) + dumpIfSetLlvm Opt_D_dump_opt_cmm "Optimised Cmm" (pprCmmGroup [fixed_cmm]) -- generate llvm code from cmm - let ((env', llvmBC), usGen) = {-# SCC "llvm_proc_gen" #-} - initUs us $ genLlvmProc env fixed_cmm + llvmBC <- withClearVars $ genLlvmProc fixed_cmm - dumpIfSet_dyn dflags Opt_D_dump_llvm "LLVM Code" - (vcat $ map (fst . pprLlvmCmmDecl env' 0) llvmBC) + -- allocate IDs for info table and code, so the mangler can later + -- make sure they end up next to each other. + itableSection <- freshSectionId + _codeSection <- freshSectionId - return (usGen, env', llvmBC) + -- pretty print + (docs, ivars) <- fmap unzip $ mapM (pprLlvmCmmDecl itableSection) llvmBC + + -- Output, note down used variables + renderLlvm (vcat docs) + mapM_ markUsedVar $ concat ivars + +cmmLlvmGen _ = return () + +-- ----------------------------------------------------------------------------- +-- | Generate meta data nodes +-- + +cmmMetaLlvmPrelude :: LlvmM () +cmmMetaLlvmPrelude = do + metas <- flip mapM stgTBAA $ \(uniq, name, parent) -> do + -- Generate / lookup meta data IDs + tbaaId <- getMetaUniqueId + setUniqMeta uniq tbaaId + parentId <- maybe (return Nothing) getUniqMeta parent + -- Build definition + return $ MetaUnamed tbaaId $ MetaStruct + [ MetaStr name + , case parentId of + Just p -> MetaNode p + Nothing -> MetaVar $ LMLitVar $ LMNullLit i8Ptr + ] + renderLlvm $ ppLlvmMetas metas + +-- ----------------------------------------------------------------------------- +-- | Marks variables as used where necessary +-- +cmmUsedLlvmGens :: LlvmM () +cmmUsedLlvmGens = do + + -- LLVM would discard variables that are internal and not obviously + -- used if we didn't provide these hints. This will generate a + -- definition of the form + -- + -- @llvm.used = appending global [42 x i8*] [i8* bitcast <var> to i8*, ...] + -- + -- Which is the LLVM way of protecting them against getting removed. + ivars <- getUsedVars + let cast x = LMBitc (LMStaticPointer (pVarLift x)) i8Ptr + ty = (LMArray (length ivars) i8Ptr) + usedArray = LMStaticArray (map cast ivars) ty + sectName = Just $ fsLit "llvm.metadata" + lmUsedVar = LMGlobalVar (fsLit "llvm.used") ty Appending sectName Nothing Constant + lmUsed = LMGlobal lmUsedVar (Just usedArray) + if null ivars + then return () + else renderLlvm $ pprLlvmData ([lmUsed], []) diff --git a/compiler/llvmGen/LlvmCodeGen/Base.hs b/compiler/llvmGen/LlvmCodeGen/Base.hs index bcfce3401e..dda2c9e05b 100644 --- a/compiler/llvmGen/LlvmCodeGen/Base.hs +++ b/compiler/llvmGen/LlvmCodeGen/Base.hs @@ -13,15 +13,23 @@ module LlvmCodeGen.Base ( LlvmVersion, defaultLlvmVersion, minSupportLlvmVersion, maxSupportLlvmVersion, - LlvmEnv, initLlvmEnv, clearVars, varLookup, varInsert, - funLookup, funInsert, getLlvmVer, setLlvmVer, getLlvmPlatform, - getDflags, ghcInternalFunctions, + LlvmM, + runLlvm, liftStream, withClearVars, varLookup, varInsert, + markStackReg, checkStackReg, + funLookup, funInsert, getLlvmVer, getDynFlags, getDynFlag, getLlvmPlatform, + dumpIfSetLlvm, renderLlvm, runUs, markUsedVar, getUsedVars, + ghcInternalFunctions, + + getMetaUniqueId, + setUniqMeta, getUniqMeta, + freshSectionId, cmmToLlvmType, widthToLlvmFloat, widthToLlvmInt, llvmFunTy, llvmFunSig, llvmStdFunAttrs, llvmFunAlign, llvmInfAlign, llvmPtrBits, mkLlvmFunc, tysToParams, - strCLabel_llvm, genCmmLabelRef, genStringLabelRef + strCLabel_llvm, strDisplayName_llvm, strProcedureName_llvm, + getGlobalPtr, generateAliases, ) where @@ -36,9 +44,15 @@ import DynFlags import FastString import Cmm import qualified Outputable as Outp +import qualified Pretty as Prt import Platform import UniqFM import Unique +import BufWrite ( BufHandle ) +import UniqSet +import UniqSupply +import ErrUtils +import qualified Stream -- ---------------------------------------------------------------------------- -- * Some Data Types @@ -93,30 +107,32 @@ llvmGhcCC dflags | otherwise = CC_Ncc 10 -- | Llvm Function type for Cmm function -llvmFunTy :: DynFlags -> LiveGlobalRegs -> LlvmType -llvmFunTy dflags live = LMFunction $ llvmFunSig' dflags live (fsLit "a") ExternallyVisible +llvmFunTy :: LiveGlobalRegs -> LlvmM LlvmType +llvmFunTy live = return . LMFunction =<< llvmFunSig' live (fsLit "a") ExternallyVisible -- | Llvm Function signature -llvmFunSig :: LlvmEnv -> LiveGlobalRegs -> CLabel -> LlvmLinkageType -> LlvmFunctionDecl -llvmFunSig env live lbl link - = llvmFunSig' (getDflags env) live (strCLabel_llvm env lbl) link - -llvmFunSig' :: DynFlags -> LiveGlobalRegs -> LMString -> LlvmLinkageType -> LlvmFunctionDecl -llvmFunSig' dflags live lbl link - = let toParams x | isPointer x = (x, [NoAlias, NoCapture]) - | otherwise = (x, []) - in LlvmFunctionDecl lbl link (llvmGhcCC dflags) LMVoid FixedArgs - (map (toParams . getVarType) (llvmFunArgs dflags live)) - (llvmFunAlign dflags) +llvmFunSig :: LiveGlobalRegs -> CLabel -> LlvmLinkageType -> LlvmM LlvmFunctionDecl +llvmFunSig live lbl link = do + lbl' <- strCLabel_llvm lbl + llvmFunSig' live lbl' link + +llvmFunSig' :: LiveGlobalRegs -> LMString -> LlvmLinkageType -> LlvmM LlvmFunctionDecl +llvmFunSig' live lbl link + = do let toParams x | isPointer x = (x, [NoAlias, NoCapture]) + | otherwise = (x, []) + dflags <- getDynFlags + return $ LlvmFunctionDecl lbl link (llvmGhcCC dflags) LMVoid FixedArgs + (map (toParams . getVarType) (llvmFunArgs dflags live)) + (llvmFunAlign dflags) -- | Create a Haskell function in LLVM. -mkLlvmFunc :: LlvmEnv -> LiveGlobalRegs -> CLabel -> LlvmLinkageType -> LMSection -> LlvmBlocks - -> LlvmFunction -mkLlvmFunc env live lbl link sec blks - = let dflags = getDflags env - funDec = llvmFunSig env live lbl link - funArgs = map (fsLit . getPlainName) (llvmFunArgs dflags live) - in LlvmFunction funDec funArgs llvmStdFunAttrs sec blks +mkLlvmFunc :: LiveGlobalRegs -> CLabel -> LlvmLinkageType -> LMSection -> LlvmBlocks + -> LlvmM LlvmFunction +mkLlvmFunc live lbl link sec blks + = do funDec <- llvmFunSig live lbl link + dflags <- getDynFlags + let funArgs = map (fsLit . Outp.showSDoc dflags . ppPlainName) (llvmFunArgs dflags live) + return $ LlvmFunction funDec funArgs llvmStdFunAttrs sec blks -- | Alignment to use for functions llvmFunAlign :: DynFlags -> LMAlign @@ -166,102 +182,292 @@ minSupportLlvmVersion :: LlvmVersion minSupportLlvmVersion = 28 maxSupportLlvmVersion :: LlvmVersion -maxSupportLlvmVersion = 33 +maxSupportLlvmVersion = 34 -- ---------------------------------------------------------------------------- -- * Environment Handling -- --- two maps, one for functions and one for local vars. -newtype LlvmEnv = LlvmEnv (LlvmEnvMap, LlvmEnvMap, LlvmVersion, DynFlags) +data LlvmEnv = LlvmEnv + { envVersion :: LlvmVersion -- ^ LLVM version + , envDynFlags :: DynFlags -- ^ Dynamic flags + , envOutput :: BufHandle -- ^ Output buffer + , envUniq :: UniqSupply -- ^ Supply of unique values + , envNextSection :: Int -- ^ Supply of fresh section IDs + , envFreshMeta :: Int -- ^ Supply of fresh metadata IDs + , envUniqMeta :: UniqFM Int -- ^ Global metadata nodes + , envFunMap :: LlvmEnvMap -- ^ Global functions so far, with type + , envAliases :: UniqSet LMString -- ^ Globals that we had to alias, see [Llvm Forward References] + , envUsedVars :: [LlvmVar] -- ^ Pointers to be added to llvm.used (see @cmmUsedLlvmGens@) + + -- the following get cleared for every function (see @withClearVars@) + , envVarMap :: LlvmEnvMap -- ^ Local variables so far, with type + , envStackRegs :: [GlobalReg] -- ^ Non-constant registers (alloca'd in the function prelude) + } type LlvmEnvMap = UniqFM LlvmType --- | Get initial Llvm environment. -initLlvmEnv :: DynFlags -> LlvmEnv -initLlvmEnv dflags = LlvmEnv (initFuncs, emptyUFM, defaultLlvmVersion, dflags) - where initFuncs = listToUFM $ [ (n, LMFunction ty) | (n, ty) <- ghcInternalFunctions dflags ] +-- | The Llvm monad. Wraps @LlvmEnv@ state as well as the @IO@ monad +newtype LlvmM a = LlvmM { runLlvmM :: LlvmEnv -> IO (a, LlvmEnv) } +instance Monad LlvmM where + return x = LlvmM $ \env -> return (x, env) + m >>= f = LlvmM $ \env -> do (x, env') <- runLlvmM m env + runLlvmM (f x) env' +instance Functor LlvmM where + fmap f m = LlvmM $ \env -> do (x, env') <- runLlvmM m env + return (f x, env') --- | Here we pre-initialise some functions that are used internally by GHC --- so as to make sure they have the most general type in the case that --- user code also uses these functions but with a different type than GHC --- internally. (Main offender is treating return type as 'void' instead of --- 'void *'. Fixes trac #5486. -ghcInternalFunctions :: DynFlags -> [(LMString, LlvmFunctionDecl)] -ghcInternalFunctions dflags = - [ mk "memcpy" i8Ptr [i8Ptr, i8Ptr, llvmWord dflags] - , mk "memmove" i8Ptr [i8Ptr, i8Ptr, llvmWord dflags] - , mk "memset" i8Ptr [i8Ptr, llvmWord dflags, llvmWord dflags] - , mk "newSpark" (llvmWord dflags) [i8Ptr, i8Ptr] - ] - where - mk n ret args = - let n' = fsLit n - in (n', LlvmFunctionDecl n' ExternallyVisible CC_Ccc ret - FixedArgs (tysToParams args) Nothing) - --- | Clear variables from the environment. -clearVars :: LlvmEnv -> LlvmEnv -clearVars (LlvmEnv (e1, _, n, p)) = {-# SCC "llvm_env_clear" #-} - LlvmEnv (e1, emptyUFM, n, p) - --- | Insert local variables into the environment. -varInsert :: Uniquable key => key -> LlvmType -> LlvmEnv -> LlvmEnv -varInsert s t (LlvmEnv (e1, e2, n, p)) = {-# SCC "llvm_env_vinsert" #-} - LlvmEnv (e1, addToUFM e2 s t, n, p) - --- | Insert functions into the environment. -funInsert :: Uniquable key => key -> LlvmType -> LlvmEnv -> LlvmEnv -funInsert s t (LlvmEnv (e1, e2, n, p)) = {-# SCC "llvm_env_finsert" #-} - LlvmEnv (addToUFM e1 s t, e2, n, p) - --- | Lookup local variables in the environment. -varLookup :: Uniquable key => key -> LlvmEnv -> Maybe LlvmType -varLookup s (LlvmEnv (_, e2, _, _)) = {-# SCC "llvm_env_vlookup" #-} - lookupUFM e2 s - --- | Lookup functions in the environment. -funLookup :: Uniquable key => key -> LlvmEnv -> Maybe LlvmType -funLookup s (LlvmEnv (e1, _, _, _)) = {-# SCC "llvm_env_flookup" #-} - lookupUFM e1 s +instance HasDynFlags LlvmM where + getDynFlags = LlvmM $ \env -> return (envDynFlags env, env) + +-- | Lifting of IO actions. Not exported, as we want to encapsulate IO. +liftIO :: IO a -> LlvmM a +liftIO m = LlvmM $ \env -> do x <- m + return (x, env) + +-- | Get initial Llvm environment. +runLlvm :: DynFlags -> LlvmVersion -> BufHandle -> UniqSupply -> LlvmM () -> IO () +runLlvm dflags ver out us m = do + _ <- runLlvmM m env + return () + where env = LlvmEnv { envFunMap = emptyUFM + , envVarMap = emptyUFM + , envStackRegs = [] + , envUsedVars = [] + , envAliases = emptyUniqSet + , envVersion = ver + , envDynFlags = dflags + , envOutput = out + , envUniq = us + , envFreshMeta = 0 + , envUniqMeta = emptyUFM + , envNextSection = 1 + } + +-- | Get environment (internal) +getEnv :: (LlvmEnv -> a) -> LlvmM a +getEnv f = LlvmM (\env -> return (f env, env)) + +-- | Modify environment (internal) +modifyEnv :: (LlvmEnv -> LlvmEnv) -> LlvmM () +modifyEnv f = LlvmM (\env -> return ((), f env)) + +-- | Lift a stream into the LlvmM monad +liftStream :: Stream.Stream IO a x -> Stream.Stream LlvmM a x +liftStream s = Stream.Stream $ do + r <- liftIO $ Stream.runStream s + case r of + Left b -> return (Left b) + Right (a, r2) -> return (Right (a, liftStream r2)) + +-- | Clear variables from the environment for a subcomputation +withClearVars :: LlvmM a -> LlvmM a +withClearVars m = LlvmM $ \env -> do + (x, env') <- runLlvmM m env { envVarMap = emptyUFM, envStackRegs = [] } + return (x, env' { envVarMap = emptyUFM, envStackRegs = [] }) + +-- | Insert variables or functions into the environment. +varInsert, funInsert :: Uniquable key => key -> LlvmType -> LlvmM () +varInsert s t = modifyEnv $ \env -> env { envVarMap = addToUFM (envVarMap env) s t } +funInsert s t = modifyEnv $ \env -> env { envFunMap = addToUFM (envFunMap env) s t } + +-- | Lookup variables or functions in the environment. +varLookup, funLookup :: Uniquable key => key -> LlvmM (Maybe LlvmType) +varLookup s = getEnv (flip lookupUFM s . envVarMap) +funLookup s = getEnv (flip lookupUFM s . envFunMap) + +-- | Set a register as allocated on the stack +markStackReg :: GlobalReg -> LlvmM () +markStackReg r = modifyEnv $ \env -> env { envStackRegs = r : envStackRegs env } + +-- | Check whether a register is allocated on the stack +checkStackReg :: GlobalReg -> LlvmM Bool +checkStackReg r = getEnv ((elem r) . envStackRegs) + +-- | Allocate a new global unnamed metadata identifier +getMetaUniqueId :: LlvmM Int +getMetaUniqueId = LlvmM $ \env -> return (envFreshMeta env, env { envFreshMeta = envFreshMeta env + 1}) -- | Get the LLVM version we are generating code for -getLlvmVer :: LlvmEnv -> LlvmVersion -getLlvmVer (LlvmEnv (_, _, n, _)) = n +getLlvmVer :: LlvmM LlvmVersion +getLlvmVer = getEnv envVersion --- | Set the LLVM version we are generating code for -setLlvmVer :: LlvmVersion -> LlvmEnv -> LlvmEnv -setLlvmVer n (LlvmEnv (e1, e2, _, p)) = LlvmEnv (e1, e2, n, p) +-- | Get the platform we are generating code for +getDynFlag :: (DynFlags -> a) -> LlvmM a +getDynFlag f = getEnv (f . envDynFlags) -- | Get the platform we are generating code for -getLlvmPlatform :: LlvmEnv -> Platform -getLlvmPlatform (LlvmEnv (_, _, _, d)) = targetPlatform d +getLlvmPlatform :: LlvmM Platform +getLlvmPlatform = getDynFlag targetPlatform + +-- | Dumps the document if the corresponding flag has been set by the user +dumpIfSetLlvm :: DumpFlag -> String -> Outp.SDoc -> LlvmM () +dumpIfSetLlvm flag hdr doc = do + dflags <- getDynFlags + liftIO $ dumpIfSet_dyn dflags flag hdr doc + +-- | Prints the given contents to the output handle +renderLlvm :: Outp.SDoc -> LlvmM () +renderLlvm sdoc = do + + -- Write to output + dflags <- getDynFlags + out <- getEnv envOutput + let doc = Outp.withPprStyleDoc dflags (Outp.mkCodeStyle Outp.CStyle) sdoc + liftIO $ Prt.bufLeftRender out doc + + -- Dump, if requested + dumpIfSetLlvm Opt_D_dump_llvm "LLVM Code" sdoc + return () + +-- | Run a @UniqSM@ action with our unique supply +runUs :: UniqSM a -> LlvmM a +runUs m = LlvmM $ \env -> do + let (x, us') = initUs (envUniq env) m + return (x, env { envUniq = us' }) + +-- | Marks a variable as "used" +markUsedVar :: LlvmVar -> LlvmM () +markUsedVar v = modifyEnv $ \env -> env { envUsedVars = v : envUsedVars env } + +-- | Return all variables marked as "used" so far +getUsedVars :: LlvmM [LlvmVar] +getUsedVars = getEnv envUsedVars + +-- | Saves that at some point we didn't know the type of the label and +-- generated a reference to a type variable instead +saveAlias :: LMString -> LlvmM () +saveAlias lbl = modifyEnv $ \env -> env { envAliases = addOneToUniqSet (envAliases env) lbl } + +-- | Sets metadata node for a given unique +setUniqMeta :: Unique -> Int -> LlvmM () +setUniqMeta f m = modifyEnv $ \env -> env { envUniqMeta = addToUFM (envUniqMeta env) f m } +-- | Gets metadata node for given unique +getUniqMeta :: Unique -> LlvmM (Maybe Int) +getUniqMeta s = getEnv (flip lookupUFM s . envUniqMeta) + +-- | Returns a fresh section ID +freshSectionId :: LlvmM Int +freshSectionId = LlvmM $ \env -> return (envNextSection env, env { envNextSection = envNextSection env + 1}) + +-- ---------------------------------------------------------------------------- +-- * Internal functions +-- --- | Get the DynFlags for this compilation pass -getDflags :: LlvmEnv -> DynFlags -getDflags (LlvmEnv (_, _, _, d)) = d +-- | Here we pre-initialise some functions that are used internally by GHC +-- so as to make sure they have the most general type in the case that +-- user code also uses these functions but with a different type than GHC +-- internally. (Main offender is treating return type as 'void' instead of +-- 'void *'). Fixes trac #5486. +ghcInternalFunctions :: LlvmM () +ghcInternalFunctions = do + dflags <- getDynFlags + mk "memcpy" i8Ptr [i8Ptr, i8Ptr, llvmWord dflags] + mk "memmove" i8Ptr [i8Ptr, i8Ptr, llvmWord dflags] + mk "memset" i8Ptr [i8Ptr, llvmWord dflags, llvmWord dflags] + mk "newSpark" (llvmWord dflags) [i8Ptr, i8Ptr] + where + mk n ret args = do + let n' = fsLit n + decl = LlvmFunctionDecl n' ExternallyVisible CC_Ccc ret + FixedArgs (tysToParams args) Nothing + renderLlvm $ ppLlvmFunctionDecl decl + funInsert n' (LMFunction decl) -- ---------------------------------------------------------------------------- -- * Label handling -- -- | Pretty print a 'CLabel'. -strCLabel_llvm :: LlvmEnv -> CLabel -> LMString -strCLabel_llvm env l = {-# SCC "llvm_strCLabel" #-} - (fsLit . toString . pprCLabel (getLlvmPlatform env)) l - where dflags = getDflags env - style = Outp.mkCodeStyle Outp.CStyle - toString doc = Outp.renderWithStyle dflags doc style - --- | Create an external definition for a 'CLabel' defined in another module. -genCmmLabelRef :: LlvmEnv -> CLabel -> LMGlobal -genCmmLabelRef env = genStringLabelRef (getDflags env) . strCLabel_llvm env - --- | As above ('genCmmLabelRef') but taking a 'LMString', not 'CLabel'. -genStringLabelRef :: DynFlags -> LMString -> LMGlobal -genStringLabelRef dflags cl - = let ty = LMPointer $ LMArray 0 (llvmWord dflags) - in (LMGlobalVar cl ty External Nothing Nothing False, Nothing) +strCLabel_llvm :: CLabel -> LlvmM LMString +strCLabel_llvm lbl = do + platform <- getLlvmPlatform + dflags <- getDynFlags + let sdoc = pprCLabel platform lbl + str = Outp.renderWithStyle dflags sdoc (Outp.mkCodeStyle Outp.CStyle) + return (fsLit str) + +strDisplayName_llvm :: CLabel -> LlvmM LMString +strDisplayName_llvm lbl = do + platform <- getLlvmPlatform + dflags <- getDynFlags + let sdoc = pprCLabel platform lbl + depth = Outp.PartWay 1 + style = Outp.mkUserStyle (const Outp.NameNotInScope2, const True) depth + str = Outp.renderWithStyle dflags sdoc style + return (fsLit (dropInfoSuffix str)) + +dropInfoSuffix :: String -> String +dropInfoSuffix = go + where go "_info" = [] + go "_static_info" = [] + go "_con_info" = [] + go (x:xs) = x:go xs + go [] = [] + +strProcedureName_llvm :: CLabel -> LlvmM LMString +strProcedureName_llvm lbl = do + platform <- getLlvmPlatform + dflags <- getDynFlags + let sdoc = pprCLabel platform lbl + depth = Outp.PartWay 1 + style = Outp.mkUserStyle (const Outp.NameUnqual, const False) depth + str = Outp.renderWithStyle dflags sdoc style + return (fsLit str) + +-- ---------------------------------------------------------------------------- +-- * Global variables / forward references +-- + +-- | Create/get a pointer to a global value. Might return an alias if +-- the value in question hasn't been defined yet. We especially make +-- no guarantees on the type of the returned pointer. +getGlobalPtr :: LMString -> LlvmM LlvmVar +getGlobalPtr llvmLbl = do + m_ty <- funLookup llvmLbl + let mkGlbVar lbl ty = LMGlobalVar lbl (LMPointer ty) Private Nothing Nothing + case m_ty of + -- Directly reference if we have seen it already + Just ty -> return $ mkGlbVar llvmLbl ty Global + -- Otherwise use a forward alias of it + Nothing -> do + saveAlias llvmLbl + return $ mkGlbVar (llvmLbl `appendFS` fsLit "$alias") i8 Alias + +-- | Generate definitions for aliases forward-referenced by @getGlobalPtr@. +-- +-- Must be called at a point where we are sure that no new global definitions +-- will be generated anymore! +generateAliases :: LlvmM ([LMGlobal], [LlvmType]) +generateAliases = do + delayed <- fmap uniqSetToList $ getEnv envAliases + defss <- flip mapM delayed $ \lbl -> do + let var ty = LMGlobalVar lbl (LMPointer ty) External Nothing Nothing Global + aliasLbl = lbl `appendFS` fsLit "$alias" + aliasVar = LMGlobalVar aliasLbl i8Ptr Private Nothing Nothing Alias + -- If we have a definition, set the alias value using a + -- cost. Otherwise, declare it as an undefined external symbol. + m_ty <- funLookup lbl + case m_ty of + Just ty -> return [LMGlobal aliasVar $ Just $ LMBitc (LMStaticPointer (var ty)) i8Ptr] + Nothing -> return [LMGlobal (var i8) Nothing, + LMGlobal aliasVar $ Just $ LMStaticPointer (var i8) ] + -- Reset forward list + modifyEnv $ \env -> env { envAliases = emptyUniqSet } + return (concat defss, []) + +-- Note [Llvm Forward References] +-- +-- The issue here is that LLVM insists on being strongly typed at +-- every corner, so the first time we mention something, we have to +-- settle what type we assign to it. That makes things awkward, as Cmm +-- will often reference things before their definition, and we have no +-- idea what (LLVM) type it is going to be before that point. +-- +-- Our work-around is to define "aliases" of a standard type (i8 *) in +-- these kind of situations, which we later tell LLVM to be either +-- references to their actual local definitions (involving a cast) or +-- an external reference. This obviously only works for pointers. -- ---------------------------------------------------------------------------- -- * Misc diff --git a/compiler/llvmGen/LlvmCodeGen/CodeGen.hs b/compiler/llvmGen/LlvmCodeGen/CodeGen.hs index 915981752e..6f898fa56c 100644 --- a/compiler/llvmGen/LlvmCodeGen/CodeGen.hs +++ b/compiler/llvmGen/LlvmCodeGen/CodeGen.hs @@ -29,232 +29,216 @@ import Platform import OrdList import UniqSupply import Unique -import Util -import Data.List ( partition ) +import Data.List ( nub ) +import Data.Maybe ( catMaybes ) type LlvmStatements = OrdList LlvmStatement -- ----------------------------------------------------------------------------- -- | Top-level of the LLVM proc Code generator -- -genLlvmProc :: LlvmEnv -> RawCmmDecl -> UniqSM (LlvmEnv, [LlvmCmmDecl]) -genLlvmProc env (CmmProc infos lbl live graph) = do +genLlvmProc :: RawCmmDecl -> LlvmM [LlvmCmmDecl] +genLlvmProc (CmmProc infos lbl live graph) = do let blocks = toBlockListEntryFirstFalseFallthrough graph - (env', lmblocks, lmdata) <- basicBlocksCodeGen env live blocks ([], []) + (lmblocks, lmdata) <- basicBlocksCodeGen live blocks let info = mapLookup (g_entry graph) infos proc = CmmProc info lbl live (ListGraph lmblocks) - return (env', proc:lmdata) + return (proc:lmdata) -genLlvmProc _ _ = panic "genLlvmProc: case that shouldn't reach here!" +genLlvmProc _ = panic "genLlvmProc: case that shouldn't reach here!" -- ----------------------------------------------------------------------------- -- * Block code generation -- --- | Generate code for a list of blocks that make up a complete procedure. -basicBlocksCodeGen :: LlvmEnv - -> LiveGlobalRegs - -> [CmmBlock] - -> ( [LlvmBasicBlock] , [LlvmCmmDecl] ) - -> UniqSM (LlvmEnv, [LlvmBasicBlock] , [LlvmCmmDecl] ) -basicBlocksCodeGen env live [] (blocks0, tops0) - = return (env, fblocks, tops) - where - dflags = getDflags env - blocks = reverse blocks0 - tops = reverse tops0 - (blocks', allocs) = mapAndUnzip dominateAllocs blocks - allocs' = concat allocs - (BasicBlock id fstmts : rblks) = blocks' - fblocks = (BasicBlock id $ funPrologue dflags live ++ allocs' ++ fstmts):rblks - -basicBlocksCodeGen env live (block:blocks) (lblocks, ltops) - = do (env', lb, lt) <- basicBlockCodeGen env block - basicBlocksCodeGen env' live blocks (lb : lblocks, reverse lt ++ ltops) +-- | Generate code for a list of blocks that make up a complete +-- procedure. The first block in the list is exepected to be the entry +-- point and will get the prologue. +basicBlocksCodeGen :: LiveGlobalRegs -> [CmmBlock] + -> LlvmM ([LlvmBasicBlock], [LlvmCmmDecl]) +basicBlocksCodeGen _ [] = panic "no entry block!" +basicBlocksCodeGen live (entryBlock:cmmBlocks) + = do (prologue, prologueTops) <- funPrologue live (entryBlock:cmmBlocks) + -- Generate code + (BasicBlock bid entry, entryTops) <- basicBlockCodeGen entryBlock + (blocks, topss) <- fmap unzip $ mapM basicBlockCodeGen cmmBlocks --- | Allocations need to be extracted so they can be moved to the entry --- of a function to make sure they dominate all possible paths in the CFG. -dominateAllocs :: LlvmBasicBlock -> (LlvmBasicBlock, [LlvmStatement]) -dominateAllocs (BasicBlock id stmts) - = let (allocs, stmts') = partition isAlloc stmts - isAlloc (Assignment _ (Alloca _ _)) = True - isAlloc _other = False - in (BasicBlock id stmts', allocs) + -- Compose + let entryBlock = BasicBlock bid (fromOL prologue ++ entry) + return (entryBlock : blocks, prologueTops ++ entryTops ++ concat topss) -- | Generate code for one block -basicBlockCodeGen :: LlvmEnv - -> CmmBlock - -> UniqSM ( LlvmEnv, LlvmBasicBlock, [LlvmCmmDecl] ) -basicBlockCodeGen env block +basicBlockCodeGen :: CmmBlock -> LlvmM ( LlvmBasicBlock, [LlvmCmmDecl] ) +basicBlockCodeGen block = do let (CmmEntry id, nodes, tail) = blockSplit block - let stmts = blockToList nodes - (env', mid_instrs, top) <- stmtsToInstrs env stmts (nilOL, []) - (env'', tail_instrs, top') <- stmtToInstrs env' tail + (mid_instrs, top) <- stmtsToInstrs $ blockToList nodes + (tail_instrs, top') <- stmtToInstrs tail let instrs = fromOL (mid_instrs `appOL` tail_instrs) - return (env'', BasicBlock id instrs, top' ++ top) + return (BasicBlock id instrs, top' ++ top) -- ----------------------------------------------------------------------------- -- * CmmNode code generation -- -- A statement conversion return data. --- * LlvmEnv: The new environment -- * LlvmStatements: The compiled LLVM statements. -- * LlvmCmmDecl: Any global data needed. -type StmtData = (LlvmEnv, LlvmStatements, [LlvmCmmDecl]) +type StmtData = (LlvmStatements, [LlvmCmmDecl]) -- | Convert a list of CmmNode's to LlvmStatement's -stmtsToInstrs :: LlvmEnv -> [CmmNode e x] -> (LlvmStatements, [LlvmCmmDecl]) - -> UniqSM StmtData -stmtsToInstrs env [] (llvm, top) - = return (env, llvm, top) +stmtsToInstrs :: [CmmNode e x] -> LlvmM StmtData +stmtsToInstrs stmts + = do (instrss, topss) <- fmap unzip $ mapM stmtToInstrs stmts + return (concatOL instrss, concat topss) -stmtsToInstrs env (stmt : stmts) (llvm, top) - = do (env', instrs, tops) <- stmtToInstrs env stmt - stmtsToInstrs env' stmts (llvm `appOL` instrs, top ++ tops) +-- | Convert a CmmStmt to a list of LlvmStatement's +stmtToInstrs :: CmmNode e x -> LlvmM StmtData +stmtToInstrs stmt = case stmt of --- | Convert a CmmNode to a list of LlvmStatement's -stmtToInstrs :: LlvmEnv -> CmmNode e x - -> UniqSM StmtData -stmtToInstrs env stmt = case stmt of + CmmComment _ -> return (nilOL, []) -- nuke comments - CmmComment _ -> return (env, nilOL, []) -- nuke comments + CmmAssign reg src -> genAssign reg src + CmmStore addr src -> genStore addr src - CmmAssign reg src -> genAssign env reg src - CmmStore addr src -> genStore env addr src - - CmmBranch id -> genBranch env id - CmmCondBranch arg true false -> genCondBranch env arg true false - CmmSwitch arg ids -> genSwitch env arg ids + CmmBranch id -> genBranch id + CmmCondBranch arg true false + -> genCondBranch arg true false + CmmSwitch arg ids -> genSwitch arg ids -- Foreign Call - CmmUnsafeForeignCall target res args -> genCall env target res args + CmmUnsafeForeignCall target res args + -> genCall target res args -- Tail call CmmCall { cml_target = arg, - cml_args_regs = live } -> genJump env arg live + cml_args_regs = live } -> genJump arg live _ -> panic "Llvm.CodeGen.stmtToInstrs" +-- | Wrapper function to declare an instrinct function by function type +getInstrinct2 :: LMString -> LlvmType -> LlvmM ExprData +getInstrinct2 fname fty@(LMFunction funSig) = do + + let fv = LMGlobalVar fname fty (funcLinkage funSig) Nothing Nothing Constant + + fn <- funLookup fname + tops <- case fn of + Just _ -> + return [] + Nothing -> do + funInsert fname fty + return [CmmData Data [([],[fty])]] + + return (fv, nilOL, tops) + +getInstrinct2 _ _ = error "getInstrinct2: Non-function type!" + +-- | Declares an instrinct function by return and parameter types +getInstrinct :: LMString -> LlvmType -> [LlvmType] -> LlvmM ExprData +getInstrinct fname retTy parTys = + let funSig = LlvmFunctionDecl fname ExternallyVisible CC_Ccc retTy + FixedArgs (tysToParams parTys) Nothing + fty = LMFunction funSig + in getInstrinct2 fname fty + -- | Memory barrier instruction for LLVM >= 3.0 -barrier :: LlvmEnv -> UniqSM StmtData -barrier env = do +barrier :: LlvmM StmtData +barrier = do let s = Fence False SyncSeqCst - return (env, unitOL s, []) + return (unitOL s, []) -- | Memory barrier instruction for LLVM < 3.0 -oldBarrier :: LlvmEnv -> UniqSM StmtData -oldBarrier env = do - let dflags = getDflags env - let fname = fsLit "llvm.memory.barrier" - let funSig = LlvmFunctionDecl fname ExternallyVisible CC_Ccc LMVoid - FixedArgs (tysToParams [i1, i1, i1, i1, i1]) (llvmFunAlign dflags) - let fty = LMFunction funSig - - let fv = LMGlobalVar fname fty (funcLinkage funSig) Nothing Nothing False - let tops = case funLookup fname env of - Just _ -> [] - Nothing -> [CmmData Data [([],[fty])]] +oldBarrier :: LlvmM StmtData +oldBarrier = do + + (fv, _, tops) <- getInstrinct (fsLit "llvm.memory.barrier") LMVoid [i1, i1, i1, i1, i1] let args = [lmTrue, lmTrue, lmTrue, lmTrue, lmTrue] let s1 = Expr $ Call StdCall fv args llvmStdFunAttrs - let env' = funInsert fname fty env - return (env', unitOL s1, tops) + return (unitOL s1, tops) where lmTrue :: LlvmVar lmTrue = mkIntLit i1 (-1) -- | Foreign Calls -genCall :: LlvmEnv -> ForeignTarget -> [CmmFormal] -> [CmmActual] - -> UniqSM StmtData +genCall :: ForeignTarget -> [CmmFormal] -> [CmmActual] + -> LlvmM StmtData -- Write barrier needs to be handled specially as it is implemented as an LLVM -- intrinsic function. -genCall env (PrimTarget MO_WriteBarrier) _ _ - | platformArch (getLlvmPlatform env) `elem` [ArchX86, ArchX86_64, ArchSPARC] - = return (env, nilOL, []) - | getLlvmVer env > 29 = barrier env - | otherwise = oldBarrier env - -genCall env (PrimTarget MO_Touch) _ _ - = return (env, nilOL, []) - -genCall env (PrimTarget (MO_UF_Conv w)) [dst] [e] = do - let (env1, dstV, stmts1, top1) = getCmmReg env (CmmLocal dst) - ty = cmmToLlvmType $ localRegType dst +genCall (PrimTarget MO_WriteBarrier) _ _ = do + platform <- getLlvmPlatform + ver <- getLlvmVer + case () of + _ | platformArch platform `elem` [ArchX86, ArchX86_64, ArchSPARC] + -> return (nilOL, []) + | ver > 29 -> barrier + | otherwise -> oldBarrier + +genCall (PrimTarget MO_Touch) _ _ + = return (nilOL, []) + +genCall (PrimTarget (MO_UF_Conv w)) [dst] [e] = do + dstV <- getCmmReg (CmmLocal dst) + let ty = cmmToLlvmType $ localRegType dst width = widthToLlvmFloat w castV <- mkLocalVar ty - (env2, ve, stmts2, top2) <- exprToVar env1 e + (ve, stmts, top) <- exprToVar e let stmt3 = Assignment castV $ Cast LM_Uitofp ve width stmt4 = Store castV dstV - stmts = stmts1 `appOL` stmts2 `snocOL` stmt3 `snocOL` stmt4 - return (env2, stmts, top1 ++ top2) + return (stmts `snocOL` stmt3 `snocOL` stmt4, top) -genCall _ (PrimTarget (MO_UF_Conv _)) [_] args = +genCall (PrimTarget (MO_UF_Conv _)) [_] args = panic $ "genCall: Too many arguments to MO_UF_Conv. " ++ "Can only handle 1, given" ++ show (length args) ++ "." -- Handle prefetching data -genCall env t@(PrimTarget MO_Prefetch_Data) [] args = do - let dflags = getDflags env - argTy = [i8Ptr, i32, i32, i32] +genCall t@(PrimTarget MO_Prefetch_Data) [] args = do + ver <- getLlvmVer + let argTy | ver <= 29 = [i8Ptr, i32, i32] + | otherwise = [i8Ptr, i32, i32, i32] funTy = \name -> LMFunction $ LlvmFunctionDecl name ExternallyVisible CC_Ccc LMVoid FixedArgs (tysToParams argTy) Nothing let (_, arg_hints) = foreignTargetHints t let args_hints' = zip args arg_hints - (env1, argVars, stmts1, top1) <- arg_vars env args_hints' ([], nilOL, []) - (env2, fptr, stmts2, top2) <- getFunPtr env1 funTy t - (argVars', stmts3) <- castVars dflags $ zip argVars argTy - - let arguments = argVars' ++ [mkIntLit i32 0, mkIntLit i32 3, mkIntLit i32 1] - call = Expr $ Call StdCall fptr arguments [] + (argVars, stmts1, top1) <- arg_vars args_hints' ([], nilOL, []) + (fptr, stmts2, top2) <- getFunPtr funTy t + (argVars', stmts3) <- castVars $ zip argVars argTy + + trash <- getTrashStmts + let argSuffix | ver <= 29 = [mkIntLit i32 0, mkIntLit i32 3] + | otherwise = [mkIntLit i32 0, mkIntLit i32 3, mkIntLit i32 1] + call = Expr $ Call StdCall fptr (argVars' ++ argSuffix) [] stmts = stmts1 `appOL` stmts2 `appOL` stmts3 - `appOL` trashStmts (getDflags env) `snocOL` call - return (env2, stmts, top1 ++ top2) - --- Handle popcnt function specifically since GHC only really has i32 and i64 --- types and things like Word8 are backed by an i32 and just present a logical --- i8 range. So we must handle conversions from i32 to i8 explicitly as LLVM --- is strict about types. -genCall env t@(PrimTarget (MO_PopCnt w)) [dst] args = do - let dflags = getDflags env - width = widthToLlvmInt w - dstTy = cmmToLlvmType $ localRegType dst - funTy = \n -> LMFunction $ LlvmFunctionDecl n ExternallyVisible - CC_Ccc width FixedArgs (tysToParams [width]) Nothing - (env1, dstV, stmts1, top1) = getCmmReg env (CmmLocal dst) + `appOL` trash `snocOL` call + return (stmts, top1 ++ top2) - let (_, arg_hints) = foreignTargetHints t - let args_hints = zip args arg_hints - (env2, argsV, stmts2, top2) <- arg_vars env1 args_hints ([], nilOL, []) - (env3, fptr, stmts3, top3) <- getFunPtr env2 funTy t - (argsV', stmts4) <- castVars dflags $ zip argsV [width] - (retV, s1) <- doExpr width $ Call StdCall fptr argsV' [] - ([retV'], stmts5) <- castVars dflags [(retV,dstTy)] - let s2 = Store retV' dstV - - let stmts = stmts1 `appOL` stmts2 `appOL` stmts3 `appOL` stmts4 `snocOL` - s1 `appOL` stmts5 `snocOL` s2 - return (env3, stmts, top1 ++ top2 ++ top3) +-- Handle PopCnt and BSwap that need to only convert arg and return types +genCall t@(PrimTarget (MO_PopCnt w)) dsts args = + genCallSimpleCast w t dsts args +genCall t@(PrimTarget (MO_BSwap w)) dsts args = + genCallSimpleCast w t dsts args -- Handle memcpy function specifically since llvm's intrinsic version takes -- some extra parameters. -genCall env t@(PrimTarget op) [] args' +genCall t@(PrimTarget op) [] args' | op == MO_Memcpy || op == MO_Memset || op == MO_Memmove = do - let dflags = getDflags env - (args, alignVal) = splitAlignVal args' - (isVolTy, isVolVal) = if getLlvmVer env >= 28 - then ([i1], [mkIntLit i1 0]) else ([], []) + ver <- getLlvmVer + dflags <- getDynFlags + let (args, alignVal) = splitAlignVal args' + (isVolTy, isVolVal) + | ver >= 28 = ([i1], [mkIntLit i1 0]) + | otherwise = ([], []) argTy | op == MO_Memset = [i8Ptr, i8, llvmWord dflags, i32] ++ isVolTy | otherwise = [i8Ptr, i8Ptr, llvmWord dflags, i32] ++ isVolTy funTy = \name -> LMFunction $ LlvmFunctionDecl name ExternallyVisible @@ -262,16 +246,16 @@ genCall env t@(PrimTarget op) [] args' let (_, arg_hints) = foreignTargetHints t let args_hints = zip args arg_hints - (env1, argVars, stmts1, top1) <- arg_vars env args_hints ([], nilOL, []) - (env2, fptr, stmts2, top2) <- getFunPtr env1 funTy t - (argVars', stmts3) <- castVars dflags $ zip argVars argTy + (argVars, stmts1, top1) <- arg_vars args_hints ([], nilOL, []) + (fptr, stmts2, top2) <- getFunPtr funTy t + (argVars', stmts3) <- castVars $ zip argVars argTy + stmts4 <- getTrashStmts let arguments = argVars' ++ (alignVal:isVolVal) call = Expr $ Call StdCall fptr arguments [] stmts = stmts1 `appOL` stmts2 `appOL` stmts3 - `appOL` trashStmts (getDflags env) `snocOL` call - return (env2, stmts, top1 ++ top2) - + `appOL` stmts4 `snocOL` call + return (stmts, top1 ++ top2) where splitAlignVal xs = (init xs, extractLit $ last xs) @@ -284,9 +268,9 @@ genCall env t@(PrimTarget op) [] args' mkIntLit i32 0 -- Handle all other foreign calls and prim ops. -genCall env target res args = do +genCall target res args = do - let dflags = getDflags env + dflags <- getDynFlags -- parameter types let arg_type (_, AddrHint) = i8Ptr @@ -301,10 +285,11 @@ genCall env target res args = do ++ " 0 or 1, given " ++ show (length t) ++ "." -- extract Cmm call convention, and translate to LLVM call convention + platform <- getLlvmPlatform let lmconv = case target of ForeignTarget _ (ForeignConvention conv _ _ _) -> case conv of - StdCallConv -> case platformArch (getLlvmPlatform env) of + StdCallConv -> case platformArch platform of ArchX86 -> CC_X86_Stdcc ArchX86_64 -> CC_X86_Stdcc _ -> CC_Ccc @@ -341,22 +326,22 @@ genCall env target res args = do lmconv retTy FixedArgs argTy (llvmFunAlign dflags) - - (env1, argVars, stmts1, top1) <- arg_vars env args_hints ([], nilOL, []) - (env2, fptr, stmts2, top2) <- getFunPtr env1 funTy target + (argVars, stmts1, top1) <- arg_vars args_hints ([], nilOL, []) + (fptr, stmts2, top2) <- getFunPtr funTy target let retStmt | ccTy == TailCall = unitOL $ Return Nothing | never_returns = unitOL $ Unreachable | otherwise = nilOL - let stmts = stmts1 `appOL` stmts2 `appOL` trashStmts (getDflags env) + stmts3 <- getTrashStmts + let stmts = stmts1 `appOL` stmts2 `appOL` stmts3 -- make the actual call case retTy of LMVoid -> do let s1 = Expr $ Call ccTy fptr argVars fnAttrs let allStmts = stmts `snocOL` s1 `appOL` retStmt - return (env2, allStmts, top1 ++ top2) + return (allStmts, top1 ++ top2) _ -> do (v1, s1) <- doExpr retTy $ Call ccTy fptr argVars fnAttrs @@ -365,13 +350,13 @@ genCall env target res args = do ret_reg t = panic $ "genCall: Bad number of registers! Can only handle" ++ " 1, given " ++ show (length t) ++ "." let creg = ret_reg res - let (env3, vreg, stmts3, top3) = getCmmReg env2 (CmmLocal creg) - let allStmts = stmts `snocOL` s1 `appOL` stmts3 + vreg <- getCmmReg (CmmLocal creg) + let allStmts = stmts `snocOL` s1 if retTy == pLower (getVarType vreg) then do let s2 = Store v1 vreg - return (env3, allStmts `snocOL` s2 `appOL` retStmt, - top1 ++ top2 ++ top3) + return (allStmts `snocOL` s2 `appOL` retStmt, + top1 ++ top2) else do let ty = pLower $ getVarType vreg let op = case ty of @@ -383,102 +368,110 @@ genCall env target res args = do (v2, s2) <- doExpr ty $ Cast op v1 ty let s3 = Store v2 vreg - return (env3, allStmts `snocOL` s2 `snocOL` s3 - `appOL` retStmt, top1 ++ top2 ++ top3) + return (allStmts `snocOL` s2 `snocOL` s3 + `appOL` retStmt, top1 ++ top2) + +-- Handle simple function call that only need simple type casting, of the form: +-- truncate arg >>= \a -> call(a) >>= zext +-- +-- since GHC only really has i32 and i64 types and things like Word8 are backed +-- by an i32 and just present a logical i8 range. So we must handle conversions +-- from i32 to i8 explicitly as LLVM is strict about types. +genCallSimpleCast :: Width -> ForeignTarget -> [CmmFormal] -> [CmmActual] + -> LlvmM StmtData +genCallSimpleCast w t@(PrimTarget op) [dst] args = do + let width = widthToLlvmInt w + dstTy = cmmToLlvmType $ localRegType dst + + fname <- cmmPrimOpFunctions op + (fptr, _, top3) <- getInstrinct fname width [width] + + dstV <- getCmmReg (CmmLocal dst) + + let (_, arg_hints) = foreignTargetHints t + let args_hints = zip args arg_hints + (argsV, stmts2, top2) <- arg_vars args_hints ([], nilOL, []) + (argsV', stmts4) <- castVars $ zip argsV [width] + (retV, s1) <- doExpr width $ Call StdCall fptr argsV' [] + ([retV'], stmts5) <- castVars [(retV,dstTy)] + let s2 = Store retV' dstV + let stmts = stmts2 `appOL` stmts4 `snocOL` + s1 `appOL` stmts5 `snocOL` s2 + return (stmts, top2 ++ top3) +genCallSimpleCast _ _ dsts _ = + panic ("genCallSimpleCast: " ++ show (length dsts) ++ " dsts") -- | Create a function pointer from a target. -getFunPtr :: LlvmEnv -> (LMString -> LlvmType) -> ForeignTarget - -> UniqSM ExprData -getFunPtr env funTy targ = case targ of - ForeignTarget (CmmLit (CmmLabel lbl)) _ -> litCase $ strCLabel_llvm env lbl +getFunPtr :: (LMString -> LlvmType) -> ForeignTarget + -> LlvmM ExprData +getFunPtr funTy targ = case targ of + ForeignTarget (CmmLit (CmmLabel lbl)) _ -> do + name <- strCLabel_llvm lbl + getHsFunc' name (funTy name) ForeignTarget expr _ -> do - (env', v1, stmts, top) <- exprToVar env expr + (v1, stmts, top) <- exprToVar expr + dflags <- getDynFlags let fty = funTy $ fsLit "dynamic" cast = case getVarType v1 of ty | isPointer ty -> LM_Bitcast ty | isInt ty -> LM_Inttoptr ty -> panic $ "genCall: Expr is of bad type for function" - ++ " call! (" ++ show (ty) ++ ")" + ++ " call! (" ++ showSDoc dflags (ppr ty) ++ ")" (v2,s1) <- doExpr (pLift fty) $ Cast cast v1 (pLift fty) - return (env', v2, stmts `snocOL` s1, top) - - PrimTarget mop -> litCase $ cmmPrimOpFunctions env mop - - where - litCase name = do - case funLookup name env of - Just ty'@(LMFunction sig) -> do - -- Function in module in right form - let fun = LMGlobalVar name ty' (funcLinkage sig) - Nothing Nothing False - return (env, fun, nilOL, []) - - Just ty' -> do - -- label in module but not function pointer, convert - let fty@(LMFunction sig) = funTy name - fun = LMGlobalVar name (pLift ty') (funcLinkage sig) - Nothing Nothing False - (v1, s1) <- doExpr (pLift fty) - $ Cast LM_Bitcast fun (pLift fty) - return (env, v1, unitOL s1, []) - - Nothing -> do - -- label not in module, create external reference - let fty@(LMFunction sig) = funTy name - fun = LMGlobalVar name fty (funcLinkage sig) - Nothing Nothing False - top = [CmmData Data [([],[fty])]] - env' = funInsert name fty env - return (env', fun, nilOL, top) + return (v2, stmts `snocOL` s1, top) + PrimTarget mop -> do + name <- cmmPrimOpFunctions mop + let fty = funTy name + getInstrinct2 name fty -- | Conversion of call arguments. -arg_vars :: LlvmEnv - -> [(CmmActual, ForeignHint)] +arg_vars :: [(CmmActual, ForeignHint)] -> ([LlvmVar], LlvmStatements, [LlvmCmmDecl]) - -> UniqSM (LlvmEnv, [LlvmVar], LlvmStatements, [LlvmCmmDecl]) + -> LlvmM ([LlvmVar], LlvmStatements, [LlvmCmmDecl]) -arg_vars env [] (vars, stmts, tops) - = return (env, vars, stmts, tops) +arg_vars [] (vars, stmts, tops) + = return (vars, stmts, tops) -arg_vars env ((e, AddrHint):rest) (vars, stmts, tops) - = do (env', v1, stmts', top') <- exprToVar env e +arg_vars ((e, AddrHint):rest) (vars, stmts, tops) + = do (v1, stmts', top') <- exprToVar e + dflags <- getDynFlags let op = case getVarType v1 of ty | isPointer ty -> LM_Bitcast ty | isInt ty -> LM_Inttoptr a -> panic $ "genCall: Can't cast llvmType to i8*! (" - ++ show a ++ ")" + ++ showSDoc dflags (ppr a) ++ ")" (v2, s1) <- doExpr i8Ptr $ Cast op v1 i8Ptr - arg_vars env' rest (vars ++ [v2], stmts `appOL` stmts' `snocOL` s1, + arg_vars rest (vars ++ [v2], stmts `appOL` stmts' `snocOL` s1, tops ++ top') -arg_vars env ((e, _):rest) (vars, stmts, tops) - = do (env', v1, stmts', top') <- exprToVar env e - arg_vars env' rest (vars ++ [v1], stmts `appOL` stmts', tops ++ top') +arg_vars ((e, _):rest) (vars, stmts, tops) + = do (v1, stmts', top') <- exprToVar e + arg_vars rest (vars ++ [v1], stmts `appOL` stmts', tops ++ top') -- | Cast a collection of LLVM variables to specific types. -castVars :: DynFlags -> [(LlvmVar, LlvmType)] - -> UniqSM ([LlvmVar], LlvmStatements) -castVars dflags vars = do - done <- mapM (uncurry (castVar dflags)) vars +castVars :: [(LlvmVar, LlvmType)] + -> LlvmM ([LlvmVar], LlvmStatements) +castVars vars = do + done <- mapM (uncurry castVar) vars let (vars', stmts) = unzip done return (vars', toOL stmts) -- | Cast an LLVM variable to a specific type, panicing if it can't be done. -castVar :: DynFlags -> LlvmVar -> LlvmType -> UniqSM (LlvmVar, LlvmStatement) -castVar dflags v t - | getVarType v == t +castVar :: LlvmVar -> LlvmType -> LlvmM (LlvmVar, LlvmStatement) +castVar v t | getVarType v == t = return (v, Nop) | otherwise - = let op = case (getVarType v, t) of + = do dflags <- getDynFlags + let op = case (getVarType v, t) of (LMInt n, LMInt m) -> if n < m then LM_Sext else LM_Trunc (vt, _) | isFloat vt && isFloat t @@ -492,14 +485,24 @@ castVar dflags v t (vt, _) | isVector vt && isVector t -> LM_Bitcast (vt, _) -> panic $ "castVars: Can't cast this type (" - ++ show vt ++ ") to (" ++ show t ++ ")" - in doExpr t $ Cast op v t + ++ showSDoc dflags (ppr vt) ++ ") to (" ++ showSDoc dflags (ppr t) ++ ")" + doExpr t $ Cast op v t -- | Decide what C function to use to implement a CallishMachOp -cmmPrimOpFunctions :: LlvmEnv -> CallishMachOp -> LMString -cmmPrimOpFunctions env mop - = case mop of +cmmPrimOpFunctions :: CallishMachOp -> LlvmM LMString +cmmPrimOpFunctions mop = do + + ver <- getLlvmVer + dflags <- getDynFlags + let intrinTy1 = (if ver >= 28 + then "p0i8.p0i8." else "") ++ showSDoc dflags (ppr $ llvmWord dflags) + intrinTy2 = (if ver >= 28 + then "p0i8." else "") ++ showSDoc dflags (ppr $ llvmWord dflags) + unsupported = panic ("cmmPrimOpFunctions: " ++ show mop + ++ " not supported here") + + return $ case mop of MO_F32_Exp -> fsLit "expf" MO_F32_Log -> fsLit "logf" MO_F32_Sqrt -> fsLit "llvm.sqrt.f32" @@ -538,7 +541,8 @@ cmmPrimOpFunctions env mop MO_Memmove -> fsLit $ "llvm.memmove." ++ intrinTy1 MO_Memset -> fsLit $ "llvm.memset." ++ intrinTy2 - (MO_PopCnt w) -> fsLit $ "llvm.ctpop." ++ show (widthToLlvmInt w) + (MO_PopCnt w) -> fsLit $ "llvm.ctpop." ++ showSDoc dflags (ppr $ widthToLlvmInt w) + (MO_BSwap w) -> fsLit $ "llvm.bswap." ++ showSDoc dflags (ppr $ widthToLlvmInt w) MO_Prefetch_Data -> fsLit "llvm.prefetch" @@ -551,44 +555,36 @@ cmmPrimOpFunctions env mop MO_Touch -> unsupported MO_UF_Conv _ -> unsupported - where - dflags = getDflags env - intrinTy1 = (if getLlvmVer env >= 28 - then "p0i8.p0i8." else "") ++ show (llvmWord dflags) - intrinTy2 = (if getLlvmVer env >= 28 - then "p0i8." else "") ++ show (llvmWord dflags) - unsupported = panic ("cmmPrimOpFunctions: " ++ show mop - ++ " not supported here") - -- | Tail function calls -genJump :: LlvmEnv -> CmmExpr -> [GlobalReg] -> UniqSM StmtData +genJump :: CmmExpr -> [GlobalReg] -> LlvmM StmtData -- Call to known function -genJump env (CmmLit (CmmLabel lbl)) live = do - (env', vf, stmts, top) <- getHsFunc env live lbl - (stgRegs, stgStmts) <- funEpilogue env live +genJump (CmmLit (CmmLabel lbl)) live = do + (vf, stmts, top) <- getHsFunc live lbl + (stgRegs, stgStmts) <- funEpilogue live let s1 = Expr $ Call TailCall vf stgRegs llvmStdFunAttrs let s2 = Return Nothing - return (env', stmts `appOL` stgStmts `snocOL` s1 `snocOL` s2, top) + return (stmts `appOL` stgStmts `snocOL` s1 `snocOL` s2, top) -- Call to unknown function / address -genJump env expr live = do - let fty = llvmFunTy (getDflags env) live - (env', vf, stmts, top) <- exprToVar env expr +genJump expr live = do + fty <- llvmFunTy live + (vf, stmts, top) <- exprToVar expr + dflags <- getDynFlags let cast = case getVarType vf of ty | isPointer ty -> LM_Bitcast ty | isInt ty -> LM_Inttoptr ty -> panic $ "genJump: Expr is of bad type for function call! (" - ++ show (ty) ++ ")" + ++ showSDoc dflags (ppr ty) ++ ")" (v1, s1) <- doExpr (pLift fty) $ Cast cast vf (pLift fty) - (stgRegs, stgStmts) <- funEpilogue env live + (stgRegs, stgStmts) <- funEpilogue live let s2 = Expr $ Call TailCall v1 stgRegs llvmStdFunAttrs let s3 = Return Nothing - return (env', stmts `snocOL` s1 `appOL` stgStmts `snocOL` s2 `snocOL` s3, + return (stmts `snocOL` s1 `appOL` stgStmts `snocOL` s2 `snocOL` s3, top) @@ -596,81 +592,81 @@ genJump env expr live = do -- -- We use stack allocated variables for CmmReg. The optimiser will replace -- these with registers when possible. -genAssign :: LlvmEnv -> CmmReg -> CmmExpr -> UniqSM StmtData -genAssign env reg val = do - let dflags = getDflags env - (env1, vreg, stmts1, top1) = getCmmReg env reg - (env2, vval, stmts2, top2) <- exprToVar env1 val - let stmts = stmts1 `appOL` stmts2 +genAssign :: CmmReg -> CmmExpr -> LlvmM StmtData +genAssign reg val = do + vreg <- getCmmReg reg + (vval, stmts2, top2) <- exprToVar val + let stmts = stmts2 let ty = (pLower . getVarType) vreg + dflags <- getDynFlags case ty of -- Some registers are pointer types, so need to cast value to pointer LMPointer _ | getVarType vval == llvmWord dflags -> do (v, s1) <- doExpr ty $ Cast LM_Inttoptr vval ty let s2 = Store v vreg - return (env2, stmts `snocOL` s1 `snocOL` s2, top1 ++ top2) + return (stmts `snocOL` s1 `snocOL` s2, top2) LMVector _ _ -> do (v, s1) <- doExpr ty $ Cast LM_Bitcast vval ty let s2 = Store v vreg - return (env2, stmts `snocOL` s1 `snocOL` s2, top1 ++ top2) + return (stmts `snocOL` s1 `snocOL` s2, top2) _ -> do let s1 = Store vval vreg - return (env2, stmts `snocOL` s1, top1 ++ top2) + return (stmts `snocOL` s1, top2) -- | CmmStore operation -genStore :: LlvmEnv -> CmmExpr -> CmmExpr -> UniqSM StmtData +genStore :: CmmExpr -> CmmExpr -> LlvmM StmtData -- First we try to detect a few common cases and produce better code for -- these then the default case. We are mostly trying to detect Cmm code -- like I32[Sp + n] and use 'getelementptr' operations instead of the -- generic case that uses casts and pointer arithmetic -genStore env addr@(CmmReg (CmmGlobal r)) val - = genStore_fast env addr r 0 val +genStore addr@(CmmReg (CmmGlobal r)) val + = genStore_fast addr r 0 val -genStore env addr@(CmmRegOff (CmmGlobal r) n) val - = genStore_fast env addr r n val +genStore addr@(CmmRegOff (CmmGlobal r) n) val + = genStore_fast addr r n val -genStore env addr@(CmmMachOp (MO_Add _) [ +genStore addr@(CmmMachOp (MO_Add _) [ (CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))]) val - = genStore_fast env addr r (fromInteger n) val + = genStore_fast addr r (fromInteger n) val -genStore env addr@(CmmMachOp (MO_Sub _) [ +genStore addr@(CmmMachOp (MO_Sub _) [ (CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))]) val - = genStore_fast env addr r (negate $ fromInteger n) val + = genStore_fast addr r (negate $ fromInteger n) val -- generic case -genStore env addr val = genStore_slow env addr val [other] +genStore addr val + = do other <- getTBAAMeta otherN + genStore_slow addr val other -- | CmmStore operation -- This is a special case for storing to a global register pointer -- offset such as I32[Sp+8]. -genStore_fast :: LlvmEnv -> CmmExpr -> GlobalReg -> Int -> CmmExpr - -> UniqSM StmtData -genStore_fast env addr r n val - = let dflags = getDflags env - gr = lmGlobalRegVar (getDflags env) r - meta = [getTBAA r] - grt = (pLower . getVarType) gr - (ix,rem) = n `divMod` ((llvmWidthInBits dflags . pLower) grt `div` 8) - in case isPointer grt && rem == 0 of +genStore_fast :: CmmExpr -> GlobalReg -> Int -> CmmExpr + -> LlvmM StmtData +genStore_fast addr r n val + = do dflags <- getDynFlags + (gv, grt, s1) <- getCmmRegVal (CmmGlobal r) + meta <- getTBAARegMeta r + let (ix,rem) = n `divMod` ((llvmWidthInBits dflags . pLower) grt `div` 8) + case isPointer grt && rem == 0 of True -> do - (env', vval, stmts, top) <- exprToVar env val - (gv, s1) <- doExpr grt $ Load gr + (vval, stmts, top) <- exprToVar val (ptr, s2) <- doExpr grt $ GetElemPtr True gv [toI32 ix] -- We might need a different pointer type, so check case pLower grt == getVarType vval of -- were fine True -> do let s3 = MetaStmt meta $ Store vval ptr - return (env', stmts `snocOL` s1 `snocOL` s2 + return (stmts `appOL` s1 `snocOL` s2 `snocOL` s3, top) -- cast to pointer type needed @@ -678,68 +674,69 @@ genStore_fast env addr r n val let ty = (pLift . getVarType) vval (ptr', s3) <- doExpr ty $ Cast LM_Bitcast ptr ty let s4 = MetaStmt meta $ Store vval ptr' - return (env', stmts `snocOL` s1 `snocOL` s2 + return (stmts `appOL` s1 `snocOL` s2 `snocOL` s3 `snocOL` s4, top) -- If its a bit type then we use the slow method since -- we can't avoid casting anyway. - False -> genStore_slow env addr val meta + False -> genStore_slow addr val meta -- | CmmStore operation -- Generic case. Uses casts and pointer arithmetic if needed. -genStore_slow :: LlvmEnv -> CmmExpr -> CmmExpr -> [MetaData] -> UniqSM StmtData -genStore_slow env addr val meta = do - (env1, vaddr, stmts1, top1) <- exprToVar env addr - (env2, vval, stmts2, top2) <- exprToVar env1 val +genStore_slow :: CmmExpr -> CmmExpr -> [MetaAnnot] -> LlvmM StmtData +genStore_slow addr val meta = do + (vaddr, stmts1, top1) <- exprToVar addr + (vval, stmts2, top2) <- exprToVar val let stmts = stmts1 `appOL` stmts2 + dflags <- getDynFlags case getVarType vaddr of -- sometimes we need to cast an int to a pointer before storing LMPointer ty@(LMPointer _) | getVarType vval == llvmWord dflags -> do (v, s1) <- doExpr ty $ Cast LM_Inttoptr vval ty let s2 = MetaStmt meta $ Store v vaddr - return (env2, stmts `snocOL` s1 `snocOL` s2, top1 ++ top2) + return (stmts `snocOL` s1 `snocOL` s2, top1 ++ top2) LMPointer _ -> do let s1 = MetaStmt meta $ Store vval vaddr - return (env2, stmts `snocOL` s1, top1 ++ top2) + return (stmts `snocOL` s1, top1 ++ top2) i@(LMInt _) | i == llvmWord dflags -> do let vty = pLift $ getVarType vval (vptr, s1) <- doExpr vty $ Cast LM_Inttoptr vaddr vty let s2 = MetaStmt meta $ Store vval vptr - return (env2, stmts `snocOL` s1 `snocOL` s2, top1 ++ top2) + return (stmts `snocOL` s1 `snocOL` s2, top1 ++ top2) other -> pprPanic "genStore: ptr not right type!" (PprCmm.pprExpr addr <+> text ( "Size of Ptr: " ++ show (llvmPtrBits dflags) ++ ", Size of var: " ++ show (llvmWidthInBits dflags other) ++ - ", Var: " ++ show vaddr)) - where dflags = getDflags env + ", Var: " ++ showSDoc dflags (ppr vaddr))) -- | Unconditional branch -genBranch :: LlvmEnv -> BlockId -> UniqSM StmtData -genBranch env id = +genBranch :: BlockId -> LlvmM StmtData +genBranch id = let label = blockIdToLlvm id - in return (env, unitOL $ Branch label, []) + in return (unitOL $ Branch label, []) -- | Conditional branch -genCondBranch :: LlvmEnv -> CmmExpr -> BlockId -> BlockId -> UniqSM StmtData -genCondBranch env cond idT idF = do +genCondBranch :: CmmExpr -> BlockId -> BlockId -> LlvmM StmtData +genCondBranch cond idT idF = do let labelT = blockIdToLlvm idT let labelF = blockIdToLlvm idF -- See Note [Literals and branch conditions]. - (env', vc, stmts, top) <- exprToVarOpt env i1Option cond + (vc, stmts, top) <- exprToVarOpt i1Option cond if getVarType vc == i1 then do let s1 = BranchIf vc labelT labelF - return $ (env', stmts `snocOL` s1, top) - else - panic $ "genCondBranch: Cond expr not bool! (" ++ show vc ++ ")" + return (stmts `snocOL` s1, top) + else do + dflags <- getDynFlags + panic $ "genCondBranch: Cond expr not bool! (" ++ showSDoc dflags (ppr vc) ++ ")" {- Note [Literals and branch conditions] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -797,9 +794,9 @@ For a real example of this, see ./rts/StgStdThunks.cmm -- -- N.B. We remove Nothing's from the list of branches, as they are 'undefined'. -- However, they may be defined one day, so we better document this behaviour. -genSwitch :: LlvmEnv -> CmmExpr -> [Maybe BlockId] -> UniqSM StmtData -genSwitch env cond maybe_ids = do - (env', vc, stmts, top) <- exprToVar env cond +genSwitch :: CmmExpr -> [Maybe BlockId] -> LlvmM StmtData +genSwitch cond maybe_ids = do + (vc, stmts, top) <- exprToVar cond let ty = getVarType vc let pairs = [ (ix, id) | (ix,Just id) <- zip [0..] maybe_ids ] @@ -808,7 +805,7 @@ genSwitch env cond maybe_ids = do let (_, defLbl) = head labels let s1 = Switch vc defLbl labels - return $ (env', stmts `snocOL` s1, top) + return $ (stmts `snocOL` s1, top) -- ----------------------------------------------------------------------------- @@ -816,11 +813,10 @@ genSwitch env cond maybe_ids = do -- -- | An expression conversion return data: --- * LlvmEnv: The new enviornment -- * LlvmVar: The var holding the result of the expression -- * LlvmStatements: Any statements needed to evaluate the expression -- * LlvmCmmDecl: Any global data needed for this expression -type ExprData = (LlvmEnv, LlvmVar, LlvmStatements, [LlvmCmmDecl]) +type ExprData = (LlvmVar, LlvmStatements, [LlvmCmmDecl]) -- | Values which can be passed to 'exprToVar' to configure its -- behaviour in certain circumstances. @@ -840,47 +836,47 @@ wordOption = EOption False -- | Convert a CmmExpr to a list of LlvmStatements with the result of the -- expression being stored in the returned LlvmVar. -exprToVar :: LlvmEnv -> CmmExpr -> UniqSM ExprData -exprToVar env = exprToVarOpt env wordOption +exprToVar :: CmmExpr -> LlvmM ExprData +exprToVar = exprToVarOpt wordOption -exprToVarOpt :: LlvmEnv -> EOption -> CmmExpr -> UniqSM ExprData -exprToVarOpt env opt e = case e of +exprToVarOpt :: EOption -> CmmExpr -> LlvmM ExprData +exprToVarOpt opt e = case e of CmmLit lit - -> genLit opt env lit + -> genLit opt lit CmmLoad e' ty - -> genLoad env e' ty + -> genLoad e' ty -- Cmmreg in expression is the value, so must load. If you want actual -- reg pointer, call getCmmReg directly. CmmReg r -> do - let (env', vreg, stmts, top) = getCmmReg env r - (v1, s1) <- doExpr (pLower $ getVarType vreg) $ Load vreg - case (isPointer . getVarType) v1 of + (v1, ty, s1) <- getCmmRegVal r + case isPointer ty of True -> do -- Cmm wants the value, so pointer types must be cast to ints + dflags <- getDynFlags (v2, s2) <- doExpr (llvmWord dflags) $ Cast LM_Ptrtoint v1 (llvmWord dflags) - return (env', v2, stmts `snocOL` s1 `snocOL` s2, top) + return (v2, s1 `snocOL` s2, []) - False -> return (env', v1, stmts `snocOL` s1, top) + False -> return (v1, s1, []) CmmMachOp op exprs - -> genMachOp env opt op exprs + -> genMachOp opt op exprs CmmRegOff r i - -> exprToVar env $ expandCmmReg dflags (r, i) + -> do dflags <- getDynFlags + exprToVar $ expandCmmReg dflags (r, i) CmmStackSlot _ _ -> panic "exprToVar: CmmStackSlot not supported!" - where dflags = getDflags env -- | Handle CmmMachOp expressions -genMachOp :: LlvmEnv -> EOption -> MachOp -> [CmmExpr] -> UniqSM ExprData +genMachOp :: EOption -> MachOp -> [CmmExpr] -> LlvmM ExprData -- Unary Machop -genMachOp env _ op [x] = case op of +genMachOp _ op [x] = case op of MO_Not w -> let all1 = mkIntLit (widthToLlvmInt w) (-1) @@ -980,29 +976,28 @@ genMachOp env _ op [x] = case op of MO_VF_Quot _ _ -> panicOp where - dflags = getDflags env - negate ty v2 negOp = do - (env', vx, stmts, top) <- exprToVar env x + (vx, stmts, top) <- exprToVar x (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx - return (env', v1, stmts `snocOL` s1, top) + return (v1, stmts `snocOL` s1, top) negateVec ty v2 negOp = do - (env', vx, stmts1, top) <- exprToVar env x - ([vx'], stmts2) <- castVars dflags [(vx, ty)] + (vx, stmts1, top) <- exprToVar x + ([vx'], stmts2) <- castVars [(vx, ty)] (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx' - return (env', v1, stmts1 `appOL` stmts2 `snocOL` s1, top) + return (v1, stmts1 `appOL` stmts2 `snocOL` s1, top) fiConv ty convOp = do - (env', vx, stmts, top) <- exprToVar env x + (vx, stmts, top) <- exprToVar x (v1, s1) <- doExpr ty $ Cast convOp vx ty - return (env', v1, stmts `snocOL` s1, top) + return (v1, stmts `snocOL` s1, top) sameConv from ty reduce expand = do - x'@(env', vx, stmts, top) <- exprToVar env x + x'@(vx, stmts, top) <- exprToVar x let sameConv' op = do (v1, s1) <- doExpr ty $ Cast op vx ty - return (env', v1, stmts `snocOL` s1, top) + return (v1, stmts `snocOL` s1, top) + dflags <- getDynFlags let toWidth = llvmWidthInBits dflags ty -- LLVM doesn't like trying to convert to same width, so -- need to check for that as we do get Cmm code doing it. @@ -1015,88 +1010,82 @@ genMachOp env _ op [x] = case op of ++ "with one argument! (" ++ show op ++ ")" -- Handle GlobalRegs pointers -genMachOp env opt o@(MO_Add _) e@[(CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))] - = genMachOp_fast env opt o r (fromInteger n) e +genMachOp opt o@(MO_Add _) e@[(CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))] + = genMachOp_fast opt o r (fromInteger n) e -genMachOp env opt o@(MO_Sub _) e@[(CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))] - = genMachOp_fast env opt o r (negate . fromInteger $ n) e +genMachOp opt o@(MO_Sub _) e@[(CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))] + = genMachOp_fast opt o r (negate . fromInteger $ n) e -- Generic case -genMachOp env opt op e = genMachOp_slow env opt op e +genMachOp opt op e = genMachOp_slow opt op e -- | Handle CmmMachOp expressions -- This is a specialised method that handles Global register manipulations like -- 'Sp - 16', using the getelementptr instruction. -genMachOp_fast :: LlvmEnv -> EOption -> MachOp -> GlobalReg -> Int -> [CmmExpr] - -> UniqSM ExprData -genMachOp_fast env opt op r n e - = let dflags = getDflags env - gr = lmGlobalRegVar dflags r - grt = (pLower . getVarType) gr - (ix,rem) = n `divMod` ((llvmWidthInBits dflags . pLower) grt `div` 8) - in case isPointer grt && rem == 0 of +genMachOp_fast :: EOption -> MachOp -> GlobalReg -> Int -> [CmmExpr] + -> LlvmM ExprData +genMachOp_fast opt op r n e + = do (gv, grt, s1) <- getCmmRegVal (CmmGlobal r) + dflags <- getDynFlags + let (ix,rem) = n `divMod` ((llvmWidthInBits dflags . pLower) grt `div` 8) + case isPointer grt && rem == 0 of True -> do - (gv, s1) <- doExpr grt $ Load gr (ptr, s2) <- doExpr grt $ GetElemPtr True gv [toI32 ix] (var, s3) <- doExpr (llvmWord dflags) $ Cast LM_Ptrtoint ptr (llvmWord dflags) - return (env, var, unitOL s1 `snocOL` s2 `snocOL` s3, []) + return (var, s1 `snocOL` s2 `snocOL` s3, []) - False -> genMachOp_slow env opt op e + False -> genMachOp_slow opt op e -- | Handle CmmMachOp expressions -- This handles all the cases not handle by the specialised genMachOp_fast. -genMachOp_slow :: LlvmEnv -> EOption -> MachOp -> [CmmExpr] -> UniqSM ExprData +genMachOp_slow :: EOption -> MachOp -> [CmmExpr] -> LlvmM ExprData -- Element extraction -genMachOp_slow env _ (MO_V_Extract l w) [val, idx] = do - (env1, vval, stmts1, top1) <- exprToVar env val - (env2, vidx, stmts2, top2) <- exprToVar env1 idx - ([vval'], stmts3) <- castVars dflags [(vval, LMVector l ty)] - (v1, s1) <- doExpr ty $ Extract vval' vidx - return (env2, v1, stmts1 `appOL` stmts2 `appOL` stmts3 `snocOL` s1, top1 ++ top2) +genMachOp_slow _ (MO_V_Extract l w) [val, idx] = do + (vval, stmts1, top1) <- exprToVar val + (vidx, stmts2, top2) <- exprToVar idx + ([vval'], stmts3) <- castVars [(vval, LMVector l ty)] + (v1, s1) <- doExpr ty $ Extract vval' vidx + return (v1, stmts1 `appOL` stmts2 `appOL` stmts3 `snocOL` s1, top1 ++ top2) where - dflags = getDflags env ty = widthToLlvmInt w -genMachOp_slow env _ (MO_VF_Extract l w) [val, idx] = do - (env1, vval, stmts1, top1) <- exprToVar env val - (env2, vidx, stmts2, top2) <- exprToVar env1 idx - ([vval'], stmts3) <- castVars dflags [(vval, LMVector l ty)] - (v1, s1) <- doExpr ty $ Extract vval' vidx - return (env2, v1, stmts1 `appOL` stmts2 `appOL` stmts3 `snocOL` s1, top1 ++ top2) +genMachOp_slow _ (MO_VF_Extract l w) [val, idx] = do + (vval, stmts1, top1) <- exprToVar val + (vidx, stmts2, top2) <- exprToVar idx + ([vval'], stmts3) <- castVars [(vval, LMVector l ty)] + (v1, s1) <- doExpr ty $ Extract vval' vidx + return (v1, stmts1 `appOL` stmts2 `appOL` stmts3 `snocOL` s1, top1 ++ top2) where - dflags = getDflags env ty = widthToLlvmFloat w -- Element insertion -genMachOp_slow env _ (MO_V_Insert l w) [val, elt, idx] = do - (env1, vval, stmts1, top1) <- exprToVar env val - (env2, velt, stmts2, top2) <- exprToVar env1 elt - (env3, vidx, stmts3, top3) <- exprToVar env2 idx - ([vval'], stmts4) <- castVars dflags [(vval, ty)] - (v1, s1) <- doExpr ty $ Insert vval' velt vidx - return (env3, v1, stmts1 `appOL` stmts2 `appOL` stmts3 `appOL` stmts4 `snocOL` s1, +genMachOp_slow _ (MO_V_Insert l w) [val, elt, idx] = do + (vval, stmts1, top1) <- exprToVar val + (velt, stmts2, top2) <- exprToVar elt + (vidx, stmts3, top3) <- exprToVar idx + ([vval'], stmts4) <- castVars [(vval, ty)] + (v1, s1) <- doExpr ty $ Insert vval' velt vidx + return (v1, stmts1 `appOL` stmts2 `appOL` stmts3 `appOL` stmts4 `snocOL` s1, top1 ++ top2 ++ top3) where - dflags = getDflags env ty = LMVector l (widthToLlvmInt w) -genMachOp_slow env _ (MO_VF_Insert l w) [val, elt, idx] = do - (env1, vval, stmts1, top1) <- exprToVar env val - (env2, velt, stmts2, top2) <- exprToVar env1 elt - (env3, vidx, stmts3, top3) <- exprToVar env2 idx - ([vval'], stmts4) <- castVars dflags [(vval, ty)] - (v1, s1) <- doExpr ty $ Insert vval' velt vidx - return (env3, v1, stmts1 `appOL` stmts2 `appOL` stmts3 `appOL` stmts4 `snocOL` s1, +genMachOp_slow _ (MO_VF_Insert l w) [val, elt, idx] = do + (vval, stmts1, top1) <- exprToVar val + (velt, stmts2, top2) <- exprToVar elt + (vidx, stmts3, top3) <- exprToVar idx + ([vval'], stmts4) <- castVars [(vval, ty)] + (v1, s1) <- doExpr ty $ Insert vval' velt vidx + return (v1, stmts1 `appOL` stmts2 `appOL` stmts3 `appOL` stmts4 `snocOL` s1, top1 ++ top2 ++ top3) where - dflags = getDflags env ty = LMVector l (widthToLlvmFloat w) -- Binary MachOp -genMachOp_slow env opt op [x, y] = case op of +genMachOp_slow opt op [x, y] = case op of MO_Eq _ -> genBinComp opt LM_CMP_Eq MO_Ne _ -> genBinComp opt LM_CMP_Ne @@ -1177,21 +1166,19 @@ genMachOp_slow env opt op [x, y] = case op of MO_VF_Neg {} -> panicOp where - dflags = getDflags env - binLlvmOp ty binOp = do - (env1, vx, stmts1, top1) <- exprToVar env x - (env2, vy, stmts2, top2) <- exprToVar env1 y + (vx, stmts1, top1) <- exprToVar x + (vy, stmts2, top2) <- exprToVar y if getVarType vx == getVarType vy then do (v1, s1) <- doExpr (ty vx) $ binOp vx vy - return (env2, v1, stmts1 `appOL` stmts2 `snocOL` s1, + return (v1, stmts1 `appOL` stmts2 `snocOL` s1, top1 ++ top2) else do -- Error. Continue anyway so we can debug the generated ll file. - let dflags = getDflags env - style = mkCodeStyle CStyle + dflags <- getDynFlags + let style = mkCodeStyle CStyle toString doc = renderWithStyle dflags doc style cmmToStr = (lines . toString . PprCmm.pprExpr) let dx = Comment $ map fsLit $ cmmToStr x @@ -1199,31 +1186,32 @@ genMachOp_slow env opt op [x, y] = case op of (v1, s1) <- doExpr (ty vx) $ binOp vx vy let allStmts = stmts1 `appOL` stmts2 `snocOL` dx `snocOL` dy `snocOL` s1 - return (env2, v1, allStmts, top1 ++ top2) + return (v1, allStmts, top1 ++ top2) binCastLlvmOp ty binOp = do - (env1, vx, stmts1, top1) <- exprToVar env x - (env2, vy, stmts2, top2) <- exprToVar env1 y - ([vx', vy'], stmts3) <- castVars dflags [(vx, ty), (vy, ty)] + (vx, stmts1, top1) <- exprToVar x + (vy, stmts2, top2) <- exprToVar y + ([vx', vy'], stmts3) <- castVars [(vx, ty), (vy, ty)] (v1, s1) <- doExpr ty $ binOp vx' vy' - return (env2, v1, stmts1 `appOL` stmts2 `appOL` stmts3 `snocOL` s1, + return (v1, stmts1 `appOL` stmts2 `appOL` stmts3 `snocOL` s1, top1 ++ top2) -- | Need to use EOption here as Cmm expects word size results from -- comparisons while LLVM return i1. Need to extend to llvmWord type -- if expected. See Note [Literals and branch conditions]. genBinComp opt cmp = do - ed@(env', v1, stmts, top) <- binLlvmOp (\_ -> i1) (Compare cmp) + ed@(v1, stmts, top) <- binLlvmOp (\_ -> i1) (Compare cmp) + dflags <- getDynFlags if getVarType v1 == i1 then case i1Expected opt of True -> return ed False -> do let w_ = llvmWord dflags (v2, s1) <- doExpr w_ $ Cast LM_Zext v1 w_ - return (env', v2, stmts `snocOL` s1, top) + return (v2, stmts `snocOL` s1, top) else panic $ "genBinComp: Compare returned type other then i1! " - ++ (show $ getVarType v1) + ++ (showSDoc dflags $ ppr $ getVarType v1) genBinMach op = binLlvmOp getVarType (LlvmOp op) @@ -1233,11 +1221,12 @@ genMachOp_slow env opt op [x, y] = case op of -- CmmExpr's. This is the LLVM assembly equivalent of the NCG -- implementation. Its much longer due to type information/safety. -- This should actually compile to only about 3 asm instructions. - isSMulOK :: Width -> CmmExpr -> CmmExpr -> UniqSM ExprData + isSMulOK :: Width -> CmmExpr -> CmmExpr -> LlvmM ExprData isSMulOK _ x y = do - (env1, vx, stmts1, top1) <- exprToVar env x - (env2, vy, stmts2, top2) <- exprToVar env1 y + (vx, stmts1, top1) <- exprToVar x + (vy, stmts2, top2) <- exprToVar y + dflags <- getDynFlags let word = getVarType vx let word2 = LMInt $ 2 * (llvmWidthInBits dflags $ getVarType vx) let shift = llvmWidthInBits dflags word @@ -1256,127 +1245,151 @@ genMachOp_slow env opt op [x, y] = case op of (dst, s8) <- doExpr word $ LlvmOp LM_MO_Sub rlow2 rhigh2 let stmts = (unitOL s1) `snocOL` s2 `snocOL` s3 `snocOL` s4 `snocOL` s5 `snocOL` s6 `snocOL` s7 `snocOL` s8 - return (env2, dst, stmts1 `appOL` stmts2 `appOL` stmts, + return (dst, stmts1 `appOL` stmts2 `appOL` stmts, top1 ++ top2) else - panic $ "isSMulOK: Not bit type! (" ++ show word ++ ")" + panic $ "isSMulOK: Not bit type! (" ++ showSDoc dflags (ppr word) ++ ")" panicOp = panic $ "LLVM.CodeGen.genMachOp_slow: unary op encourntered" ++ "with two arguments! (" ++ show op ++ ")" -- More then two expression, invalid! -genMachOp_slow _ _ _ _ = panic "genMachOp: More then 2 expressions in MachOp!" +genMachOp_slow _ _ _ = panic "genMachOp: More then 2 expressions in MachOp!" -- | Handle CmmLoad expression. -genLoad :: LlvmEnv -> CmmExpr -> CmmType -> UniqSM ExprData +genLoad :: CmmExpr -> CmmType -> LlvmM ExprData -- First we try to detect a few common cases and produce better code for -- these then the default case. We are mostly trying to detect Cmm code -- like I32[Sp + n] and use 'getelementptr' operations instead of the -- generic case that uses casts and pointer arithmetic -genLoad env e@(CmmReg (CmmGlobal r)) ty - = genLoad_fast env e r 0 ty +genLoad e@(CmmReg (CmmGlobal r)) ty + = genLoad_fast e r 0 ty -genLoad env e@(CmmRegOff (CmmGlobal r) n) ty - = genLoad_fast env e r n ty +genLoad e@(CmmRegOff (CmmGlobal r) n) ty + = genLoad_fast e r n ty -genLoad env e@(CmmMachOp (MO_Add _) [ +genLoad e@(CmmMachOp (MO_Add _) [ (CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))]) ty - = genLoad_fast env e r (fromInteger n) ty + = genLoad_fast e r (fromInteger n) ty -genLoad env e@(CmmMachOp (MO_Sub _) [ +genLoad e@(CmmMachOp (MO_Sub _) [ (CmmReg (CmmGlobal r)), (CmmLit (CmmInt n _))]) ty - = genLoad_fast env e r (negate $ fromInteger n) ty + = genLoad_fast e r (negate $ fromInteger n) ty -- generic case -genLoad env e ty = genLoad_slow env e ty [other] +genLoad e ty + = do other <- getTBAAMeta otherN + genLoad_slow e ty other -- | Handle CmmLoad expression. -- This is a special case for loading from a global register pointer -- offset such as I32[Sp+8]. -genLoad_fast :: LlvmEnv -> CmmExpr -> GlobalReg -> Int -> CmmType - -> UniqSM ExprData -genLoad_fast env e r n ty = - let dflags = getDflags env - gr = lmGlobalRegVar dflags r - meta = [getTBAA r] - grt = (pLower . getVarType) gr - ty' = cmmToLlvmType ty +genLoad_fast :: CmmExpr -> GlobalReg -> Int -> CmmType + -> LlvmM ExprData +genLoad_fast e r n ty = do + dflags <- getDynFlags + (gv, grt, s1) <- getCmmRegVal (CmmGlobal r) + meta <- getTBAARegMeta r + let ty' = cmmToLlvmType ty (ix,rem) = n `divMod` ((llvmWidthInBits dflags . pLower) grt `div` 8) - in case isPointer grt && rem == 0 of + case isPointer grt && rem == 0 of True -> do - (gv, s1) <- doExpr grt $ Load gr (ptr, s2) <- doExpr grt $ GetElemPtr True gv [toI32 ix] -- We might need a different pointer type, so check case grt == ty' of -- were fine True -> do - (var, s3) <- doExpr ty' (MetaExpr meta $ Load ptr) - return (env, var, unitOL s1 `snocOL` s2 `snocOL` s3, + (var, s3) <- doExpr ty' (MExpr meta $ Load ptr) + return (var, s1 `snocOL` s2 `snocOL` s3, []) -- cast to pointer type needed False -> do let pty = pLift ty' (ptr', s3) <- doExpr pty $ Cast LM_Bitcast ptr pty - (var, s4) <- doExpr ty' (MetaExpr meta $ Load ptr') - return (env, var, unitOL s1 `snocOL` s2 `snocOL` s3 + (var, s4) <- doExpr ty' (MExpr meta $ Load ptr') + return (var, s1 `snocOL` s2 `snocOL` s3 `snocOL` s4, []) -- If its a bit type then we use the slow method since -- we can't avoid casting anyway. - False -> genLoad_slow env e ty meta + False -> genLoad_slow e ty meta -- | Handle Cmm load expression. -- Generic case. Uses casts and pointer arithmetic if needed. -genLoad_slow :: LlvmEnv -> CmmExpr -> CmmType -> [MetaData] -> UniqSM ExprData -genLoad_slow env e ty meta = do - (env', iptr, stmts, tops) <- exprToVar env e +genLoad_slow :: CmmExpr -> CmmType -> [MetaAnnot] -> LlvmM ExprData +genLoad_slow e ty meta = do + (iptr, stmts, tops) <- exprToVar e + dflags <- getDynFlags case getVarType iptr of LMPointer _ -> do (dvar, load) <- doExpr (cmmToLlvmType ty) - (MetaExpr meta $ Load iptr) - return (env', dvar, stmts `snocOL` load, tops) + (MExpr meta $ Load iptr) + return (dvar, stmts `snocOL` load, tops) i@(LMInt _) | i == llvmWord dflags -> do let pty = LMPointer $ cmmToLlvmType ty (ptr, cast) <- doExpr pty $ Cast LM_Inttoptr iptr pty (dvar, load) <- doExpr (cmmToLlvmType ty) - (MetaExpr meta $ Load ptr) - return (env', dvar, stmts `snocOL` cast `snocOL` load, tops) + (MExpr meta $ Load ptr) + return (dvar, stmts `snocOL` cast `snocOL` load, tops) - other -> pprPanic "exprToVar: CmmLoad expression is not right type!" + other -> do dflags <- getDynFlags + pprPanic "exprToVar: CmmLoad expression is not right type!" (PprCmm.pprExpr e <+> text ( "Size of Ptr: " ++ show (llvmPtrBits dflags) ++ ", Size of var: " ++ show (llvmWidthInBits dflags other) ++ - ", Var: " ++ show iptr)) - where dflags = getDflags env - --- | Handle CmmReg expression --- --- We allocate CmmReg on the stack. This avoids having to map a CmmReg to an --- equivalent SSA form and avoids having to deal with Phi node insertion. --- This is also the approach recommended by LLVM developers. -getCmmReg :: LlvmEnv -> CmmReg -> ExprData -getCmmReg env r@(CmmLocal (LocalReg un _)) - = let exists = varLookup un env - (newv, stmts) = allocReg r - nenv = varInsert un (pLower $ getVarType newv) env - in case exists of - Just ety -> (env, (LMLocalVar un $ pLift ety), nilOL, []) - Nothing -> (nenv, newv, stmts, []) - -getCmmReg env (CmmGlobal g) = (env, lmGlobalRegVar (getDflags env) g, nilOL, []) - - --- | Allocate a CmmReg on the stack + ", Var: " ++ showSDoc dflags (ppr iptr))) + + +-- | Handle CmmReg expression. This will return a pointer to the stack +-- location of the register. Throws an error if it isn't allocated on +-- the stack. +getCmmReg :: CmmReg -> LlvmM LlvmVar +getCmmReg (CmmLocal (LocalReg un _)) + = do exists <- varLookup un + dflags <- getDynFlags + case exists of + Just ety -> return (LMLocalVar un $ pLift ety) + Nothing -> fail $ "getCmmReg: Cmm register " ++ showSDoc dflags (ppr un) ++ " was not allocated!" + -- This should never happen, as every local variable should + -- have been assigned a value at some point, triggering + -- "funPrologue" to allocate it on the stack. + +getCmmReg (CmmGlobal g) + = do onStack <- checkStackReg g + dflags <- getDynFlags + if onStack + then return (lmGlobalRegVar dflags g) + else fail $ "getCmmReg: Cmm register " ++ showSDoc dflags (ppr g) ++ " not stack-allocated!" + +-- | Return the value of a given register, as well as its type. Might +-- need to be load from stack. +getCmmRegVal :: CmmReg -> LlvmM (LlvmVar, LlvmType, LlvmStatements) +getCmmRegVal reg = + case reg of + CmmGlobal g -> do + onStack <- checkStackReg g + dflags <- getDynFlags + if onStack then loadFromStack else do + let r = lmGlobalRegArg dflags g + return (r, getVarType r, nilOL) + _ -> loadFromStack + where loadFromStack = do + ptr <- getCmmReg reg + let ty = pLower $ getVarType ptr + (v, s) <- doExpr ty (Load ptr) + return (v, ty, unitOL s) + +-- | Allocate a local CmmReg on the stack allocReg :: CmmReg -> (LlvmVar, LlvmStatements) allocReg (CmmLocal (LocalReg un ty)) = let ty' = cmmToLlvmType ty @@ -1389,8 +1402,8 @@ allocReg _ = panic $ "allocReg: Global reg encountered! Global registers should" -- | Generate code for a literal -genLit :: EOption -> LlvmEnv -> CmmLit -> UniqSM ExprData -genLit opt env (CmmInt i w) +genLit :: EOption -> CmmLit -> LlvmM ExprData +genLit opt (CmmInt i w) -- See Note [Literals and branch conditions]. = let width | i1Expected opt = i1 | otherwise = LMInt (widthInBits w) @@ -1398,56 +1411,41 @@ genLit opt env (CmmInt i w) -- , fsLit $ "Width : " ++ show w -- , fsLit $ "Width' : " ++ show (widthInBits w) -- ] - in return (env, mkIntLit width i, nilOL, []) + in return (mkIntLit width i, nilOL, []) -genLit _ env (CmmFloat r w) - = return (env, LMLitVar $ LMFloatLit (fromRational r) (widthToLlvmFloat w), +genLit _ (CmmFloat r w) + = return (LMLitVar $ LMFloatLit (fromRational r) (widthToLlvmFloat w), nilOL, []) - -genLit opt env (CmmVec ls) + +genLit opt (CmmVec ls) = do llvmLits <- mapM toLlvmLit ls - return (env, LMLitVar $ LMVectorLit llvmLits, nilOL, []) + return (LMLitVar $ LMVectorLit llvmLits, nilOL, []) where - toLlvmLit :: CmmLit -> UniqSM LlvmLit + toLlvmLit :: CmmLit -> LlvmM LlvmLit toLlvmLit lit = do - (_, llvmLitVar, _, _) <- genLit opt env lit + (llvmLitVar, _, _) <- genLit opt lit case llvmLitVar of LMLitVar llvmLit -> return llvmLit _ -> panic "genLit" -genLit _ env cmm@(CmmLabel l) - = let dflags = getDflags env - label = strCLabel_llvm env l - ty = funLookup label env - lmty = cmmToLlvmType $ cmmLitType dflags cmm - in case ty of - -- Make generic external label definition and then pointer to it - Nothing -> do - let glob@(var, _) = genStringLabelRef dflags label - let ldata = [CmmData Data [([glob], [])]] - let env' = funInsert label (pLower $ getVarType var) env - (v1, s1) <- doExpr lmty $ Cast LM_Ptrtoint var (llvmWord dflags) - return (env', v1, unitOL s1, ldata) - - -- Referenced data exists in this module, retrieve type and make - -- pointer to it. - Just ty' -> do - let var = LMGlobalVar label (LMPointer ty') - ExternallyVisible Nothing Nothing False - (v1, s1) <- doExpr lmty $ Cast LM_Ptrtoint var (llvmWord dflags) - return (env, v1, unitOL s1, []) - -genLit opt env (CmmLabelOff label off) = do - let dflags = getDflags env - (env', vlbl, stmts, stat) <- genLit opt env (CmmLabel label) +genLit _ cmm@(CmmLabel l) + = do var <- getGlobalPtr =<< strCLabel_llvm l + dflags <- getDynFlags + let lmty = cmmToLlvmType $ cmmLitType dflags cmm + (v1, s1) <- doExpr lmty $ Cast LM_Ptrtoint var (llvmWord dflags) + return (v1, unitOL s1, []) + +genLit opt (CmmLabelOff label off) = do + dflags <- getDynFlags + (vlbl, stmts, stat) <- genLit opt (CmmLabel label) let voff = toIWord dflags off (v1, s1) <- doExpr (getVarType vlbl) $ LlvmOp LM_MO_Add vlbl voff - return (env', v1, stmts `snocOL` s1, stat) + return (v1, stmts `snocOL` s1, stat) -genLit opt env (CmmLabelDiffOff l1 l2 off) = do - let dflags = getDflags env - (env1, vl1, stmts1, stat1) <- genLit opt env (CmmLabel l1) - (env2, vl2, stmts2, stat2) <- genLit opt env1 (CmmLabel l2) +genLit opt (CmmLabelDiffOff l1 l2 off) = do + dflags <- getDynFlags + (vl1, stmts1, stat1) <- genLit opt (CmmLabel l1) + (vl2, stmts2, stat2) <- genLit opt (CmmLabel l2) let voff = toIWord dflags off let ty1 = getVarType vl1 let ty2 = getVarType vl2 @@ -1457,16 +1455,16 @@ genLit opt env (CmmLabelDiffOff l1 l2 off) = do then do (v1, s1) <- doExpr (getVarType vl1) $ LlvmOp LM_MO_Sub vl1 vl2 (v2, s2) <- doExpr (getVarType v1 ) $ LlvmOp LM_MO_Add v1 voff - return (env2, v2, stmts1 `appOL` stmts2 `snocOL` s1 `snocOL` s2, + return (v2, stmts1 `appOL` stmts2 `snocOL` s1 `snocOL` s2, stat1 ++ stat2) else panic "genLit: CmmLabelDiffOff encountered with different label ty!" -genLit opt env (CmmBlock b) - = genLit opt env (CmmLabel $ infoTblLbl b) +genLit opt (CmmBlock b) + = genLit opt (CmmLabel $ infoTblLbl b) -genLit _ _ CmmHighStackMark +genLit _ CmmHighStackMark = panic "genStaticLit - CmmHighStackMark unsupported!" @@ -1474,51 +1472,82 @@ genLit _ _ CmmHighStackMark -- * Misc -- --- | Function prologue. Load STG arguments into variables for function. -funPrologue :: DynFlags -> LiveGlobalRegs -> [LlvmStatement] -funPrologue dflags live = concat $ map getReg $ activeStgRegs platform - where platform = targetPlatform dflags - isLive r = r `elem` alwaysLive || r `elem` live - getReg rr = - let reg = lmGlobalRegVar dflags rr - arg = lmGlobalRegArg dflags rr - ty = (pLower . getVarType) reg - trash = LMLitVar $ LMUndefLit ty - alloc = Assignment reg $ Alloca (pLower $ getVarType reg) 1 - in - if isLive rr - then [alloc, Store arg reg] - else [alloc, Store trash reg] - +-- | Find CmmRegs that get assigned and allocate them on the stack +-- +-- Any register that gets written needs to be allcoated on the +-- stack. This avoids having to map a CmmReg to an equivalent SSA form +-- and avoids having to deal with Phi node insertion. This is also +-- the approach recommended by LLVM developers. +-- +-- On the other hand, this is unecessarily verbose if the register in +-- question is never written. Therefore we skip it where we can to +-- save a few lines in the output and hopefully speed compilation up a +-- bit. +funPrologue :: LiveGlobalRegs -> [CmmBlock] -> LlvmM StmtData +funPrologue live cmmBlocks = do + + trash <- getTrashRegs + let getAssignedRegs :: CmmNode O O -> [CmmReg] + getAssignedRegs (CmmAssign reg _) = [reg] + -- Calls will trash all registers. Unfortunately, this needs them to + -- be stack-allocated in the first place. + getAssignedRegs (CmmUnsafeForeignCall _ rs _) = map CmmGlobal trash ++ map CmmLocal rs + getAssignedRegs _ = [] + getRegsBlock (_, body, _) = concatMap getAssignedRegs $ blockToList body + assignedRegs = nub $ concatMap (getRegsBlock . blockSplit) cmmBlocks + isLive r = r `elem` alwaysLive || r `elem` live + + dflags <- getDynFlags + stmtss <- flip mapM assignedRegs $ \reg -> + case reg of + CmmLocal (LocalReg un _) -> do + let (newv, stmts) = allocReg reg + varInsert un (pLower $ getVarType newv) + return stmts + CmmGlobal r -> do + let reg = lmGlobalRegVar dflags r + arg = lmGlobalRegArg dflags r + ty = (pLower . getVarType) reg + trash = LMLitVar $ LMUndefLit ty + rval = if isLive r then arg else trash + alloc = Assignment reg $ Alloca (pLower $ getVarType reg) 1 + markStackReg r + return $ toOL [alloc, Store rval reg] + + return (concatOL stmtss, []) -- | Function epilogue. Load STG variables to use as argument for call. -- STG Liveness optimisation done here. -funEpilogue :: LlvmEnv -> LiveGlobalRegs -> UniqSM ([LlvmVar], LlvmStatements) +funEpilogue :: LiveGlobalRegs -> LlvmM ([LlvmVar], LlvmStatements) +funEpilogue live = do + + -- Have information and liveness optimisation is enabled? + let liveRegs = alwaysLive ++ live + isSSE (FloatReg _) = True + isSSE (DoubleReg _) = True + isSSE (XmmReg _) = True + isSSE _ = False + + -- Set to value or "undef" depending on whether the register is + -- actually live + dflags <- getDynFlags + let loadExpr r = do + (v, _, s) <- getCmmRegVal (CmmGlobal r) + return (Just $ v, s) + loadUndef r = do + let ty = (pLower . getVarType $ lmGlobalRegVar dflags r) + return (Just $ LMLitVar $ LMUndefLit ty, nilOL) + platform <- getDynFlag targetPlatform + loads <- flip mapM (activeStgRegs platform) $ \r -> case () of + _ | r `elem` liveRegs -> loadExpr r + | not (isSSE r) -> loadUndef r + | otherwise -> return (Nothing, nilOL) --- Have information and liveness optimisation is enabled -funEpilogue env live = do - loads <- mapM loadExpr (filter isPassed (activeStgRegs platform)) let (vars, stmts) = unzip loads - return (vars, concatOL stmts) - where - dflags = getDflags env - platform = targetPlatform dflags - isLive r = r `elem` alwaysLive || r `elem` live - isPassed r = not (isSSE r) || isLive r - isSSE (FloatReg _) = True - isSSE (DoubleReg _) = True - isSSE (XmmReg _) = True - isSSE _ = False - loadExpr r | isLive r = do - let reg = lmGlobalRegVar dflags r - (v,s) <- doExpr (pLower $ getVarType reg) $ Load reg - return (v, unitOL s) - loadExpr r = do - let ty = (pLower . getVarType $ lmGlobalRegVar dflags r) - return (LMLitVar $ LMUndefLit ty, unitOL Nop) - - --- | A serries of statements to trash all the STG registers. + return (catMaybes vars, concatOL stmts) + + +-- | A series of statements to trash all the STG registers. -- -- In LLVM we pass the STG registers around everywhere in function calls. -- So this means LLVM considers them live across the entire function, when @@ -1529,59 +1558,47 @@ funEpilogue env live = do -- before the call by assigning the 'undef' value to them. The ones we -- need are restored from the Cmm local var and the ones we don't need -- are fine to be trashed. -trashStmts :: DynFlags -> LlvmStatements -trashStmts dflags = concatOL $ map trashReg $ activeStgRegs platform - where platform = targetPlatform dflags - trashReg r = - let reg = lmGlobalRegVar dflags r - ty = (pLower . getVarType) reg - trash = unitOL $ Store (LMLitVar $ LMUndefLit ty) reg - in case callerSaves (targetPlatform dflags) r of - True -> trash - False -> nilOL - +getTrashStmts :: LlvmM LlvmStatements +getTrashStmts = do + regs <- getTrashRegs + stmts <- flip mapM regs $ \ r -> do + reg <- getCmmReg (CmmGlobal r) + let ty = (pLower . getVarType) reg + return $ Store (LMLitVar $ LMUndefLit ty) reg + return $ toOL stmts + +getTrashRegs :: LlvmM [GlobalReg] +getTrashRegs = do plat <- getLlvmPlatform + return $ filter (callerSaves plat) (activeStgRegs plat) -- | Get a function pointer to the CLabel specified. -- -- This is for Haskell functions, function type is assumed, so doesn't work -- with foreign functions. -getHsFunc :: LlvmEnv -> LiveGlobalRegs -> CLabel -> UniqSM ExprData -getHsFunc env live lbl - = let dflags = getDflags env - fn = strCLabel_llvm env lbl - ty = funLookup fn env - in case ty of - -- Function in module in right form - Just ty'@(LMFunction sig) -> do - let fun = LMGlobalVar fn ty' (funcLinkage sig) Nothing Nothing False - return (env, fun, nilOL, []) - - -- label in module but not function pointer, convert - Just ty' -> do - let fun = LMGlobalVar fn (pLift ty') ExternallyVisible - Nothing Nothing False - (v1, s1) <- doExpr (pLift (llvmFunTy dflags live)) $ - Cast LM_Bitcast fun (pLift (llvmFunTy dflags live)) - return (env, v1, unitOL s1, []) - - -- label not in module, create external reference - Nothing -> do - let ty' = LMFunction $ llvmFunSig env live lbl ExternallyVisible - let fun = LMGlobalVar fn ty' ExternallyVisible Nothing Nothing False - let top = CmmData Data [([],[ty'])] - let env' = funInsert fn ty' env - return (env', fun, nilOL, [top]) - +getHsFunc :: LiveGlobalRegs -> CLabel -> LlvmM ExprData +getHsFunc live lbl + = do fty <- llvmFunTy live + name <- strCLabel_llvm lbl + getHsFunc' name fty + +getHsFunc' :: LMString -> LlvmType -> LlvmM ExprData +getHsFunc' name fty + = do fun <- getGlobalPtr name + if getVarType fun == fty + then return (fun, nilOL, []) + else do (v1, s1) <- doExpr (pLift fty) + $ Cast LM_Bitcast fun (pLift fty) + return (v1, unitOL s1, []) -- | Create a new local var -mkLocalVar :: LlvmType -> UniqSM LlvmVar +mkLocalVar :: LlvmType -> LlvmM LlvmVar mkLocalVar ty = do - un <- getUniqueUs + un <- runUs getUniqueUs return $ LMLocalVar un ty -- | Execute an expression, assigning result to a var -doExpr :: LlvmType -> LlvmExpression -> UniqSM (LlvmVar, LlvmStatement) +doExpr :: LlvmType -> LlvmExpression -> LlvmM (LlvmVar, LlvmStatement) doExpr ty expr = do v <- mkLocalVar ty return (v, Assignment v expr) @@ -1618,3 +1635,13 @@ panic s = Outputable.panic $ "LlvmCodeGen.CodeGen." ++ s pprPanic :: String -> SDoc -> a pprPanic s d = Outputable.pprPanic ("LlvmCodeGen.CodeGen." ++ s) d + +-- | Returns TBAA meta data by unique +getTBAAMeta :: Unique -> LlvmM [MetaAnnot] +getTBAAMeta u = do + mi <- getUniqMeta u + return [MetaAnnot tbaa (MetaNode i) | let Just i = mi] + +-- | Returns TBAA meta data for given register +getTBAARegMeta :: GlobalReg -> LlvmM [MetaAnnot] +getTBAARegMeta = getTBAAMeta . getTBAA diff --git a/compiler/llvmGen/LlvmCodeGen/Data.hs b/compiler/llvmGen/LlvmCodeGen/Data.hs index 83b5453aa9..6212cfc9fb 100644 --- a/compiler/llvmGen/LlvmCodeGen/Data.hs +++ b/compiler/llvmGen/LlvmCodeGen/Data.hs @@ -3,7 +3,7 @@ -- module LlvmCodeGen.Data ( - genLlvmData, resolveLlvmDatas, resolveLlvmData + genLlvmData ) where #include "HsVersions.h" @@ -18,8 +18,6 @@ import Cmm import FastString import qualified Outputable -import Data.List (foldl') - -- ---------------------------------------------------------------------------- -- * Constants -- @@ -32,43 +30,23 @@ structStr = fsLit "_struct" -- * Top level -- --- | Pass a CmmStatic section to an equivalent Llvm code. Can't --- complete this completely though as we need to pass all CmmStatic --- sections before all references can be resolved. This last step is --- done by 'resolveLlvmData'. -genLlvmData :: LlvmEnv -> (Section, CmmStatics) -> LlvmUnresData -genLlvmData env (sec, Statics lbl xs) = - let dflags = getDflags env - static = map genData xs - label = strCLabel_llvm env lbl - - types = map getStatTypes static - getStatTypes (Left x) = cmmToLlvmType $ cmmLitType dflags x - getStatTypes (Right x) = getStatType x +-- | Pass a CmmStatic section to an equivalent Llvm code. +genLlvmData :: (Section, CmmStatics) -> LlvmM LlvmData +genLlvmData (sec, Statics lbl xs) = do + label <- strCLabel_llvm lbl + static <- mapM genData xs + let types = map getStatType static strucTy = LMStruct types alias = LMAlias ((label `appendFS` structStr), strucTy) - in (lbl, sec, alias, static) - -resolveLlvmDatas :: LlvmEnv -> [LlvmUnresData] -> (LlvmEnv, [LlvmData]) -resolveLlvmDatas env ldata - = foldl' res (env, []) ldata - where res (e, xs) ll = - let (e', nd) = resolveLlvmData e ll - in (e', nd:xs) - --- | Fix up CLabel references now that we should have passed all CmmData. -resolveLlvmData :: LlvmEnv -> LlvmUnresData -> (LlvmEnv, LlvmData) -resolveLlvmData env (lbl, sec, alias, unres) = - let (env', static, refs) = resDatas env unres ([], []) struct = Just $ LMStaticStruc static alias - label = strCLabel_llvm env lbl link = if (externallyVisibleCLabel lbl) then ExternallyVisible else Internal - const = isSecConstant sec + const = if isSecConstant sec then Constant else Global glob = LMGlobalVar label alias link Nothing Nothing const - in (env', ((glob,struct):refs, [alias])) + + return ([LMGlobal glob struct], [alias]) -- | Should a data in this section be considered constant isSecConstant :: Section -> Bool @@ -82,80 +60,19 @@ isSecConstant (OtherSection _) = False -- ---------------------------------------------------------------------------- --- ** Resolve Data/CLabel references --- - --- | Resolve data list -resDatas :: LlvmEnv -> [UnresStatic] -> ([LlvmStatic], [LMGlobal]) - -> (LlvmEnv, [LlvmStatic], [LMGlobal]) - -resDatas env [] (stats, glob) - = (env, stats, glob) - -resDatas env (cmm:rest) (stats, globs) - = let (env', nstat, nglob) = resData env cmm - in resDatas env' rest (stats ++ [nstat], globs ++ nglob) - --- | Resolve an individual static label if it needs to be. --- --- We check the 'LlvmEnv' to see if the reference has been defined in this --- module. If it has we can retrieve its type and make a pointer, otherwise --- we introduce a generic external definition for the referenced label and --- then make a pointer. -resData :: LlvmEnv -> UnresStatic -> (LlvmEnv, LlvmStatic, [LMGlobal]) - -resData env (Right stat) = (env, stat, []) - -resData env (Left cmm@(CmmLabel l)) = - let dflags = getDflags env - label = strCLabel_llvm env l - ty = funLookup label env - lmty = cmmToLlvmType $ cmmLitType dflags cmm - in case ty of - -- Make generic external label defenition and then pointer to it - Nothing -> - let glob@(var, _) = genStringLabelRef dflags label - env' = funInsert label (pLower $ getVarType var) env - ptr = LMStaticPointer var - in (env', LMPtoI ptr lmty, [glob]) - -- Referenced data exists in this module, retrieve type and make - -- pointer to it. - Just ty' -> - let var = LMGlobalVar label (LMPointer ty') - ExternallyVisible Nothing Nothing False - ptr = LMStaticPointer var - in (env, LMPtoI ptr lmty, []) - -resData env (Left (CmmLabelOff label off)) = - let dflags = getDflags env - (env', var, glob) = resData env (Left (CmmLabel label)) - offset = LMStaticLit $ LMIntLit (toInteger off) (llvmWord dflags) - in (env', LMAdd var offset, glob) - -resData env (Left (CmmLabelDiffOff l1 l2 off)) = - let dflags = getDflags env - (env1, var1, glob1) = resData env (Left (CmmLabel l1)) - (env2, var2, glob2) = resData env1 (Left (CmmLabel l2)) - var = LMSub var1 var2 - offset = LMStaticLit $ LMIntLit (toInteger off) (llvmWord dflags) - in (env2, LMAdd var offset, glob1 ++ glob2) - -resData _ _ = panic "resData: Non CLabel expr as left type!" - --- ---------------------------------------------------------------------------- -- * Generate static data -- -- | Handle static data -genData :: CmmStatic -> UnresStatic +genData :: CmmStatic -> LlvmM LlvmStatic -genData (CmmString str) = +genData (CmmString str) = do let v = map (\x -> LMStaticLit $ LMIntLit (fromIntegral x) i8) str ve = v ++ [LMStaticLit $ LMIntLit 0 i8] - in Right $ LMStaticArray ve (LMArray (length ve) i8) + return $ LMStaticArray ve (LMArray (length ve) i8) genData (CmmUninitialised bytes) - = Right $ LMUninitType (LMArray bytes i8) + = return $ LMUninitType (LMArray bytes i8) genData (CmmStaticLit lit) = genStaticLit lit @@ -164,27 +81,47 @@ genData (CmmStaticLit lit) -- -- Will either generate the code or leave it unresolved if it is a 'CLabel' -- which isn't yet known. -genStaticLit :: CmmLit -> UnresStatic +genStaticLit :: CmmLit -> LlvmM LlvmStatic genStaticLit (CmmInt i w) - = Right $ LMStaticLit (LMIntLit i (LMInt $ widthInBits w)) + = return $ LMStaticLit (LMIntLit i (LMInt $ widthInBits w)) genStaticLit (CmmFloat r w) - = Right $ LMStaticLit (LMFloatLit (fromRational r) (widthToLlvmFloat w)) + = return $ LMStaticLit (LMFloatLit (fromRational r) (widthToLlvmFloat w)) genStaticLit (CmmVec ls) - = Right $ LMStaticLit (LMVectorLit (map toLlvmLit ls)) + = do sls <- mapM toLlvmLit ls + return $ LMStaticLit (LMVectorLit sls) where - toLlvmLit :: CmmLit -> LlvmLit - toLlvmLit lit = case genStaticLit lit of - Right (LMStaticLit llvmLit) -> llvmLit - _ -> panic "genStaticLit" + toLlvmLit :: CmmLit -> LlvmM LlvmLit + toLlvmLit lit = do + slit <- genStaticLit lit + case slit of + LMStaticLit llvmLit -> return llvmLit + _ -> panic "genStaticLit" -- Leave unresolved, will fix later -genStaticLit c@(CmmLabel _ ) = Left $ c -genStaticLit c@(CmmLabelOff _ _) = Left $ c -genStaticLit c@(CmmLabelDiffOff _ _ _) = Left $ c +genStaticLit cmm@(CmmLabel l) = do + var <- getGlobalPtr =<< strCLabel_llvm l + dflags <- getDynFlags + let ptr = LMStaticPointer var + lmty = cmmToLlvmType $ cmmLitType dflags cmm + return $ LMPtoI ptr lmty + +genStaticLit (CmmLabelOff label off) = do + dflags <- getDynFlags + var <- genStaticLit (CmmLabel label) + let offset = LMStaticLit $ LMIntLit (toInteger off) (llvmWord dflags) + return $ LMAdd var offset + +genStaticLit (CmmLabelDiffOff l1 l2 off) = do + dflags <- getDynFlags + var1 <- genStaticLit (CmmLabel l1) + var2 <- genStaticLit (CmmLabel l2) + let var = LMSub var1 var2 + offset = LMStaticLit $ LMIntLit (toInteger off) (llvmWord dflags) + return $ LMAdd var offset -genStaticLit (CmmBlock b) = Left $ CmmLabel $ infoTblLbl b +genStaticLit (CmmBlock b) = genStaticLit $ CmmLabel $ infoTblLbl b genStaticLit (CmmHighStackMark) = panic "genStaticLit: CmmHighStackMark unsupported!" diff --git a/compiler/llvmGen/LlvmCodeGen/Ppr.hs b/compiler/llvmGen/LlvmCodeGen/Ppr.hs index c699631e9c..1c63d3f67f 100644 --- a/compiler/llvmGen/LlvmCodeGen/Ppr.hs +++ b/compiler/llvmGen/LlvmCodeGen/Ppr.hs @@ -11,7 +11,6 @@ module LlvmCodeGen.Ppr ( import Llvm import LlvmCodeGen.Base import LlvmCodeGen.Data -import LlvmCodeGen.Regs import CLabel import Cmm @@ -28,12 +27,7 @@ import Unique -- | Header code for LLVM modules pprLlvmHeader :: SDoc -pprLlvmHeader = sdocWithDynFlags $ \dflags -> - moduleLayout - $+$ text "" - $+$ ppLlvmFunctionDecls (map snd (ghcInternalFunctions dflags)) - $+$ ppLlvmMetas stgTBAA - $+$ text "" +pprLlvmHeader = moduleLayout -- | LLVM module layout description for the host target @@ -61,6 +55,9 @@ moduleLayout = sdocWithPlatform $ \platform -> Platform { platformArch = ArchARM {}, platformOS = OSAndroid } -> text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32\"" $+$ text "target triple = \"arm-unknown-linux-androideabi\"" + Platform { platformArch = ArchARM {}, platformOS = OSQNXNTO } -> + text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32\"" + $+$ text "target triple = \"arm-unknown-nto-qnx8.0.0eabi\"" Platform { platformArch = ArchARM {}, platformOS = OSiOS } -> text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32\"" $+$ text "target triple = \"arm-apple-darwin10\"" @@ -72,63 +69,61 @@ moduleLayout = sdocWithPlatform $ \platform -> -- | Pretty print LLVM data code pprLlvmData :: LlvmData -> SDoc pprLlvmData (globals, types) = - let tryConst (v, Just s ) = ppLlvmGlobal (v, Just s) - tryConst g@(_, Nothing) = ppLlvmGlobal g - - ppLlvmTys (LMAlias a) = ppLlvmAlias a + let ppLlvmTys (LMAlias a) = ppLlvmAlias a ppLlvmTys (LMFunction f) = ppLlvmFunctionDecl f ppLlvmTys _other = empty types' = vcat $ map ppLlvmTys types - globals' = vcat $ map tryConst globals + globals' = ppLlvmGlobals globals in types' $+$ globals' -- | Pretty print LLVM code -pprLlvmCmmDecl :: LlvmEnv -> Int -> LlvmCmmDecl -> (SDoc, [LlvmVar]) -pprLlvmCmmDecl _ _ (CmmData _ lmdata) - = (vcat $ map pprLlvmData lmdata, []) +pprLlvmCmmDecl :: Int -> LlvmCmmDecl -> LlvmM (SDoc, [LlvmVar]) +pprLlvmCmmDecl _ (CmmData _ lmdata) + = return (vcat $ map pprLlvmData lmdata, []) -pprLlvmCmmDecl env count (CmmProc mb_info entry_lbl live (ListGraph blks)) - = let (idoc, ivar) = case mb_info of - Nothing -> (empty, []) +pprLlvmCmmDecl count (CmmProc mb_info entry_lbl live (ListGraph blks)) + = do (idoc, ivar) <- case mb_info of + Nothing -> return (empty, []) Just (Statics info_lbl dat) - -> pprInfoTable env count info_lbl (Statics entry_lbl dat) - in (idoc $+$ ( - let sec = mkLayoutSection (count + 1) - (lbl',sec') = case mb_info of + -> pprInfoTable count info_lbl (Statics entry_lbl dat) + + let sec = mkLayoutSection (count + 1) + (lbl',sec') = case mb_info of Nothing -> (entry_lbl, Nothing) Just (Statics info_lbl _) -> (info_lbl, sec) - link = if externallyVisibleCLabel lbl' + link = if externallyVisibleCLabel lbl' then ExternallyVisible else Internal - lmblocks = map (\(BasicBlock id stmts) -> + lmblocks = map (\(BasicBlock id stmts) -> LlvmBlock (getUnique id) stmts) blks - fun = mkLlvmFunc env live lbl' link sec' lmblocks - in ppLlvmFunction fun - ), ivar) + + fun <- mkLlvmFunc live lbl' link sec' lmblocks + + return (idoc $+$ ppLlvmFunction fun, ivar) -- | Pretty print CmmStatic -pprInfoTable :: LlvmEnv -> Int -> CLabel -> CmmStatics -> (SDoc, [LlvmVar]) -pprInfoTable env count info_lbl stat - = let dflags = getDflags env - unres = genLlvmData env (Text, stat) - (_, (ldata, ltypes)) = resolveLlvmData env unres - - setSection ((LMGlobalVar _ ty l _ _ c), d) - = let sec = mkLayoutSection count - ilabel = strCLabel_llvm env info_lbl - `appendFS` fsLit iTableSuf - gv = LMGlobalVar ilabel ty l sec (llvmInfAlign dflags) c - v = if l == Internal then [gv] else [] - in ((gv, d), v) - setSection v = (v,[]) - - (ldata', llvmUsed) = setSection (last ldata) - in if length ldata /= 1 +pprInfoTable :: Int -> CLabel -> CmmStatics -> LlvmM (SDoc, [LlvmVar]) +pprInfoTable count info_lbl stat + = do (ldata, ltypes) <- genLlvmData (Text, stat) + + dflags <- getDynFlags + let setSection (LMGlobal (LMGlobalVar _ ty l _ _ c) d) = do + lbl <- strCLabel_llvm info_lbl + let sec = mkLayoutSection count + ilabel = lbl `appendFS` fsLit iTableSuf + gv = LMGlobalVar ilabel ty l sec (llvmInfAlign dflags) c + v = if l == Internal then [gv] else [] + funInsert ilabel ty + return (LMGlobal gv d, v) + setSection v = return (v,[]) + + (ldata', llvmUsed) <- setSection (last ldata) + if length ldata /= 1 then Outputable.panic "LlvmCodeGen.Ppr: invalid info table!" - else (pprLlvmData ([ldata'], ltypes), llvmUsed) + else return (pprLlvmData ([ldata'], ltypes), llvmUsed) -- | We generate labels for info tables by converting them to the same label diff --git a/compiler/llvmGen/LlvmCodeGen/Regs.hs b/compiler/llvmGen/LlvmCodeGen/Regs.hs index 7271c2f3d9..1b87929499 100644 --- a/compiler/llvmGen/LlvmCodeGen/Regs.hs +++ b/compiler/llvmGen/LlvmCodeGen/Regs.hs @@ -4,7 +4,7 @@ module LlvmCodeGen.Regs ( lmGlobalRegArg, lmGlobalRegVar, alwaysLive, - stgTBAA, top, base, stack, heap, rx, other, tbaa, getTBAA + stgTBAA, baseN, stackN, heapN, rxN, otherN, tbaa, getTBAA ) where #include "HsVersions.h" @@ -15,6 +15,7 @@ import CmmExpr import DynFlags import FastString import Outputable ( panic ) +import Unique -- | Get the LlvmVar function variable storing the real register lmGlobalRegVar :: DynFlags -> GlobalReg -> LlvmVar @@ -76,48 +77,38 @@ lmGlobalReg dflags suf reg alwaysLive :: [GlobalReg] alwaysLive = [BaseReg, Sp, Hp, SpLim, HpLim, node] --- | STG Type Based Alias Analysis metadata -stgTBAA :: [LlvmMeta] +-- | STG Type Based Alias Analysis hierarchy +stgTBAA :: [(Unique, LMString, Maybe Unique)] stgTBAA - = [ MetaUnamed topN [MetaStr (fsLit "top")] - , MetaUnamed stackN [MetaStr (fsLit "stack"), MetaNode topN] - , MetaUnamed heapN [MetaStr (fsLit "heap"), MetaNode topN] - , MetaUnamed rxN [MetaStr (fsLit "rx"), MetaNode heapN] - , MetaUnamed baseN [MetaStr (fsLit "base"), MetaNode topN] + = [ (topN, fsLit "top", Nothing) + , (stackN, fsLit "stack", Just topN) + , (heapN, fsLit "heap", Just topN) + , (rxN, fsLit "rx", Just heapN) + , (baseN, fsLit "base", Just topN) -- FIX: Not 100% sure about 'others' place. Might need to be under 'heap'. -- OR I think the big thing is Sp is never aliased, so might want -- to change the hieracy to have Sp on its own branch that is never -- aliased (e.g never use top as a TBAA node). - , MetaUnamed otherN [MetaStr (fsLit "other"), MetaNode topN] + , (otherN, fsLit "other", Just topN) ] -- | Id values -topN, stackN, heapN, rxN, baseN, otherN:: LlvmMetaUnamed -topN = LMMetaUnamed 0 -stackN = LMMetaUnamed 1 -heapN = LMMetaUnamed 2 -rxN = LMMetaUnamed 3 -baseN = LMMetaUnamed 4 -otherN = LMMetaUnamed 5 - --- | The various TBAA types -top, heap, stack, rx, base, other :: MetaData -top = (tbaa, topN) -heap = (tbaa, heapN) -stack = (tbaa, stackN) -rx = (tbaa, rxN) -base = (tbaa, baseN) -other = (tbaa, otherN) +topN, stackN, heapN, rxN, baseN, otherN :: Unique +topN = getUnique (fsLit "LlvmCodeGen.Regs.topN") +stackN = getUnique (fsLit "LlvmCodeGen.Regs.stackN") +heapN = getUnique (fsLit "LlvmCodeGen.Regs.heapN") +rxN = getUnique (fsLit "LlvmCodeGen.Regs.rxN") +baseN = getUnique (fsLit "LlvmCodeGen.Regs.baseN") +otherN = getUnique (fsLit "LlvmCodeGen.Regs.otherN") -- | The TBAA metadata identifier tbaa :: LMString tbaa = fsLit "tbaa" -- | Get the correct TBAA metadata information for this register type -getTBAA :: GlobalReg -> MetaData -getTBAA BaseReg = base -getTBAA Sp = stack -getTBAA Hp = heap -getTBAA (VanillaReg _ _) = rx -getTBAA _ = top - +getTBAA :: GlobalReg -> Unique +getTBAA BaseReg = baseN +getTBAA Sp = stackN +getTBAA Hp = heapN +getTBAA (VanillaReg _ _) = rxN +getTBAA _ = topN |