|
1 | 1 | module TTImp.Elab.Utils
|
2 | 2 |
|
| 3 | +import Core.CaseTree |
3 | 4 | import Core.Context
|
4 | 5 | import Core.Core
|
5 | 6 | import Core.Env
|
@@ -116,3 +117,169 @@ bindReq {vs = n :: _} fc (b :: env) (KeepCons p) ns tm
|
116 | 117 | (Bind fc _ (Pi (binderLoc b) (multiplicity b) Explicit (binderType b')) tm)
|
117 | 118 | bindReq fc (b :: env) (DropCons p) ns tm
|
118 | 119 | = bindReq fc env p ns tm
|
| 120 | + |
| 121 | +-- This machinery is to calculate whether any top level argument is used |
| 122 | +-- more than once in a case block, in which case inlining wouldn't be safe |
| 123 | +-- since it might duplicate work. |
| 124 | + |
| 125 | +data ArgUsed = Used1 -- been used |
| 126 | + | Used0 -- not used |
| 127 | + | LocalVar -- don't care if it's used |
| 128 | + |
| 129 | +data Usage : List Name -> Type where |
| 130 | + Nil : Usage [] |
| 131 | + (::) : ArgUsed -> Usage xs -> Usage (x :: xs) |
| 132 | + |
| 133 | +initUsed : (xs : List Name) -> Usage xs |
| 134 | +initUsed [] = [] |
| 135 | +initUsed (x :: xs) = Used0 :: initUsed xs |
| 136 | + |
| 137 | +initUsedCase : (xs : List Name) -> Usage xs |
| 138 | +initUsedCase [] = [] |
| 139 | +initUsedCase [x] = [Used0] |
| 140 | +initUsedCase (x :: xs) = LocalVar :: initUsedCase xs |
| 141 | + |
| 142 | +setUsedVar : {idx : _} -> |
| 143 | + (0 _ : IsVar n idx xs) -> Usage xs -> Usage xs |
| 144 | +setUsedVar First (Used0 :: us) = Used1 :: us |
| 145 | +setUsedVar (Later p) (x :: us) = x :: setUsedVar p us |
| 146 | +setUsedVar First us = us |
| 147 | + |
| 148 | +isUsed : {idx : _} -> |
| 149 | + (0 _ : IsVar n idx xs) -> Usage xs -> Bool |
| 150 | +isUsed First (Used1 :: us) = True |
| 151 | +isUsed First (_ :: us) = False |
| 152 | +isUsed (Later p) (_ :: us) = isUsed p us |
| 153 | + |
| 154 | +data Used : Type where |
| 155 | + |
| 156 | +setUsed : {idx : _} -> |
| 157 | + {auto u : Ref Used (Usage vars)} -> |
| 158 | + (0 _ : IsVar n idx vars) -> Core () |
| 159 | +setUsed p |
| 160 | + = do used <- get Used |
| 161 | + put Used (setUsedVar p used) |
| 162 | + |
| 163 | +extendUsed : ArgUsed -> (new : List Name) -> Usage vars -> Usage (new ++ vars) |
| 164 | +extendUsed a [] x = x |
| 165 | +extendUsed a (y :: xs) x = a :: extendUsed a xs x |
| 166 | + |
| 167 | +dropUsed : (new : List Name) -> Usage (new ++ vars) -> Usage vars |
| 168 | +dropUsed [] x = x |
| 169 | +dropUsed (x :: xs) (u :: us) = dropUsed xs us |
| 170 | + |
| 171 | +inExtended : ArgUsed -> (new : List Name) -> |
| 172 | + {auto u : Ref Used (Usage vars)} -> |
| 173 | + (Ref Used (Usage (new ++ vars)) -> Core a) -> |
| 174 | + Core a |
| 175 | +inExtended a new sc |
| 176 | + = do used <- get Used |
| 177 | + u' <- newRef Used (extendUsed a new used) |
| 178 | + res <- sc u' |
| 179 | + put Used (dropUsed new !(get Used @{u'})) |
| 180 | + pure res |
| 181 | + |
| 182 | +termInlineSafe : {vars : _} -> |
| 183 | + {auto u : Ref Used (Usage vars)} -> |
| 184 | + Term vars -> Core Bool |
| 185 | +termInlineSafe (Local fc isLet idx p) |
| 186 | + = if isUsed p !(get Used) |
| 187 | + then pure False |
| 188 | + else do setUsed p |
| 189 | + pure True |
| 190 | +termInlineSafe (Meta fc x y xs) |
| 191 | + = allInlineSafe xs |
| 192 | + where |
| 193 | + allInlineSafe : List (Term vars) -> Core Bool |
| 194 | + allInlineSafe [] = pure True |
| 195 | + allInlineSafe (x :: xs) |
| 196 | + = do xok <- termInlineSafe x |
| 197 | + if xok |
| 198 | + then allInlineSafe xs |
| 199 | + else pure False |
| 200 | +termInlineSafe (Bind fc x b scope) |
| 201 | + = do bok <- binderInlineSafe b |
| 202 | + if bok |
| 203 | + then inExtended LocalVar [x] (\u' => termInlineSafe scope) |
| 204 | + else pure False |
| 205 | + where |
| 206 | + binderInlineSafe : Binder (Term vars) -> Core Bool |
| 207 | + binderInlineSafe (Let _ _ val _) = termInlineSafe val |
| 208 | + binderInlineSafe _ = pure True |
| 209 | +termInlineSafe (App fc fn arg) |
| 210 | + = do fok <- termInlineSafe fn |
| 211 | + if fok |
| 212 | + then termInlineSafe arg |
| 213 | + else pure False |
| 214 | +termInlineSafe (As fc x as pat) = termInlineSafe pat |
| 215 | +termInlineSafe (TDelayed fc x ty) = termInlineSafe ty |
| 216 | +termInlineSafe (TDelay fc x ty arg) = termInlineSafe arg |
| 217 | +termInlineSafe (TForce fc x val) = termInlineSafe val |
| 218 | +termInlineSafe _ = pure True |
| 219 | + |
| 220 | +mutual |
| 221 | + caseInlineSafe : {vars : _} -> |
| 222 | + {auto u : Ref Used (Usage vars)} -> |
| 223 | + CaseTree vars -> Core Bool |
| 224 | + caseInlineSafe (Case idx p scTy xs) |
| 225 | + = if isUsed p !(get Used) |
| 226 | + then pure False |
| 227 | + else do setUsed p |
| 228 | + altsSafe xs |
| 229 | + where |
| 230 | + altsSafe : List (CaseAlt vars) -> Core Bool |
| 231 | + altsSafe [] = pure True |
| 232 | + altsSafe (a :: as) |
| 233 | + = do u <- get Used |
| 234 | + aok <- caseAltInlineSafe a |
| 235 | + if aok |
| 236 | + then do -- We can reset the usage information, because we're |
| 237 | + -- only going to use one alternative at a time |
| 238 | + put Used u |
| 239 | + altsSafe as |
| 240 | + else pure False |
| 241 | + caseInlineSafe (STerm x tm) = termInlineSafe tm |
| 242 | + caseInlineSafe (Unmatched msg) = pure True |
| 243 | + caseInlineSafe Impossible = pure True |
| 244 | + |
| 245 | + caseAltInlineSafe : {vars : _} -> |
| 246 | + {auto u : Ref Used (Usage vars)} -> |
| 247 | + CaseAlt vars -> Core Bool |
| 248 | + caseAltInlineSafe (ConCase x tag args sc) |
| 249 | + = inExtended Used0 args (\u' => caseInlineSafe sc) |
| 250 | + caseAltInlineSafe (DelayCase ty arg sc) |
| 251 | + = inExtended Used0 [ty, arg] (\u' => caseInlineSafe sc) |
| 252 | + caseAltInlineSafe (ConstCase x sc) = caseInlineSafe sc |
| 253 | + caseAltInlineSafe (DefaultCase sc) = caseInlineSafe sc |
| 254 | + |
| 255 | +-- An inlining is safe if no variable is used more than once in the tree, |
| 256 | +-- which means that there's no risk of an input being evaluated more than |
| 257 | +-- once after the definition is expanded. |
| 258 | +export |
| 259 | +inlineSafe : {vars : _} -> |
| 260 | + CaseTree vars -> Core Bool |
| 261 | +inlineSafe t |
| 262 | + = do u <- newRef Used (initUsed vars) |
| 263 | + caseInlineSafe t |
| 264 | + |
| 265 | +export |
| 266 | +canInlineDef : {auto c : Ref Ctxt Defs} -> |
| 267 | + Name -> Core Bool |
| 268 | +canInlineDef n |
| 269 | + = do defs <- get Ctxt |
| 270 | + Just (PMDef _ _ _ rtree _) <- lookupDefExact n (gamma defs) |
| 271 | + | _ => pure False |
| 272 | + inlineSafe rtree |
| 273 | + |
| 274 | +-- This is a special case because the only argument we actually care about |
| 275 | +-- is the last one, since the others are just variables passed through from |
| 276 | +-- the environment, and duplicating a variable doesn't cost anything. |
| 277 | +export |
| 278 | +canInlineCaseBlock : {auto c : Ref Ctxt Defs} -> |
| 279 | + Name -> Core Bool |
| 280 | +canInlineCaseBlock n |
| 281 | + = do defs <- get Ctxt |
| 282 | + Just (PMDef _ vars _ rtree _) <- lookupDefExact n (gamma defs) |
| 283 | + | _ => pure False |
| 284 | + u <- newRef Used (initUsedCase vars) |
| 285 | + caseInlineSafe rtree |
0 commit comments