Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prove the partial correctness of a small program with a loop #91

Merged
merged 12 commits into from
Aug 20, 2024

Conversation

shigoel
Copy link
Collaborator

@shigoel shigoel commented Aug 16, 2024

Description:

Used the verification condition generation framework to prove the partial correctness of a simple (memory access free) program with a loop that computes the addition of two numbers x and y by incrementing y by one x times. Proof of termination remains to be done.

Also simplified the AbsVCG proof by leveraging the sym1_n tactic.

Testing:

make all succeeds.

License:

By submitting this pull request, I confirm that my contribution is
made under the terms of the Apache 2.0 license.

@shigoel shigoel marked this pull request as ready for review August 16, 2024 17:32
@bollu
Copy link
Collaborator

bollu commented Aug 16, 2024

@shigoel
Copy link
Collaborator Author

shigoel commented Aug 16, 2024

Any idea what's up with the mismatch here? https://github.com/leanprover/LNSym/actions/runs/10423880410/job/28871475418?pr=91#step:8:7334

No, it's bizarre. Didn't touch any instruction semantics. Trying to debug...

@shigoel
Copy link
Collaborator Author

shigoel commented Aug 16, 2024

I couldn't reproduce the alleged mismatch. I'm inclined to think the failure is bogus, but I don't know how to isolate the problem yet.

@bollu
Copy link
Collaborator

bollu commented Aug 16, 2024

@shigoel Another nice thing would be to print the random seed we use so we can rerun a test.

@shigoel
Copy link
Collaborator Author

shigoel commented Aug 16, 2024

We do print out the inputs to the armsimulate script. It's not ideal, but yes, ideally, we should print out a simple one-liner to reproduce the mismatch.

@shigoel
Copy link
Collaborator Author

shigoel commented Aug 16, 2024

@alexkeizer Whenever you get the chance, it'd be great to have your eyes here from the point of view of symbolic simulation automation. (We could discuss in person next week too).

Comment on lines +21 to +36
-- (TODO) Move to Arm/BitVec.lean?
/-- Unexpander to represent bitvector literals in hexadecimal -- this overrides
the unexpander in the Lean BitVec library. -/
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNatToHex : Lean.PrettyPrinter.Unexpander
| `($(_) $n:num $i:num) =>
let i' := i.getNat
let n' := n.getNat
let trimmed_hex := -- Remove leading zeroes...
String.dropWhile (BitVec.ofNat n' i').toHex
(fun c => c = '0')
-- ... but keep one if the literal is all zeros.
let trimmed_hex := if trimmed_hex.isEmpty then "0" else trimmed_hex
let bv := Lean.Syntax.mkNumLit s!"0x{trimmed_hex}#{n'}"
`($bv:num)
| _ => throw ()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! I think this would be useful to have more broadly; especially since we name step-theorems according to their address in hex

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One reason why I'm hesitant re. this unexpander is that register indices also appear in hex then. E.g., StateField.GPR 0xc#5 and StateField.SFP 0xd#5, which is inconvenient.

Maybe we need more directed unexpanders...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something I've been thinking about: it might be nice to introduce a simple abbreviation for addresses and raw instructions

abbrev Address := BitVec 64

abbrev RawInstr := BitVec 32

Then, we could somehow define the unexpanders to apply just for those types, not for all bitvectors

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's a good idea, regardless of the unexpander issue.

Note that it also helps to see machine components' values as hex; for instance, consider:

r (StateField.GPR 1#5) s = 18446744073709551614#64

vs.

r (StateField.GPR 1#5) s = 0xfffffffffffffffe#64

Comment on lines 32 to 34
-- (FIXME) We don't really need the stack pointer to be aligned, but the
-- `sym1_n` tactic expects this. Can we make this optional?
CheckSPAlignment s
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we totally can make it optional (I wasn't sure if there was a need for that, but seems like there is)

Comment on lines +171 to 178
theorem nextc_to_run_from_0x4005d0 (h : abs_pre s0) :
(Correctness.nextc (Sys.run s0 1)) = run 4 s0 := by
simp only [abs_pre, state_simp_rules] at h
obtain ⟨h_s0_pc, h_s0_program, h_s0_err, h_s0_sp_aligned⟩ := h
simp only
[Correctness.nextc, Correctness.arm_run,
Spec'.cut, minimal_theory]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I assume this serves to decouple the reasoning about the number of steps to take from the reasoning about the effects of these steps (so that we can use existing symbolic eval machinery for the latter).

It does feel a bit repetitive, so I imagine we would still like to integrate some of this step-by-step reasoning into the tactic

Copy link
Collaborator Author

@shigoel shigoel Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's exactly right. And I'm glad this appears repetitive --- that was the goal, to see if there's a pattern that can be mechanized.

I'm hoping we can avoid such nextc_to_run.. theorems entirely, and provide automation for theorems like effects_of_nextc_.. directly where the number of steps to simulate and the effects are determined "dynamically" in tandem. If possible, it'd be good to build support for this atop the existing symbolic eval machinery so that we're not married to the VCG approach for all LNSym proofs. E.g., the VCG approach is pretty tedious for verifying straight-line code but convenient for loops. For the former, we could use the existing symbolic eval machinery to simulate the code and then immediately reason about the program's effects. For the latter, we could use the VCG-supporting symbolic eval machinery.

Comment on lines 146 to 163
-- (FIXME) Obtain *_cut theorems for each instruction automatically.

theorem program.stepi_0x4005a4_cut (s sn : ArmState)
(h_program : s.program = program)
(h_pc : r StateField.PC s = 0x4005a4#64)
(h_err : r StateField.ERR s = StateError.None)
(h_sp_aligned : CheckSPAlignment s)
(h_step : sn = run 1 s) :
cut sn = false ∧
r StateField.PC sn = 0x4005b0#64 ∧
r StateField.ERR sn = .None ∧
sn.program = program ∧
CheckSPAlignment sn := by
have := program.stepi_0x4005a4 s (stepi s) h_program h_pc h_err
simp only [minimal_theory] at this
simp_all only [run, cut, this,
state_simp_rules, bitvec_rules, minimal_theory]
done
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we want out of the _cut theorems exactly? It seems to combine axiomatizing the effects with reasoning about whether some specific instruction is a cutpoint.

I guess you mean we should incorporate the axiomatic effects into the regular stepi theorems, and then build a second layer on top that adds a conjunct with cut sn = true/false to it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you mean we should incorporate the axiomatic effects into the regular stepi theorems, and then build a second layer on top that adds a conjunct with cut sn = true/false to it?

Yes, that'd certainly be helpful. All these _cut theorems do is help in the unwinding of Correctness.nextc: cut sn = true/false is there for obvious reasons, but the rest of the conjuncts help in relieving the hyps. of the next applicable _cut theorem.

-- TODO: Tactic to "explode" conjunctions?
obtain ⟨h_s0_pc, h_s0_program, h_s0_err, h_s0_sp_aligned⟩ := h_pre
-- Symbolic simulation
-- (FIXME) sym1_n doesn't play well with unconditional branches.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, right now I've assumed the PC simply increments each instruction. I should implement something more sophisticated

Comment on lines +327 to +328
-- (FIXME) better stepi lemma generation
simp (config := {ground := true}) only at h_step_2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What, concretely, needs improving here?

Copy link
Collaborator Author

@shigoel shigoel Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Badly placed comment there. sym1_n 1 at s1 (the previous tactic) generates proof state with subterms like:

(AddWithCarry (r (StateField.GPR 0x0#5) s1)
          (Decidable.rec (fun h => (0x0#1, 0x0#64)) (fun h => (0x1#1, 0xffffffffffffffff#64))
              (instDecidableEqBitVec 0x1#1 0x1#1)).snd
          (Decidable.rec (fun h => (0x0#1, 0x0#64)) (fun h => (0x1#1, 0xffffffffffffffff#64))
              (instDecidableEqBitVec 0x1#1 0x1#1)).fst)

which are simplified to the following with simp/ground.

(AddWithCarry (r (StateField.GPR 0x0#5) s1) 0xffffffffffffffff#64 0x1#1)

In an ideal world, we shouldn't need this simp/ground.

@shigoel shigoel merged commit 9e0f494 into main Aug 20, 2024
4 checks passed
@shigoel shigoel deleted the add_loop_vcg branch August 21, 2024 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants