From 49a8e5c021009430d373d6224b29004c7d18c408 Mon Sep 17 00:00:00 2001 From: David Terei Date: Tue, 15 Jun 2010 09:47:14 +0000 Subject: [PATCH] Add new LLVM code generator to GHC. (Version 2) This was done as part of an honours thesis at UNSW, the paper describing the work and results can be found at: http://www.cse.unsw.edu.au/~pls/thesis/davidt-thesis.pdf A Homepage for the backend can be found at: http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/Backends/LLVM Quick summary of performance is that for the 'nofib' benchmark suite, runtimes are within 5% slower than the NCG and generally better than the C code generator. For some code though, such as the DPH projects benchmark, the LLVM code generator outperforms the NCG and C code generator by about a 25% reduction in run times. --- compiler/cmm/Cmm.hs | 1 + compiler/cmm/CmmUtils.hs | 2 +- compiler/codeGen/CgForeignCall.hs | 3 +- compiler/codeGen/CgUtils.hs | 206 ++++++- compiler/ghc.cabal.in | 19 +- compiler/ghc.mk | 12 +- compiler/llvmGen/Llvm.hs | 52 ++ compiler/llvmGen/Llvm/AbsSyn.hs | 209 +++++++ compiler/llvmGen/Llvm/PpLlvm.hs | 325 ++++++++++ compiler/llvmGen/Llvm/Types.hs | 719 ++++++++++++++++++++++ compiler/llvmGen/LlvmCodeGen.hs | 166 ++++++ compiler/llvmGen/LlvmCodeGen/Base.hs | 164 +++++ compiler/llvmGen/LlvmCodeGen/CodeGen.hs | 958 ++++++++++++++++++++++++++++++ compiler/llvmGen/LlvmCodeGen/Data.hs | 190 ++++++ compiler/llvmGen/LlvmCodeGen/Ppr.hs | 91 +++ compiler/llvmGen/LlvmCodeGen/Regs.hs | 54 ++ compiler/main/CodeOutput.lhs | 31 +- compiler/main/DriverPhases.hs | 17 +- compiler/main/DriverPipeline.hs | 85 ++- compiler/main/DynFlags.hs | 56 +- compiler/main/SysTools.lhs | 28 +- compiler/nativeGen/Alpha/Regs.hs | 1 - compiler/nativeGen/AsmCodeGen.lhs | 89 +-- compiler/nativeGen/PPC/CodeGen.hs | 12 +- compiler/nativeGen/PPC/Regs.hs | 16 - compiler/nativeGen/SPARC/CodeGen/Base.hs | 11 +- compiler/nativeGen/SPARC/Regs.hs | 18 - compiler/nativeGen/X86/CodeGen.hs | 12 +- compiler/nativeGen/X86/Regs.hs | 16 - compiler/typecheck/TcForeign.lhs | 55 +- driver/mangler/ghc-asm.lprl | 6 +- mk/config.mk.in | 3 + rules/build-perl.mk | 1 + 33 files changed, 3402 insertions(+), 226 deletions(-) create mode 100644 compiler/llvmGen/Llvm.hs create mode 100644 compiler/llvmGen/Llvm/AbsSyn.hs create mode 100644 compiler/llvmGen/Llvm/PpLlvm.hs create mode 100644 compiler/llvmGen/Llvm/Types.hs create mode 100644 compiler/llvmGen/LlvmCodeGen.hs create mode 100644 compiler/llvmGen/LlvmCodeGen/Base.hs create mode 100644 compiler/llvmGen/LlvmCodeGen/CodeGen.hs create mode 100644 compiler/llvmGen/LlvmCodeGen/Data.hs create mode 100644 compiler/llvmGen/LlvmCodeGen/Ppr.hs create mode 100644 compiler/llvmGen/LlvmCodeGen/Regs.hs diff --git a/compiler/cmm/Cmm.hs b/compiler/cmm/Cmm.hs index c48269e..5c02622 100644 --- a/compiler/cmm/Cmm.hs +++ b/compiler/cmm/Cmm.hs @@ -181,6 +181,7 @@ data ClosureTypeInfo data CmmReturnInfo = CmmMayReturn | CmmNeverReturns + deriving ( Eq ) -- TODO: These types may need refinement data ProfilingInfo = ProfilingInfo CmmLit CmmLit -- closure_type, closure_desc diff --git a/compiler/cmm/CmmUtils.hs b/compiler/cmm/CmmUtils.hs index e01d8f5..69320a2 100644 --- a/compiler/cmm/CmmUtils.hs +++ b/compiler/cmm/CmmUtils.hs @@ -6,7 +6,7 @@ -- ----------------------------------------------------------------------------- -module CmmUtils( +module CmmUtils( CmmStmts, noStmts, oneStmt, mkStmts, plusStmts, stmtList, isNopStmt, diff --git a/compiler/codeGen/CgForeignCall.hs b/compiler/codeGen/CgForeignCall.hs index 901dd96..ce689c4 100644 --- a/compiler/codeGen/CgForeignCall.hs +++ b/compiler/codeGen/CgForeignCall.hs @@ -122,9 +122,10 @@ emitForeignCall' safety results target args vols _srt ret | not (playSafe safety) = do temp_args <- load_args_into_temps args let (caller_save, caller_load) = callerSaveVolatileRegs vols + let caller_load' = if ret == CmmNeverReturns then [] else caller_load stmtsC caller_save stmtC (CmmCall target results temp_args CmmUnsafe ret) - stmtsC caller_load + stmtsC caller_load' | otherwise = do -- Both 'id' and 'new_base' are GCKindNonPtr because they're diff --git a/compiler/codeGen/CgUtils.hs b/compiler/codeGen/CgUtils.hs index f8b41a0..d22fee1 100644 --- a/compiler/codeGen/CgUtils.hs +++ b/compiler/codeGen/CgUtils.hs @@ -26,6 +26,7 @@ module CgUtils ( tagToClosure, callerSaveVolatileRegs, get_GlobalReg_addr, + activeStgRegs, fixStgRegisters, cmmAndWord, cmmOrWord, cmmNegate, cmmEqWord, cmmNeWord, cmmUGtWord, @@ -423,33 +424,6 @@ callerSaveVolatileRegs vols = (caller_save, caller_load) : next | otherwise = next --- ----------------------------------------------------------------------------- --- Global registers - --- We map STG registers onto appropriate CmmExprs. Either they map --- to real machine registers or stored as offsets from BaseReg. Given --- a GlobalReg, get_GlobalReg_addr always produces the --- register table address for it. --- (See also get_GlobalReg_reg_or_addr in MachRegs) - -get_GlobalReg_addr :: GlobalReg -> CmmExpr -get_GlobalReg_addr BaseReg = regTableOffset 0 -get_GlobalReg_addr mid = get_Regtable_addr_from_offset - (globalRegType mid) (baseRegOffset mid) - --- Calculate a literal representing an offset into the register table. --- Used when we don't have an actual BaseReg to offset from. -regTableOffset n = - CmmLit (CmmLabelOff mkMainCapabilityLabel (oFFSET_Capability_r + n)) - -get_Regtable_addr_from_offset :: CmmType -> Int -> CmmExpr -get_Regtable_addr_from_offset rep offset = -#ifdef REG_Base - CmmRegOff (CmmGlobal BaseReg) offset -#else - regTableOffset offset -#endif - -- | Returns @True@ if this global register is stored in a caller-saves -- machine register. @@ -1010,3 +984,181 @@ clHasCafRefs (ClosureInfo {closureSRT = srt}) = case srt of NoC_SRT -> NoCafRefs _ -> MayHaveCafRefs clHasCafRefs (ConInfo {}) = NoCafRefs + +-- ----------------------------------------------------------------------------- +-- +-- STG/Cmm GlobalReg +-- +-- ----------------------------------------------------------------------------- + +-- | Here is where the STG register map is defined for each target arch. +-- The order matters (for the llvm backend anyway)! We must make sure to +-- maintain the order here with the order used in the LLVM calling conventions. +-- Note that also, this isn't all registers, just the ones that are currently +-- possbily mapped to real registers. +activeStgRegs :: [GlobalReg] +activeStgRegs = [ +#ifdef REG_Base + BaseReg +#endif +#ifdef REG_Sp + ,Sp +#endif +#ifdef REG_Hp + ,Hp +#endif +#ifdef REG_R1 + ,VanillaReg 1 VGcPtr +#endif +#ifdef REG_R2 + ,VanillaReg 2 VGcPtr +#endif +#ifdef REG_R3 + ,VanillaReg 3 VGcPtr +#endif +#ifdef REG_R4 + ,VanillaReg 4 VGcPtr +#endif +#ifdef REG_R5 + ,VanillaReg 5 VGcPtr +#endif +#ifdef REG_R6 + ,VanillaReg 6 VGcPtr +#endif +#ifdef REG_R7 + ,VanillaReg 7 VGcPtr +#endif +#ifdef REG_R8 + ,VanillaReg 8 VGcPtr +#endif +#ifdef REG_SpLim + ,SpLim +#endif +#ifdef REG_F1 + ,FloatReg 1 +#endif +#ifdef REG_F2 + ,FloatReg 2 +#endif +#ifdef REG_F3 + ,FloatReg 3 +#endif +#ifdef REG_F4 + ,FloatReg 4 +#endif +#ifdef REG_D1 + ,DoubleReg 1 +#endif +#ifdef REG_D2 + ,DoubleReg 2 +#endif + ] + +-- | We map STG registers onto appropriate CmmExprs. Either they map +-- to real machine registers or stored as offsets from BaseReg. Given +-- a GlobalReg, get_GlobalReg_addr always produces the +-- register table address for it. +get_GlobalReg_addr :: GlobalReg -> CmmExpr +get_GlobalReg_addr BaseReg = regTableOffset 0 +get_GlobalReg_addr mid = get_Regtable_addr_from_offset + (globalRegType mid) (baseRegOffset mid) + +-- Calculate a literal representing an offset into the register table. +-- Used when we don't have an actual BaseReg to offset from. +regTableOffset n = + CmmLit (CmmLabelOff mkMainCapabilityLabel (oFFSET_Capability_r + n)) + +get_Regtable_addr_from_offset :: CmmType -> Int -> CmmExpr +get_Regtable_addr_from_offset rep offset = +#ifdef REG_Base + CmmRegOff (CmmGlobal BaseReg) offset +#else + regTableOffset offset +#endif + +-- | Fixup global registers so that they assign to locations within the +-- RegTable if they aren't pinned for the current target. +fixStgRegisters :: RawCmmTop -> RawCmmTop +fixStgRegisters top@(CmmData _ _) = top + +fixStgRegisters (CmmProc info lbl params (ListGraph blocks)) = + let blocks' = map fixStgRegBlock blocks + in CmmProc info lbl params $ ListGraph blocks' + +fixStgRegBlock :: CmmBasicBlock -> CmmBasicBlock +fixStgRegBlock (BasicBlock id stmts) = + let stmts' = map fixStgRegStmt stmts + in BasicBlock id stmts' + +fixStgRegStmt :: CmmStmt -> CmmStmt +fixStgRegStmt stmt + = case stmt of + CmmAssign (CmmGlobal reg) src -> + let src' = fixStgRegExpr src + baseAddr = get_GlobalReg_addr reg + in case reg `elem` activeStgRegs of + True -> CmmAssign (CmmGlobal reg) src' + False -> CmmStore baseAddr src' + + CmmAssign reg src -> + let src' = fixStgRegExpr src + in CmmAssign reg src' + + CmmStore addr src -> CmmStore (fixStgRegExpr addr) (fixStgRegExpr src) + + CmmCall target regs args srt returns -> + let target' = case target of + CmmCallee e conv -> CmmCallee (fixStgRegExpr e) conv + other -> other + args' = map (\(CmmHinted arg hint) -> + (CmmHinted (fixStgRegExpr arg) hint)) args + in CmmCall target' regs args' srt returns + + CmmCondBranch test dest -> CmmCondBranch (fixStgRegExpr test) dest + + CmmSwitch expr ids -> CmmSwitch (fixStgRegExpr expr) ids + + CmmJump addr regs -> CmmJump (fixStgRegExpr addr) regs + + -- CmmNop, CmmComment, CmmBranch, CmmReturn + _other -> stmt + + +fixStgRegExpr :: CmmExpr -> CmmExpr +fixStgRegExpr expr + = case expr of + CmmLoad addr ty -> CmmLoad (fixStgRegExpr addr) ty + + CmmMachOp mop args -> CmmMachOp mop args' + where args' = map fixStgRegExpr args + + CmmReg (CmmGlobal reg) -> + -- Replace register leaves with appropriate StixTrees for + -- the given target. MagicIds which map to a reg on this + -- arch are left unchanged. For the rest, BaseReg is taken + -- to mean the address of the reg table in MainCapability, + -- and for all others we generate an indirection to its + -- location in the register table. + case reg `elem` activeStgRegs of + True -> expr + False -> + let baseAddr = get_GlobalReg_addr reg + in case reg of + BaseReg -> fixStgRegExpr baseAddr + _other -> fixStgRegExpr + (CmmLoad baseAddr (globalRegType reg)) + + CmmRegOff (CmmGlobal reg) offset -> + -- RegOf leaves are just a shorthand form. If the reg maps + -- to a real reg, we keep the shorthand, otherwise, we just + -- expand it and defer to the above code. + case reg `elem` activeStgRegs of + True -> expr + False -> fixStgRegExpr (CmmMachOp (MO_Add wordWidth) [ + CmmReg (CmmGlobal reg), + CmmLit (CmmInt (fromIntegral offset) + wordWidth)]) + + -- CmmLit, CmmReg (CmmLocal), CmmStackSlot + _other -> expr + diff --git a/compiler/ghc.cabal.in b/compiler/ghc.cabal.in index 448d27b..479e56d 100644 --- a/compiler/ghc.cabal.in +++ b/compiler/ghc.cabal.in @@ -130,7 +130,9 @@ Library ghci hsSyn iface + llvmGen main + nativeGen parser prelude profiling @@ -153,6 +155,16 @@ Library Id IdInfo Literal + Llvm + Llvm.AbsSyn + Llvm.PpLlvm + Llvm.Types + LlvmCodeGen + LlvmCodeGen.Base + LlvmCodeGen.CodeGen + LlvmCodeGen.Data + LlvmCodeGen.Ppr + LlvmCodeGen.Regs MkId Module Name @@ -198,6 +210,7 @@ Library MkZipCfg MkZipCfgCmm OptimizationFuel + PprBase PprC PprCmm PprCmmZ @@ -447,10 +460,9 @@ Library VectUtils Vectorise + -- We only need to expose more modules as some of the ncg code is used + -- by the LLVM backend so its always included if flag(ncg) - hs-source-dirs: - nativeGen - Exposed-Modules: AsmCodeGen TargetReg @@ -459,7 +471,6 @@ Library Size Reg RegClass - PprBase PIC Platform Alpha.Regs diff --git a/compiler/ghc.mk b/compiler/ghc.mk index 1c61494..9cbacf4 100644 --- a/compiler/ghc.mk +++ b/compiler/ghc.mk @@ -43,6 +43,12 @@ compiler/stage2/package-data.mk : $(compiler_CONFIG_HS) compiler/stage3/package-data.mk : $(compiler_CONFIG_HS) endif +ifeq "$(GhcEnableTablesNextToCode)" "NO" +GhcWithLlvmCodeGen = YES +else +GhcWithLlvmCodeGen = NO +endif + $(compiler_CONFIG_HS) : mk/config.mk mk/project.mk "$(RM)" $(RM_OPTS) $@ @echo "Creating $@ ... " @@ -67,6 +73,8 @@ $(compiler_CONFIG_HS) : mk/config.mk mk/project.mk @echo "cGhcWithInterpreter = \"$(GhcWithInterpreter)\"" >> $@ @echo "cGhcWithNativeCodeGen :: String" >> $@ @echo "cGhcWithNativeCodeGen = \"$(GhcWithNativeCodeGen)\"" >> $@ + @echo "cGhcWithLlvmCodeGen :: String" >> $@ + @echo "cGhcWithLlvmCodeGen = \"$(GhcWithLlvmCodeGen)\"" >> $@ @echo "cGhcWithSMP :: String" >> $@ @echo "cGhcWithSMP = \"$(GhcWithSMP)\"" >> $@ @echo "cGhcRTSWays :: String" >> $@ @@ -313,7 +321,7 @@ ifeq "$(GhcEnableTablesNextToCode) $(GhcUnregisterised)" "YES NO" # or not? # XXX This should logically be a CPP option, but there doesn't seem to # be a flag for that -compiler_stage2_CONFIGURE_OPTS += --ghc-option=-DGHCI_TABLES_NEXT_TO_CODE +compiler_CONFIGURE_OPTS += --ghc-option=-DGHCI_TABLES_NEXT_TO_CODE endif # Should the debugger commands be enabled? @@ -338,6 +346,8 @@ ifeq "$(HOSTPLATFORM)" "ia64-unknown-linux" # needed for generating proper relocation in large binaries: trac #856 compiler_CONFIGURE_OPTS += --ld-option=-Wl,--relax endif +else +compiler_CONFIGURE_OPTS += --ghc-option=-DNO_REGS endif # We need to turn on profiling either if we have been asked to diff --git a/compiler/llvmGen/Llvm.hs b/compiler/llvmGen/Llvm.hs new file mode 100644 index 0000000..7a322bd --- /dev/null +++ b/compiler/llvmGen/Llvm.hs @@ -0,0 +1,52 @@ +-- ---------------------------------------------------------------------------- +-- | This module supplies bindings to generate Llvm IR from Haskell +-- (). +-- +-- Note: this module is developed in a demand driven way. It is no complete +-- LLVM binding library in Haskell, but enough to generate code for GHC. +-- +-- This code is derived from code taken from the Essential Haskell Compiler +-- (EHC) project (). +-- + +module Llvm ( + + -- * Modules, Functions and Blocks + LlvmModule(..), + + LlvmFunction(..), LlvmFunctionDecl(..), + LlvmFunctions, LlvmFunctionDecls, + LlvmStatement(..), LlvmExpression(..), + LlvmBlocks, LlvmBlock(..), LlvmBlockId, + + -- * Call Handling + LlvmCallConvention(..), LlvmCallType(..), LlvmParameterListType(..), + LlvmLinkageType(..), LlvmFuncAttr(..), + + -- * Operations and Comparisons + LlvmCmpOp(..), LlvmMachOp(..), LlvmCastOp(..), + + -- * Variables and Type System + LlvmVar(..), LlvmStatic(..), LlvmLit(..), LlvmType(..), + LMGlobal, LMString, LMConstant, + + -- ** Some basic types + i64, i32, i16, i8, i1, llvmWord, llvmWordPtr, + + -- ** Operations on the type system. + isGlobal, getLitType, getLit, getName, getPlainName, getVarType, + getStatType, getGlobalVar, getGlobalType, pVarLower, pLift, pLower, + isInt, isFloat, isPointer, llvmWidthInBits, + + -- * Pretty Printing + ppLlvmModule, ppLlvmComments, ppLlvmComment, ppLlvmConstants, + ppLlvmConstant, ppLlvmGlobals, ppLlvmGlobal, ppLlvmFunctionDecls, + ppLlvmFunctionDecl, ppLlvmFunctions, ppLlvmFunction, ppLlvmType, + ppLlvmTypes, llvmSDoc + + ) where + +import Llvm.AbsSyn +import Llvm.PpLlvm +import Llvm.Types + diff --git a/compiler/llvmGen/Llvm/AbsSyn.hs b/compiler/llvmGen/Llvm/AbsSyn.hs new file mode 100644 index 0000000..1b8527b --- /dev/null +++ b/compiler/llvmGen/Llvm/AbsSyn.hs @@ -0,0 +1,209 @@ +-------------------------------------------------------------------------------- +-- | The LLVM abstract syntax. +-- + +module Llvm.AbsSyn where + +import Llvm.Types + +import Unique + +-- | Block labels +type LlvmBlockId = Unique + +-- | A block of LLVM code. +data LlvmBlock = LlvmBlock { + -- | The code label for this block + blockLabel :: LlvmBlockId, + + -- | A list of LlvmStatement's representing the code for this block. + -- This list must end with a control flow statement. + blockStmts :: [LlvmStatement] + } + +type LlvmBlocks = [LlvmBlock] + +-- | An LLVM Module. This is a top level contianer in LLVM. +data LlvmModule = LlvmModule { + -- | Comments to include at the start of the module. + modComments :: [LMString], + + -- | Constants to include in the module. + modConstants :: [LMConstant], + + -- | Global variables to include in the module. + modGlobals :: [LMGlobal], + + -- | LLVM Functions used in this module but defined in other modules. + modFwdDecls :: LlvmFunctionDecls, + + -- | LLVM Functions defined in this module. + modFuncs :: LlvmFunctions + } + +-- | An LLVM Function +data LlvmFunction = LlvmFunction { + -- | The signature of this declared function. + funcDecl :: LlvmFunctionDecl, + + -- | The function attributes. + funcAttrs :: [LlvmFuncAttr], + + -- | The body of the functions. + funcBody :: LlvmBlocks + } + +type LlvmFunctions = [LlvmFunction] + + +-- | Llvm Statements +data LlvmStatement + {- | + Assign an expression to an variable: + * dest: Variable to assign to + * source: Source expression + -} + = Assignment LlvmVar LlvmExpression + + {- | + Always branch to the target label + -} + | Branch LlvmVar + + {- | + Branch to label targetTrue if cond is true otherwise to label targetFalse + * cond: condition that will be tested, must be of type i1 + * targetTrue: label to branch to if cond is true + * targetFalse: label to branch to if cond is false + -} + | BranchIf LlvmVar LlvmVar LlvmVar + + {- | + Comment + Plain comment. + -} + | Comment [LMString] + + {- | + Set a label on this position. + * name: Identifier of this label, unique for this module + -} + | MkLabel LlvmBlockId + + {- | + Store variable value in pointer ptr. If value is of type t then ptr must + be of type t*. + * value: Variable/Constant to store. + * ptr: Location to store the value in + -} + | Store LlvmVar LlvmVar + + {- | + Mutliway branch + * scrutinee: Variable or constant which must be of integer type that is + determines which arm is chosen. + * def: The default label if there is no match in target. + * target: A list of (value,label) where the value is an integer + constant and label the corresponding label to jump to if the + scrutinee matches the value. + -} + | Switch LlvmVar LlvmVar [(LlvmVar, LlvmVar)] + + {- | + Return a result. + * result: The variable or constant to return + -} + | Return (Maybe LlvmVar) + + {- | + An instruction for the optimizer that the code following is not reachable + -} + | Unreachable + + {- | + Raise an expression to a statement (if don't want result or want to use + Llvm unamed values. + -} + | Expr LlvmExpression + + deriving (Show, Eq) + + +-- | Llvm Expressions +data LlvmExpression + {- | + Allocate amount * sizeof(tp) bytes on the stack + * tp: LlvmType to reserve room for + * amount: The nr of tp's which must be allocated + -} + = Alloca LlvmType Int + + {- | + Perform the machine operator op on the operands left and right + * op: operator + * left: left operand + * right: right operand + -} + | LlvmOp LlvmMachOp LlvmVar LlvmVar + + {- | + Perform a compare operation on the operands left and right + * op: operator + * left: left operand + * right: right operand + -} + | Compare LlvmCmpOp LlvmVar LlvmVar + + {- | + Allocate amount * sizeof(tp) bytes on the heap + * tp: LlvmType to reserve room for + * amount: The nr of tp's which must be allocated + -} + | Malloc LlvmType Int + + {- | + Load the value at location ptr + -} + | Load LlvmVar + + {- | + Navigate in an structure, selecting elements + * ptr: Location of the structure + * indexes: A list of indexes to select the correct value. For example + the first element of the third element of the structure ptr + is selected with [3,1] (zero indexed) + -} + | GetElemPtr LlvmVar [Int] + + {- | + Cast the variable from to the to type. This is an abstraction of three + cast operators in Llvm, inttoptr, prttoint and bitcast. + * cast: Cast type + * from: Variable to cast + * to: type to cast to + -} + | Cast LlvmCastOp LlvmVar LlvmType + + {- | + Call a function. The result is the value of the expression. + * 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: Concrete arguments for the parameters + * attrs: A list of function attributes for the call. Only NoReturn, + NoUnwind, ReadOnly and ReadNone are valid here. + -} + | Call LlvmCallType LlvmVar [LlvmVar] [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 + precessors variables. + * precessors: A list of variables and the basic block that they originate + from. + -} + | Phi LlvmType [(LlvmVar,LlvmVar)] + + deriving (Show, Eq) + diff --git a/compiler/llvmGen/Llvm/PpLlvm.hs b/compiler/llvmGen/Llvm/PpLlvm.hs new file mode 100644 index 0000000..8d36511 --- /dev/null +++ b/compiler/llvmGen/Llvm/PpLlvm.hs @@ -0,0 +1,325 @@ +-------------------------------------------------------------------------------- +-- | Pretty print LLVM IR Code. +-- + +module Llvm.PpLlvm ( + + -- * Top level LLVM objects. + ppLlvmModule, + ppLlvmComments, + ppLlvmComment, + ppLlvmConstants, + ppLlvmConstant, + ppLlvmGlobals, + ppLlvmGlobal, + ppLlvmType, + ppLlvmTypes, + ppLlvmFunctionDecls, + ppLlvmFunctionDecl, + ppLlvmFunctions, + ppLlvmFunction, + llvmSDoc + + ) where + +#include "HsVersions.h" + +import Llvm.AbsSyn +import Llvm.Types + +import Data.List ( intersperse ) +import Pretty +import qualified Outputable as Outp +import Unique + +-------------------------------------------------------------------------------- +-- * Top Level Print functions +-------------------------------------------------------------------------------- + +-- | Print out a whole LLVM module. +ppLlvmModule :: LlvmModule -> Doc +ppLlvmModule (LlvmModule comments constants globals decls funcs) + = ppLlvmComments comments + $+$ empty + $+$ ppLlvmConstants constants + $+$ ppLlvmGlobals globals + $+$ empty + $+$ ppLlvmFunctionDecls decls + $+$ empty + $+$ ppLlvmFunctions funcs + +-- | Print out a multi-line comment, can be inside a function or on its own +ppLlvmComments :: [LMString] -> Doc +ppLlvmComments comments = vcat $ map ppLlvmComment comments + +-- | Print out a comment, can be inside a function or on its own +ppLlvmComment :: LMString -> Doc +ppLlvmComment com = semi <+> (ftext com) + + +-- | Print out a list of global mutable variable definitions +ppLlvmGlobals :: [LMGlobal] -> Doc +ppLlvmGlobals ls = vcat $ map ppLlvmGlobal ls + +-- | Print out a global mutable variable definition +ppLlvmGlobal :: LMGlobal -> Doc +ppLlvmGlobal (var@(LMGlobalVar _ _ link), Nothing) = + ppAssignment var $ text (show link) <+> text "global" <+> + (text $ show (pLower $ getVarType var)) + +ppLlvmGlobal (var@(LMGlobalVar _ _ link), (Just stat)) = + ppAssignment var $ text (show link) <+> text "global" <+> text (show stat) + +ppLlvmGlobal oth = error $ "Non Global var ppr as global! " ++ show oth + + +-- | Print out a list global constant variable +ppLlvmConstants :: [LMConstant] -> Doc +ppLlvmConstants cons = vcat $ map ppLlvmConstant cons + +-- | Print out a global constant variable +ppLlvmConstant :: LMConstant -> Doc +ppLlvmConstant (dst@(LMGlobalVar _ _ link), src) = + ppAssignment dst $ text (show link) <+> text "constant" <+> text (show src) + +ppLlvmConstant c = error $ "Non global var as constant! " ++ show c + + +-- | Print out a list of LLVM type aliases. +ppLlvmTypes :: [LlvmType] -> Doc +ppLlvmTypes tys = vcat $ map ppLlvmType tys + +-- | Print out an LLVM type alias. +ppLlvmType :: LlvmType -> Doc + +ppLlvmType al@(LMAlias _ t) + = (text $ show al) <+> equals <+> (text "type") <+> (text $ show t) + +ppLlvmType (LMFunction t) + = ppLlvmFunctionDecl t + +ppLlvmType _ = empty + + +-- | Print out a list of function definitions. +ppLlvmFunctions :: LlvmFunctions -> Doc +ppLlvmFunctions funcs = vcat $ map ppLlvmFunction funcs + +-- | Print out a function definition. +ppLlvmFunction :: LlvmFunction -> Doc +ppLlvmFunction (LlvmFunction dec attrs body) = + let attrDoc = ppSpaceJoin attrs + in (text "define") <+> (ppLlvmFuncDecSig dec) + <+> attrDoc + $+$ lbrace + $+$ ppLlvmBlocks body + $+$ rbrace + + +-- | Print out a list of function declaration. +ppLlvmFunctionDecls :: LlvmFunctionDecls -> Doc +ppLlvmFunctionDecls decs = vcat $ map ppLlvmFunctionDecl decs + +-- | Print out a function declaration. +-- Declarations define the function type but don't define the actual body of +-- the function. +ppLlvmFunctionDecl :: LlvmFunctionDecl -> Doc +ppLlvmFunctionDecl dec = (text "declare") <+> ppLlvmFuncDecSig dec + +-- | Print out a functions type signature. +-- This differs from [ppLlvmFunctionDecl] in that it is used for both function +-- declarations and defined functions to print out the type. +ppLlvmFuncDecSig :: LlvmFunctionDecl -> Doc +ppLlvmFuncDecSig (LlvmFunctionDecl name link cc retTy argTy params) + = let linkTxt = show link + linkDoc | linkTxt == "" = empty + | otherwise = (text linkTxt) <> space + ppParams = either ppCommaJoin ppCommaJoin params <> + (case argTy of + VarArgs -> (text ", ...") + FixedArgs -> empty) + in linkDoc <> (text $ show cc) <+> (text $ show retTy) + <+> atsym <> (ftext name) <> lparen <+> ppParams <+> rparen + + +-- | Print out a list of LLVM blocks. +ppLlvmBlocks :: LlvmBlocks -> Doc +ppLlvmBlocks blocks = vcat $ map ppLlvmBlock blocks + +-- | Print out an LLVM block. +-- It must be part of a function definition. +ppLlvmBlock :: LlvmBlock -> Doc +ppLlvmBlock (LlvmBlock blockId stmts) + = ppLlvmStatement (MkLabel blockId) + $+$ nest 4 (vcat $ map ppLlvmStatement stmts) + + +-- | Print out an LLVM statement. +ppLlvmStatement :: LlvmStatement -> Doc +ppLlvmStatement stmt + = case stmt of + Assignment dst expr -> ppAssignment dst (ppLlvmExpression expr) + Branch target -> ppBranch target + BranchIf cond ifT ifF -> ppBranchIf cond ifT ifF + Comment comments -> ppLlvmComments comments + MkLabel label -> (llvmSDoc $ pprUnique label) <> colon + Store value ptr -> ppStore value ptr + Switch scrut def tgs -> ppSwitch scrut def tgs + Return result -> ppReturn result + Expr expr -> ppLlvmExpression expr + Unreachable -> text "unreachable" + + +-- | Print out an LLVM expression. +ppLlvmExpression :: LlvmExpression -> Doc +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 + Cast op from to -> ppCast op from to + Compare op left right -> ppCmpOp op left right + GetElemPtr ptr indexes -> ppGetElementPtr ptr indexes + Load ptr -> ppLoad ptr + Malloc tp amount -> ppMalloc tp amount + Phi tp precessors -> ppPhi tp precessors + + +-------------------------------------------------------------------------------- +-- * Individual print functions +-------------------------------------------------------------------------------- + +-- | 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] -> Doc +ppCall ct fptr vals attrs = case fptr of + -- + -- if local var function pointer, unwrap + LMLocalVar _ (LMPointer (LMFunction d)) -> ppCall' d + + -- should be function type otherwise + LMGlobalVar _ (LMFunction d) _ -> ppCall' d + + -- not pointer or function, so error + _other -> error $ "ppCall called with non LMFunction type!\nMust be " + ++ " called with either global var of function type or " + ++ "local var of pointer function type." + + where + ppCall' (LlvmFunctionDecl _ _ cc ret argTy params) = + let tc = if ct == TailCall then text "tail " else empty + ppValues = ppCommaJoin vals + ppArgTy = either ppCommaJoin (\x -> ppCommaJoin $ map getVarType x) params <> + (case argTy of + VarArgs -> (text ", ...") + FixedArgs -> empty) + fnty = space <> lparen <> ppArgTy <> rparen <> (text "*") + attrDoc = ppSpaceJoin attrs + in tc <> (text "call") <+> (text $ show cc) <+> (text $ show ret) + <> fnty <+> (text $ getName fptr) <> lparen <+> ppValues + <+> rparen <+> attrDoc + + +ppMachOp :: LlvmMachOp -> LlvmVar -> LlvmVar -> Doc +ppMachOp op left right = + (text $ show op) <+> (text $ show (getVarType left)) <+> (text $ getName left) + <> comma <+> (text $ getName right) + + +ppCmpOp :: LlvmCmpOp -> LlvmVar -> LlvmVar -> Doc +ppCmpOp op left right = + let cmpOp + | isInt (getVarType left) && isInt (getVarType right) = text "icmp" + | isFloat (getVarType left) && isFloat (getVarType right) = text "fcmp" + | otherwise = error ("can't compare different types, left = " + ++ (show $ getVarType left) ++ ", right = " + ++ (show $ getVarType right)) + in cmpOp <+> (text $ show op) <+> (text $ show (getVarType left)) + <+> (text $ getName left) <> comma <+> (text $ getName right) + + +ppAssignment :: LlvmVar -> Doc -> Doc +ppAssignment var expr = (text $ getName var) <+> equals <+> expr + + +ppLoad :: LlvmVar -> Doc +ppLoad var = (text "load") <+> (text $ show var) + + +ppStore :: LlvmVar -> LlvmVar -> Doc +ppStore val dst = + (text "store") <+> (text $ show val) <> comma <+> (text $ show dst) + + +ppCast :: LlvmCastOp -> LlvmVar -> LlvmType -> Doc +ppCast op from to = + let castOp = text $ show op + in castOp <+> (text $ show from) <+> (text "to") <+> (text $ show to) + + +ppMalloc :: LlvmType -> Int -> Doc +ppMalloc tp amount = + let amount' = LMLitVar $ LMIntLit (toInteger amount) i32 + in (text "malloc") <+> (text $ show tp) <> comma <+> (text $ show amount') + + +ppAlloca :: LlvmType -> Int -> Doc +ppAlloca tp amount = + let amount' = LMLitVar $ LMIntLit (toInteger amount) i32 + in (text "alloca") <+> (text $ show tp) <> comma <+> (text $ show amount') + + +ppGetElementPtr :: LlvmVar -> [Int] -> Doc +ppGetElementPtr ptr idx = + let indexes = hcat $ map ((comma <+> (text $ show i32) <+>) . text . show) idx + in (text "getelementptr") <+> (text $ show ptr) <> indexes + + +ppReturn :: Maybe LlvmVar -> Doc +ppReturn (Just var) = (text "ret") <+> (text $ show var) +ppReturn Nothing = (text "ret") <+> (text $ show LMVoid) + + +ppBranch :: LlvmVar -> Doc +ppBranch var = (text "br") <+> (text $ show var) + + +ppBranchIf :: LlvmVar -> LlvmVar -> LlvmVar -> Doc +ppBranchIf cond trueT falseT + = (text "br") <+> (text $ show cond) <> comma <+> (text $ show trueT) <> comma + <+> (text $ show falseT) + + +ppPhi :: LlvmType -> [(LlvmVar,LlvmVar)] -> Doc +ppPhi tp preds = + let ppPreds (val, label) = brackets $ (text $ getName val) <> comma + <+> (text $ getName label) + in (text "phi") <+> (text $ show tp) + <+> (hcat $ intersperse comma (map ppPreds preds)) + + +ppSwitch :: LlvmVar -> LlvmVar -> [(LlvmVar,LlvmVar)] -> Doc +ppSwitch scrut dflt targets = + let ppTarget (val, lab) = (text $ show val) <> comma <+> (text $ show lab) + ppTargets xs = brackets $ vcat (map ppTarget xs) + in (text "switch") <+> (text $ show scrut) <> comma <+> (text $ show dflt) + <+> (ppTargets targets) + + +-------------------------------------------------------------------------------- +-- * Misc functions +-------------------------------------------------------------------------------- +atsym :: Doc +atsym = text "@" + +ppCommaJoin :: (Show a) => [a] -> Doc +ppCommaJoin strs = hcat $ intersperse comma (map (text . show) strs) + +ppSpaceJoin :: (Show a) => [a] -> Doc +ppSpaceJoin strs = hcat $ intersperse space (map (text . show) strs) + +-- | Convert SDoc to Doc +llvmSDoc :: Outp.SDoc -> Doc +llvmSDoc d + = Outp.withPprStyleDoc (Outp.mkCodeStyle Outp.CStyle) d + diff --git a/compiler/llvmGen/Llvm/Types.hs b/compiler/llvmGen/Llvm/Types.hs new file mode 100644 index 0000000..a4080c4 --- /dev/null +++ b/compiler/llvmGen/Llvm/Types.hs @@ -0,0 +1,719 @@ +-------------------------------------------------------------------------------- +-- | The LLVM Type System. +-- + +module Llvm.Types where + +#include "HsVersions.h" +#include "ghcconfig.h" + +import Data.Char +import Numeric + +import Constants +import FastString +import Unique + +-- from NCG +import PprBase + +-- ----------------------------------------------------------------------------- +-- * LLVM Basic Types and Variables +-- + +-- | A global mutable variable. Maybe defined or external +type LMGlobal = (LlvmVar, Maybe LlvmStatic) +-- | A global constant variable +type LMConstant = (LlvmVar, LlvmStatic) +-- | A String in LLVM +type LMString = FastString + + +-- | Llvm Types. +data LlvmType + = LMInt Int -- ^ An integer with a given width in bits. + | LMFloat -- ^ 32 bit floating point + | LMDouble -- ^ 64 bit floating point + | LMFloat80 -- ^ 80 bit (x86 only) floating point + | LMFloat128 -- ^ 128 bit floating point + | LMPointer LlvmType -- ^ A pointer to a 'LlvmType' + | LMArray Int LlvmType -- ^ An array of 'LlvmType' + | LMLabel -- ^ A 'LlvmVar' can represent a label (address) + | LMVoid -- ^ Void type + | LMStruct [LlvmType] -- ^ Structure type + | LMAlias LMString LlvmType -- ^ A type alias + + -- | 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 (LMLabel ) = "label" + show (LMVoid ) = "void" + show (LMStruct tys ) = "{" ++ (commaCat tys) ++ "}" + + show (LMFunction (LlvmFunctionDecl _ _ _ r VarArgs p)) + = (show r) ++ " (" ++ (either commaCat commaCat p) ++ ", ...)" + show (LMFunction (LlvmFunctionDecl _ _ _ r FixedArgs p)) + = (show r) ++ " (" ++ (either commaCat commaCat p) ++ ")" + + show (LMAlias s _ ) = "%" ++ unpackFS s + + +-- | Llvm Variables +data LlvmVar + -- | Variables with a global scope. + = LMGlobalVar LMString LlvmType LlvmLinkageType + -- | Variables local to a function or parameters. + | LMLocalVar Unique LlvmType + -- | Named local variables. Sometimes we need to be able to explicitly name + -- variables (e.g for function arguments). + | LMNLocalVar LMString LlvmType + -- | A constant variable + | LMLitVar LlvmLit + deriving (Eq) + +instance Show LlvmVar where + show (LMLitVar x) = show x + show (x ) = show (getVarType x) ++ " " ++ getName x + + +-- | Llvm Literal Data. +-- +-- These can be used inline in expressions. +data LlvmLit + -- | Refers to an integer constant (i64 42). + = LMIntLit Integer LlvmType + -- | Floating point literal + | LMFloatLit Rational LlvmType + deriving (Eq) + +instance Show LlvmLit where + show l = show (getLitType l) ++ " " ++ getLit l + + +-- | Llvm Static Data. +-- +-- These represent the possible global level variables and constants. +data LlvmStatic + = LMComment LMString -- ^ A comment in a static section + | LMStaticLit LlvmLit -- ^ A static variant of a literal value + | LMUninitType LlvmType -- ^ For uninitialised data + | LMStaticStr LMString LlvmType -- ^ Defines a static 'LMString' + | LMStaticArray [LlvmStatic] LlvmType -- ^ A static array + | LMStaticStruc [LlvmStatic] LlvmType -- ^ A static structure type + | LMStaticPointer LlvmVar -- ^ A pointer to other data + + -- static expressions, could split out but leave + -- for moment for ease of use. Not many of them. + + | LMPtoI LlvmStatic LlvmType -- ^ Pointer to Integer conversion + | LMAdd LlvmStatic LlvmStatic -- ^ Constant addition operation + | LMSub LlvmStatic LlvmStatic -- ^ Constant subtraction operation + deriving (Eq) + +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) + = let struc = case d of + [] -> "[]" + ts -> "[" ++ + (show (head ts) ++ concat (map (\x -> "," ++ show x) + (tail ts))) + ++ "]" + in show t ++ " " ++ struc + + show (LMStaticStruc d t) + = let struc = case d of + [] -> "{}" + ts -> "{" ++ + (show (head ts) ++ concat (map (\x -> "," ++ show x) + (tail ts))) + ++ "}" + in show t ++ " " ++ struc + + show (LMStaticPointer v) = show v + + show (LMPtoI v t) + = show t ++ " ptrtoint (" ++ show v ++ " to " ++ show t ++ ")" + + show (LMAdd s1 s2) + = let ty1 = getStatType s1 + in if ty1 == getStatType s2 + then show ty1 ++ " add (" ++ show s1 ++ "," ++ show s2 ++ ")" + else error $ "LMAdd with different types! s1: " + ++ show s1 ++ ", s2: " ++ show s2 + show (LMSub s1 s2) + = let ty1 = getStatType s1 + in if ty1 == getStatType s2 + then show ty1 ++ " sub (" ++ 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 [] = "" +commaCat x = show (head x) ++ (concat $ map (\y -> "," ++ show y) (tail x)) + +-- ----------------------------------------------------------------------------- +-- ** Operations on LLVM Basic Types and Variables +-- + +-- | 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 + +-- | 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 _ ) = show x +getPlainName (LMNLocalVar x _ ) = unpackFS x +getPlainName (LMLitVar x ) = getLit x + +-- | Print a literal value. No type. +getLit :: LlvmLit -> String +getLit (LMIntLit i _) = show ((fromInteger i)::Int) +-- In Llvm float literals can be printed in a big-endian hexadecimal format, +-- regardless of underlying architecture. +getLit (LMFloatLit r LMFloat) = fToStr $ fromRational r +getLit (LMFloatLit r LMDouble) = dToStr $ fromRational r +getLit l = error $ "getLit: Usupported LlvmLit type! " ++ show (getLitType l) + +-- | Return the 'LlvmType' of the 'LlvmVar' +getVarType :: LlvmVar -> LlvmType +getVarType (LMGlobalVar _ y _) = y +getVarType (LMLocalVar _ y ) = y +getVarType (LMNLocalVar _ y ) = y +getVarType (LMLitVar l ) = getLitType l + +-- | Return the 'LlvmType' of a 'LlvmLit' +getLitType :: LlvmLit -> LlvmType +getLitType (LMIntLit _ t) = t +getLitType (LMFloatLit _ t) = t + +-- | Return the 'LlvmType' of the 'LlvmStatic' +getStatType :: LlvmStatic -> LlvmType +getStatType (LMStaticLit l ) = getLitType l +getStatType (LMUninitType t) = t +getStatType (LMStaticStr _ t) = t +getStatType (LMStaticArray _ t) = t +getStatType (LMStaticStruc _ t) = t +getStatType (LMStaticPointer v) = getVarType v +getStatType (LMPtoI _ t) = t +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 +getLink _ = ExternallyVisible + +-- | 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 + +-- | Remove the pointer indirection of the supplied type. Only 'LMPointer' +-- constructors can be lowered. +pLower :: LlvmType -> LlvmType +pLower (LMPointer x) = x +pLower x = error $ show x ++ " is a unlowerable type, need a pointer" + +-- | Lower a variable of 'LMPointer' type. +pVarLower :: LlvmVar -> LlvmVar +pVarLower (LMGlobalVar s t l) = LMGlobalVar s (pLower t) l +pVarLower (LMLocalVar s t ) = LMLocalVar s (pLower t) +pVarLower (LMNLocalVar s t ) = LMNLocalVar s (pLower t) +pVarLower (LMLitVar _ ) = error $ "Can't lower a literal type!" + +-- | Test if the given 'LlvmType' is an integer +isInt :: LlvmType -> Bool +isInt (LMInt _) = True +isInt _ = False + +-- | Test if the given 'LlvmType' is a floating point type +isFloat :: LlvmType -> Bool +isFloat LMFloat = True +isFloat LMDouble = True +isFloat LMFloat80 = True +isFloat LMFloat128 = True +isFloat _ = False + +-- | Test if the given 'LlvmType' is an 'LMPointer' construct +isPointer :: LlvmType -> Bool +isPointer (LMPointer _) = True +isPointer _ = False + +-- | Test if a 'LlvmVar' is global. +isGlobal :: LlvmVar -> Bool +isGlobal (LMGlobalVar _ _ _) = True +isGlobal _ = False + +-- | Width in bits of an 'LlvmType', returns 0 if not applicable +llvmWidthInBits :: LlvmType -> Int +llvmWidthInBits (LMInt n) = n +llvmWidthInBits (LMFloat) = 32 +llvmWidthInBits (LMDouble) = 64 +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. +llvmWidthInBits (LMPointer _) = llvmWidthInBits llvmWord +llvmWidthInBits (LMArray _ _) = llvmWidthInBits llvmWord +llvmWidthInBits LMLabel = 0 +llvmWidthInBits LMVoid = 0 +llvmWidthInBits (LMStruct tys) = sum $ map llvmWidthInBits tys +llvmWidthInBits (LMFunction _) = 0 +llvmWidthInBits (LMAlias _ t) = llvmWidthInBits t + + +-- ----------------------------------------------------------------------------- +-- ** Shortcut for Common Types +-- + +i128, i64, i32, i16, i8, i1 :: LlvmType +i128 = LMInt 128 +i64 = LMInt 64 +i32 = LMInt 32 +i16 = LMInt 16 +i8 = LMInt 8 +i1 = LMInt 1 + +-- | The target architectures word size +llvmWord :: LlvmType +llvmWord = LMInt (wORD_SIZE * 8) + +-- | The target architectures pointer size +llvmWordPtr :: LlvmType +llvmWordPtr = pLift llvmWord + + +-- ----------------------------------------------------------------------------- +-- * LLVM Function Types +-- + +-- | An LLVM Function +data LlvmFunctionDecl = LlvmFunctionDecl { + -- | Unique identifier of the function. + decName :: LMString, + -- | LinkageType of the function. + funcLinkage :: LlvmLinkageType, + -- | The calling convention of the function. + funcCc :: LlvmCallConvention, + -- | Type of the returned value + decReturnType :: LlvmType, + -- | Indicates if this function uses varargs + decVarargs :: LlvmParameterListType, + -- | Signature of the parameters, can be just types or full vars + -- if parameter names are required. + decParams :: Either [LlvmType] [LlvmVar] + } + +instance Show LlvmFunctionDecl where + show (LlvmFunctionDecl n l c r VarArgs p) + = (show l) ++ " " ++ (show c) ++ " " ++ (show r) + ++ " @" ++ unpackFS n ++ "(" ++ (either commaCat commaCat p) ++ ", ...)" + show (LlvmFunctionDecl n l c r FixedArgs p) + = (show l) ++ " " ++ (show c) ++ " " ++ (show r) + ++ " @" ++ unpackFS n ++ "(" ++ (either commaCat commaCat p) ++ ")" + +instance Eq LlvmFunctionDecl where + (LlvmFunctionDecl n1 l1 c1 r1 v1 p1) == (LlvmFunctionDecl n2 l2 c2 r2 v2 p2) + = (n1 == n2) && (l1 == l2) && (c1 == c2) && (r1 == r2) + && (v1 == v2) && (p1 == p2) + +type LlvmFunctionDecls = [LlvmFunctionDecl] + + +-- | Llvm Function Attributes. +-- +-- Function attributes are set to communicate additional information about a +-- function. Function attributes are considered to be part of the function, +-- not of the function type, so functions with different parameter attributes +-- can have the same function type. Functions can have multiple attributes. +-- +-- Descriptions taken from +data LlvmFuncAttr + -- | This attribute indicates that the inliner should attempt to inline this + -- function into callers whenever possible, ignoring any active inlining + -- size threshold for this caller. + = AlwaysInline + -- | This attribute indicates that the source code contained a hint that + -- inlining this function is desirable (such as the \"inline\" keyword in + -- C/C++). It is just a hint; it imposes no requirements on the inliner. + | InlineHint + -- | This attribute indicates that the inliner should never inline this + -- function in any situation. This attribute may not be used together + -- with the alwaysinline attribute. + | NoInline + -- | This attribute suggests that optimization passes and code generator + -- passes make choices that keep the code size of this function low, and + -- otherwise do optimizations specifically to reduce code size. + | OptSize + -- | This function attribute indicates that the function never returns + -- normally. This produces undefined behavior at runtime if the function + -- ever does dynamically return. + | NoReturn + -- | This function attribute indicates that the function never returns with + -- an unwind or exceptional control flow. If the function does unwind, its + -- runtime behavior is undefined. + | NoUnwind + -- | This attribute indicates that the function computes its result (or + -- decides to unwind an exception) based strictly on its arguments, without + -- dereferencing any pointer arguments or otherwise accessing any mutable + -- state (e.g. memory, control registers, etc) visible to caller functions. + -- It does not write through any pointer arguments (including byval + -- arguments) and never changes any state visible to callers. This means + -- that it cannot unwind exceptions by calling the C++ exception throwing + -- methods, but could use the unwind instruction. + | ReadNone + -- | This attribute indicates that the function does not write through any + -- pointer arguments (including byval arguments) or otherwise modify any + -- state (e.g. memory, control registers, etc) visible to caller functions. + -- It may dereference pointer arguments and read state that may be set in + -- the caller. A readonly function always returns the same value (or unwinds + -- an exception identically) when called with the same set of arguments and + -- global state. It cannot unwind an exception by calling the C++ exception + -- throwing methods, but may use the unwind instruction. + | ReadOnly + -- | This attribute indicates that the function should emit a stack smashing + -- protector. It is in the form of a \"canary\"—a random value placed on the + -- stack before the local variables that's checked upon return from the + -- function to see if it has been overwritten. A heuristic is used to + -- determine if a function needs stack protectors or not. + -- + -- If a function that has an ssp attribute is inlined into a function that + -- doesn't have an ssp attribute, then the resulting function will have an + -- ssp attribute. + | Ssp + -- | This attribute indicates that the function should always emit a stack + -- smashing protector. This overrides the ssp function attribute. + -- + -- If a function that has an sspreq attribute is inlined into a function + -- that doesn't have an sspreq attribute or which has an ssp attribute, + -- then the resulting function will have an sspreq attribute. + | SspReq + -- | This attribute indicates that the code generator should not use a red + -- zone, even if the target-specific ABI normally permits it. + | NoRedZone + -- | This attributes disables implicit floating point instructions. + | NoImplicitFloat + -- | This attribute disables prologue / epilogue emission for the function. + -- This can have very system-specific consequences. + | 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" + + +-- | Different types to call a function. +data LlvmCallType + -- | Normal call, allocate a new stack frame. + = StdCall + -- | Tail call, perform the call in the current stack frame. + | TailCall + deriving (Eq,Show) + +-- | Different calling conventions a function can use. +data LlvmCallConvention + -- | The C calling convention. + -- This calling convention (the default if no other calling convention is + -- specified) matches the target C calling conventions. This calling + -- convention supports varargs function calls and tolerates some mismatch in + -- the declared prototype and implemented declaration of the function (as + -- does normal C). + = CC_Ccc + -- | This calling convention attempts to make calls as fast as possible + -- (e.g. by passing things in registers). This calling convention allows + -- the target to use whatever tricks it wants to produce fast code for the + -- target, without having to conform to an externally specified ABI + -- (Application Binary Interface). Implementations of this convention should + -- allow arbitrary tail call optimization to be supported. This calling + -- convention does not support varargs and requires the prototype of al + -- callees to exactly match the prototype of the function definition. + | CC_Fastcc + -- | This calling convention attempts to make code in the caller as efficient + -- as possible under the assumption that the call is not commonly executed. + -- As such, these calls often preserve all registers so that the call does + -- not break any live ranges in the caller side. This calling convention + -- does not support varargs and requires the prototype of all callees to + -- exactly match the prototype of the function definition. + | CC_Coldcc + -- | Any calling convention may be specified by number, allowing + -- target-specific calling conventions to be used. Target specific calling + -- conventions start at 64. + | CC_Ncc Int + -- | X86 Specific 'StdCall' convention. LLVM includes a specific alias for it + -- rather than just using CC_Ncc. + | 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" + + +-- | Functions can have a fixed amount of parameters, or a variable amount. +data LlvmParameterListType + -- Fixed amount of arguments. + = FixedArgs + -- Variable amount of arguments. + | VarArgs + deriving (Eq,Show) + + +-- | Linkage type of a symbol. +-- +-- The description of the constructors is copied from the Llvm Assembly Language +-- Reference Manual , because +-- they correspond to the Llvm linkage types. +data LlvmLinkageType + -- | Global values with internal linkage are only directly accessible by + -- objects in the current module. In particular, linking code into a module + -- with an internal global value may cause the internal to be renamed as + -- necessary to avoid collisions. Because the symbol is internal to the + -- module, all references can be updated. This corresponds to the notion + -- of the @static@ keyword in C. + = Internal + -- | Globals with @linkonce@ linkage are merged with other globals of the + -- same name when linkage occurs. This is typically used to implement + -- inline functions, templates, or other code which must be generated + -- in each translation unit that uses it. Unreferenced linkonce globals are + -- allowed to be discarded. + | LinkOnce + -- | @weak@ linkage is exactly the same as linkonce linkage, except that + -- unreferenced weak globals may not be discarded. This is used for globals + -- that may be emitted in multiple translation units, but that are not + -- guaranteed to be emitted into every translation unit that uses them. One + -- example of this are common globals in C, such as @int X;@ at global + -- scope. + | Weak + -- | @appending@ linkage may only be applied to global variables of pointer + -- to array type. When two global variables with appending linkage are + -- linked together, the two global arrays are appended together. This is + -- the Llvm, typesafe, equivalent of having the system linker append + -- together @sections@ with identical names when .o files are linked. + | Appending + -- | The semantics of this linkage follow the ELF model: the symbol is weak + -- until linked, if not linked, the symbol becomes null instead of being an + -- undefined reference. + | ExternWeak + -- | The symbol participates in linkage and can be used to resolve external + -- symbol references. + | ExternallyVisible + -- | Alias for 'ExternallyVisible' but with explicit textual form in LLVM + -- assembly. + | External + deriving (Eq) + +instance Show LlvmLinkageType where + show Internal = "internal" + show LinkOnce = "linkonce" + show Weak = "weak" + show Appending = "appending" + show ExternWeak = "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" + + +-- ----------------------------------------------------------------------------- +-- * LLVM Operations +-- + +-- | Llvm binary operators machine operations. +data LlvmMachOp + = LM_MO_Add -- ^ add two integer, floating point or vector values. + | LM_MO_Sub -- ^ subtract two ... + | LM_MO_Mul -- ^ multiply .. + | LM_MO_UDiv -- ^ unsigned integer or vector division. + | LM_MO_SDiv -- ^ signed integer .. + | LM_MO_FDiv -- ^ floating point .. + | LM_MO_URem -- ^ unsigned integer or vector remainder (mod) + | LM_MO_SRem -- ^ signed ... + | LM_MO_FRem -- ^ floating point ... + + -- | Left shift + | LM_MO_Shl + -- | Logical shift right + -- Shift right, filling with zero + | LM_MO_LShr + -- | Arithmetic shift right + -- The most significant bits of the result will be equal to the sign bit of + -- the left operand. + | LM_MO_AShr + + | LM_MO_And -- ^ AND bitwise logical operation. + | LM_MO_Or -- ^ OR bitwise logical operation. + | 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_FDiv = "fdiv" + show LM_MO_URem = "urem" + show LM_MO_SRem = "srem" + 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" + + +-- | Llvm compare operations. +data LlvmCmpOp + = LM_CMP_Eq -- ^ Equal (Signed and Unsigned) + | LM_CMP_Ne -- ^ Not equal (Signed and Unsigned) + | LM_CMP_Ugt -- ^ Unsigned greater than + | LM_CMP_Uge -- ^ Unsigned greater than or equal + | LM_CMP_Ult -- ^ Unsigned less than + | LM_CMP_Ule -- ^ Unsigned less than or equal + | LM_CMP_Sgt -- ^ Signed greater than + | LM_CMP_Sge -- ^ Signed greater than or equal + | LM_CMP_Slt -- ^ Signed less than + | LM_CMP_Sle -- ^ Signed less than or equal + + -- Float comparisons. GHC uses a mix of ordered and unordered float + -- comparisons. + | LM_CMP_Feq -- ^ Float equal + | LM_CMP_Fne -- ^ Float not equal + | LM_CMP_Fgt -- ^ Float greater than + | LM_CMP_Fge -- ^ Float greater than or equal + | LM_CMP_Flt -- ^ Float less than + | 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" + + +-- | Llvm cast operations. +data LlvmCastOp + = LM_Trunc -- ^ Integer truncate + | LM_Zext -- ^ Integer extend (zero fill) + | LM_Sext -- ^ Integer extend (sign fill) + | LM_Fptrunc -- ^ Float truncate + | LM_Fpext -- ^ Float extend + | LM_Fptoui -- ^ Float to unsigned Integer + | LM_Fptosi -- ^ Float to signed Integer + | LM_Uitofp -- ^ Unsigned Integer to Float + | LM_Sitofp -- ^ Signed Int to Float + | LM_Ptrtoint -- ^ Pointer to Integer + | LM_Inttoptr -- ^ Integer to Pointer + | 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" + + +-- ----------------------------------------------------------------------------- +-- * Floating point conversion +-- + +-- | Convert a Haskell Float to an LLVM hex encoded floating point form +fToStr :: Float -> String +fToStr f = dToStr $ realToFrac f + +-- | Convert a Haskell Double to an LLVM hex encoded floating point form +dToStr :: Double -> String +dToStr d = + let bs = doubleToBytes d + hex d' = case showHex d' "" of + [] -> error "dToStr: too few hex digits for float" + [x] -> ['0',x] + [x,y] -> [x,y] + _ -> error "dToStr: too many hex digits for float" + + str' = concat . fixEndian . (map hex) $ bs + str = map toUpper str' + in "0x" ++ str + +-- | Reverse or leave byte data alone to fix endianness on this +-- target. LLVM generally wants things in Big-Endian form +-- regardless of target architecture. +fixEndian :: [a] -> [a] +#ifdef WORDS_BIGENDIAN +fixEndian = id +#else +fixEndian = reverse +#endif + diff --git a/compiler/llvmGen/LlvmCodeGen.hs b/compiler/llvmGen/LlvmCodeGen.hs new file mode 100644 index 0000000..e0485e7 --- /dev/null +++ b/compiler/llvmGen/LlvmCodeGen.hs @@ -0,0 +1,166 @@ +-- ----------------------------------------------------------------------------- +-- | This is the top-level module in the LLVM code generator. +-- + +module LlvmCodeGen ( llvmCodeGen ) where + +#include "HsVersions.h" + +import LlvmCodeGen.Base +import LlvmCodeGen.CodeGen +import LlvmCodeGen.Data +import LlvmCodeGen.Ppr + +import Cmm +import CgUtils ( fixStgRegisters ) +import PprCmm + +import BufWrite +import DynFlags +import ErrUtils +import Outputable +import qualified Pretty as Prt +import UniqSupply + +import System.IO + +-- ----------------------------------------------------------------------------- +-- | Top-level of the llvm codegen +-- +llvmCodeGen :: DynFlags -> Handle -> UniqSupply -> [RawCmm] -> IO () +llvmCodeGen dflags h us cmms + = do + let cmm = concat $ map extractRawCmm cmms + + bufh <- newBufHandle h + + Prt.bufLeftRender bufh $ pprLlvmHeader + + env <- cmmDataLlvmGens dflags bufh cmm + cmmProcLlvmGens dflags bufh us env cmm + + bFlush bufh + + return () + + where extractRawCmm (Cmm tops) = tops + + +-- ----------------------------------------------------------------------------- +-- | Do llvm code generation on all these cmms data sections. +-- +cmmDataLlvmGens + :: DynFlags + -> BufHandle + -> [RawCmmTop] + -> IO ( LlvmEnv ) + +cmmDataLlvmGens _ _ [] + = return ( initLlvmEnv ) + +cmmDataLlvmGens dflags h cmm = + let exData (CmmData s d) = [(s,d)] + exData _ = [] + + exProclbl (CmmProc _ l _ _) = [(strCLabel_llvm l)] + exProclbl _ = [] + + cdata = concat $ map exData cmm + -- put the functions into the enviornment + cproc = concat $ map exProclbl cmm + env = foldl (\e l -> funInsert l llvmFunTy e) initLlvmEnv cproc + in cmmDataLlvmGens' dflags h env cdata [] + +cmmDataLlvmGens' + :: DynFlags + -> BufHandle + -> LlvmEnv + -> [(Section, [CmmStatic])] + -> [LlvmUnresData] + -> IO ( LlvmEnv ) + +cmmDataLlvmGens' dflags h env [] lmdata + = do + let (env', lmdata') = resolveLlvmDatas dflags env lmdata [] + let lmdoc = Prt.vcat $ map (pprLlvmData dflags) lmdata' + + dumpIfSet_dyn dflags Opt_D_dump_llvm "LLVM Code" $ docToSDoc lmdoc + + Prt.bufLeftRender h lmdoc + return env' + +cmmDataLlvmGens' dflags h env (cmm:cmms) lmdata + = do + let lmdata'@(l, ty, _) = genLlvmData dflags cmm + let env' = funInsert (strCLabel_llvm l) ty env + cmmDataLlvmGens' dflags h env' cmms (lmdata ++ [lmdata']) + + +-- ----------------------------------------------------------------------------- +-- | Do llvm code generation on all these cmms procs. +-- +cmmProcLlvmGens + :: DynFlags + -> BufHandle + -> UniqSupply + -> LlvmEnv + -> [RawCmmTop] + -> IO () + +cmmProcLlvmGens _ _ _ _ [] + = return () + +cmmProcLlvmGens dflags h us env (cmm : cmms) + = do + (us', env', llvm) <- cmmLlvmGen dflags us (clearVars env) cmm + + Prt.bufLeftRender h $ Prt.vcat $ map (pprLlvmCmmTop dflags) llvm + + cmmProcLlvmGens dflags h us' env' cmms + + +-- | Complete llvm code generation phase for a single top-level chunk of Cmm. +cmmLlvmGen + :: DynFlags + -> UniqSupply + -> LlvmEnv + -> RawCmmTop -- ^ the cmm to generate code for + -> IO ( UniqSupply, + LlvmEnv, + [LlvmCmmTop] ) -- llvm code + +cmmLlvmGen dflags us env cmm + = do + -- rewrite assignments to global regs + let fixed_cmm = fixStgRegisters cmm + + dumpIfSet_dyn dflags Opt_D_dump_opt_cmm "Optimised Cmm" + (pprCmm $ Cmm [fixed_cmm]) + + -- generate llvm code from cmm + let ((env', llvmBC), usGen) = initUs us $ genLlvmCode dflags env fixed_cmm + + dumpIfSet_dyn dflags Opt_D_dump_llvm "LLVM Code" + (vcat $ map (docToSDoc . pprLlvmCmmTop dflags) llvmBC) + + return (usGen, env', llvmBC) + + +-- ----------------------------------------------------------------------------- +-- | Instruction selection +-- +genLlvmCode + :: DynFlags + -> LlvmEnv + -> RawCmmTop + -> UniqSM (LlvmEnv, [LlvmCmmTop]) + +genLlvmCode _ env (CmmData _ _) + = return (env, []) + +genLlvmCode _ env (CmmProc _ _ _ (ListGraph [])) + = return (env, []) + +genLlvmCode _ env cp@(CmmProc _ _ _ _) + = genLlvmProc env cp + diff --git a/compiler/llvmGen/LlvmCodeGen/Base.hs b/compiler/llvmGen/LlvmCodeGen/Base.hs new file mode 100644 index 0000000..36ffa18 --- /dev/null +++ b/compiler/llvmGen/LlvmCodeGen/Base.hs @@ -0,0 +1,164 @@ +-- ---------------------------------------------------------------------------- +-- | Base LLVM Code Generation module +-- +-- Contains functions useful through out the code generator. +-- + +module LlvmCodeGen.Base ( + + LlvmCmmTop, LlvmBasicBlock, + LlvmUnresData, LlvmData, UnresLabel, UnresStatic, + + LlvmEnv, initLlvmEnv, clearVars, varLookup, varInsert, + funLookup, funInsert, + + cmmToLlvmType, widthToLlvmFloat, widthToLlvmInt, llvmFunTy, + llvmFunSig, llvmStdFunAttrs, llvmPtrBits, llvmGhcCC, + + strCLabel_llvm, + genCmmLabelRef, genStringLabelRef + + ) where + +#include "HsVersions.h" + +import Llvm +import LlvmCodeGen.Regs + +import CgUtils ( activeStgRegs ) +import CLabel +import Cmm + +import FastString +import qualified Outputable as Outp +import Unique +import UniqFM + +-- ---------------------------------------------------------------------------- +-- * Some Data Types +-- + +type LlvmCmmTop = GenCmmTop LlvmData [CmmStatic] (ListGraph LlvmStatement) +type LlvmBasicBlock = GenBasicBlock LlvmStatement + +-- | Unresolved code. +-- Of the form: (data label, data type, unresovled data) +type LlvmUnresData = (CLabel, LlvmType, [UnresStatic]) + +-- | Top level LLVM Data (globals and type aliases) +type LlvmData = ([LMGlobal], [LlvmType]) + +-- | An unresolved Label. +-- +-- Labels are unresolved when we haven't yet determined if they are defined in +-- the module we are currently compiling, or an external one. +type UnresLabel = CmmLit +type UnresStatic = Either UnresLabel LlvmStatic + +-- ---------------------------------------------------------------------------- +-- * Type translations +-- + +-- | Translate a basic CmmType to an LlvmType. +cmmToLlvmType :: CmmType -> LlvmType +cmmToLlvmType ty | isFloatType ty = widthToLlvmFloat $ typeWidth ty + | otherwise = widthToLlvmInt $ typeWidth ty + +-- | Translate a Cmm Float Width to a LlvmType. +widthToLlvmFloat :: Width -> LlvmType +widthToLlvmFloat W32 = LMFloat +widthToLlvmFloat W64 = LMDouble +widthToLlvmFloat W80 = LMFloat80 +widthToLlvmFloat W128 = LMFloat128 +widthToLlvmFloat w = panic $ "widthToLlvmFloat: Bad float size: " ++ show w + +-- | Translate a Cmm Bit Width to a LlvmType. +widthToLlvmInt :: Width -> LlvmType +widthToLlvmInt w = LMInt $ widthInBits w + +-- | GHC Call Convention for LLVM +llvmGhcCC :: LlvmCallConvention +llvmGhcCC = CC_Ncc 10 + +-- | Llvm Function type for Cmm function +llvmFunTy :: LlvmType +llvmFunTy + = LMFunction $ + LlvmFunctionDecl (fsLit "a") ExternallyVisible llvmGhcCC LMVoid FixedArgs + (Left $ map getVarType llvmFunArgs) + +-- | Llvm Function signature +llvmFunSig :: CLabel -> LlvmLinkageType -> LlvmFunctionDecl +llvmFunSig lbl link + = let n = strCLabel_llvm lbl + in LlvmFunctionDecl n link llvmGhcCC LMVoid FixedArgs + (Right llvmFunArgs) + +-- | A Function's arguments +llvmFunArgs :: [LlvmVar] +llvmFunArgs = map lmGlobalRegArg activeStgRegs + +-- | Llvm standard fun attributes +llvmStdFunAttrs :: [LlvmFuncAttr] +llvmStdFunAttrs = [NoUnwind] + +-- | Pointer width +llvmPtrBits :: Int +llvmPtrBits = widthInBits $ typeWidth gcWord + + +-- ---------------------------------------------------------------------------- +-- * Environment Handling +-- + +type LlvmEnvMap = UniqFM LlvmType +-- two maps, one for functions and one for local vars. +type LlvmEnv = (LlvmEnvMap, LlvmEnvMap) + +-- | Get initial Llvm environment. +initLlvmEnv :: LlvmEnv +initLlvmEnv = (emptyUFM, emptyUFM) + +-- | Clear variables from the environment. +clearVars :: LlvmEnv -> LlvmEnv +clearVars (e1, _) = (e1, emptyUFM) + +-- | Insert functions into the environment. +varInsert, funInsert :: Uniquable key => key -> LlvmType -> LlvmEnv -> LlvmEnv +varInsert s t (e1, e2) = (e1, addToUFM e2 s t) +funInsert s t (e1, e2) = (addToUFM e1 s t, e2) + +-- | Lookup functions in the environment. +varLookup, funLookup :: Uniquable key => key -> LlvmEnv -> Maybe LlvmType +varLookup s (_, e2) = lookupUFM e2 s +funLookup s (e1, _) = lookupUFM e1 s + + +-- ---------------------------------------------------------------------------- +-- * Label handling +-- + +-- | Pretty print a 'CLabel'. +strCLabel_llvm :: CLabel -> LMString +strCLabel_llvm l = (fsLit . show . llvmSDoc . pprCLabel) l + +-- | Create an external definition for a 'CLabel' defined in another module. +genCmmLabelRef :: CLabel -> LMGlobal +genCmmLabelRef cl = + let mcl = strCLabel_llvm cl + in (LMGlobalVar mcl (LMPointer (LMArray 0 llvmWord)) External, Nothing) + +-- | As above ('genCmmLabelRef') but taking a 'LMString', not 'CLabel'. +genStringLabelRef :: LMString -> LMGlobal +genStringLabelRef cl = + (LMGlobalVar cl (LMPointer (LMArray 0 llvmWord)) External, Nothing) + + +-- ---------------------------------------------------------------------------- +-- * Misc +-- + +-- | Error function +panic :: String -> a +panic s = Outp.panic $ "LlvmCodeGen.Base." ++ s + diff --git a/compiler/llvmGen/LlvmCodeGen/CodeGen.hs b/compiler/llvmGen/LlvmCodeGen/CodeGen.hs new file mode 100644 index 0000000..fb29f7a --- /dev/null +++ b/compiler/llvmGen/LlvmCodeGen/CodeGen.hs @@ -0,0 +1,958 @@ +-- ---------------------------------------------------------------------------- +-- | Handle conversion of CmmProc to LLVM code. +-- + +module LlvmCodeGen.CodeGen ( genLlvmProc ) where + +#include "HsVersions.h" + +import Llvm +import LlvmCodeGen.Base +import LlvmCodeGen.Regs + +import BlockId +import CgUtils ( activeStgRegs ) +import CLabel +import Cmm +import qualified PprCmm +import OrdList + +import BasicTypes +import FastString +import ForeignCall +import Outputable hiding ( panic, pprPanic ) +import qualified Outputable +import UniqSupply +import Unique +import Util + +type LlvmStatements = OrdList LlvmStatement + +-- ----------------------------------------------------------------------------- +-- | Top-level of the llvm proc codegen +-- +genLlvmProc :: LlvmEnv -> RawCmmTop -> UniqSM (LlvmEnv, [LlvmCmmTop]) +genLlvmProc env (CmmData _ _) + = return (env, []) + +genLlvmProc env (CmmProc _ _ _ (ListGraph [])) + = return (env, []) + +genLlvmProc env (CmmProc info lbl params (ListGraph blocks)) + = do + (env', lmblocks, lmdata) <- basicBlocksCodeGen env blocks ([], []) + + let proc = CmmProc info lbl params (ListGraph lmblocks) + let tops = lmdata ++ [proc] + + return (env', tops) + + +-- ----------------------------------------------------------------------------- +-- * Block code generation +-- + +-- | Generate code for a list of blocks that make up a complete procedure. +basicBlocksCodeGen :: LlvmEnv + -> [CmmBasicBlock] + -> ( [LlvmBasicBlock] , [LlvmCmmTop] ) + -> UniqSM (LlvmEnv, [LlvmBasicBlock] , [LlvmCmmTop] ) +basicBlocksCodeGen env ([]) (blocks, tops) + = do let (blocks', allocs) = mapAndUnzip dominateAllocs blocks + let allocs' = concat allocs + let ((BasicBlock id fstmts):rblocks) = blocks' + let fblocks = (BasicBlock id (funPrologue ++ allocs' ++ fstmts)):rblocks + return (env, fblocks, tops) + +basicBlocksCodeGen env (block:blocks) (lblocks', ltops') + = do (env', lb, lt) <- basicBlockCodeGen env block + let lblocks = lblocks' ++ lb + let ltops = ltops' ++ lt + basicBlocksCodeGen env' blocks (lblocks, ltops) + + +-- | Generate code for one block +basicBlockCodeGen :: LlvmEnv + -> CmmBasicBlock + -> UniqSM ( LlvmEnv, [LlvmBasicBlock], [LlvmCmmTop] ) +basicBlockCodeGen env (BasicBlock id stmts) + = do (env', instrs, top) <- stmtsToInstrs env stmts (nilOL, []) + return (env', [BasicBlock id (fromOL instrs)], top) + + +-- | Allocations need to be extracted so they can be moved to the entry +-- of a function to make sure they dominate all posible paths in the CFG. +dominateAllocs :: LlvmBasicBlock -> (LlvmBasicBlock, [LlvmStatement]) +dominateAllocs (BasicBlock id stmts) + = (BasicBlock id allstmts, allallocs) + where + (allstmts, allallocs) = foldl split ([],[]) stmts + split (stmts', allocs) s@(Assignment _ (Alloca _ _)) + = (stmts', allocs ++ [s]) + split (stmts', allocs) other + = (stmts' ++ [other], allocs) + + +-- ----------------------------------------------------------------------------- +-- * CmmStmt code generation +-- + +-- A statement conversion return data. +-- * LlvmEnv: The new enviornment +-- * LlvmStatements: The compiled llvm statements. +-- * LlvmCmmTop: Any global data needed. +type StmtData = (LlvmEnv, LlvmStatements, [LlvmCmmTop]) + + +-- | Convert a list of CmmStmt's to LlvmStatement's +stmtsToInstrs :: LlvmEnv -> [CmmStmt] -> (LlvmStatements, [LlvmCmmTop]) + -> UniqSM StmtData +stmtsToInstrs env [] (llvm, top) + = return (env, llvm, top) + +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 :: LlvmEnv -> CmmStmt + -> UniqSM StmtData +stmtToInstrs env stmt = case stmt of + + CmmNop -> return (env, nilOL, []) + CmmComment _ -> return (env, nilOL, []) -- nuke comments +-- CmmComment s -> return (env, unitOL $ Comment (lines $ unpackFS s), +-- []) + + CmmAssign reg src -> genAssign env reg src + CmmStore addr src -> genStore env addr src + + CmmBranch id -> genBranch env id + CmmCondBranch arg id -> genCondBranch env arg id + CmmSwitch arg ids -> genSwitch env arg ids + + -- Foreign Call + CmmCall target res args _ ret + -> genCall env target res args ret + + -- Tail call + CmmJump arg _ -> genJump env arg + + -- CPS, only tail calls, no return's + -- Actually, there are a few return statements that occur because of hand + -- written cmm code. + CmmReturn _ + -> return (env, unitOL $ Return Nothing, []) + + +-- | Foreign Calls +genCall :: LlvmEnv -> CmmCallTarget -> HintedCmmFormals -> HintedCmmActuals + -> CmmReturnInfo -> UniqSM StmtData + +-- Write barrier needs to be handled specially as it is implemented as an llvm +-- intrinsic function. +genCall env (CmmPrim MO_WriteBarrier) _ _ _ = do + let fname = fsLit "llvm.memory.barrier" + let funSig = + LlvmFunctionDecl + fname + ExternallyVisible + CC_Ccc + LMVoid + FixedArgs + (Left [i1, i1, i1, i1, i1]) + let fty = LMFunction funSig + + let fv = LMGlobalVar fname fty (funcLinkage funSig) + let tops = case funLookup fname env of + Just _ -> [] + Nothing -> [CmmData Data [([],[fty])]] + + 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) + + where + lmTrue :: LlvmVar + lmTrue = LMLitVar $ LMIntLit (-1) i1 + +-- Handle all other foreign calls and prim ops. +genCall env target res args ret = do + + -- paramater types + let arg_type (CmmHinted _ AddrHint) = pLift i8 + -- cast pointers to i8*. Llvm equivalent of void* + arg_type (CmmHinted expr _ ) = cmmToLlvmType $ cmmExprType expr + + -- ret type + let ret_type ([]) = LMVoid + ret_type ([CmmHinted _ AddrHint]) = pLift i8 + ret_type ([CmmHinted reg _]) = cmmToLlvmType $ localRegType reg + ret_type t = panic $ "genCall: Too many return values! Can only handle" + ++ " 0 or 1, given " ++ show (length t) ++ "." + + -- extract cmm call convention + let cconv = case target of + CmmCallee _ conv -> conv + CmmPrim _ -> PrimCallConv + + -- translate to llvm call convention + let lmconv = case cconv of +#if i386_TARGET_ARCH || x86_64_TARGET_ARCH + StdCallConv -> CC_X86_Stdcc +#else + StdCallConv -> CC_Ccc +#endif + CCallConv -> CC_Ccc + PrimCallConv -> CC_Ccc + CmmCallConv -> panic "CmmCallConv not supported here!" + + {- + Some of the possibilities here are a worry with the use of a custom + calling convention for passing STG args. In practice the more + dangerous combinations (e.g StdCall + llvmGhcCC) don't occur. + + The native code generator only handles StdCall and CCallConv. + -} + + -- call attributes + let fnAttrs | ret == CmmNeverReturns = NoReturn : llvmStdFunAttrs + | otherwise = llvmStdFunAttrs + + -- fun type + let ccTy = StdCall -- tail calls should be done through CmmJump + let retTy = ret_type res + let argTy = Left $ map arg_type args + let funTy name = LMFunction $ + LlvmFunctionDecl name ExternallyVisible lmconv retTy FixedArgs argTy + + -- get paramter values + (env1, argVars, stmts1, top1) <- arg_vars env args ([], nilOL, []) + + -- get the return register + let ret_reg ([CmmHinted reg hint]) = (reg, hint) + ret_reg t = panic $ "genCall: Bad number of registers! Can only handle" + ++ " 1, given " ++ show (length t) ++ "." + + -- deal with call types + let getFunPtr :: CmmCallTarget -> UniqSM ExprData + getFunPtr targ = case targ of + CmmCallee (CmmLit (CmmLabel lbl)) _ -> do + let name = strCLabel_llvm lbl + case funLookup name env1 of + Just ty'@(LMFunction sig) -> do + -- Function in module in right form + let fun = LMGlobalVar name ty' (funcLinkage sig) + return (env1, fun, nilOL, []) + + Just _ -> do + -- label in module but not function pointer, convert + let fty@(LMFunction sig) = funTy name + let fun = LMGlobalVar name fty (funcLinkage sig) + (v1, s1) <- doExpr (pLift fty) + $ Cast LM_Bitcast fun (pLift fty) + return (env1, v1, unitOL s1, []) + + Nothing -> do + -- label not in module, create external reference + let fty@(LMFunction sig) = funTy name + let fun = LMGlobalVar name fty (funcLinkage sig) + let top = CmmData Data [([],[fty])] + let env' = funInsert name fty env1 + return (env', fun, nilOL, [top]) + + CmmCallee expr _ -> do + (env', v1, stmts, top) <- exprToVar env1 expr + let fty = funTy $ fsLit "dynamic" + let 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) ++ ")" + + (v2,s1) <- doExpr (pLift fty) $ Cast cast v1 (pLift fty) + return (env', v2, stmts `snocOL` s1, top) + + CmmPrim mop -> do + let name = cmmPrimOpFunctions mop + let lbl = mkForeignLabel name Nothing + ForeignLabelInExternalPackage IsFunction + getFunPtr $ CmmCallee (CmmLit (CmmLabel lbl)) CCallConv + + (env2, fptr, stmts2, top2) <- getFunPtr target + + let retStmt | ccTy == TailCall = unitOL $ Return Nothing + | ret == CmmNeverReturns = unitOL $ Unreachable + | otherwise = nilOL + + -- make the actual call + case retTy of + LMVoid -> do + let s1 = Expr $ Call ccTy fptr argVars fnAttrs + let allStmts = stmts1 `appOL` stmts2 `snocOL` s1 `appOL` retStmt + return (env2, allStmts, top1 ++ top2) + + _ -> do + let (creg, _) = ret_reg res + let (env3, vreg, stmts3, top3) = getCmmReg env2 (CmmLocal creg) + let allStmts = stmts1 `appOL` stmts2 `appOL` stmts3 + (v1, s1) <- doExpr retTy $ Call ccTy fptr argVars fnAttrs + if retTy == pLower (getVarType vreg) + then do + let s2 = Store v1 vreg + return (env3, allStmts `snocOL` s1 `snocOL` s2 + `appOL` retStmt, top1 ++ top2 ++ top3) + else do + let ty = pLower $ getVarType vreg + let op = case ty of + vt | isPointer vt -> LM_Bitcast + | isInt vt -> LM_Ptrtoint + | otherwise -> + panic $ "genCall: CmmReg bad match for" + ++ " returned type!" + + (v2, s2) <- doExpr ty $ Cast op v1 ty + let s3 = Store v2 vreg + return (env3, allStmts `snocOL` s1 `snocOL` s2 `snocOL` s3 + `appOL` retStmt, top1 ++ top2 ++ top3) + + +-- | Conversion of call arguments. +arg_vars :: LlvmEnv + -> HintedCmmActuals + -> ([LlvmVar], LlvmStatements, [LlvmCmmTop]) + -> UniqSM (LlvmEnv, [LlvmVar], LlvmStatements, [LlvmCmmTop]) + +arg_vars env [] (vars, stmts, tops) + = return (env, vars, stmts, tops) + +arg_vars env (CmmHinted e AddrHint:rest) (vars, stmts, tops) + = do (env', v1, stmts', top') <- exprToVar env e + 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 ++ ")" + + (v2, s1) <- doExpr (pLift i8) $ Cast op v1 (pLift i8) + arg_vars env' rest (vars ++ [v2], stmts `appOL` stmts' `snocOL` s1, tops ++ top') + +arg_vars env (CmmHinted e _:rest) (vars, stmts, tops) + = do (env', v1, stmts', top') <- exprToVar env e + arg_vars env' rest (vars ++ [v1], stmts `appOL` stmts', tops ++ top') + +-- | Decide what C function to use to implement a CallishMachOp +cmmPrimOpFunctions :: CallishMachOp -> FastString +cmmPrimOpFunctions mop + = case mop of + MO_F32_Exp -> fsLit "expf" + MO_F32_Log -> fsLit "logf" + MO_F32_Sqrt -> fsLit "sqrtf" + MO_F32_Pwr -> fsLit "powf" + + MO_F32_Sin -> fsLit "sinf" + MO_F32_Cos -> fsLit "cosf" + MO_F32_Tan -> fsLit "tanf" + + MO_F32_Asin -> fsLit "asinf" + MO_F32_Acos -> fsLit "acosf" + MO_F32_Atan -> fsLit "atanf" + + MO_F32_Sinh -> fsLit "sinhf" + MO_F32_Cosh -> fsLit "coshf" + MO_F32_Tanh -> fsLit "tanhf" + + MO_F64_Exp -> fsLit "exp" + MO_F64_Log -> fsLit "log" + MO_F64_Sqrt -> fsLit "sqrt" + MO_F64_Pwr -> fsLit "pow" + + MO_F64_Sin -> fsLit "sin" + MO_F64_Cos -> fsLit "cos" + MO_F64_Tan -> fsLit "tan" + + MO_F64_Asin -> fsLit "asin" + MO_F64_Acos -> fsLit "acos" + MO_F64_Atan -> fsLit "atan" + + MO_F64_Sinh -> fsLit "sinh" + MO_F64_Cosh -> fsLit "cosh" + MO_F64_Tanh -> fsLit "tanh" + + a -> panic $ "cmmPrimOpFunctions: Unknown callish op! (" ++ show a ++ ")" + + +-- | Tail function calls +genJump :: LlvmEnv -> CmmExpr -> UniqSM StmtData + +-- Call to known function +genJump env (CmmLit (CmmLabel lbl)) = do + (env', vf, stmts, top) <- getHsFunc env lbl + (stgRegs, stgStmts) <- funEpilogue + let s1 = Expr $ Call TailCall vf stgRegs llvmStdFunAttrs + let s2 = Return Nothing + return (env', stmts `appOL` stgStmts `snocOL` s1 `snocOL` s2, top) + + +-- Call to unknown function / address +genJump env expr = do + let fty = llvmFunTy + (env', vf, stmts, top) <- exprToVar env expr + + 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) ++ ")" + + (v1, s1) <- doExpr (pLift fty) $ Cast cast vf (pLift fty) + (stgRegs, stgStmts) <- funEpilogue + let s2 = Expr $ Call TailCall v1 stgRegs llvmStdFunAttrs + let s3 = Return Nothing + return (env', stmts `snocOL` s1 `appOL` stgStmts `snocOL` s2 `snocOL` s3, + top) + + +-- | CmmAssign operation +-- +-- 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 (env1, vreg, stmts1, top1) = getCmmReg env reg + (env2, vval, stmts2, top2) <- exprToVar env1 val + let s1 = Store vval vreg + return (env2, stmts1 `appOL` stmts2 `snocOL` s1, top1 ++ top2) + + +-- | CmmStore operation +genStore :: LlvmEnv -> CmmExpr -> CmmExpr -> UniqSM StmtData +genStore env addr val = do + (env1, vaddr, stmts1, top1) <- exprToVar env addr + (env2, vval, stmts2, top2) <- exprToVar env1 val + if getVarType vaddr == llvmWord + then do + let vty = pLift $ getVarType vval + (vptr, s1) <- doExpr vty $ Cast LM_Inttoptr vaddr vty + let s2 = Store vval vptr + return (env2, stmts1 `appOL` stmts2 `snocOL` s1 `snocOL` s2, + top1 ++ top2) + + else + panic $ "genStore: ptr not of word size! (" ++ show vaddr ++ ")" + + +-- | Unconditional branch +genBranch :: LlvmEnv -> BlockId -> UniqSM StmtData +genBranch env id = + let label = blockIdToLlvm id + in return (env, unitOL $ Branch label, []) + + +-- | Conditional branch +genCondBranch :: LlvmEnv -> CmmExpr -> BlockId -> UniqSM StmtData +genCondBranch env cond idT = do + idF <- getUniqueUs + let labelT = blockIdToLlvm idT + let labelF = LMLocalVar idF LMLabel + (env', vc, stmts, top) <- exprToVarOpt env i1Option cond + if getVarType vc == i1 + then do + let s1 = BranchIf vc labelT labelF + let s2 = MkLabel idF + return $ (env', stmts `snocOL` s1 `snocOL` s2, top) + else + panic $ "genCondBranch: Cond expr not bool! (" ++ show vc ++ ")" + + +-- | Switch branch +-- +-- 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 + let ty = getVarType vc + + let pairs = [ (ix, id) | (ix,Just id) <- zip ([0..]::[Integer]) maybe_ids ] + let labels = map (\(ix, b) -> (mkIntLit ix ty, blockIdToLlvm b)) pairs + -- out of range is undefied, so lets just branch to first label + let (_, defLbl) = head labels + + let s1 = Switch vc defLbl labels + return $ (env', stmts `snocOL` s1, top) + + +-- ----------------------------------------------------------------------------- +-- * CmmExpr code generation +-- + +-- | 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 +-- * LlvmCmmTop: Any global data needed for this expression +type ExprData = (LlvmEnv, LlvmVar, LlvmStatements, [LlvmCmmTop]) + +-- | Values which can be passed to 'exprToVar' to configure its +-- behaviour in certain circumstances. +data EOption = EOption { + -- | The expected LlvmType for the returned variable. + -- + -- Currently just used for determining if a comparison should return + -- a boolean (i1) or a int (i32/i64). + eoExpectedType :: Maybe LlvmType + } + +i1Option :: EOption +i1Option = EOption (Just i1) + +wordOption :: EOption +wordOption = EOption (Just llvmWord) + + +-- | 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 + +exprToVarOpt :: LlvmEnv -> EOption -> CmmExpr -> UniqSM ExprData +exprToVarOpt env opt e = case e of + + CmmLit lit + -> genLit env lit + + CmmLoad e' ty + -> genCmmLoad env 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 + return (env', v1, stmts `snocOL` s1 , top) + + CmmMachOp op exprs + -> genMachOp env opt op exprs + + CmmRegOff r i + -> exprToVar env $ expandCmmReg (r, i) + + CmmStackSlot _ _ + -> panic "exprToVar: CmmStackSlot not supported!" + + +-- | Handle CmmMachOp expressions +genMachOp :: LlvmEnv -> EOption -> MachOp -> [CmmExpr] -> UniqSM ExprData + +-- Unary Machop +genMachOp env _ op [x] = case op of + + MO_Not w -> + let all1 = mkIntLit (-1::Int) (widthToLlvmInt w) + in negate (widthToLlvmInt w) all1 LM_MO_Xor + + MO_S_Neg w -> + let all0 = mkIntLit (0::Int) (widthToLlvmInt w) + in negate (widthToLlvmInt w) all0 LM_MO_Sub + + MO_F_Neg w -> + let all0 = LMLitVar $ LMFloatLit 0 (widthToLlvmFloat w) + in negate (widthToLlvmFloat w) all0 LM_MO_Sub + + MO_SF_Conv _ w -> fiConv (widthToLlvmFloat w) LM_Sitofp + MO_FS_Conv _ w -> fiConv (widthToLlvmInt w) LM_Fptosi + + MO_SS_Conv from to + -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Sext + + MO_UU_Conv from to + -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Zext + + MO_FF_Conv from to + -> sameConv from (widthToLlvmFloat to) LM_Fptrunc LM_Fpext + + a -> panic $ "genMachOp: unmatched unary CmmMachOp! (" ++ show a ++ ")" + + where + negate ty v2 negOp = do + (env', vx, stmts, top) <- exprToVar env x + (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx + return (env', v1, stmts `snocOL` s1, top) + + fiConv ty convOp = do + (env', vx, stmts, top) <- exprToVar env x + (v1, s1) <- doExpr ty $ Cast convOp vx ty + return (env', v1, stmts `snocOL` s1, top) + + sameConv from ty reduce expand = do + x'@(env', vx, stmts, top) <- exprToVar env x + let sameConv' op = do + (v1, s1) <- doExpr ty $ Cast op vx ty + return (env', v1, stmts `snocOL` s1, top) + let toWidth = llvmWidthInBits 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. + case widthInBits from of + w | w < toWidth -> sameConv' expand + w | w > toWidth -> sameConv' reduce + _w -> return x' + + +-- Binary MachOp +genMachOp env opt op [x, y] = case op of + + MO_Eq _ -> genBinComp opt LM_CMP_Eq + MO_Ne _ -> genBinComp opt LM_CMP_Ne + + MO_S_Gt _ -> genBinComp opt LM_CMP_Sgt + MO_S_Ge _ -> genBinComp opt LM_CMP_Sge + MO_S_Lt _ -> genBinComp opt LM_CMP_Slt + MO_S_Le _ -> genBinComp opt LM_CMP_Sle + + MO_U_Gt _ -> genBinComp opt LM_CMP_Ugt + MO_U_Ge _ -> genBinComp opt LM_CMP_Uge + MO_U_Lt _ -> genBinComp opt LM_CMP_Ult + MO_U_Le _ -> genBinComp opt LM_CMP_Ule + + MO_Add _ -> genBinMach LM_MO_Add + MO_Sub _ -> genBinMach LM_MO_Sub + MO_Mul _ -> genBinMach LM_MO_Mul + + MO_U_MulMayOflo _ -> panic "genMachOp: MO_U_MulMayOflo unsupported!" + + MO_S_MulMayOflo w -> isSMulOK w x y + + MO_S_Quot _ -> genBinMach LM_MO_SDiv + MO_S_Rem _ -> genBinMach LM_MO_SRem + + MO_U_Quot _ -> genBinMach LM_MO_UDiv + MO_U_Rem _ -> genBinMach LM_MO_URem + + MO_F_Eq _ -> genBinComp opt LM_CMP_Feq + MO_F_Ne _ -> genBinComp opt LM_CMP_Fne + MO_F_Gt _ -> genBinComp opt LM_CMP_Fgt + MO_F_Ge _ -> genBinComp opt LM_CMP_Fge + MO_F_Lt _ -> genBinComp opt LM_CMP_Flt + MO_F_Le _ -> genBinComp opt LM_CMP_Fle + + MO_F_Add _ -> genBinMach LM_MO_Add + MO_F_Sub _ -> genBinMach LM_MO_Sub + MO_F_Mul _ -> genBinMach LM_MO_Mul + MO_F_Quot _ -> genBinMach LM_MO_FDiv + + MO_And _ -> genBinMach LM_MO_And + MO_Or _ -> genBinMach LM_MO_Or + MO_Xor _ -> genBinMach LM_MO_Xor + MO_Shl _ -> genBinMach LM_MO_Shl + MO_U_Shr _ -> genBinMach LM_MO_LShr + MO_S_Shr _ -> genBinMach LM_MO_AShr + + a -> panic $ "genMachOp: unmatched binary CmmMachOp! (" ++ show a ++ ")" + + where + binLlvmOp ty binOp = do + (env1, vx, stmts1, top1) <- exprToVar env x + (env2, vy, stmts2, top2) <- exprToVar env1 y + if getVarType vx == getVarType vy + then do + (v1, s1) <- doExpr (ty vx) $ binOp vx vy + return (env2, v1, stmts1 `appOL` stmts2 `snocOL` s1, + top1 ++ top2) + + else do + -- XXX: Error. Continue anyway so we can debug the generated + -- ll file. + let cmmToStr = (lines . show . llvmSDoc . PprCmm.pprExpr) + let dx = Comment $ map fsLit $ cmmToStr x + let dy = Comment $ map fsLit $ cmmToStr y + (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) + + -- let o = case binOp vx vy of + -- Compare op _ _ -> show op + -- LlvmOp op _ _ -> show op + -- _ -> "unknown" + -- panic $ "genMachOp: comparison between different types (" + -- ++ o ++ " "++ show vx ++ ", " ++ show vy ++ ")" + -- ++ "\ne1: " ++ (show.llvmSDoc.PprCmm.pprExpr $ x) + -- ++ "\ne2: " ++ (show.llvmSDoc.PprCmm.pprExpr $ y) + + -- | 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 + genBinComp opt cmp = do + ed@(env', v1, stmts, top) <- binLlvmOp (\_ -> i1) $ Compare cmp + + if getVarType v1 == i1 + then + case eoExpectedType opt of + Nothing -> + return ed + + Just t | t == i1 -> + return ed + + | isInt t -> do + (v2, s1) <- doExpr t $ Cast LM_Zext v1 t + return (env', v2, stmts `snocOL` s1, top) + + | otherwise -> + panic $ "genBinComp: Can't case i1 compare" + ++ "res to non int type " ++ show (t) + else + panic $ "genBinComp: Compare returned type other then i1! " + ++ (show $ getVarType v1) + + genBinMach op = binLlvmOp getVarType (LlvmOp op) + + -- | Detect if overflow will occur in signed multiply of the two + -- 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 _ x y = do + (env1, vx, stmts1, top1) <- exprToVar env x + (env2, vy, stmts2, top2) <- exprToVar env1 y + + let word = getVarType vx + let word2 = LMInt $ 2 * (llvmWidthInBits $ getVarType vx) + let shift = llvmWidthInBits word + let shift1 = mkIntLit (shift - 1) llvmWord + let shift2 = mkIntLit shift llvmWord + + if isInt word + then do + (x1, s1) <- doExpr word2 $ Cast LM_Sext vx word2 + (y1, s2) <- doExpr word2 $ Cast LM_Sext vy word2 + (r1, s3) <- doExpr word2 $ LlvmOp LM_MO_Mul x1 y1 + (rlow1, s4) <- doExpr word $ Cast LM_Trunc r1 word + (rlow2, s5) <- doExpr word $ LlvmOp LM_MO_AShr rlow1 shift1 + (rhigh1, s6) <- doExpr word2 $ LlvmOp LM_MO_AShr r1 shift2 + (rhigh2, s7) <- doExpr word $ Cast LM_Trunc rhigh1 word + (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, + top1 ++ top2) + + else + panic $ "isSMulOK: Not bit type! (" ++ show word ++ ")" + + +-- More then two expression, invalid! +genMachOp _ _ _ _ = panic "genMachOp: More then 2 expressions in MachOp!" + + +-- | Handle CmmLoad expression +genCmmLoad :: LlvmEnv -> CmmExpr -> CmmType -> UniqSM ExprData +genCmmLoad env e ty = do + (env', iptr, stmts, tops) <- exprToVar env e + let ety = getVarType iptr + case (isInt ety) of + True | llvmPtrBits == llvmWidthInBits ety -> do + let pty = LMPointer $ cmmToLlvmType ty + (ptr, cast) <- doExpr pty $ Cast LM_Inttoptr iptr pty + (dvar, load) <- doExpr (cmmToLlvmType ty) $ Load ptr + return (env', dvar, stmts `snocOL` cast `snocOL` load, tops) + + | otherwise + -> pprPanic + ("exprToVar: can't cast to pointer as int not of " + ++ "pointer size!") + (PprCmm.pprExpr e <+> text ( + "Size of Ptr: " ++ show llvmPtrBits ++ + ", Size of var: " ++ show (llvmWidthInBits ety) ++ + ", Var: " ++ show iptr)) + + False -> panic "exprToVar: CmmLoad expression is not of type int!" + + +-- | 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 g, nilOL, []) + + +-- | Allocate a CmmReg on the stack +allocReg :: CmmReg -> (LlvmVar, LlvmStatements) +allocReg (CmmLocal (LocalReg un ty)) + = let ty' = cmmToLlvmType ty + var = LMLocalVar un (LMPointer ty') + alc = Alloca ty' 1 + in (var, unitOL $ Assignment var alc) + +allocReg _ = panic $ "allocReg: Global reg encountered! Global registers should" + ++ " have been handled elsewhere!" + + +-- | Generate code for a literal +genLit :: LlvmEnv -> CmmLit -> UniqSM ExprData +genLit env (CmmInt i w) + = return (env, mkIntLit i (LMInt $ widthInBits w), nilOL, []) + +genLit env (CmmFloat r w) + = return (env, LMLitVar $ LMFloatLit r (widthToLlvmFloat w), nilOL, []) + +genLit env cmm@(CmmLabel l) + = let label = strCLabel_llvm l + ty = funLookup label env + lmty = cmmToLlvmType $ cmmLitType cmm + in case ty of + -- Make generic external label defenition and then pointer to it + Nothing -> do + let glob@(var, _) = genStringLabelRef label + let ldata = [CmmData Data [([glob], [])]] + let env' = funInsert label (pLower $ getVarType var) env + (v1, s1) <- doExpr lmty $ Cast LM_Ptrtoint var llvmWord + 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 + (v1, s1) <- doExpr lmty $ Cast LM_Ptrtoint var llvmWord + return (env, v1, unitOL s1, []) + +genLit env (CmmLabelOff label off) = do + (env', vlbl, stmts, stat) <- genLit env (CmmLabel label) + let voff = mkIntLit off llvmWord + (v1, s1) <- doExpr (getVarType vlbl) $ LlvmOp LM_MO_Add vlbl voff + return (env', v1, stmts `snocOL` s1, stat) + +genLit env (CmmLabelDiffOff l1 l2 off) = do + (env1, vl1, stmts1, stat1) <- genLit env (CmmLabel l1) + (env2, vl2, stmts2, stat2) <- genLit env1 (CmmLabel l2) + let voff = mkIntLit off llvmWord + let ty1 = getVarType vl1 + let ty2 = getVarType vl2 + if (isInt ty1) && (isInt ty2) + && (llvmWidthInBits ty1 == llvmWidthInBits ty2) + + 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, + stat1 ++ stat2) + + else + panic "genLit: CmmLabelDiffOff encountered with different label ty!" + +genLit env (CmmBlock b) + = genLit env (CmmLabel $ infoTblLbl b) + +genLit _ CmmHighStackMark + = panic "genStaticLit - CmmHighStackMark unsupported!" + + +-- ----------------------------------------------------------------------------- +-- * Misc +-- + +-- | Function prologue. Load STG arguments into variables for function. +funPrologue :: [LlvmStatement] +funPrologue = concat $ map getReg activeStgRegs + where getReg rr = + let reg = lmGlobalRegVar rr + arg = lmGlobalRegArg rr + alloc = Assignment reg $ Alloca (pLower $ getVarType reg) 1 + store = Store arg reg + in [alloc, store] + + +-- | Function epilogue. Load STG variables to use as argument for call. +funEpilogue :: UniqSM ([LlvmVar], LlvmStatements) +funEpilogue = do + let loadExpr r = do + (v,s) <- doExpr (pLower $ getVarType r) $ Load r + return (v, unitOL s) + loads <- mapM (loadExpr . lmGlobalRegVar) activeStgRegs + let (vars, stmts) = unzip loads + return (vars, concatOL stmts) + + +-- | 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 -> CLabel -> UniqSM ExprData +getHsFunc env lbl + = let fname = strCLabel_llvm lbl + ty = funLookup fname env + in case ty of + Just ty'@(LMFunction sig) -> do + -- Function in module in right form + let fun = LMGlobalVar fname ty' (funcLinkage sig) + return (env, fun, nilOL, []) + Just ty' -> do + -- label in module but not function pointer, convert + let fun = LMGlobalVar fname (pLift ty') ExternallyVisible + (v1, s1) <- doExpr (pLift llvmFunTy) $ Cast LM_Bitcast fun (pLift llvmFunTy) + return (env, v1, unitOL s1, []) + Nothing -> do + -- label not in module, create external reference + let ty' = LMFunction $ llvmFunSig lbl ExternallyVisible + let fun = LMGlobalVar fname ty' ExternallyVisible + let top = CmmData Data [([],[ty'])] + let env' = funInsert fname ty' env + return (env', fun, nilOL, [top]) + + +-- | Create a new local var +mkLocalVar :: LlvmType -> UniqSM LlvmVar +mkLocalVar ty = do + un <- getUniqueUs + return $ LMLocalVar un ty + + +-- | Execute an expression, assigning result to a var +doExpr :: LlvmType -> LlvmExpression -> UniqSM (LlvmVar, LlvmStatement) +doExpr ty expr = do + v <- mkLocalVar ty + return (v, Assignment v expr) + + +-- | Expand CmmRegOff +expandCmmReg :: (CmmReg, Int) -> CmmExpr +expandCmmReg (reg, off) + = let width = typeWidth (cmmRegType reg) + voff = CmmLit $ CmmInt (fromIntegral off) width + in CmmMachOp (MO_Add width) [CmmReg reg, voff] + + +-- | Convert a block id into a appropriate Llvm label +blockIdToLlvm :: BlockId -> LlvmVar +blockIdToLlvm bid = LMLocalVar (getUnique bid) LMLabel + + +-- | Create Llvm int Literal +mkIntLit :: Integral a => a -> LlvmType -> LlvmVar +mkIntLit i ty = LMLitVar $ LMIntLit (toInteger i) ty + + +-- | Error functions +panic :: String -> a +panic s = Outputable.panic $ "LlvmCodeGen.CodeGen." ++ s + +pprPanic :: String -> SDoc -> a +pprPanic s d = Outputable.pprPanic ("LlvmCodeGen.CodeGen." ++ s) d + diff --git a/compiler/llvmGen/LlvmCodeGen/Data.hs b/compiler/llvmGen/LlvmCodeGen/Data.hs new file mode 100644 index 0000000..a5b82aa --- /dev/null +++ b/compiler/llvmGen/LlvmCodeGen/Data.hs @@ -0,0 +1,190 @@ +-- ---------------------------------------------------------------------------- +-- | Handle conversion of CmmData to LLVM code. +-- + +module LlvmCodeGen.Data ( + genLlvmData, resolveLlvmDatas, resolveLlvmData + ) where + +#include "HsVersions.h" + +import Llvm +import LlvmCodeGen.Base + +import BlockId +import CLabel +import Cmm + +import DynFlags +import FastString +import qualified Outputable + +import Data.Maybe + + +-- ---------------------------------------------------------------------------- +-- * Constants +-- + +-- | The string appended to a variable name to create its structure type alias +structStr :: LMString +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 :: DynFlags -> (Section, [CmmStatic]) -> LlvmUnresData +genLlvmData _ ( _ , (CmmDataLabel lbl):xs) = + let static = map genData xs + label = strCLabel_llvm lbl + + types = map getStatTypes static + getStatTypes (Left x) = cmmToLlvmType $ cmmLitType x + getStatTypes (Right x) = getStatType x + + strucTy = LMStruct types + alias = LMAlias (label `appendFS` structStr) strucTy + in (lbl, alias, static) + +genLlvmData _ _ = panic "genLlvmData: CmmData section doesn't start with label!" + +resolveLlvmDatas :: DynFlags -> LlvmEnv -> [LlvmUnresData] -> [LlvmData] + -> (LlvmEnv, [LlvmData]) +resolveLlvmDatas _ env [] ldata + = (env, ldata) + +resolveLlvmDatas dflags env (udata : rest) ldata + = let (env', ndata) = resolveLlvmData dflags env udata + in resolveLlvmDatas dflags env' rest (ldata ++ [ndata]) + +-- | Fix up CLabel references now that we should have passed all CmmData. +resolveLlvmData :: DynFlags -> LlvmEnv -> LlvmUnresData -> (LlvmEnv, LlvmData) +resolveLlvmData _ env (lbl, alias, unres) = + let (env', static, refs) = resDatas env unres ([], []) + refs' = catMaybes refs + struct = Just $ LMStaticStruc static alias + label = strCLabel_llvm lbl + link = if (externallyVisibleCLabel lbl) + then ExternallyVisible else Internal + glob = LMGlobalVar label alias link + in (env', (refs' ++ [(glob, struct)], [alias])) + + +-- ---------------------------------------------------------------------------- +-- ** Resolve Data/CLabel references +-- + +-- | Resolve data list +resDatas :: LlvmEnv -> [UnresStatic] -> ([LlvmStatic], [Maybe LMGlobal]) + -> (LlvmEnv, [LlvmStatic], [Maybe LMGlobal]) + +resDatas env [] (stat, glob) + = (env, stat, 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 defenition for the referenced label and +-- then make a pointer. +resData :: LlvmEnv -> UnresStatic -> (LlvmEnv, LlvmStatic, [Maybe LMGlobal]) + +resData env (Right stat) = (env, stat, [Nothing]) + +resData env (Left cmm@(CmmLabel l)) = + let label = strCLabel_llvm l + ty = funLookup label env + lmty = cmmToLlvmType $ cmmLitType cmm + in case ty of + -- Make generic external label defenition and then pointer to it + Nothing -> + let glob@(var, _) = genStringLabelRef label + env' = funInsert label (pLower $ getVarType var) env + ptr = LMStaticPointer var + in (env', LMPtoI ptr lmty, [Just glob]) + -- Referenced data exists in this module, retrieve type and make + -- pointer to it. + Just ty' -> + let var = LMGlobalVar label (LMPointer ty') ExternallyVisible + ptr = LMStaticPointer var + in (env, LMPtoI ptr lmty, [Nothing]) + +resData env (Left (CmmLabelOff label off)) = + let (env', var, glob) = resData env (Left (CmmLabel label)) + offset = LMStaticLit $ LMIntLit (toInteger off) llvmWord + in (env', LMAdd var offset, glob) + +resData env (Left (CmmLabelDiffOff l1 l2 off)) = + let (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 + in (env2, LMAdd var offset, glob1 ++ glob2) + +resData _ _ = panic "resData: Non CLabel expr as left type!" + +-- ---------------------------------------------------------------------------- +-- * Generate static data +-- + +-- | Handle static data +-- Don't handle 'CmmAlign' or a 'CmmDataLabel'. +genData :: CmmStatic -> UnresStatic + +genData (CmmString str) = + let v = map (\x -> LMStaticLit $ LMIntLit (fromIntegral x) i8) str + ve = v ++ [LMStaticLit $ LMIntLit 0 i8] + in Right $ LMStaticArray ve (LMArray (length ve) i8) + +genData (CmmUninitialised bytes) + = Right $ LMUninitType (LMArray bytes i8) + +genData (CmmStaticLit lit) + = genStaticLit lit + +genData (CmmAlign _) + = panic "genData: Can't handle CmmAlign!" + +genData (CmmDataLabel _) + = panic "genData: Can't handle data labels not at top of data!" + + +-- | Generate Llvm code for a static literal. +-- +-- Will either generate the code or leave it unresolved if it is a 'CLabel' +-- which isn't yet known. +genStaticLit :: CmmLit -> UnresStatic +genStaticLit (CmmInt i w) + = Right $ LMStaticLit (LMIntLit i (LMInt $ widthInBits w)) + +genStaticLit (CmmFloat r w) + = Right $ LMStaticLit (LMFloatLit r (widthToLlvmFloat w)) + +-- Leave unresolved, will fix later +genStaticLit c@(CmmLabel _ ) = Left $ c +genStaticLit c@(CmmLabelOff _ _) = Left $ c +genStaticLit c@(CmmLabelDiffOff _ _ _) = Left $ c + +genStaticLit (CmmBlock b) = Left $ CmmLabel $ infoTblLbl b + +genStaticLit (CmmHighStackMark) + = panic "genStaticLit: CmmHighStackMark unsupported!" + + +-- ----------------------------------------------------------------------------- +-- * Misc +-- + +-- | Error Function +panic :: String -> a +panic s = Outputable.panic $ "LlvmCodeGen.Data." ++ s + diff --git a/compiler/llvmGen/LlvmCodeGen/Ppr.hs b/compiler/llvmGen/LlvmCodeGen/Ppr.hs new file mode 100644 index 0000000..bccc336 --- /dev/null +++ b/compiler/llvmGen/LlvmCodeGen/Ppr.hs @@ -0,0 +1,91 @@ +-- ---------------------------------------------------------------------------- +-- | Pretty print helpers for the LLVM Code generator. +-- + +module LlvmCodeGen.Ppr ( + pprLlvmHeader, pprLlvmCmmTop, pprLlvmData + ) where + +#include "HsVersions.h" + +import Llvm +import LlvmCodeGen.Base +import LlvmCodeGen.Data + +import CLabel +import Cmm + +import DynFlags +import Pretty +import Unique + +-- ---------------------------------------------------------------------------- +-- * Top level +-- + +-- | LLVM module layout description for the host target +moduleLayout :: Doc +moduleLayout = +#ifdef i386_TARGET_ARCH + +#ifdef darwin_TARGET_OS + (text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128\"") + $+$ (text "target triple = \"i386-apple-darwin9.8\"") +#else + (text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32\"") + $+$ (text "target triple = \"i386-linux-gnu\"") +#endif + +#else + +#ifdef x86_64_TARGET_ARCH + (text "target datalayout = \"e-p:64:64:64-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:128:128-a0:0:64-s0:64:64-f80:128:128\"") + $+$ (text "target triple = \"x86_64-linux-gnu\"") + +#else /* Not i386 */ + -- FIX: Other targets + empty +#endif + +#endif + +-- | Header code for LLVM modules +pprLlvmHeader :: Doc +pprLlvmHeader = moduleLayout + +-- | Pretty print LLVM code +pprLlvmCmmTop :: DynFlags -> LlvmCmmTop -> Doc +pprLlvmCmmTop dflags (CmmData _ lmdata) + = vcat $ map (pprLlvmData dflags) lmdata + +pprLlvmCmmTop dflags (CmmProc info lbl _ (ListGraph blocks)) + = ( + let static = CmmDataLabel (entryLblToInfoLbl lbl) : info + in if not (null info) + then pprCmmStatic dflags static + else empty + ) $+$ ( + let link = if (externallyVisibleCLabel lbl) + then ExternallyVisible else Internal + funDec = llvmFunSig lbl link + lmblocks = map (\(BasicBlock id stmts) -> LlvmBlock (getUnique id) stmts) blocks + fun = LlvmFunction funDec [NoUnwind] lmblocks + in ppLlvmFunction fun + ) + + +-- | Pretty print LLVM data code +pprLlvmData :: DynFlags -> LlvmData -> Doc +pprLlvmData _ (globals, types ) = + let globals' = ppLlvmGlobals globals + types' = ppLlvmTypes types + in types' $+$ globals' + + +-- | Pretty print CmmStatic +pprCmmStatic :: DynFlags -> [CmmStatic] -> Doc +pprCmmStatic dflags stat + = let unres = genLlvmData dflags (Data,stat) + (_, ldata) = resolveLlvmData dflags initLlvmEnv unres + in pprLlvmData dflags ldata + diff --git a/compiler/llvmGen/LlvmCodeGen/Regs.hs b/compiler/llvmGen/LlvmCodeGen/Regs.hs new file mode 100644 index 0000000..b731a86 --- /dev/null +++ b/compiler/llvmGen/LlvmCodeGen/Regs.hs @@ -0,0 +1,54 @@ +-- ---------------------------------------------------------------------------- +-- | Deal with Cmm registers +-- + +module LlvmCodeGen.Regs ( + lmGlobalRegArg, lmGlobalRegVar + ) where + +#include "HsVersions.h" + +import Llvm + +import CmmExpr +import Outputable ( panic ) +import FastString + +-- | Get the LlvmVar function variable storing the real register +lmGlobalRegVar :: GlobalReg -> LlvmVar +lmGlobalRegVar = lmGlobalReg "_Var" + +-- | Get the LlvmVar function argument storing the real register +lmGlobalRegArg :: GlobalReg -> LlvmVar +lmGlobalRegArg = (pVarLower . lmGlobalReg "_Arg") + +{- Need to make sure the names here can't conflict with the unique generated + names. Uniques generated names containing only base62 chars. So using say + the '_' char guarantees this. +-} +lmGlobalReg :: String -> GlobalReg -> LlvmVar +lmGlobalReg suf reg + = case reg of + BaseReg -> wordGlobal $ "Base" ++ suf + Sp -> wordGlobal $ "Sp" ++ suf + Hp -> wordGlobal $ "Hp" ++ suf + VanillaReg 1 _ -> wordGlobal $ "R1" ++ suf + VanillaReg 2 _ -> wordGlobal $ "R2" ++ suf + VanillaReg 3 _ -> wordGlobal $ "R3" ++ suf + VanillaReg 4 _ -> wordGlobal $ "R4" ++ suf + VanillaReg 5 _ -> wordGlobal $ "R5" ++ suf + VanillaReg 6 _ -> wordGlobal $ "R6" ++ suf + SpLim -> wordGlobal $ "SpLim" ++ suf + FloatReg 1 -> floatGlobal $"F1" ++ suf + FloatReg 2 -> floatGlobal $"F2" ++ suf + FloatReg 3 -> floatGlobal $"F3" ++ suf + FloatReg 4 -> floatGlobal $"F4" ++ suf + DoubleReg 1 -> doubleGlobal $ "D1" ++ suf + DoubleReg 2 -> doubleGlobal $ "D2" ++ suf + _other -> panic $ "LlvmCodeGen.Reg: GlobalReg (" ++ (show reg) + ++ ") not supported!" + where + wordGlobal name = LMNLocalVar (fsLit name) llvmWordPtr + floatGlobal name = LMNLocalVar (fsLit name) $ pLift LMFloat + doubleGlobal name = LMNLocalVar (fsLit name) $ pLift LMDouble + diff --git a/compiler/main/CodeOutput.lhs b/compiler/main/CodeOutput.lhs index 83f23cf..40f4f11 100644 --- a/compiler/main/CodeOutput.lhs +++ b/compiler/main/CodeOutput.lhs @@ -9,10 +9,14 @@ module CodeOutput( codeOutput, outputForeignStubs ) where #include "HsVersions.h" #ifndef OMIT_NATIVE_CODEGEN -import UniqSupply ( mkSplitUniqSupply ) import AsmCodeGen ( nativeCodeGen ) #endif +import UniqSupply ( mkSplitUniqSupply ) +#ifndef GHCI_TABLES_NEXT_TO_CODE +import qualified LlvmCodeGen ( llvmCodeGen ) +#endif + #ifdef JAVA import JavaGen ( javaGen ) import qualified PrintJava @@ -81,6 +85,7 @@ codeOutput dflags this_mod location foreign_stubs pkg_deps flat_abstractC HscInterpreted -> return (); HscAsm -> outputAsm dflags filenm flat_abstractC; HscC -> outputC dflags filenm flat_abstractC pkg_deps; + HscLlvm -> outputLlvm dflags filenm flat_abstractC; HscJava -> #ifdef JAVA outputJava dflags filenm mod_name tycons core_binds; @@ -168,6 +173,30 @@ outputAsm _ _ _ %************************************************************************ %* * +\subsection{LLVM} +%* * +%************************************************************************ + +\begin{code} +outputLlvm :: DynFlags -> FilePath -> [RawCmm] -> IO () + +#ifndef GHCI_TABLES_NEXT_TO_CODE +outputLlvm dflags filenm flat_absC + = do ncg_uniqs <- mkSplitUniqSupply 'n' + doOutput filenm $ \f -> + LlvmCodeGen.llvmCodeGen dflags f ncg_uniqs flat_absC +#else +outputLlvm _ _ _ + = pprPanic "This compiler was built with the LLVM backend disabled" + (text ("This is because the TABLES_NEXT_TO_CODE optimisation is" + ++ " enabled, which the LLVM backend doesn't support right now.") + $+$ text "Use -fasm instead") +#endif +\end{code} + + +%************************************************************************ +%* * \subsection{Java} %* * %************************************************************************ diff --git a/compiler/main/DriverPhases.hs b/compiler/main/DriverPhases.hs index eb64134..398da79 100644 --- a/compiler/main/DriverPhases.hs +++ b/compiler/main/DriverPhases.hs @@ -80,6 +80,9 @@ data Phase | SplitMangle -- after mangler if splitting | SplitAs | As + | LlvmAs -- LLVM assembly to bitcode file + | LlvmOpt -- Run LLVM opt tool over llvm assembly + | LlvmLlc -- LLVM bitcode to native assembly | CmmCpp -- pre-process Cmm source | Cmm -- parse & compile Cmm code @@ -109,6 +112,9 @@ eqPhase Mangle Mangle = True eqPhase SplitMangle SplitMangle = True eqPhase SplitAs SplitAs = True eqPhase As As = True +eqPhase LlvmAs LlvmAs = True +eqPhase LlvmOpt LlvmOpt = True +eqPhase LlvmLlc LlvmLlc = True eqPhase CmmCpp CmmCpp = True eqPhase Cmm Cmm = True eqPhase StopLn StopLn = True @@ -133,6 +139,9 @@ nextPhase HCc = Mangle nextPhase Mangle = SplitMangle nextPhase SplitMangle = As nextPhase As = SplitAs +nextPhase LlvmAs = LlvmOpt +nextPhase LlvmOpt = LlvmLlc +nextPhase LlvmLlc = As nextPhase SplitAs = StopLn nextPhase Ccpp = As nextPhase Cc = As @@ -160,6 +169,9 @@ startPhase "raw_s" = Mangle startPhase "split_s" = SplitMangle startPhase "s" = As startPhase "S" = As +startPhase "ll" = LlvmAs +startPhase "bc" = LlvmOpt +startPhase "opt_bc" = LlvmLlc startPhase "o" = StopLn startPhase "cmm" = CmmCpp startPhase "cmmcpp" = Cmm @@ -184,6 +196,9 @@ phaseInputExt Cc = "c" phaseInputExt Mangle = "raw_s" phaseInputExt SplitMangle = "split_s" -- not really generated phaseInputExt As = "s" +phaseInputExt LlvmAs = "ll" +phaseInputExt LlvmOpt = "bc" +phaseInputExt LlvmLlc = "opt_bc" phaseInputExt SplitAs = "split_s" -- not really generated phaseInputExt CmmCpp = "cmm" phaseInputExt Cmm = "cmmcpp" @@ -195,7 +210,7 @@ haskellish_src_suffixes, haskellish_suffixes, cish_suffixes, haskellish_src_suffixes = haskellish_user_src_suffixes ++ [ "hspp", "hscpp", "hcr", "cmm" ] haskellish_suffixes = haskellish_src_suffixes ++ ["hc", "raw_s"] -cish_suffixes = [ "c", "cpp", "C", "cc", "cxx", "s", "S" ] +cish_suffixes = [ "c", "cpp", "C", "cc", "cxx", "s", "S", "ll", "bc", "opt_bc" ] extcoreish_suffixes = [ "hcr" ] -- Will not be deleted as temp files: haskellish_user_src_suffixes = [ "hs", "lhs", "hs-boot", "lhs-boot" ] diff --git a/compiler/main/DriverPipeline.hs b/compiler/main/DriverPipeline.hs index c6d3d0a..7274f2a 100644 --- a/compiler/main/DriverPipeline.hs +++ b/compiler/main/DriverPipeline.hs @@ -605,6 +605,7 @@ getOutputFilename stop_phase output basename keep_hc = dopt Opt_KeepHcFiles dflags keep_raw_s = dopt Opt_KeepRawSFiles dflags keep_s = dopt Opt_KeepSFiles dflags + keep_bc = dopt Opt_KeepLlvmFiles dflags myPhaseInputExt HCc = hcsuf myPhaseInputExt StopLn = osuf @@ -615,11 +616,13 @@ getOutputFilename stop_phase output basename -- sometimes, we keep output from intermediate stages keep_this_output = case next_phase of - StopLn -> True - Mangle | keep_raw_s -> True - As | keep_s -> True - HCc | keep_hc -> True - _other -> False + StopLn -> True + Mangle | keep_raw_s -> True + As | keep_s -> True + LlvmAs | keep_bc -> True + LlvmOpt | keep_bc -> True + HCc | keep_hc -> True + _other -> False suffix = myPhaseInputExt next_phase @@ -1232,6 +1235,77 @@ runPhase SplitAs _stop hsc_env _basename _suff _input_fn get_output_fn maybe_loc return (StopLn, dflags, maybe_loc, output_fn) + +----------------------------------------------------------------------------- +-- LlvmAs phase + +runPhase LlvmAs _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc + = liftIO $ do + let dflags = hsc_dflags hsc_env + let la_opts = getOpts dflags opt_la + + output_fn <- get_output_fn dflags LlvmOpt maybe_loc + + SysTools.runLlvmAs dflags + (map SysTools.Option la_opts + ++ [ SysTools.FileOption "" input_fn, + SysTools.Option "-o", SysTools.FileOption "" output_fn]) + + return (LlvmOpt, dflags, maybe_loc, output_fn) + + +----------------------------------------------------------------------------- +-- LlvmOpt phase + +runPhase LlvmOpt _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc + = liftIO $ do + let dflags = hsc_dflags hsc_env + let lo_opts = getOpts dflags opt_lo + let opt_lvl = max 0 (min 2 $ optLevel dflags) + + -- only run if > 0 OR opt options given by user + if opt_lvl /= 0 || lo_opts /= [] + then do + output_fn <- get_output_fn dflags LlvmLlc maybe_loc + + SysTools.runLlvmOpt dflags + (map SysTools.Option lo_opts + ++ [ SysTools.FileOption "" input_fn, + SysTools.Option (llvmOpts !! opt_lvl), + SysTools.Option "-o", + SysTools.FileOption "" output_fn]) + + return (LlvmLlc, dflags, maybe_loc, output_fn) + + else + return (LlvmLlc, dflags, maybe_loc, input_fn) + where + llvmOpts = ["-O1", "-O2", "-O3"] + + +----------------------------------------------------------------------------- +-- LlvmLlc phase + +runPhase LlvmLlc _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc + = liftIO $ do + let dflags = hsc_dflags hsc_env + let lc_opts = getOpts dflags opt_lc + let opt_lvl = max 0 (min 2 $ optLevel dflags) + + output_fn <- get_output_fn dflags As maybe_loc + + SysTools.runLlvmLlc dflags + (map SysTools.Option lc_opts + ++ [ -- SysTools.Option "-tailcallopt", + SysTools.Option (llvmOpts !! opt_lvl), + SysTools.FileOption "" input_fn, + SysTools.Option "-o", SysTools.FileOption "" output_fn]) + + return (As, dflags, maybe_loc, output_fn) + where + llvmOpts = ["", "-O2", "-O3"] + + -- warning suppression runPhase other _stop _dflags _basename _suff _input_fn _get_output_fn _maybe_loc = panic ("runPhase: don't know how to run phase " ++ show other) @@ -1832,6 +1906,7 @@ hscNextPhase dflags _ hsc_lang = HscC -> HCc HscAsm | dopt Opt_SplitObjs dflags -> SplitMangle | otherwise -> As + HscLlvm -> LlvmAs HscNothing -> StopLn HscInterpreted -> StopLn _other -> StopLn diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs index 1f83d29..70b1355 100644 --- a/compiler/main/DynFlags.hs +++ b/compiler/main/DynFlags.hs @@ -107,6 +107,8 @@ data DynFlag | Opt_D_dump_asm_conflicts | Opt_D_dump_asm_stats | Opt_D_dump_asm_expanded + | Opt_D_dump_llvm + | Opt_D_dump_llvm_opt | Opt_D_dump_cpranal | Opt_D_dump_deriv | Opt_D_dump_ds @@ -332,6 +334,7 @@ data DynFlag | Opt_KeepRawSFiles | Opt_KeepTmpFiles | Opt_KeepRawTokenStream + | Opt_KeepLlvmFiles deriving (Eq, Show) @@ -420,6 +423,9 @@ data DynFlags = DynFlags { opt_a :: [String], opt_l :: [String], opt_windres :: [String], + opt_la :: [String], -- LLVM: llvm-as assembler + opt_lo :: [String], -- LLVM: llvm optimiser + opt_lc :: [String], -- LLVM: llc static compiler -- commands for particular phases pgm_L :: String, @@ -434,6 +440,9 @@ data DynFlags = DynFlags { pgm_T :: String, pgm_sysman :: String, pgm_windres :: String, + pgm_la :: (String,[Option]), -- LLVM: llvm-as assembler + pgm_lo :: (String,[Option]), -- LLVM: opt llvm optimiser + pgm_lc :: (String,[Option]), -- LLVM: llc static compiler -- For ghc -M depMakefile :: FilePath, @@ -498,6 +507,7 @@ wayNames = map wayName . ways data HscTarget = HscC -- ^ Generate C code. | HscAsm -- ^ Generate assembly using the native code generator. + | HscLlvm -- ^ Generate assembly using the llvm code generator. | HscJava -- ^ Generate Java bytecode. | HscInterpreted -- ^ Generate bytecode. (Requires 'LinkInMemory') | HscNothing -- ^ Don't generate any code. See notes above. @@ -507,6 +517,7 @@ data HscTarget isObjectTarget :: HscTarget -> Bool isObjectTarget HscC = True isObjectTarget HscAsm = True +isObjectTarget HscLlvm = True isObjectTarget _ = False -- | The 'GhcMode' tells us whether we're doing multi-module @@ -656,6 +667,9 @@ defaultDynFlags = opt_m = [], opt_l = [], opt_windres = [], + opt_la = [], + opt_lo = [], + opt_lc = [], extraPkgConfs = [], packageFlags = [], @@ -682,6 +696,9 @@ defaultDynFlags = pgm_T = panic "defaultDynFlags: No pgm_T", pgm_sysman = panic "defaultDynFlags: No pgm_sysman", pgm_windres = panic "defaultDynFlags: No pgm_windres", + pgm_la = panic "defaultDynFlags: No pgm_la", + pgm_lo = panic "defaultDynFlags: No pgm_lo", + pgm_lc = panic "defaultDynFlags: No pgm_lc", -- end of initSysTools values -- ghc -M values depMakefile = "Makefile", @@ -770,8 +787,9 @@ getVerbFlag dflags setObjectDir, setHiDir, setStubDir, setOutputDir, setDylibInstallName, setObjectSuf, setHiSuf, setHcSuf, parseDynLibLoaderMode, setPgmP, setPgmL, setPgmF, setPgmc, setPgmm, setPgms, setPgma, setPgml, setPgmdll, setPgmwindres, - addOptL, addOptP, addOptF, addOptc, addOptm, addOpta, addOptl, addOptwindres, - addCmdlineFramework, addHaddockOpts + setPgmla, setPgmlo, setPgmlc, + addOptL, addOptP, addOptF, addOptc, addOptm, addOpta, addOptl, addOptwindres, addOptla, addOptlo, + addOptlc, addCmdlineFramework, addHaddockOpts :: String -> DynFlags -> DynFlags setOutputFile, setOutputHi, setDumpPrefixForce :: Maybe String -> DynFlags -> DynFlags @@ -815,6 +833,9 @@ setPgma f d = d{ pgm_a = (f,[])} setPgml f d = d{ pgm_l = (f,[])} setPgmdll f d = d{ pgm_dll = (f,[])} setPgmwindres f d = d{ pgm_windres = f} +setPgmla f d = d{ pgm_la = (f,[])} +setPgmlo f d = d{ pgm_lo = (f,[])} +setPgmlc f d = d{ pgm_lc = (f,[])} addOptL f d = d{ opt_L = f : opt_L d} addOptP f d = d{ opt_P = f : opt_P d} @@ -824,6 +845,9 @@ addOptm f d = d{ opt_m = f : opt_m d} addOpta f d = d{ opt_a = f : opt_a d} addOptl f d = d{ opt_l = f : opt_l d} addOptwindres f d = d{ opt_windres = f : opt_windres d} +addOptla f d = d{ opt_la = f : opt_la d} +addOptlo f d = d{ opt_lo = f : opt_lo d} +addOptlc f d = d{ opt_lc = f : opt_lc d} setDepMakefile :: FilePath -> DynFlags -> DynFlags setDepMakefile f d = d { depMakefile = deOptDep f } @@ -1018,6 +1042,11 @@ dynamic_flags = [ , Flag "v" (OptIntSuffix setVerbosity) Supported ------- Specific phases -------------------------------------------- + -- need to appear before -pgmL to be parsed as LLVM flags. + , Flag "pgmla" (HasArg (upd . setPgmla)) Supported + , Flag "pgmlo" (HasArg (upd . setPgmlo)) Supported + , Flag "pgmlc" (HasArg (upd . setPgmlc)) Supported + , Flag "pgmL" (HasArg (upd . setPgmL)) Supported , Flag "pgmP" (HasArg (upd . setPgmP)) Supported , Flag "pgmF" (HasArg (upd . setPgmF)) Supported @@ -1029,6 +1058,11 @@ dynamic_flags = [ , Flag "pgmdll" (HasArg (upd . setPgmdll)) Supported , Flag "pgmwindres" (HasArg (upd . setPgmwindres)) Supported + -- need to appear before -optl/-opta to be parsed as LLVM flags. + , Flag "optla" (HasArg (upd . addOptla)) Supported + , Flag "optlo" (HasArg (upd . addOptlo)) Supported + , Flag "optlc" (HasArg (upd . addOptlc)) Supported + , Flag "optL" (HasArg (upd . addOptL)) Supported , Flag "optP" (HasArg (upd . addOptP)) Supported , Flag "optF" (HasArg (upd . addOptF)) Supported @@ -1102,6 +1136,8 @@ dynamic_flags = [ , Flag "keep-s-files" (NoArg (setDynFlag Opt_KeepSFiles)) Supported , Flag "keep-raw-s-file" (NoArg (setDynFlag Opt_KeepRawSFiles)) Supported , Flag "keep-raw-s-files" (NoArg (setDynFlag Opt_KeepRawSFiles)) Supported + , Flag "keep-llvm-file" (NoArg (setDynFlag Opt_KeepLlvmFiles)) Supported + , Flag "keep-llvm-files" (NoArg (setDynFlag Opt_KeepLlvmFiles)) Supported -- This only makes sense as plural , Flag "keep-tmp-files" (NoArg (setDynFlag Opt_KeepTmpFiles)) Supported @@ -1162,6 +1198,11 @@ dynamic_flags = [ Supported , Flag "ddump-asm-expanded" (setDumpFlag Opt_D_dump_asm_expanded) Supported + , Flag "ddump-llvm" (NoArg (do { setObjTarget HscLlvm + ; setDumpFlag' Opt_D_dump_llvm})) + Supported + , Flag "ddump-opt-llvm" (setDumpFlag Opt_D_dump_llvm_opt) + Supported , Flag "ddump-cpranal" (setDumpFlag Opt_D_dump_cpranal) Supported , Flag "ddump-deriv" (setDumpFlag Opt_D_dump_deriv) @@ -1384,6 +1425,7 @@ dynamic_flags = [ , Flag "fasm" (NoArg (setObjTarget HscAsm)) Supported , Flag "fvia-c" (NoArg (setObjTarget HscC)) Supported , Flag "fvia-C" (NoArg (setObjTarget HscC)) Supported + , Flag "fllvm" (NoArg (setObjTarget HscLlvm)) Supported , Flag "fno-code" (NoArg (do upd $ \d -> d{ ghcLink=NoLink } setTarget HscNothing)) @@ -1787,9 +1829,12 @@ unSetDynFlag f = upd (\dfs -> dopt_unset dfs f) -------------------------- setDumpFlag :: DynFlag -> OptKind DynP -setDumpFlag dump_flag - = NoArg (do { setDynFlag dump_flag - ; when want_recomp forceRecompile }) +setDumpFlag dump_flag = NoArg (setDumpFlag' dump_flag) + +setDumpFlag' :: DynFlag -> DynP () +setDumpFlag' dump_flag + = do { setDynFlag dump_flag + ; when want_recomp forceRecompile } where -- Certain dumpy-things are really interested in what's going -- on during recompilation checking, so in those cases we @@ -2185,6 +2230,7 @@ compilerInfo = [("Project name", String cProjectName), ("Have interpreter", String cGhcWithInterpreter), ("Object splitting", String cSplitObjs), ("Have native code generator", String cGhcWithNativeCodeGen), + ("Have llvm code generator", String cGhcWithLlvmCodeGen), ("Support SMP", String cGhcWithSMP), ("Unregisterised", String cGhcUnregisterised), ("Tables next to code", String cGhcEnableTablesNextToCode), diff --git a/compiler/main/SysTools.lhs b/compiler/main/SysTools.lhs index c479a66..29889db 100644 --- a/compiler/main/SysTools.lhs +++ b/compiler/main/SysTools.lhs @@ -18,6 +18,9 @@ module SysTools ( runAs, runLink, -- [Option] -> IO () runMkDLL, runWindres, + runLlvmAs, + runLlvmOpt, + runLlvmLlc, touch, -- String -> String -> IO () copy, @@ -219,6 +222,11 @@ initSysTools mbMinusB dflags0 ; let as_prog = gcc_prog ld_prog = gcc_prog + -- figure out llvm location. (TODO: Acutally implement). + ; let la_prog = "llvm-as" + lc_prog = "llc" + lo_prog = "opt" + ; return dflags1{ ghcUsagePath = ghc_usage_msg_path, ghciUsagePath = ghci_usage_msg_path, @@ -235,7 +243,10 @@ initSysTools mbMinusB dflags0 pgm_dll = (mkdll_prog,mkdll_args), pgm_T = touch_path, pgm_sysman = top_dir ++ "/ghc/rts/parallel/SysMan", - pgm_windres = windres_path + pgm_windres = windres_path, + pgm_la = (la_prog,[]), + pgm_lo = (lo_prog,[]), + pgm_lc = (lc_prog,[]) -- Hans: this isn't right in general, but you can -- elaborate it in the same way as the others } @@ -381,6 +392,21 @@ runAs dflags args = do mb_env <- getGccEnv args1 runSomethingFiltered dflags id "Assembler" p args1 mb_env +runLlvmAs :: DynFlags -> [Option] -> IO () +runLlvmAs dflags args = do + let (p,args0) = pgm_la dflags + runSomething dflags "LLVM Assembler" p (args0++args) + +runLlvmOpt :: DynFlags -> [Option] -> IO () +runLlvmOpt dflags args = do + let (p,args0) = pgm_lo dflags + runSomething dflags "LLVM Optimiser" p (args0++args) + +runLlvmLlc :: DynFlags -> [Option] -> IO () +runLlvmLlc dflags args = do + let (p,args0) = pgm_lc dflags + runSomething dflags "LLVM Compiler" p (args0++args) + runLink :: DynFlags -> [Option] -> IO () runLink dflags args = do let (p,args0) = pgm_l dflags diff --git a/compiler/nativeGen/Alpha/Regs.hs b/compiler/nativeGen/Alpha/Regs.hs index 2d85c5f..ee49050 100644 --- a/compiler/nativeGen/Alpha/Regs.hs +++ b/compiler/nativeGen/Alpha/Regs.hs @@ -27,7 +27,6 @@ import RegsBase import BlockId import Cmm -import CgUtils ( get_GlobalReg_addr ) import CLabel ( CLabel, mkMainCapabilityLabel ) import Pretty import Outputable ( Outputable(..), pprPanic, panic ) diff --git a/compiler/nativeGen/AsmCodeGen.lhs b/compiler/nativeGen/AsmCodeGen.lhs index d79fbb6..d73cb89 100644 --- a/compiler/nativeGen/AsmCodeGen.lhs +++ b/compiler/nativeGen/AsmCodeGen.lhs @@ -73,6 +73,7 @@ import RegClass import NCGMonad import BlockId +import CgUtils ( fixStgRegisters ) import Cmm import CmmOpt ( cmmMiniInline, cmmMachOpFold ) import PprCmm @@ -278,9 +279,9 @@ cmmNativeGen dflags us cmm count = do -- rewrite assignments to global regs - let (fixed_cmm, usFix) = - {-# SCC "fixAssignsTop" #-} - initUs us $ fixAssignsTop cmm + let fixed_cmm = + {-# SCC "fixStgRegisters" #-} + fixStgRegisters cmm -- cmm to cmm optimisations let (opt_cmm, imports) = @@ -294,13 +295,12 @@ cmmNativeGen dflags us cmm count -- generate native code from cmm let ((native, lastMinuteImports), usGen) = {-# SCC "genMachCode" #-} - initUs usFix $ genMachCode dflags opt_cmm + initUs us $ genMachCode dflags opt_cmm dumpIfSet_dyn dflags Opt_D_dump_asm_native "Native code" (vcat $ map (docToSDoc . pprNatCmmTop) native) - -- tag instructions with register liveness information let (withLiveness, usLive) = {-# SCC "regLiveness" #-} @@ -309,7 +309,6 @@ cmmNativeGen dflags us cmm count dumpIfSet_dyn dflags Opt_D_dump_asm_liveness "Liveness annotations added" (vcat $ map ppr withLiveness) - -- allocate registers (alloced, usAlloc, ppr_raStatsColor, ppr_raStatsLinear) <- @@ -323,7 +322,6 @@ cmmNativeGen dflags us cmm count emptyUFM $ allocatableRegs - -- do the graph coloring register allocation let ((alloced, regAllocStats), usAlloc) = {-# SCC "RegAlloc" #-} @@ -697,44 +695,6 @@ genMachCode dflags cmm_top else pprPanic "genMachCode: nonzero final delta" (int final_delta) } --- ----------------------------------------------------------------------------- --- Fixup assignments to global registers so that they assign to --- locations within the RegTable, if appropriate. - --- Note that we currently don't fixup reads here: they're done by --- the generic optimiser below, to avoid having two separate passes --- over the Cmm. - -fixAssignsTop :: RawCmmTop -> UniqSM RawCmmTop -fixAssignsTop top@(CmmData _ _) = returnUs top -fixAssignsTop (CmmProc info lbl params (ListGraph blocks)) = - mapUs fixAssignsBlock blocks `thenUs` \ blocks' -> - returnUs (CmmProc info lbl params (ListGraph blocks')) - -fixAssignsBlock :: CmmBasicBlock -> UniqSM CmmBasicBlock -fixAssignsBlock (BasicBlock id stmts) = - fixAssigns stmts `thenUs` \ stmts' -> - returnUs (BasicBlock id stmts') - -fixAssigns :: [CmmStmt] -> UniqSM [CmmStmt] -fixAssigns stmts = - mapUs fixAssign stmts `thenUs` \ stmtss -> - returnUs (concat stmtss) - -fixAssign :: CmmStmt -> UniqSM [CmmStmt] -fixAssign (CmmAssign (CmmGlobal reg) src) - | Left realreg <- reg_or_addr - = returnUs [CmmAssign (CmmGlobal reg) src] - | Right baseRegAddr <- reg_or_addr - = returnUs [CmmStore baseRegAddr src] - -- Replace register leaves with appropriate StixTrees for - -- the given target. GlobalRegs which map to a reg on this - -- arch are left unchanged. Assigning to BaseReg is always - -- illegal, so we check for that. - where - reg_or_addr = get_GlobalReg_reg_or_addr reg - -fixAssign other_stmt = returnUs [other_stmt] -- ----------------------------------------------------------------------------- -- Generic Cmm optimiser @@ -745,10 +705,7 @@ Here we do: (a) Constant folding (b) Simple inlining: a temporary which is assigned to and then used, once, can be shorted. - (c) Replacement of references to GlobalRegs which do not have - machine registers by the appropriate memory load (eg. - Hp ==> *(BaseReg + 34) ). - (d) Position independent code and dynamic linking + (c) Position independent code and dynamic linking (i) introduce the appropriate indirections and position independent refs (ii) compile a list of imported symbols @@ -883,42 +840,8 @@ cmmExprConFold referenceKind expr CmmLit (CmmLabel (mkCmmCodeLabel rtsPackageId (fsLit "__stg_gc_fun"))) #endif - CmmReg (CmmGlobal mid) - -- Replace register leaves with appropriate StixTrees for - -- the given target. MagicIds which map to a reg on this - -- arch are left unchanged. For the rest, BaseReg is taken - -- to mean the address of the reg table in MainCapability, - -- and for all others we generate an indirection to its - -- location in the register table. - -> case get_GlobalReg_reg_or_addr mid of - Left realreg -> return expr - Right baseRegAddr - -> case mid of - BaseReg -> cmmExprConFold DataReference baseRegAddr - other -> cmmExprConFold DataReference - (CmmLoad baseRegAddr (globalRegType mid)) - -- eliminate zero offsets - CmmRegOff reg 0 - -> cmmExprConFold referenceKind (CmmReg reg) - - CmmRegOff (CmmGlobal mid) offset - -- RegOf leaves are just a shorthand form. If the reg maps - -- to a real reg, we keep the shorthand, otherwise, we just - -- expand it and defer to the above code. - -> case get_GlobalReg_reg_or_addr mid of - Left realreg -> return expr - Right baseRegAddr - -> cmmExprConFold DataReference (CmmMachOp (MO_Add wordWidth) [ - CmmReg (CmmGlobal mid), - CmmLit (CmmInt (fromIntegral offset) - wordWidth)]) other -> return other --- ----------------------------------------------------------------------------- --- Utils - -bind f x = x $! f - \end{code} diff --git a/compiler/nativeGen/PPC/CodeGen.hs b/compiler/nativeGen/PPC/CodeGen.hs index a1b55ce..8a4228b 100644 --- a/compiler/nativeGen/PPC/CodeGen.hs +++ b/compiler/nativeGen/PPC/CodeGen.hs @@ -180,12 +180,12 @@ getRegisterReg (CmmLocal (LocalReg u pk)) = RegVirtual $ mkVirtualReg u (cmmTypeSize pk) getRegisterReg (CmmGlobal mid) - = case get_GlobalReg_reg_or_addr mid of - Left reg -> reg - _other -> pprPanic "getRegisterReg-memory" (ppr $ CmmGlobal mid) - -- By this stage, the only MagicIds remaining should be the - -- ones which map to a real machine register on this - -- platform. Hence ... + = case globalRegMaybe mid of + Just reg -> reg + Nothing -> pprPanic "getRegisterReg-memory" (ppr $ CmmGlobal mid) + -- By this stage, the only MagicIds remaining should be the + -- ones which map to a real machine register on this + -- platform. Hence ... {- diff --git a/compiler/nativeGen/PPC/Regs.hs b/compiler/nativeGen/PPC/Regs.hs index d649d84..e00dd7e 100644 --- a/compiler/nativeGen/PPC/Regs.hs +++ b/compiler/nativeGen/PPC/Regs.hs @@ -40,7 +40,6 @@ module PPC.Regs ( -- horrow show freeReg, globalRegMaybe, - get_GlobalReg_reg_or_addr, allocatableRegs ) @@ -55,7 +54,6 @@ import Reg import RegClass import Size -import CgUtils ( get_GlobalReg_addr ) import BlockId import Cmm import CLabel ( CLabel ) @@ -595,20 +593,6 @@ globalRegMaybe _ = panic "PPC.Regs.globalRegMaybe: not defined" #endif /* powerpc_TARGET_ARCH */ --- We map STG registers onto appropriate CmmExprs. Either they map --- to real machine registers or stored as offsets from BaseReg. Given --- a GlobalReg, get_GlobalReg_reg_or_addr produces either the real --- register it is in, on this platform, or a CmmExpr denoting the --- address in the register table holding it. --- (See also get_GlobalReg_addr in CgUtils.) - -get_GlobalReg_reg_or_addr :: GlobalReg -> Either Reg CmmExpr -get_GlobalReg_reg_or_addr mid - = case globalRegMaybe mid of - Just rr -> Left rr - Nothing -> Right (get_GlobalReg_addr mid) - - -- allocatableRegs is allMachRegNos with the fixed-use regs removed. -- i.e., these are the regs for which we are prepared to allow the -- register allocator to attempt to map VRegs to. diff --git a/compiler/nativeGen/SPARC/CodeGen/Base.hs b/compiler/nativeGen/SPARC/CodeGen/Base.hs index 54bbf9b..c85d806 100644 --- a/compiler/nativeGen/SPARC/CodeGen/Base.hs +++ b/compiler/nativeGen/SPARC/CodeGen/Base.hs @@ -18,6 +18,7 @@ import SPARC.Instr import SPARC.Cond import SPARC.AddrMode import SPARC.Regs +import SPARC.RegPlate import Size import Reg @@ -95,11 +96,11 @@ getRegisterReg (CmmLocal (LocalReg u pk)) = RegVirtual $ mkVirtualReg u (cmmTypeSize pk) getRegisterReg (CmmGlobal mid) - = case get_GlobalReg_reg_or_addr mid of - Left rr -> RegReal rr - - _ -> pprPanic "SPARC.CodeGen.Base.getRegisterReg: global is in memory" - (ppr $ CmmGlobal mid) + = case globalRegMaybe mid of + Just reg -> RegReal reg + Nothing -> pprPanic + "SPARC.CodeGen.Base.getRegisterReg: global is in memory" + (ppr $ CmmGlobal mid) -- Expand CmmRegOff. ToDo: should we do it this way around, or convert diff --git a/compiler/nativeGen/SPARC/Regs.hs b/compiler/nativeGen/SPARC/Regs.hs index cd19138..98151ec 100644 --- a/compiler/nativeGen/SPARC/Regs.hs +++ b/compiler/nativeGen/SPARC/Regs.hs @@ -18,7 +18,6 @@ module SPARC.Regs ( -- allocatable allocatableRegs, - get_GlobalReg_reg_or_addr, -- args argRegs, @@ -38,9 +37,7 @@ import Reg import RegClass import Size -import Cmm import PprCmm () -import CgUtils ( get_GlobalReg_addr ) import Unique import Outputable @@ -214,21 +211,6 @@ allocatableRegs in filter isFree allRealRegs - --- We map STG registers onto appropriate CmmExprs. Either they map --- to real machine registers or stored as offsets from BaseReg. Given --- a GlobalReg, get_GlobalReg_reg_or_addr produces either the real --- register it is in, on this platform, or a CmmExpr denoting the --- address in the register table holding it. --- (See also get_GlobalReg_addr in CgUtils.) - -get_GlobalReg_reg_or_addr :: GlobalReg -> Either RealReg CmmExpr -get_GlobalReg_reg_or_addr mid - = case globalRegMaybe mid of - Just rr -> Left rr - Nothing -> Right (get_GlobalReg_addr mid) - - -- | The registers to place arguments for function calls, -- for some number of arguments. -- diff --git a/compiler/nativeGen/X86/CodeGen.hs b/compiler/nativeGen/X86/CodeGen.hs index 2698406..89a26a9 100644 --- a/compiler/nativeGen/X86/CodeGen.hs +++ b/compiler/nativeGen/X86/CodeGen.hs @@ -226,12 +226,12 @@ getRegisterReg use_sse2 (CmmLocal (LocalReg u pk)) else RegVirtual (mkVirtualReg u sz) getRegisterReg _ (CmmGlobal mid) - = case get_GlobalReg_reg_or_addr mid of - Left reg -> RegReal $ reg - _other -> pprPanic "getRegisterReg-memory" (ppr $ CmmGlobal mid) - -- By this stage, the only MagicIds remaining should be the - -- ones which map to a real machine register on this - -- platform. Hence ... + = case globalRegMaybe mid of + Just reg -> RegReal $ reg + Nothing -> pprPanic "getRegisterReg-memory" (ppr $ CmmGlobal mid) + -- By this stage, the only MagicIds remaining should be the + -- ones which map to a real machine register on this + -- platform. Hence ... -- | Memory addressing modes passed up the tree. diff --git a/compiler/nativeGen/X86/Regs.hs b/compiler/nativeGen/X86/Regs.hs index a04e854..b9a23a6 100644 --- a/compiler/nativeGen/X86/Regs.hs +++ b/compiler/nativeGen/X86/Regs.hs @@ -40,7 +40,6 @@ module X86.Regs ( freeReg, globalRegMaybe, - get_GlobalReg_reg_or_addr, allocatableRegs ) @@ -54,7 +53,6 @@ where import Reg import RegClass -import CgUtils ( get_GlobalReg_addr ) import BlockId import Cmm import CLabel ( CLabel ) @@ -662,20 +660,6 @@ callClobberedRegs = panic "X86.Regs.globalRegMaybe: not defined" #endif --- We map STG registers onto appropriate CmmExprs. Either they map --- to real machine registers or stored as offsets from BaseReg. Given --- a GlobalReg, get_GlobalReg_reg_or_addr produces either the real --- register it is in, on this platform, or a CmmExpr denoting the --- address in the register table holding it. --- (See also get_GlobalReg_addr in CgUtils.) - -get_GlobalReg_reg_or_addr :: GlobalReg -> Either RealReg CmmExpr -get_GlobalReg_reg_or_addr mid - = case globalRegMaybe mid of - Just rr -> Left rr - Nothing -> Right (get_GlobalReg_addr mid) - - -- allocatableRegs is allMachRegNos with the fixed-use regs removed. -- i.e., these are the regs for which we are prepared to allow the -- register allocator to attempt to map VRegs to. diff --git a/compiler/typecheck/TcForeign.lhs b/compiler/typecheck/TcForeign.lhs index fdb7ce5..935127c 100644 --- a/compiler/typecheck/TcForeign.lhs +++ b/compiler/typecheck/TcForeign.lhs @@ -94,7 +94,7 @@ tcCheckFIType :: Type -> [Type] -> Type -> ForeignImport -> TcM ForeignImport tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport _ safety _ (CLabel _)) = ASSERT( null arg_tys ) - do { checkCg checkCOrAsmOrInterp + do { checkCg checkCOrAsmOrLlvmOrInterp ; checkSafety safety ; check (isFFILabelTy res_ty) (illegalForeignTyErr empty sig_ty) ; return idecl } -- NB check res_ty not sig_ty! @@ -106,7 +106,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety _ CWrapper) = do -- valid foreign type. For legacy reasons ft -> IO (Ptr ft) as well -- as ft -> IO Addr is accepted, too. The use of the latter two forms -- is DEPRECATED, though. - checkCg checkCOrAsmOrInterp + checkCg checkCOrAsmOrLlvmOrInterp checkCConv cconv checkSafety safety case arg_tys of @@ -121,7 +121,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety _ CWrapper) = do tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety _ (CFunction target)) | isDynamicTarget target = do -- Foreign import dynamic - checkCg checkCOrAsmOrInterp + checkCg checkCOrAsmOrLlvmOrInterp checkCConv cconv checkSafety safety case arg_tys of -- The first arg must be Ptr, FunPtr, or Addr @@ -139,7 +139,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety _ (CFunction tar dflags <- getDOpts check (dopt Opt_GHCForeignImportPrim dflags) (text "Use -XGHCForeignImportPrim to allow `foreign import prim'.") - checkCg (checkCOrAsmOrDotNetOrInterp) + checkCg (checkCOrAsmOrLlvmOrDotNetOrInterp) checkCTarget target check (playSafe safety) (text "The safe/unsafe annotation should not be used with `foreign import prim'.") @@ -148,7 +148,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety _ (CFunction tar checkForeignRes nonIOok (isFFIPrimResultTy dflags) res_ty return idecl | otherwise = do -- Normal foreign import - checkCg (checkCOrAsmOrDotNetOrInterp) + checkCg (checkCOrAsmOrLlvmOrDotNetOrInterp) checkCConv cconv checkSafety safety checkCTarget target @@ -163,7 +163,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety _ (CFunction tar -- that the C identifier is valid for C checkCTarget :: CCallTarget -> TcM () checkCTarget (StaticTarget str _) = do - checkCg checkCOrAsmOrDotNetOrInterp + checkCg checkCOrAsmOrLlvmOrDotNetOrInterp check (isCLabelString str) (badCName str) checkCTarget DynamicTarget = panic "checkCTarget DynamicTarget" @@ -247,7 +247,7 @@ tcFExport d = pprPanic "tcFExport" (ppr d) \begin{code} tcCheckFEType :: Type -> ForeignExport -> TcM () tcCheckFEType sig_ty (CExport (CExportStatic str cconv)) = do - checkCg checkCOrAsm + checkCg checkCOrAsmOrLlvm check (isCLabelString str) (badCName str) checkCConv cconv checkForeignArgs isFFIExternalTy arg_tys @@ -297,25 +297,28 @@ checkForeignRes non_io_result_ok pred_res_ty ty \end{code} \begin{code} -checkCOrAsm :: HscTarget -> Maybe SDoc -checkCOrAsm HscC = Nothing -checkCOrAsm HscAsm = Nothing -checkCOrAsm _ - = Just (text "requires via-C or native code generation (-fvia-C)") - -checkCOrAsmOrInterp :: HscTarget -> Maybe SDoc -checkCOrAsmOrInterp HscC = Nothing -checkCOrAsmOrInterp HscAsm = Nothing -checkCOrAsmOrInterp HscInterpreted = Nothing -checkCOrAsmOrInterp _ - = Just (text "requires interpreted, C or native code generation") - -checkCOrAsmOrDotNetOrInterp :: HscTarget -> Maybe SDoc -checkCOrAsmOrDotNetOrInterp HscC = Nothing -checkCOrAsmOrDotNetOrInterp HscAsm = Nothing -checkCOrAsmOrDotNetOrInterp HscInterpreted = Nothing -checkCOrAsmOrDotNetOrInterp _ - = Just (text "requires interpreted, C or native code generation") +checkCOrAsmOrLlvm :: HscTarget -> Maybe SDoc +checkCOrAsmOrLlvm HscC = Nothing +checkCOrAsmOrLlvm HscAsm = Nothing +checkCOrAsmOrLlvm HscLlvm = Nothing +checkCOrAsmOrLlvm _ + = Just (text "requires via-C, llvm (-fllvm) or native code generation (-fvia-C)") + +checkCOrAsmOrLlvmOrInterp :: HscTarget -> Maybe SDoc +checkCOrAsmOrLlvmOrInterp HscC = Nothing +checkCOrAsmOrLlvmOrInterp HscAsm = Nothing +checkCOrAsmOrLlvmOrInterp HscLlvm = Nothing +checkCOrAsmOrLlvmOrInterp HscInterpreted = Nothing +checkCOrAsmOrLlvmOrInterp _ + = Just (text "requires interpreted, C, Llvm or native code generation") + +checkCOrAsmOrLlvmOrDotNetOrInterp :: HscTarget -> Maybe SDoc +checkCOrAsmOrLlvmOrDotNetOrInterp HscC = Nothing +checkCOrAsmOrLlvmOrDotNetOrInterp HscAsm = Nothing +checkCOrAsmOrLlvmOrDotNetOrInterp HscLlvm = Nothing +checkCOrAsmOrLlvmOrDotNetOrInterp HscInterpreted = Nothing +checkCOrAsmOrLlvmOrDotNetOrInterp _ + = Just (text "requires interpreted, C, Llvm or native code generation") checkCg :: (HscTarget -> Maybe SDoc) -> TcM () checkCg check = do diff --git a/driver/mangler/ghc-asm.lprl b/driver/mangler/ghc-asm.lprl index 1ff78a4..a354caa 100644 --- a/driver/mangler/ghc-asm.lprl +++ b/driver/mangler/ghc-asm.lprl @@ -1445,7 +1445,8 @@ sub mangle_asm { # If this is an entry point with an info table, # eliminate the entry symbol and all directives involving it. - if (defined($infochk{$symb}) && $TargetPlatform !~ /^ia64-/m) { + if (defined($infochk{$symb}) && $TargetPlatform !~ /^ia64-/m + && $TABLES_NEXT_TO_CODE ~~ "YES") { @o = (); foreach $l (split(/\n/m,$c)) { next if $l =~ /^.*$symb_(entry|ret)${T_POST_LBL}/m; @@ -1880,7 +1881,8 @@ sub rev_tbl { # use vars '$discard1'; # Unused? local($symb, $tbl, $discard1) = @_; - return ($tbl) if ($TargetPlatform =~ /^ia64-/m); + return ($tbl) if ($TargetPlatform =~ /^ia64-/m + || $TABLES_NEXT_TO_CODE ~~ "NO"); local($before) = ''; local($label) = ''; diff --git a/mk/config.mk.in b/mk/config.mk.in index 40f1ea7..e1a124c 100644 --- a/mk/config.mk.in +++ b/mk/config.mk.in @@ -139,6 +139,9 @@ GhcWithNativeCodeGen=$(strip\ $(if $(filter YESYESNO,\ $(OsSupportsNCG)$(ArchSupportsNCG)$(GhcUnregisterised)),YES,NO)) +# Build a compiler with the llvm code generator backend +GhcWithLlvmCodeGen=NO + HaveLibDL = @HaveLibDL@ # ArchSupportsSMP should be set iff there is support for that arch in diff --git a/rules/build-perl.mk b/rules/build-perl.mk index cfa71cc..5ee1d23 100644 --- a/rules/build-perl.mk +++ b/rules/build-perl.mk @@ -45,6 +45,7 @@ $1/$2/$$($1_$2_PROG): $1/$2/$$($1_$2_PROG).prl "$$(RM)" $$(RM_OPTS) $$@ echo '#!$$(PERL)' >> $$@ echo '$$$$TARGETPLATFORM = "$$(TARGETPLATFORM)";' >> $$@ + echo '$$$$TABLES_NEXT_TO_CODE = "$(GhcEnableTablesNextToCode)";' >> $$@ cat $$< >> $$@ $$($1_$2_INPLACE): $1/$2/$$($1_$2_PROG) | $$$$(dir $$$$@)/. -- 1.7.10.4