DeepSpec / InteractionTrees

A Library for Representing Recursive and Impure Programs in Coq
MIT License
204 stars 51 forks source link

Relating interaction tree and small-step semantics #176

Closed alxest closed 2 years ago

alxest commented 4 years ago

I am trying to prove refinement between two different interpretations of the same program -- one in interaction tree and the other in small-step semantics (in fact, CompCert Clight). Specifically, I give small-step semantics to an interaction tree (a state consists of interaction tree) and then use traditional simulation techniques between the two small-step semantics. However, I cannot see a simple simulation relation because the two take quite different approaches to modeling loop/recursion (and its continuation). I have some rough ideas on doing this, but I guess there is some related work and I am reinventing the wheel in an ugly way...

Any advice is appreciated. Thank you!

alxest commented 4 years ago

FYI: My rough idea is to define two marker events to mark the beginning (event <) and the end (event >) of a function, merely to give simulation relation. (these events are ignored in top-level) That way, I can regain stack-like structure from an interaction tree and can give a simulation relation between source/target stacks.

Lysxia commented 4 years ago

I'm afraid I don't have any references to point to. Maybe one of my coauthor does?

How are function calls represented, in both the itree and the original small-step semantics? In particular I'm curious about the details of how you handle recursion in itree.

alxest commented 4 years ago

@Lysxia (I guess I have found a solution, but I will write these as a reference.)

Problem

In itree, I defined recursion using mrec combinator. Specifically, each function has type list val -> itree (MyCallE +' E) val and I use mrec to make it list val -> itree E val. (MyCallE consists of list val) The problem here is that, the resulting itree has unfolded (or, flattened?) all the recursive function calls, while in small-step semantics we do not. As an example, consider this simple program.

f(n) = if(n>0) {
L0:      print "A" ;
L1:      f(n-1) ;
L2:      print "A" 
       }

Let's say that I want to show f(5) has the same behavior in these two semantics. At the beginning of f(3),

Here, I need to give a simulation relation that relates State (n ~> 3) L0 with the first 6 occurrences of print "A", and relates Stack ... with the last 2 occurrences of print "A". However, doing this requires in-depth understanding of f, where the proof technique that I want should be general and not specialized to a specific program.

Solution

I give an intermediate semantics as a bridge between the two. The idea is to give a stack-like structure to itree, which is useful for better simulation relation. Specifically, its state is (itree (MyCallE +' E) val), List (ktree (MyCallE +' E) val val). (Note that we do have MyCallE event here, because we don't use mrec to handle them; we handle them in step relation.) Its step relation is like the original one, but it handles MyCallE by pushing a new itree into the stack. (and handles Ret correspondingly)

Now we can prove itree-semantics f(4) ~= Clight-semantics f(4) with transitivity.

Lysxia commented 4 years ago

Thanks, this is quite instructive!

One detail that's itching me is that the state of the intermediate semantics, a pair (t, [k1; ...; kn]) : (itree (MyCallE +' E) val) * List (ktree (MyCallE +' E) val val), is eventually intended to represent a single itree interp something (t >>= k1 >>= ... >>= kn). Thus it seems possible to make the intermediate semantics directly an itree, as a denotational semantics of the original Clight state. The benefits of this slight reformulation is that it allows the intermediate semantics to (re)use a generic step relation on itrees (whereas there currently seems to be an ad-hoc step relation), and the relation itree ~= itree-stack can simply be eutt.

alxest commented 4 years ago

@Lysxia Thanks for your reply. It seems I didn't make the point clear. Let me try to clarify my viewpoint again.

My verification strategy is as follows: spec (itree) >= ... >= impl (itree) >= impl (Clight).


To address this discrepancy, I put f_impl (itree-stack) between f_impl (itree) and f_impl (Clight). This f_impl (itree-stack) is close enough to both f_impl (itree) and f_impl (Clight), so proving these two separate conditions is much easier than directly proving f_impl (itree) >= f_impl (Clight).