2 -- The above warning supression flag is a temporary kludge.
3 -- While working on this module you are encouraged to remove it and fix
4 -- any warnings in the module. See
5 -- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
8 -----------------------------------------------------------------------------
10 -- (c) The University of Glasgow 2004-2006
12 -- CmmLint: checking the correctness of Cmm statements and expressions
14 -----------------------------------------------------------------------------
20 #include "HsVersions.h"
34 -- -----------------------------------------------------------------------------
35 -- Exported entry points:
37 cmmLint :: GenCmm d h (ListGraph CmmStmt) -> Maybe SDoc
38 cmmLint (Cmm tops) = runCmmLint $ mapM_ lintCmmTop tops
40 cmmLintTop :: GenCmmTop d h (ListGraph CmmStmt) -> Maybe SDoc
41 cmmLintTop top = runCmmLint $ lintCmmTop top
43 runCmmLint :: CmmLint a -> Maybe SDoc
46 Left err -> Just (ptext SLIT("Cmm lint error:") $$ nest 2 err)
49 lintCmmTop :: (GenCmmTop h i (ListGraph CmmStmt)) -> CmmLint ()
50 lintCmmTop (CmmProc _ lbl _ (ListGraph blocks))
51 = addLintInfo (text "in proc " <> pprCLabel lbl) $
52 let labels = foldl (\s b -> extendBlockSet s (blockId b)) emptyBlockSet blocks
53 in mapM_ (lintCmmBlock labels) blocks
55 lintCmmTop (CmmData {})
58 lintCmmBlock labels (BasicBlock id stmts)
59 = addLintInfo (text "in basic block " <> ppr (getUnique id)) $
60 mapM_ (lintCmmStmt labels) stmts
62 -- -----------------------------------------------------------------------------
65 -- Checks whether a CmmExpr is "type-correct", and check for obvious-looking
66 -- byte/word mismatches.
68 lintCmmExpr :: CmmExpr -> CmmLint MachRep
69 lintCmmExpr (CmmLoad expr rep) = do
71 when (machRepByteWidth rep >= wORD_SIZE) $
72 cmmCheckWordAddress expr
74 lintCmmExpr expr@(CmmMachOp op args) = do
75 mapM_ lintCmmExpr args
76 if map cmmExprRep args == machOpArgReps op
77 then cmmCheckMachOp op args
78 else cmmLintMachOpErr expr (map cmmExprRep args) (machOpArgReps op)
79 lintCmmExpr (CmmRegOff reg offset)
80 = lintCmmExpr (CmmMachOp (MO_Add rep)
81 [CmmReg reg, CmmLit (CmmInt (fromIntegral offset) rep)])
82 where rep = cmmRegRep reg
83 lintCmmExpr lit@(CmmLit (CmmInt _ rep))
85 = cmmLintErr (text "integer literal with floating MachRep: " <> ppr lit)
87 return (cmmExprRep expr)
89 -- Check for some common byte/word mismatches (eg. Sp + 1)
90 cmmCheckMachOp op args@[CmmReg reg, CmmLit (CmmInt i _)]
91 | isWordOffsetReg reg && isOffsetOp op && i `rem` fromIntegral wORD_SIZE /= 0
92 = cmmLintDubiousWordOffset (CmmMachOp op args)
93 cmmCheckMachOp op [lit@(CmmLit (CmmInt { })), reg@(CmmReg _)]
94 = cmmCheckMachOp op [reg, lit]
95 cmmCheckMachOp op@(MO_U_Conv from to) args
96 | isFloatingRep from || isFloatingRep to
97 = cmmLintErr (text "unsigned conversion from/to floating rep: "
98 <> ppr (CmmMachOp op args))
99 cmmCheckMachOp op _args
100 = return (resultRepOfMachOp op)
102 isWordOffsetReg (CmmGlobal Sp) = True
103 -- No warnings for unaligned arithmetic, which is used to tag dynamic constructor closures.
104 --isWordOffsetReg (CmmGlobal Hp) = True
105 isWordOffsetReg _ = False
107 isOffsetOp (MO_Add _) = True
108 isOffsetOp (MO_Sub _) = True
111 -- This expression should be an address from which a word can be loaded:
112 -- check for funny-looking sub-word offsets.
113 cmmCheckWordAddress e@(CmmMachOp op [arg, CmmLit (CmmInt i _)])
114 | isOffsetOp op && notNodeReg arg && i `rem` fromIntegral wORD_SIZE /= 0
115 = cmmLintDubiousWordOffset e
116 cmmCheckWordAddress e@(CmmMachOp op [CmmLit (CmmInt i _), arg])
117 | isOffsetOp op && notNodeReg arg && i `rem` fromIntegral wORD_SIZE /= 0
118 = cmmLintDubiousWordOffset e
119 cmmCheckWordAddress _
122 -- No warnings for unaligned arithmetic with the node register,
123 -- which is used to extract fields from tagged constructor closures.
124 notNodeReg (CmmReg reg) | reg == nodeReg = False
127 lintCmmStmt :: BlockSet -> CmmStmt -> CmmLint ()
128 lintCmmStmt labels = lint
129 where lint (CmmNop) = return ()
130 lint (CmmComment {}) = return ()
131 lint stmt@(CmmAssign reg expr) = do
132 erep <- lintCmmExpr expr
133 if (erep == cmmRegRep reg)
135 else cmmLintAssignErr stmt
136 lint (CmmStore l r) = do
140 lint (CmmCall target _res args _ _) =
141 lintTarget target >> mapM_ (lintCmmExpr . hintlessCmm) args
142 lint (CmmCondBranch e id) = checkTarget id >> lintCmmExpr e >> checkCond e
143 lint (CmmSwitch e branches) = do
144 mapM_ checkTarget $ catMaybes branches
145 erep <- lintCmmExpr e
148 else cmmLintErr (text "switch scrutinee is not a word: " <> ppr e)
149 lint (CmmJump e args) = lintCmmExpr e >> mapM_ (lintCmmExpr . hintlessCmm) args
150 lint (CmmReturn ress) = mapM_ (lintCmmExpr . hintlessCmm) ress
151 lint (CmmBranch id) = checkTarget id
152 checkTarget id = if elemBlockSet id labels then return ()
153 else cmmLintErr (text "Branch to nonexistent id" <+> ppr id)
155 lintTarget :: CmmCallTarget -> CmmLint ()
156 lintTarget (CmmCallee e _) = lintCmmExpr e >> return ()
157 lintTarget (CmmPrim {}) = return ()
160 checkCond (CmmMachOp mop _) | isComparisonMachOp mop = return ()
161 checkCond expr = cmmLintErr (hang (text "expression is not a conditional:") 2
164 -- -----------------------------------------------------------------------------
167 -- just a basic error monad:
169 newtype CmmLint a = CmmLint { unCL :: Either SDoc a }
171 instance Monad CmmLint where
172 CmmLint m >>= k = CmmLint $ case m of
174 Right a -> unCL (k a)
175 return a = CmmLint (Right a)
177 cmmLintErr :: SDoc -> CmmLint a
178 cmmLintErr msg = CmmLint (Left msg)
180 addLintInfo :: SDoc -> CmmLint a -> CmmLint a
181 addLintInfo info thing = CmmLint $
183 Left err -> Left (hang info 2 err)
186 cmmLintMachOpErr :: CmmExpr -> [MachRep] -> [MachRep] -> CmmLint a
187 cmmLintMachOpErr expr argsRep opExpectsRep
188 = cmmLintErr (text "in MachOp application: " $$
189 nest 2 (pprExpr expr) $$
190 (text "op is expecting: " <+> ppr opExpectsRep) $$
191 (text "arguments provide: " <+> ppr argsRep))
193 cmmLintAssignErr :: CmmStmt -> CmmLint a
194 cmmLintAssignErr stmt = cmmLintErr (text "in assignment: " $$
195 nest 2 (pprStmt stmt))
197 cmmLintDubiousWordOffset :: CmmExpr -> CmmLint a
198 cmmLintDubiousWordOffset expr
199 = cmmLintErr (text "offset is not a multiple of words: " $$
200 nest 2 (pprExpr expr))