Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 90 additions & 1 deletion Veir/Interpreter/Lemmas.lean
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Veir.Interpreter.Basic
import Veir.Dominance
import Veir.Interpreter.Refinement.Basic
import Veir.Interpreter.Refinement.Lemmas
import Veir.Verifier

namespace Veir
Expand Down Expand Up @@ -516,6 +517,89 @@ theorem exists_interpretOp'_eq_some {ctx : WfIRContext OpCode} {op : OperationPt
(mem : MemoryState) :
∃ res, op.interpret ctx.raw operands mem = some res := by sorry

/-- The relation `interpretOp'_monotone` establishes between two interpretation results. -/
abbrev InterpretResultIsRefinedBy :
Array RuntimeValue × MemoryState × Option ControlFlowAction →
Array RuntimeValue × MemoryState × Option ControlFlowAction → Prop :=
fun r₁ r₂ => r₁.1 ⊒ r₂.1 ∧ r₁.2.1 = r₂.2.1 ∧
ControlFlowAction.optionIsRefinedBy r₁.2.2 r₂.2.2
Comment on lines +521 to +525

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should probably be added in Interpreter/Basic.lean if possible. Unless there is a cycle of dependency from that?
In any case, since this is introduced, we should also use it in the other parts of the codebase I would say.


/-- `InterpretResultIsRefinedBy` is reflexive, so an interpretation result always refines itself. -/
@[grind .]
theorem interpretResult_isRefinedBy_refl
(x : Interp (Array RuntimeValue × MemoryState × Option ControlFlowAction)) :
Interp.isRefinedBy InterpretResultIsRefinedBy x x := by
rcases x with _ | (x | _) <;> grind [Interp.isRefinedBy]

/--
A register runtime value can only be refined by itself, so operand arrays that consist purely of
registers are refined only by themselves. This makes every dialect whose operands are registers
(`riscv`, `riscv_cf`, `riscv_stack`, `rv64`) monotone for free: the refined operands are the
original ones, so both sides interpret to the very same result.
-/
theorem RuntimeValue.eq_of_arrayIsRefinedBy_of_regs {a b : Array RuntimeValue}
(h : a ⊒ b) (hregs : ∀ v ∈ a, ∃ r, v = .reg r) : b = a := by
grind [arrayIsRefinedBy, reg_of_isRefinedBy, Array.getElem_mem]

@[grind =]
theorem Interp.pure_def {α : Type} (a : α) : (pure a : Interp α) = some (.ok a) := rfl

@[grind =]
theorem Interp.bind_def {α β : Type} (x : Interp α) (f : α → Interp β) :
(x >>= f) = match x with
| none => none
| some .ub => some .ub
| some (.ok a) => f a := rfl

/--
A RISC-V operation that interprets successfully produces register results and no control flow
action: a single register for the arithmetic and load opcodes, and no result at all for the stores.
Note that the memory is *not* preserved -- loads grow it via `ensureSize` and stores write to it.
-/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
-/
action: a single register for the arithmetic and load opcodes, and no result at all for the stores.

I am not sure why mentioning memory preservation here?

theorem Riscv.interpretOp'_ok_results {vals : Array RuntimeValue} {mem' : MemoryState}
{act : Option ControlFlowAction}
(h : Riscv.interpretOp' opType properties resultTypes operands blockOperands mem
= some (.ok (vals, mem', act))) :
((∃ r, vals = #[.reg r]) ∨ vals = #[]) ∧ act = none := by
cases opType <;> simp only [Riscv.interpretOp'] at h <;> grind

/--
A non-register operand is either fatal or irrelevant: every RISC-V opcode that reads its operands
pattern-matches them as registers and fails to interpret otherwise, and the opcodes that ignore
their operands (`li`, `lui`) interpret to the very same result whatever the operands are.
-/
theorem Riscv.interpretOp'_eq_none_or_eq_of_not_regs {operands operands' : Array RuntimeValue}
(hregs : ¬ ∀ v ∈ operands, ∃ r, v = .reg r) :
Riscv.interpretOp' opType properties resultTypes operands blockOperands mem = none ∨
Riscv.interpretOp' opType properties resultTypes operands blockOperands mem
= Riscv.interpretOp' opType properties resultTypes operands' blockOperands mem := by
cases opType <;>
simp only [Riscv.interpretOp'] <;>
first
| (right; trivial)
| (left; split <;> grind [Array.mem_def])

/--
`Riscv.interpretOp'` is monotone in its operands.

RISC-V operands are registers, which carry no poison, so refinement on them is equality: either
every operand is a register -- and then the refined operands are the original ones and both sides
interpret to the very same result -- or some operand is not a register, and
`Riscv.interpretOp'_eq_none_or_eq_of_not_regs` applies.
-/
theorem Riscv.interpretOp'_monotone {operands operands' : Array RuntimeValue} :
operands ⊒ operands' →
Interp.isRefinedBy InterpretResultIsRefinedBy
(Riscv.interpretOp' opType properties resultTypes operands blockOperands mem)
(Riscv.interpretOp' opType properties resultTypes operands' blockOperands mem) := by
intro h
by_cases hregs : ∀ v ∈ operands, ∃ r, v = .reg r
Comment thread
math-fehr marked this conversation as resolved.
· obtain rfl := RuntimeValue.eq_of_arrayIsRefinedBy_of_regs h hregs
apply interpretResult_isRefinedBy_refl
· rcases Riscv.interpretOp'_eq_none_or_eq_of_not_regs (operands' := operands') hregs with heq | heq
· rw [heq]; simp [Interp.isRefinedBy]
· rw [heq]; apply interpretResult_isRefinedBy_refl

set_option warn.sorry false in
theorem interpretOp'_monotone
(opType : OpCode) (properties : propertiesOf opType) (resultTypes : Array TypeAttr)
Expand All @@ -526,7 +610,12 @@ theorem interpretOp'_monotone
ControlFlowAction.optionIsRefinedBy r₁.2.2 r₂.2.2)
(interpretOp' opType properties resultTypes operands blockOperands mem)
(interpretOp' opType properties resultTypes operands' blockOperands mem) := by
sorry
intro h
cases opType
case riscv =>
simp only [interpretOp']
exact Riscv.interpretOp'_monotone h
all_goals sorry

set_option warn.sorry false in
/--
Expand Down
Loading