Skip to content

Commit 858295d

Browse files
committed
Further towards remainders
1 parent 94d1078 commit 858295d

File tree

4 files changed

+190
-49
lines changed

4 files changed

+190
-49
lines changed

booster/library/Booster/JsonRpc.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,11 @@ mkLogRewriteTrace
643643
{ reason = "Uncertain about a condition in rule"
644644
, _ruleId = Just $ getUniqueId (uniqueId $ Definition.attributes r)
645645
}
646+
RewriteRemainderPredicate rs _ _ ->
647+
Failure
648+
{ reason = "Uncertain about a condition in rule"
649+
, _ruleId = Just $ getUniqueId (uniqueId $ Definition.attributes (head rs))
650+
}
646651
DefinednessUnclear r _ undefReasons ->
647652
Failure
648653
{ reason = "Uncertain about definedness of rule because of: " <> pack (show undefReasons)

booster/library/Booster/Pattern/Bool.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ License : BSD-3-Clause
77
module Booster.Pattern.Bool (
88
foldAndBool,
99
isBottom,
10+
isFalse,
1011
negateBool,
1112
splitBoolPredicates,
1213
splitAndBools,
@@ -190,6 +191,11 @@ foldAndBool (x : xs) = AndBool x $ foldAndBool xs
190191
isBottom :: Pattern -> Bool
191192
isBottom = (Predicate FalseBool `elem`) . constraints
192193

194+
isFalse :: Predicate -> Bool
195+
isFalse = \case
196+
(Predicate FalseBool) -> True
197+
_ -> False
198+
193199
{- | We want to break apart predicates of type `Y1 andBool ... Yn` apart, in case some of the `Y`s are abstract
194200
which prevents the original expression from being fed to the LLVM simplifyBool function
195201
-}

booster/library/Booster/Pattern/Rewrite.hs

Lines changed: 171 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import Booster.Pattern.Pretty
7070
import Booster.Pattern.Util
7171
import Booster.Prettyprinter
7272
import Booster.SMT.Interface qualified as SMT
73-
import Booster.Syntax.Json.Externalise (externaliseTerm)
73+
import Booster.Syntax.Json.Externalise (externalisePredicate, externaliseSort, externaliseTerm)
7474
import Booster.Util (Flag (..))
7575

7676
{- | The @'RewriteT'@ monad encapsulates the effects needed to make a single rewrite step.
@@ -127,6 +127,9 @@ throw = RewriteT . lift . lift . throwE
127127
getDefinition :: LoggerMIO io => RewriteT io KoreDefinition
128128
getDefinition = RewriteT $ definition <$> ask
129129

130+
getSolver :: Monad m => RewriteT m SMT.SMTContext
131+
getSolver = RewriteT $ (.smtSolver) <$> ask
132+
130133
invalidateRewriterEquationsCache :: LoggerMIO io => RewriteT io ()
131134
invalidateRewriterEquationsCache =
132135
RewriteT . lift . modify $ \s@RewriteState{} ->
@@ -173,54 +176,125 @@ rewriteStep cutLabels terminalLabels pat = do
173176
processGroups [] =
174177
pure $ RewriteStuck pat
175178
processGroups (rules : rest) = do
179+
logMessage ("Trying rules with priority " <> (Text.pack . show $ ruleGroupPriority rules))
176180
-- try all rules of the priority group. This will immediately
177181
-- fail the rewrite if anything is uncertain (unification,
178182
-- definedness, rule conditions)
179183
results <-
180-
filter (/= NotApplied)
181-
<$> forM
182-
rules
183-
( \rule -> do
184-
result <- applyRule pat rule
185-
pure (fmap (rule,) result)
186-
)
187-
188-
-- simplify and filter out bottom states
189-
190-
-- At the moment, there is no point in calling simplify on the conditions of the
191-
-- resulting patterns again, since we already pruned any rule applications
192-
-- which resulted in one of the conditions being bottom.
184+
zip rules
185+
<$> mapM (applyRule pat) rules
193186

194187
let labelOf = fromMaybe "" . (.ruleLabel) . (.attributes)
195188
ruleLabelOrLocT = renderOneLineText . ruleLabelOrLoc
196189
uniqueId = (.uniqueId) . (.attributes)
197190

198-
case results of
199-
-- no rules in this group were applicable
200-
[] -> processGroups rest
201-
_ -> case concatMap (\case Applied x -> [x]; _ -> []) results of
202-
[] ->
203-
-- all remaining branches are trivial, i.e. rules which did apply had an ensures condition which evaluated to false
204-
-- if, all the other groups only generate a not applicable or trivial rewrites,
205-
-- then we return a `RewriteTrivial`.
206-
processGroups rest >>= \case
207-
RewriteStuck{} -> pure $ RewriteTrivial pat
208-
other -> pure other
209-
-- all branches but one were either not applied or trivial
210-
[(r, (x, _remainder, _subst))]
211-
| labelOf r `elem` cutLabels ->
212-
pure $ RewriteCutPoint (labelOf r) (uniqueId r) pat x
213-
| labelOf r `elem` terminalLabels ->
214-
pure $ RewriteTerminal (labelOf r) (uniqueId r) x
215-
| otherwise ->
216-
pure $ RewriteFinished (Just $ ruleLabelOrLocT r) (Just $ uniqueId r) x
217-
-- at this point, there were some Applied rules and potentially some Trivial ones.
218-
-- here, we just return all the applied rules in a `RewriteBranch`
219-
rxs ->
191+
case postProcessRuleAttempts results of
192+
OnlyTrivial ->
193+
-- all branches in this priority group are trivial,
194+
-- i.e. rules which did apply had an ensures condition which evaluated to false.
195+
-- if all the other groups only generate a not applicable or trivial rewrites,
196+
-- then we return a `RewriteTrivial`.
197+
processGroups rest >>= \case
198+
RewriteStuck{} -> pure $ RewriteTrivial pat
199+
other -> pure other
200+
AppliedRules ([], _remainder) -> processGroups rest
201+
AppliedRules ([(rule, newPat, _subst)], _remainder)
202+
-- a single rule applies, see if it's special and return an appropriate result
203+
| labelOf rule `elem` cutLabels ->
204+
pure $ RewriteCutPoint (labelOf rule) (uniqueId rule) pat newPat
205+
| labelOf rule `elem` terminalLabels ->
206+
pure $ RewriteTerminal (labelOf rule) (uniqueId rule) newPat
207+
| otherwise ->
208+
pure $ RewriteFinished (Just $ ruleLabelOrLocT rule) (Just $ uniqueId rule) newPat
209+
AppliedRules (xs, remainder)
210+
-- multiple rules apply, analyse brunching and remainders
211+
| any isFalse remainder -> do
212+
withContext CtxRemainder $ logMessage ("remainder is UNSAT" :: Text)
213+
-- the remainder predicate is trivially false, return the branching result
220214
pure $
221215
RewriteBranch pat $
222216
NE.fromList $
223-
map (\(r, (p, _remainder, _subst)) -> (ruleLabelOrLocT r, uniqueId r, p)) rxs
217+
map (\(r, p, _subst) -> (ruleLabelOrLocT r, uniqueId r, p)) xs
218+
| otherwise -> do
219+
-- otherwise, we need to check the remainder predicate with the SMT solver
220+
-- and construct an additional remainder branch if needed
221+
solver <- getSolver
222+
SMT.isSat solver (Set.toList $ pat.constraints <> remainder) >>= \case
223+
SMT.IsUnsat -> do
224+
-- the remainder condition is unsatisfiable: no need to consider the remainder branch.
225+
withContext CtxRemainder $ logMessage ("remainder is UNSAT" :: Text)
226+
-- pure resultsWithoutRemainders
227+
pure $
228+
RewriteBranch pat $
229+
NE.fromList $
230+
map (\(r, p, _subst) -> (ruleLabelOrLocT r, uniqueId r, p)) xs
231+
satRes@(SMT.IsSat{}) -> do
232+
-- the remainder condition is satisfiable.
233+
-- Have to construct the remainder branch and consider it
234+
-- To construct the "remainder pattern",
235+
-- we add the remainder condition to the predicates of the @pattr@
236+
-- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
237+
let rs = map (\(r, _p, _subst) -> r) xs
238+
throw $
239+
RewriteRemainderPredicate rs satRes . coerce . foldl1 AndTerm $
240+
map coerce . Set.toList $
241+
remainder
242+
satRes@SMT.IsUnknown{} -> do
243+
-- solver cannot solve the remainder. Descend into the remainder branch anyway
244+
-- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
245+
let rs = map (\(r, _p, _subst) -> r) xs
246+
throw $
247+
RewriteRemainderPredicate rs satRes . coerce . foldl1 AndTerm $
248+
map coerce . Set.toList $
249+
remainder
250+
251+
-- if any isFalse remainder -- no need to call SMT if any of the conditions is trivially false
252+
-- then do
253+
-- -- setRemainder mempty
254+
-- pure xs
255+
-- else do
256+
-- solver <- getSolver
257+
-- SMT.isSat solver (pat.constraints <> remainder) >>= \case
258+
-- SMT.IsUnsat -> do
259+
-- -- the remainder condition is unsatisfiable: no need to consider the remainder branch.
260+
-- setRemainder mempty
261+
-- withContext CtxRemainder $ logMessage ("remainder is UNSAT" :: Text)
262+
-- pure resultsWithoutRemainders
263+
-- SMT.IsSat _ -> do
264+
-- withContext CtxRemainder $ logMessage ("remainder is SAT" :: Text)
265+
-- -- the remainder condition is satisfiable.
266+
-- -- Have to construct the remainder branch and consider it
267+
-- -- To construct the "remainder pattern",
268+
-- -- we add the remainder condition to the predicates of the @pattr@
269+
-- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
270+
-- SMT.IsUnknown{} -> do
271+
-- withContext CtxRemainder $ logMessage ("remainder is UNKNWON" :: Text)
272+
-- -- solver cannot solve the remainder. Descend into the remainder branch anyway
273+
-- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
274+
275+
-- _ -> case concatMap (\case Applied x -> [x]; _ -> []) results of
276+
-- [] ->
277+
-- -- all remaining branches are trivial, i.e. rules which did apply had an ensures condition which evaluated to false
278+
-- -- if, all the other groups only generate a not applicable or trivial rewrites,
279+
-- -- then we return a `RewriteTrivial`.
280+
-- processGroups rest >>= \case
281+
-- RewriteStuck{} -> pure $ RewriteTrivial pat
282+
-- other -> pure other
283+
-- -- all branches but one were either not applied or trivial
284+
-- [(r, (x, _remainder, _subst))]
285+
-- | labelOf r `elem` cutLabels ->
286+
-- pure $ RewriteCutPoint (labelOf r) (uniqueId r) pat x
287+
-- | labelOf r `elem` terminalLabels ->
288+
-- pure $ RewriteTerminal (labelOf r) (uniqueId r) x
289+
-- | otherwise ->
290+
-- pure $ RewriteFinished (Just $ ruleLabelOrLocT r) (Just $ uniqueId r) x
291+
-- -- at this point, there were some Applied rules and potentially some Trivial ones.
292+
-- -- here, we just return all the applied rules in a `RewriteBranch`
293+
-- rxs ->
294+
-- pure $
295+
-- RewriteBranch pat $
296+
-- NE.fromList $
297+
-- map (\(r, (p, _remainder, _subst)) -> (ruleLabelOrLocT r, uniqueId r, p)) rxs
224298

225299
-- | Rewrite rule application transformer: may throw exceptions on non-applicable or trivial rule applications
226300
type RewriteRuleAppT m a = ExceptT RewriteRuleAppException m a
@@ -249,11 +323,11 @@ returnNotApplied = throwE RewriteRuleNotApplied
249323
* Unifies the LHS term with the pattern term
250324
* Ensures that the unification is a _match_ (one-sided substitution)
251325
* prunes any rules that turn out to have trivially-false side conditions
252-
* returns the rule and the resulting pattern if successful, otherwise Nothing
326+
* returns the resulting pattern if successful, otherwise Nothing
253327
254-
If it cannot be determined whether the rule can be applied or not, an
255-
exception is thrown which indicates the exact reason why (this will
256-
abort the entire rewrite).
328+
If it cannot be determined whether the rule can be applied or not, the second component
329+
of the result will contain a non-trivial /remainder predicate/, i.e. the indeterminate
330+
subset of the rule's side condition.
257331
-}
258332
applyRule ::
259333
forall io.
@@ -364,13 +438,6 @@ applyRule pat@Pattern{ceilConditions} rule =
364438
case unclearRequiresAfterSmt of
365439
[] -> withPatternContext rewritten $ pure (rewritten, Predicate FalseBool, subst)
366440
_ -> do
367-
failRewrite $
368-
RuleConditionUnclear rule . coerce . foldl1 AndTerm $
369-
map coerce unclearRequiresAfterSmt
370-
-- TODO the following code is intentionally dead and should be enabled to get rewrite rule remainders
371-
-- when unclearRequiresAfterSmt is non-empty, we need to add it as a rule remainder predicate, which means:
372-
-- - the resulting patten will have it conjoined to its constraints TODO is this right?
373-
-- - its negation, i.e. the remainder predicate, will be returned as the second component of the result
374441
let rewritten' = rewritten{constraints = rewritten.constraints <> Set.fromList unclearRequiresAfterSmt}
375442
in withPatternContext rewritten' $
376443
pure
@@ -494,6 +561,38 @@ applyRule pat@Pattern{ceilConditions} rule =
494561
RuleConditionUnclear rule . coerce . foldl1 AndTerm $
495562
map coerce predicates
496563

564+
data RuleGroupApplication a = OnlyTrivial | AppliedRules a
565+
566+
ruleGroupPriority :: [RewriteRule a] -> Maybe Priority
567+
ruleGroupPriority = \case
568+
[] -> Nothing
569+
(rule : _) -> Just rule.attributes.priority
570+
571+
{- | Given a list of rule application attempts, i.e. a result of applying a priority group of rules in parallel,
572+
post process them:
573+
- filter-out trivial and failed applications
574+
- extract (possibly trivial) remainder predicates of every rule
575+
and return them as a set relating to the whole group
576+
-}
577+
postProcessRuleAttempts ::
578+
[(RewriteRule "Rewrite", RewriteRuleAppResult (Pattern, Predicate, Substitution))] ->
579+
RuleGroupApplication ([(RewriteRule "Rewrite", Pattern, Substitution)], Set.Set Predicate)
580+
postProcessRuleAttempts = \case
581+
[] -> AppliedRules ([], mempty)
582+
apps -> case filter ((/= NotApplied) . snd) apps of
583+
[] -> AppliedRules ([], mempty)
584+
xs
585+
| all ((== Trivial) . snd) xs -> OnlyTrivial
586+
| otherwise -> go ([], mempty) xs
587+
where
588+
go acc@(accPatterns, accRemainders) = \case
589+
[] -> AppliedRules (reverse accPatterns, accRemainders)
590+
((rule, appRes) : xs) ->
591+
case appRes of
592+
Applied (pat, remainder, subst) -> go ((rule, pat, subst) : accPatterns, Set.singleton remainder <> accRemainders) xs
593+
NotApplied -> go acc xs
594+
Trivial -> go acc xs
595+
497596
{- | Reason why a rewrite did not produce a result. Contains additional
498597
information for logging what happened during the rewrite.
499598
-}
@@ -504,6 +603,8 @@ data RewriteFailed k
504603
RuleApplicationUnclear (RewriteRule k) Term (NonEmpty (Term, Term))
505604
| -- | A rule condition is indeterminate
506605
RuleConditionUnclear (RewriteRule k) Predicate
606+
| -- | After applying multiple rewrite rules there is a satisfiable or unknown remainder condition
607+
RewriteRemainderPredicate [RewriteRule k] (SMT.IsSatResult ()) Predicate
507608
| -- | A rewrite rule does not preserve definedness
508609
DefinednessUnclear (RewriteRule k) Pattern [NotPreservesDefinednessReason]
509610
| -- | A sort error was detected during m,atching
@@ -538,6 +639,14 @@ instance FromModifiersT mods => Pretty (PrettyWithModifiers mods (RewriteFailed
538639
, ": "
539640
, pretty' @mods predicate
540641
]
642+
RewriteRemainderPredicate rules satResult predicate ->
643+
hsep
644+
[ pretty (SMT.showIsSatResult satResult)
645+
, "remainder predicate after applying rules"
646+
, hsep $ punctuate comma $ map ruleLabelOrLoc rules
647+
, ": "
648+
, pretty' @mods predicate
649+
]
541650
DefinednessUnclear rule _pat reasons ->
542651
hsep $
543652
[ "Uncertain about definedness of rule "
@@ -921,6 +1030,20 @@ performRewrite rewriteConfig pat = do
9211030
emitRewriteTrace $ RewriteStepFailed failure
9221031
logMessage $ "Aborted after " <> showCounter counter
9231032
pure (RewriteAborted failure pat')
1033+
Left failure@(RewriteRemainderPredicate _rules _satResult remainderPredicate) -> do
1034+
emitRewriteTrace $ RewriteStepFailed failure
1035+
withPatternContext pat' . withContext CtxRemainder . withContext CtxAbort $
1036+
getPrettyModifiers >>= \case
1037+
ModifiersRep (_ :: FromModifiersT mods => Proxy mods) ->
1038+
logMessage
1039+
$ WithJsonMessage
1040+
( object
1041+
["remainder" .= externalisePredicate (externaliseSort $ sortOfPattern pat) remainderPredicate]
1042+
)
1043+
$ renderOneLineText
1044+
$ pretty' @mods failure
1045+
logMessage $ "Aborted after " <> showCounter counter
1046+
pure (RewriteAborted failure pat')
9241047
Left failure -> do
9251048
emitRewriteTrace $ RewriteStepFailed failure
9261049
let msg = "Aborted after " <> showCounter counter

booster/library/Booster/SMT/Interface.hs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module Booster.SMT.Interface (
2121
hardResetSolver,
2222
pattern IsUnknown,
2323
IsSatResult,
24+
showIsSatResult,
2425
pattern IsSat,
2526
pattern IsUnsat,
2627
IsValidResult,
@@ -217,7 +218,7 @@ instance Log.ToLogFormat UnknownReason where
217218
pattern IsUnknown :: UnknownReason -> Either UnknownReason b
218219
pattern IsUnknown u = Left u
219220

220-
newtype IsSat' a = IsSat' (Maybe a) deriving (Functor)
221+
newtype IsSat' a = IsSat' (Maybe a) deriving (Functor, Show, Eq)
221222

222223
type IsSatResult a = Either UnknownReason (IsSat' a)
223224

@@ -227,6 +228,12 @@ pattern IsSat a = Right (IsSat' (Just a))
227228
pattern IsUnsat :: IsSatResult a
228229
pattern IsUnsat = Right (IsSat' Nothing)
229230

231+
showIsSatResult :: IsSatResult a -> Text
232+
showIsSatResult = \case
233+
IsSat{} -> "SAT"
234+
IsUnsat -> "UNSAT"
235+
IsUnknown{} -> "UNKNOWN"
236+
230237
{-# COMPLETE IsSat, IsUnsat, IsUnknown #-}
231238

232239
{- | Check satisfiability of predicates and substitutions.

0 commit comments

Comments
 (0)