Module GCthread


Require Import Coqlib Integers Maps.
Require Import Registers.

Require Import RTLinject MIR DataStructures Common.

Require Import INJECT InjectedCode.

Local Open Scope positive_scope.
Local Open Scope inject_scope.

Section GCTHREAD.
  Variable bi : backend_info.
  Variable om : object_model.
  Variable p : program.
  Variable max_p: Z. (* size of the array of global pointer variables *)

GC local variables.
  Notation r := 1.
  Notation fini := 2.
  Notation rc := 3.
  Notation md := 4.
  Notation last := 5.
  Notation st := 6.
  Notation f := 7.
  Notation cc := 8.
  Notation mc := 9.
  Notation cd := 10.
  Notation rt := 11.
  Notation top := 12.
  Notation bucket := 13.
  Notation nw := 14.
  Notation nr := 15.
  Notation pnr := 16.
  Notation b := 17.
  Notation tag := 18.
  Notation hdr := 19.
  Notation sfls := 20.
  Notation sfle := 21.
  Notation flptr :=22.
  Notation curr := 23.
  Notation head := 24.
  Notation clean:= 25.
  Notation pfld := 26.
  Notation lfld := 27.
  Notation next := 28.

  Notation rmax := 64. (* write-only local variable. *)

Section HANDSHAKE.
Waits for all running mutators to put value val into field fld of their mutator data. Returns the number of running mutators (when zero, it’s time to die).
  Variable fld : int.
  Variable val : int.
  Definition wait_hs : statement :=
    Sfreeperm;;
    Repeat
      Const rc := Int.zero;;
      Const fini := Int.one;;
      Lea_g md := bi.(bi_mutator_data);;
      Lea_i last := bi.(bi_mutator_data)[md_size bi];;
      Sfence false;;
      Repeat
        Sfence false;;
        Load st := md[md_status];;
        If st <> ms_available
        Then (
          Lea rc := rc [ Int.one ];;
          If st <> ms_quick
          Then (
            Sfence false;;
            Load f := md[fld];;
            If f == val
            Then Sskip
            Else Const fini := Int.zero
          )
          Else Sfence false
        )
        Else Sfence false;;
        Lea md := md[sizeof_md bi]
      Until md = last;;
      If rc == Int.zero
      Then Sfence true;; Sreturn nil
      Else Sfence false
    Until fini;;
    Srelease.

  Definition wait_hs_high :=
    Repeat
      Atomic{
      Const rc := Int.zero;;
      Const fini := Int.one;;
      Lea_g md := bi.(bi_mutator_data);;
      Lea_i last := bi.(bi_mutator_data)[md_size bi]
      };;
      Repeat
        Atomic{ Load st := md[md_status] };;
        Atomic{
          If st <> ms_available
          Then (
            Lea rc := rc [ Int.one ];;
            If st <> ms_quick
            Then (
              Load f := md[fld];;
              If f == val
              Then Sskip
              Else Const fini := Int.zero
            )
            Else Sskip
          )
          Else Sskip;;
          Lea md := md[sizeof_md bi]
        }
      Until md = last;;
      Atomic{
        If rc == Int.zero
        Then Sreturn nil
        Else Sskip
      }
    Until fini.

End HANDSHAKE.

  Definition mark_grey (b: reg) : statement :=
    Local (Load tag := b[blk_color]);;
    If tag = cc
    Then (
      If topbi.(bi_collector_stack_size)
      Then (Sfence true;; Sabort Interleaved true Abort_OCS)
      Else Sskip;;
      Local (Store_g bi.(bi_collector_stack)[top] := b);;
      Lea top := top[Int.repr 4%Z]
    ) Else Sskip.

  Definition empty_collector_stack : statement :=
    While top <> Int.zero Do (
      Lea top := top[Int.repr (-4)%Z];;
      Local (Load_g b := bi.(bi_collector_stack)[top]);;
      Sfence false;;
      Load tag := b[blk_color];;
      If tagmc
      Then (
        Local (Load hdr := b[blk_hdr]);;
        Leak LGrey b rmax;;
        Const f := Int.zero;;
        Sfence false;;
        While f <> Int.repr (Zpos om.(om_object_size)) Do (
          om.(om_is_field_pointer_stmt) hdr f r;;
          If r <> Int.zero
          Then (
            Sfence false;;
            Load r := b + f × Int.repr 4 [blk_first_field];;
            If r <> Int.zero
            Then (
              mark_grey r
            ) Else Sskip;;
            Sfence false
          ) Else Sskip ;;
          Lea f := f[Int.one]
        );;
        Store b[blk_color] := mc
      ) Else Sskip
    ).

  Definition trace : statement :=
      Repeat
        Const clean := Int.one;;
        Lea_g md := bi.(bi_mutator_data);;
        Lea_i last := bi.(bi_mutator_data)[md_size bi];;
        Sfence false;;
        Repeat
          Lea bucket := md[md_bucket];;
          Local (Load nr := md[md_next_read]);;
          Sfence false;;
          Load nw := md[md_next_write];;
          While nrnw Do (
            Const clean := Int.zero;;
            Local (Load b := bucket + nr [Int.zero]);;
            Leak LGrey b rmax;;
            Lea nr := nr[Int.repr 4];;
            mark_grey b;;
            Sfence false;;
            empty_collector_stack
          );;
          Store md[md_next_read] := nr;;
          Sfence true;;
          Lea md := md[sizeof_md bi]
        Until md = last
      Until clean.

 Definition trace_high : statement :=
   Repeat
     Atomic{
       Const clean := Int.one;;
       Lea_i md := bi_mutator_data bi [Int.zero];;
       Lea_i last := bi_mutator_data bi [md_size bi]
     } ;;
     Repeat
       Atomic{
         Lea bucket := md [md_bucket];;
         Local (Load nr := md [md_next_read]);;
         Load nw := md [md_next_write]
         } ;;
         While nrnw Do (
           Atomic{
             Const clean := Int.zero;;
             Local (Load b := bucket + nr [Int.zero]);;
             Leak LGrey b rmax;;
             Lea nr := nr [Int.repr 4];;
             Local (Load tag := b [blk_color]);;
             If tag = cc
             Then If topbi_collector_stack_size bi
                  Then (Srelease;; Sabort Atomic true MIR.Abort_OCS) Else Sskip;;
                  Local (Store_g bi_collector_stack bi [top]:=b);;
                  Lea top := top [Int.repr 4]
             Else Sskip
           } ;;
           While top <> Int.zero Do (
             Atomic{
               Lea top := top [Int.repr (-4)];;
               Local (Load_g b := bi_collector_stack bi [top]);;
               Load tag := b [blk_color]
             } ;;
             If tagmc
             Then
               Atomic{
                 Local (Load hdr := b [blk_hdr]);;
                 Leak LGrey b rmax;;
                 Const f := Int.zero
               } ;;
               While f <> Int.repr (Zpos (om_object_size om)) Do
                 Sbranch (
                   Atomic{
                     om.(om_is_field_pointer_stmt) hdr f r;;
                     Assume r <> Int.zero;;
                     Load r := b + f × Int.repr 4 [blk_first_field]
                   } ;;
                   Atomic{
                     If r <> Int.zero
                     Then Local (Load tag := r [blk_color]);;
                          If tag = cc
                          Then If topbi_collector_stack_size bi
                               Then (Srelease;; Sabort Atomic true MIR.Abort_OCS) Else Sskip;;
                               Local (Store_g bi_collector_stack bi [top]:=r);;
                               Lea top := top [Int.repr 4]
                          Else Sskip
                     Else Sskip;;
                     Lea f := f [Int.one]
                   })
                   ( om.(om_is_field_pointer_stmt) hdr f r;;
                     Assume r == Int.zero;;
                     Lea f := f [Int.one]
                    );;
                    Store b [blk_color] := mc
               Else Sskip));;
       Atomic{
         release (Store md [md_next_read]:=nr);;
         Lea md := md [sizeof_md bi]
       }
     Until md = last
   Until clean.

  Definition clear_fields : statement :=
    Lea pfld := b[blk_first_field];;
    Lea lfld := b[object_byte_size om];;
    While pfldlfld Do (
      Local (Store pfld := r);;
      Lea pfld := pfld [ Int.repr 4 ]
    ).

  Definition sweep : statement :=
    Lea_g b := bi.(bi_heap);;
    Lea_i last := bi.(bi_heap)[heap_byte_size om];;
    Const sfls := Int.zero;;
    Const sfle := Int.zero;;
    Sfence false;;
    Repeat
      Load tag := b[blk_color];;
      If tag = cc
      Then (
        Leak LFree b rmax;;
        Const r := color_blue;;
        Local (Store b[blk_color] := r);;
        Const r := Int.zero;;
        clear_fields;;
        If sfle == Int.zero
        Then Move sfle := b
        Else Sskip;;
        Local (Store b[blk_next] := sfls);;
        Move sfls := b
      ) Else Sskip;;
      Lea b := b [object_byte_size om];;
      Sfence false
    Until b = last;;
    If sfle <> Int.zero
    Then (
      Lea_g flptr := bi.(bi_free_list);;
      Sfence false;;
      Load curr := flptr;;
      Repeat
        Move head := curr;;
        Lea next := sfle[blk_next];;
        Local (Store next := head);;
        release (Cas curr := flptr, head -> sfls)
      Until curr = head
    ) Else Sskip.

  Definition sweep_high :=
    Atomic{
       Lea_i b := bi_heap bi [Int.zero];;
       Lea_i last := bi_heap bi [heap_byte_size om];;
       Const sfls := Int.zero;;
       Const sfle := Int.zero
     } ;;
     Repeat
       Load tag := b [blk_color];;
       Atomic{
         If tag = cc
         Then Leak LFree b rmax;;
              Const r := color_blue;;
              Local (Store b [blk_color]:=r);;
              Const r := Int.zero;;
              clear_fields;;
              If sfle == Int.zero Then Move sfle := b Else Sskip;;
              Local (Store b [blk_next]:=sfls);;
              Move sfls := b Else Sskip;;
         Lea b := b [object_byte_size om]
       }
     Until b = last;;
     If sfle <> Int.zero
     Then
       Atomic{
         Lea_i flptr := bi_free_list bi [Int.zero];;
         Load curr := flptr [Int.zero]
       } ;;
       Sloop
         Atomic{
           Move head := curr;;
           Lea next := sfle [blk_next];;
           Local (Store next [Int.zero]:=head);;
           Load curr := flptr[Int.zero];; Srelease;;
           Assume currhead
         };;
       Atomic{
         Move head := curr;;
         Lea next := sfle [blk_next];;
         Local (Store next [Int.zero]:=head);;
         Load curr := flptr[Int.zero];;
         Assume curr = head;;
         release (Store flptr[Int.zero]:=sfls)
       }
     Else Sskip.

  Definition cycle : statement :=
    Const r := ms_sync1;;
    Store cd[cd_phase] := r;;
    wait_hs md_phase ms_sync1;;

    Const r := ms_sync2;;
    Store cd[cd_phase] := r;;
    wait_hs md_phase ms_sync2;;

    Const r := ms_async;;
    Store cd[cd_phase] := r;;

    Lea_g pnr := bi.(bi_ptr_globals);;
    Lea last := pnr[Int.repr max_p];;
    While pnrlast Do (
      Load rt := pnr;;
      If rt <> Int.zero
      Then (
        Local (Store_g bi.(bi_collector_stack)[top] := rt);;
        Lea top := top[Int.repr 4%Z]
      ) Else Sskip;;
      Lea pnr := pnr [Int.repr 4%Z]
    );;

    wait_hs md_phase ms_async;;

    trace;;

    sweep
    .

  Definition cycle_high :=
    Atomic{
      Const r := ms_sync1;;
      Store cd [cd_phase]:=r
      } ;;
      wait_hs_high md_phase ms_sync1;;
       Atomic{
      Const r := ms_sync2;;
      Store cd [cd_phase]:=r
      } ;;
      wait_hs_high md_phase ms_sync2;;
       Atomic{
      Const r := ms_async;;
      Store cd [cd_phase]:=r
      } ;;
      Atomic{
       Lea_i pnr := bi_ptr_globals bi [Int.zero];;
       Lea last := pnr [Int.repr 1]};;
      While pnrlast
      Do (Atomic{
          Load rt := pnr [Int.zero]
          } ;;
          Atomic{
          If rt <> Int.zero
          Then Local (Store_g bi.(bi_collector_stack)[top] := rt);;
               Lea top := top [Int.repr 4] Else Sskip;;
          Lea pnr := pnr [Int.repr 4]});;
      wait_hs_high md_phase ms_async;;
      trace_high;;
      sweep_high.

  Definition let_threads_go : statement :=
    Lea_g md := bi.(bi_mutator_data);;
    Lea_i last := bi.(bi_mutator_data)[md_size bi];;
    Repeat
      Const r := Int.zero;;
      Local (Store md[md_next_read] := r);;
      release (Load st := md[md_status]);;
      If st == ms_quick
      Then (
        Local (Store md[md_alloc_c] := cc);;
        Const r := ms_async;;
        Local (Store md[md_phase] := r);;
        Const r := ms_running;;
        Store md[md_status] := r;;
        Sfence true
      ) Else Sskip;;
      Lea md := md[sizeof_md bi]
    Until md = last.

  Definition let_threads_go_high :=
    Atomic{
      Lea_i md := bi_mutator_data bi [Int.zero];;
      Lea_i last := bi_mutator_data bi [md_size bi]
      } ;;
      Repeat
        Atomic{
             Const r := Int.zero;;
             Local (Store md [md_next_read]:=r);;
             release (Load st := md[md_status])
             } ;;
              Atomic{
             If st == ms_quick
             Then Local (Store md [md_alloc_c]:=cc);;
                  Const r := ms_async;;
                  Local (Store md [md_phase]:=r);;
                  Const r := ms_running;;
                  Store md [md_status]:=r;;
                  Sfence true Else Sskip;;
      Lea md := md [sizeof_md bi]
    }
    Until md = last.

  Definition gc_startup : statement :=
    Sfreeperm;;
    Lea_g cd := bi.(bi_collector);;
    Const r := ms_async;;
    Local (Store cd[cd_phase] := r);;
    Lea_g pnr := bi.(bi_heap);;
    Local (Store_g bi.(bi_free_list) := pnr);;
    Lea_i last := bi.(bi_heap)[Int.sub (heap_byte_size om) (object_byte_size om)];;
    Const tag := color_blue;;
    Repeat
      Lea b := pnr[object_byte_size om];;
      Local (Store pnr[blk_next] := b);;
      Local (Store pnr[blk_color] := tag);;
      Move pnr := b
    Until pnr = last;;
    Lea_g r := p.(prog_entry);;
    Lea_g pnr := bi.(bi_mutator_data);;
    Local (Store pnr[md_fp] := r);;
    Const r := ms_quick;;
    Store pnr[md_status] := r;;
    Sfence true;;
    Lea_g r := bi.(bi_thread_main);;
    Leak LSpawn pnr rmax;;
    Sreturn (r::pnr::nil).

  Definition gc_startup_high : statement :=
    Atomic{
      Lea_g cd := bi.(bi_collector);;
      Const r := ms_async;;
      Store cd[cd_phase] := r;;
      Lea_g pnr := bi.(bi_heap);;
      Store_g bi.(bi_free_list) := pnr;;
      Lea_i last := bi.(bi_heap)[Int.sub (heap_byte_size om) (object_byte_size om)];;
      Const tag := color_blue;;
      Repeat
        Lea b := pnr[object_byte_size om];;
        Store pnr[blk_next] := b;;
        Store pnr[blk_color] := tag;;
        Move pnr := b
      Until pnr = last;;
      Lea_g r := p.(prog_entry);;
      Lea_g pnr := bi.(bi_mutator_data);;
      Store pnr[md_fp] := r;;
      Const r := ms_quick;;
      Store pnr[md_status] := r;;
      Lea_g r := bi.(bi_thread_main);;
      Leak LSpawn pnr rmax;;
      Sreturn (r::pnr::nil)
    }.

  Definition gc_main : statement :=
    Const top := Int.zero;;
    Const cc := color_BW;;
    Xor mc := cc ^ color_mask;;
    (Store cd[cd_mark_c] := mc);;
    Repeat
      let_threads_go;;
      cycle;;
      Local (Load cc := cd[cd_mark_c]);;
      Xor mc := cc ^ color_mask;;
      release (Store cd[cd_mark_c] := mc);;
      Const r := Int.zero
    Until r.

  Definition gc_high :=
    Atomic{
      Const top := Int.zero;;
      Const cc := color_BW;;
      Xor mc := cc ^ color_mask;;
      Store cd [cd_mark_c]:=mc
    } ;;
    Repeat
      let_threads_go_high;;
      cycle_high;;
      Atomic{
        Local (Load cc := cd [cd_mark_c]);;
        Xor mc := cc ^ color_mask;;
        release (Store cd [cd_mark_c]:=mc);;
        Const r := Int.zero
      }
    Until r.

  Definition gc_main_sig : Ast.signature :=
    Ast.mksignature nil (Some Ast.Tint).

  Definition gc_main_func : RTLinject.function :=
    RTLinject.mkfunction gc_main_sig nil Int.zero
    (of_pair_list
        ((1, Iinject (Make_IC gc_startup gc_startup_high nil) nil (r::pnr::nil) 2)
       ::(2, Ithreadcreate r pnr 3)
       ::(3, Iinject (Make_IC gc_main gc_main nil) nil nil 4)
       ::(4, Ireturn xH)
       ::nil))
    1.

End GCTHREAD.