Skip to content

Commit c42d05d

Browse files
authored
More cut point rules (#790)
Closes #789 In particular adds: - [x] `--break-on-instrinsic-calls` breaks on calling `Intrinsic`s - [x] `--break-on-function-calls` breaks on calling `Function`s - [x] `--break-on-thunks` breaks on `thunk` - [x] `--break-every-statement` breaks every MIR `Statement` - [x] `--break-every-terminator` breaks every MIR `Terminator` - [x] `--break-on-terminator-goto` breaks on calling `TerminatorKind::Goto` - [x] `--break-on-terminator-switch-int` breaks on calling `TerminatorKind::SwitchInt` - [x] `--break-on-terminator-return` breaks on calling `TerminatorKind::Return` - [x] `--break-on-terminator-call` breaks on calling `TerminatorKind::Call` - [x] `--break-on-terminator-assert` breaks on calling `TerminatorKind::Assert` - [x] `--break-on-terminator-drop` breaks on calling `TerminatorKind::Drop` - [x] `--break-on-terminator-unreachable` breaks on calling `TerminatorKind::Unreachable` - [x] `--break-every-step` breaks every MIR `Statement` and `Terminator` And `--break-on-calls` now breaks on instrinics and function calls at the `Terminator` call since it dumps `#setUpCalleeData` node after you can see the name of the intrinsic or function still
1 parent 7530e1e commit c42d05d

File tree

5 files changed

+230
-16
lines changed

5 files changed

+230
-16
lines changed

kmir/src/kmir/__main__.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,82 @@ def _arg_parser() -> ArgumentParser:
246246
)
247247
prove_args.add_argument('--reload', action='store_true', help='Force restarting proof')
248248
prove_args.add_argument(
249-
'--break-on-calls', dest='break_on_calls', action='store_true', help='Break on function calls'
249+
'--break-on-calls', dest='break_on_calls', action='store_true', help='Break on all function and intrinsic calls'
250+
)
251+
prove_args.add_argument(
252+
'--break-on-function-calls',
253+
dest='break_on_function_calls',
254+
action='store_true',
255+
help='Break on function calls (not intrinsics)',
256+
)
257+
prove_args.add_argument(
258+
'--break-on-intrinsic-calls',
259+
dest='break_on_intrinsic_calls',
260+
action='store_true',
261+
help='Break on intrinsic calls (not other functions)',
262+
)
263+
prove_args.add_argument(
264+
'--break-on-thunk', dest='break_on_thunk', action='store_true', help='Break on thunk evaluation'
265+
)
266+
prove_args.add_argument(
267+
'--break-every-statement',
268+
dest='break_every_statement',
269+
action='store_true',
270+
help='Break on every MIR Statement execution',
271+
)
272+
prove_args.add_argument(
273+
'--break-on-terminator-goto',
274+
dest='break_on_terminator_goto',
275+
action='store_true',
276+
help='Break on Goto terminator',
277+
)
278+
prove_args.add_argument(
279+
'--break-on-terminator-switch-int',
280+
dest='break_on_terminator_switch_int',
281+
action='store_true',
282+
help='Break on SwitchInt terminator',
283+
)
284+
prove_args.add_argument(
285+
'--break-on-terminator-return',
286+
dest='break_on_terminator_return',
287+
action='store_true',
288+
help='Break on Return terminator',
289+
)
290+
prove_args.add_argument(
291+
'--break-on-terminator-call',
292+
dest='break_on_terminator_call',
293+
action='store_true',
294+
help='Break on Call terminator',
295+
)
296+
prove_args.add_argument(
297+
'--break-on-terminator-assert',
298+
dest='break_on_terminator_assert',
299+
action='store_true',
300+
help='Break on Assert terminator',
301+
)
302+
prove_args.add_argument(
303+
'--break-on-terminator-drop',
304+
dest='break_on_terminator_drop',
305+
action='store_true',
306+
help='Break on Drop terminator',
307+
)
308+
prove_args.add_argument(
309+
'--break-on-terminator-unreachable',
310+
dest='break_on_terminator_unreachable',
311+
action='store_true',
312+
help='Break on Unreachable terminator',
313+
)
314+
prove_args.add_argument(
315+
'--break-every-terminator',
316+
dest='break_every_terminator',
317+
action='store_true',
318+
help='Break on every MIR terminator execution',
319+
)
320+
prove_args.add_argument(
321+
'--break-every-step',
322+
dest='break_every_step',
323+
action='store_true',
324+
help='Break on every MIR step (statements and terminators)',
250325
)
251326

252327
proof_args = ArgumentParser(add_help=False)
@@ -419,6 +494,19 @@ def _parse_args(ns: Namespace) -> KMirOpts:
419494
smir=ns.smir,
420495
start_symbol=ns.start_symbol,
421496
break_on_calls=ns.break_on_calls,
497+
break_on_function_calls=ns.break_on_function_calls,
498+
break_on_intrinsic_calls=ns.break_on_intrinsic_calls,
499+
break_on_thunk=ns.break_on_thunk,
500+
break_every_statement=ns.break_every_statement,
501+
break_on_terminator_goto=ns.break_on_terminator_goto,
502+
break_on_terminator_switch_int=ns.break_on_terminator_switch_int,
503+
break_on_terminator_return=ns.break_on_terminator_return,
504+
break_on_terminator_call=ns.break_on_terminator_call,
505+
break_on_terminator_assert=ns.break_on_terminator_assert,
506+
break_on_terminator_drop=ns.break_on_terminator_drop,
507+
break_on_terminator_unreachable=ns.break_on_terminator_unreachable,
508+
break_every_terminator=ns.break_every_terminator,
509+
break_every_step=ns.break_every_step,
422510
)
423511
case 'link':
424512
return LinkOpts(

kmir/src/kmir/kdist/mir-semantics/kmir.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ will effectively be no-ops at this level).
9696
```k
9797
9898
// all memory accesses relegated to another module (to be added)
99-
rule <k> #execStmt(statement(statementKindAssign(PLACE, RVAL), _SPAN))
99+
rule [execStmt]: <k> #execStmt(statement(statementKindAssign(PLACE, RVAL), _SPAN))
100100
=>
101101
#setLocalValue(PLACE, RVAL)
102102
...
@@ -139,7 +139,7 @@ function call, pushing a new stack frame and returning to a different
139139
block after the call returns.
140140

141141
```k
142-
rule <k> #execTerminator(terminator(terminatorKindGoto(I), _SPAN)) ~> _CONT
142+
rule [termGoto]: <k> #execTerminator(terminator(terminatorKindGoto(I), _SPAN)) ~> _CONT
143143
=>
144144
#execBlockIdx(I)
145145
</k>
@@ -154,7 +154,7 @@ will be `129`.
154154
```k
155155
syntax KItem ::= #selectBlock ( SwitchTargets , Evaluation ) [strict(2)]
156156
157-
rule <k> #execTerminator(terminator(terminatorKindSwitchInt(DISCR, TARGETS), _SPAN)) ~> _CONT
157+
rule [termSwitchInt]: <k> #execTerminator(terminator(terminatorKindSwitchInt(DISCR, TARGETS), _SPAN)) ~> _CONT
158158
=>
159159
#selectBlock(TARGETS, DISCR)
160160
</k>
@@ -188,7 +188,7 @@ NB that a stack height of `0` cannot occur here, because the compiler prevents l
188188
If the local `_0` does not have a value (i.e., it remained uninitialised), the function returns unit and writing the value is skipped.
189189

190190
```k
191-
rule <k> #execTerminator(terminator(terminatorKindReturn, _SPAN)) ~> _
191+
rule [termReturnSome]: <k> #execTerminator(terminator(terminatorKindReturn, _SPAN)) ~> _
192192
=>
193193
#setLocalValue(DEST, #decrementRef(VAL)) ~> #execBlockIdx(TARGET)
194194
</k>
@@ -205,7 +205,7 @@ If the local `_0` does not have a value (i.e., it remained uninitialised), the f
205205
<stack> ListItem(StackFrame(NEWCALLER, NEWDEST, NEWTARGET, UNWIND, NEWLOCALS)) STACK => STACK </stack>
206206
207207
// no value to return, skip writing
208-
rule <k> #execTerminator(terminator(terminatorKindReturn, _SPAN)) ~> _
208+
rule [termReturnNone]: <k> #execTerminator(terminator(terminatorKindReturn, _SPAN)) ~> _
209209
=>
210210
#execBlockIdx(TARGET)
211211
</k>
@@ -279,14 +279,14 @@ where the returned result should go.
279279

280280
```k
281281
// Intrinsic function call - execute directly without state switching
282-
rule <k> #execTerminator(terminator(terminatorKindCall(FUNC, ARGS, DEST, TARGET, _UNWIND), _SPAN)) ~> _
282+
rule [termCallIntrinsic]: <k> #execTerminator(terminator(terminatorKindCall(FUNC, ARGS, DEST, TARGET, _UNWIND), _SPAN)) ~> _
283283
=>
284284
#execIntrinsic(lookupFunction(#tyOfCall(FUNC)), ARGS, DEST) ~> #continueAt(TARGET)
285285
</k>
286286
requires isIntrinsicFunction(lookupFunction(#tyOfCall(FUNC)))
287287
288288
// Regular function call - full state switching and stack setup
289-
rule <k> #execTerminator(terminator(terminatorKindCall(FUNC, ARGS, DEST, TARGET, UNWIND), _SPAN)) ~> _
289+
rule [termCallFunction]: <k> #execTerminator(terminator(terminatorKindCall(FUNC, ARGS, DEST, TARGET, UNWIND), _SPAN)) ~> _
290290
=>
291291
#setUpCalleeData(lookupFunction(#tyOfCall(FUNC)), ARGS)
292292
</k>
@@ -324,7 +324,7 @@ An operand may be a `Reference` (the only way a function could access another fu
324324
syntax KItem ::= #setUpCalleeData(MonoItemKind, Operands)
325325
326326
// reserve space for local variables and copy/move arguments from old locals into their place
327-
rule [call]: <k> #setUpCalleeData(
327+
rule [setupCalleeData]: <k> #setUpCalleeData(
328328
monoItemFn(_, _, someBody(body((FIRST:BasicBlock _) #as BLOCKS, NEWLOCALS, _, _, _, _))),
329329
ARGS
330330
)
@@ -403,7 +403,7 @@ Otherwise the provided message is passed to a `panic!` call, ending the program
403403
```k
404404
syntax MIRError ::= AssertError ( AssertMessage )
405405
406-
rule <k> #execTerminator(terminator(assert(COND, EXPECTED, MSG, TARGET, _UNWIND), _SPAN)) ~> _CONT
406+
rule [termAssert]: <k> #execTerminator(terminator(assert(COND, EXPECTED, MSG, TARGET, _UNWIND), _SPAN)) ~> _CONT
407407
=>
408408
#expect(COND, EXPECTED, MSG) ~> #execBlockIdx(TARGET)
409409
</k>
@@ -430,15 +430,15 @@ Other terminators that matter at the MIR level "Runtime" are `Drop` and `Unreach
430430
Drops are elaborated to Noops but still define the continuing control flow. Unreachable terminators lead to a program error.
431431

432432
```k
433-
rule <k> #execTerminator(terminator(terminatorKindDrop(_PLACE, TARGET, _UNWIND), _SPAN))
433+
rule [termDrop]: <k> #execTerminator(terminator(terminatorKindDrop(_PLACE, TARGET, _UNWIND), _SPAN))
434434
=>
435435
#execBlockIdx(TARGET)
436436
...
437437
</k>
438438
439439
syntax MIRError ::= "ReachedUnreachable"
440440
441-
rule <k> #execTerminator(terminator(terminatorKindUnreachable, _SPAN))
441+
rule [termUnreachable]: <k> #execTerminator(terminator(terminatorKindUnreachable, _SPAN))
442442
=>
443443
ReachedUnreachable
444444
...

kmir/src/kmir/kdist/mir-semantics/rt/data.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ It is also useful to capture unimplemented semantic constructs so that we can ha
102102
```k
103103
syntax Value ::= thunk ( Evaluation )
104104
105-
rule <k> EV:Evaluation => thunk(EV) ... </k> requires notBool isValue(EV) andBool notBool isTypedValue(EV) [owise]
105+
rule [thunk]: <k> EV:Evaluation => thunk(EV) ... </k> requires notBool isValue(EV) andBool notBool isTypedValue(EV) [owise]
106106
```
107107

108108
### Errors Related to Accessing Local Variables

kmir/src/kmir/kmir.py

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,60 @@ def __init__(
5656
@staticmethod
5757
def cut_point_rules(
5858
break_on_calls: bool,
59+
break_on_function_calls: bool,
60+
break_on_intrinsic_calls: bool,
61+
break_on_thunk: bool,
62+
break_every_statement: bool,
63+
break_on_terminator_goto: bool,
64+
break_on_terminator_switch_int: bool,
65+
break_on_terminator_return: bool,
66+
break_on_terminator_call: bool,
67+
break_on_terminator_assert: bool,
68+
break_on_terminator_drop: bool,
69+
break_on_terminator_unreachable: bool,
70+
break_every_terminator: bool,
71+
break_every_step: bool,
5972
) -> list[str]:
6073
cut_point_rules = []
61-
if break_on_calls:
74+
if break_on_thunk:
75+
cut_point_rules.append('RT-DATA.thunk')
76+
if break_every_statement or break_every_step:
77+
cut_point_rules.append('KMIR-CONTROL-FLOW.execStmt')
78+
if break_on_terminator_goto or break_every_terminator or break_every_step:
79+
cut_point_rules.append('KMIR-CONTROL-FLOW.termGoto')
80+
if break_on_terminator_switch_int or break_every_terminator or break_every_step:
81+
cut_point_rules.append('KMIR-CONTROL-FLOW.termSwitchInt')
82+
if break_on_terminator_return or break_every_terminator or break_every_step:
6283
cut_point_rules.extend(
6384
[
64-
'KMIR-CONTROL-FLOW.call',
85+
'KMIR-CONTROL-FLOW.termReturnSome',
86+
'KMIR-CONTROL-FLOW.termReturnNone',
87+
'KMIR-CONTROL-FLOW.endprogram-return',
88+
'KMIR-CONTROL-FLOW.endprogram-no-return',
6589
]
6690
)
91+
if (
92+
break_on_intrinsic_calls
93+
or break_on_calls
94+
or break_on_terminator_call
95+
or break_every_terminator
96+
or break_every_step
97+
):
98+
cut_point_rules.append('KMIR-CONTROL-FLOW.termCallIntrinsic')
99+
if (
100+
break_on_function_calls
101+
or break_on_calls
102+
or break_on_terminator_call
103+
or break_every_terminator
104+
or break_every_step
105+
):
106+
cut_point_rules.append('KMIR-CONTROL-FLOW.termCallFunction')
107+
if break_on_terminator_assert or break_every_terminator or break_every_step:
108+
cut_point_rules.append('KMIR-CONTROL-FLOW.termAssert')
109+
if break_on_terminator_drop or break_every_terminator or break_every_step:
110+
cut_point_rules.append('KMIR-CONTROL-FLOW.termDrop')
111+
if break_on_terminator_unreachable or break_every_terminator or break_every_step:
112+
cut_point_rules.append('KMIR-CONTROL-FLOW.termUnreachable')
67113
return cut_point_rules
68114

69115
@staticmethod
@@ -197,7 +243,22 @@ def prove_rs(opts: ProveRSOpts) -> APRProof:
197243
if apr_proof.passed:
198244
return apr_proof
199245

200-
cut_point_rules = KMIR.cut_point_rules(break_on_calls=opts.break_on_calls)
246+
cut_point_rules = KMIR.cut_point_rules(
247+
break_on_calls=opts.break_on_calls,
248+
break_on_function_calls=opts.break_on_function_calls,
249+
break_on_intrinsic_calls=opts.break_on_intrinsic_calls,
250+
break_on_thunk=opts.break_on_thunk,
251+
break_every_statement=opts.break_every_statement,
252+
break_on_terminator_goto=opts.break_on_terminator_goto,
253+
break_on_terminator_switch_int=opts.break_on_terminator_switch_int,
254+
break_on_terminator_return=opts.break_on_terminator_return,
255+
break_on_terminator_call=opts.break_on_terminator_call,
256+
break_on_terminator_assert=opts.break_on_terminator_assert,
257+
break_on_terminator_drop=opts.break_on_terminator_drop,
258+
break_on_terminator_unreachable=opts.break_on_terminator_unreachable,
259+
break_every_terminator=opts.break_every_terminator,
260+
break_every_step=opts.break_every_step,
261+
)
201262

202263
with kmir.kcfg_explore(label) as kcfg_explore:
203264
prover = APRProver(kcfg_explore, execute_depth=opts.max_depth, cut_point_rules=cut_point_rules)

kmir/src/kmir/options.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ class ProveOpts(KMirOpts):
4242
max_iterations: int | None
4343
reload: bool
4444
break_on_calls: bool
45+
break_on_function_calls: bool
46+
break_on_intrinsic_calls: bool
47+
break_on_thunk: bool
48+
break_every_statement: bool
49+
break_on_terminator_goto: bool
50+
break_on_terminator_switch_int: bool
51+
break_on_terminator_return: bool
52+
break_on_terminator_call: bool
53+
break_on_terminator_assert: bool
54+
break_on_terminator_drop: bool
55+
break_on_terminator_unreachable: bool
56+
break_every_terminator: bool
57+
break_every_step: bool
4558

4659
def __init__(
4760
self,
@@ -51,13 +64,39 @@ def __init__(
5164
max_iterations: int | None = None,
5265
reload: bool = False,
5366
break_on_calls: bool = False,
67+
break_on_function_calls: bool = False,
68+
break_on_intrinsic_calls: bool = False,
69+
break_on_thunk: bool = False,
70+
break_every_statement: bool = False,
71+
break_on_terminator_goto: bool = False,
72+
break_on_terminator_switch_int: bool = False,
73+
break_on_terminator_return: bool = False,
74+
break_on_terminator_call: bool = False,
75+
break_on_terminator_assert: bool = False,
76+
break_on_terminator_drop: bool = False,
77+
break_on_terminator_unreachable: bool = False,
78+
break_every_terminator: bool = False,
79+
break_every_step: bool = False,
5480
) -> None:
5581
self.proof_dir = Path(proof_dir).resolve() if proof_dir is not None else None
5682
self.bug_report = bug_report
5783
self.max_depth = max_depth
5884
self.max_iterations = max_iterations
5985
self.reload = reload
6086
self.break_on_calls = break_on_calls
87+
self.break_on_function_calls = break_on_function_calls
88+
self.break_on_intrinsic_calls = break_on_intrinsic_calls
89+
self.break_on_thunk = break_on_thunk
90+
self.break_every_statement = break_every_statement
91+
self.break_on_terminator_goto = break_on_terminator_goto
92+
self.break_on_terminator_switch_int = break_on_terminator_switch_int
93+
self.break_on_terminator_return = break_on_terminator_return
94+
self.break_on_terminator_call = break_on_terminator_call
95+
self.break_on_terminator_assert = break_on_terminator_assert
96+
self.break_on_terminator_drop = break_on_terminator_drop
97+
self.break_on_terminator_unreachable = break_on_terminator_unreachable
98+
self.break_every_terminator = break_every_terminator
99+
self.break_every_step = break_every_step
61100

62101

63102
@dataclass
@@ -79,6 +118,19 @@ def __init__(
79118
smir: bool = False,
80119
start_symbol: str = 'main',
81120
break_on_calls: bool = False,
121+
break_on_function_calls: bool = False,
122+
break_on_intrinsic_calls: bool = False,
123+
break_on_thunk: bool = False,
124+
break_every_statement: bool = False,
125+
break_on_terminator_goto: bool = False,
126+
break_on_terminator_switch_int: bool = False,
127+
break_on_terminator_return: bool = False,
128+
break_on_terminator_call: bool = False,
129+
break_on_terminator_assert: bool = False,
130+
break_on_terminator_drop: bool = False,
131+
break_on_terminator_unreachable: bool = False,
132+
break_every_terminator: bool = False,
133+
break_every_step: bool = False,
82134
) -> None:
83135
self.rs_file = rs_file
84136
self.proof_dir = Path(proof_dir).resolve() if proof_dir is not None else None
@@ -90,6 +142,19 @@ def __init__(
90142
self.smir = smir
91143
self.start_symbol = start_symbol
92144
self.break_on_calls = break_on_calls
145+
self.break_on_function_calls = break_on_function_calls
146+
self.break_on_intrinsic_calls = break_on_intrinsic_calls
147+
self.break_on_thunk = break_on_thunk
148+
self.break_every_statement = break_every_statement
149+
self.break_on_terminator_goto = break_on_terminator_goto
150+
self.break_on_terminator_switch_int = break_on_terminator_switch_int
151+
self.break_on_terminator_return = break_on_terminator_return
152+
self.break_on_terminator_call = break_on_terminator_call
153+
self.break_on_terminator_assert = break_on_terminator_assert
154+
self.break_on_terminator_drop = break_on_terminator_drop
155+
self.break_on_terminator_unreachable = break_on_terminator_unreachable
156+
self.break_every_terminator = break_every_terminator
157+
self.break_every_step = break_every_step
93158

94159

95160
@dataclass

0 commit comments

Comments
 (0)