Merge branch 'local-ocamlbuild' into local-trunk
[SXSI/xpathcomp.git] / src / runtime.ml
diff --git a/src/runtime.ml b/src/runtime.ml
new file mode 100644 (file)
index 0000000..c355f5e
--- /dev/null
@@ -0,0 +1,404 @@
+INCLUDE "debug.ml"
+INCLUDE "utils.ml"
+
+open Format
+open Ata
+module type S = sig
+  type result_set
+  val top_down_run : Ata.t -> Tree.t -> Tree.node -> result_set
+  val bottom_up_run : Ata.t -> Tree.t -> Compile.text_query * string -> result_set
+end
+
+module Make (U : ResJIT.S) : S with type result_set = U.NS.t =
+  struct
+
+    type result_set = U.NS.t;;
+
+    let eval_form auto s1 s2 f =
+      let rec loop f =
+       match Formula.expr f with
+         | Formula.False | Formula.True | Formula.Pred _ -> f, []
+         | Formula.Atom(`Left, b, q) ->
+             Formula.of_bool (b == (StateSet.mem q s1)),
+             if b && StateSet.mem q auto.topdown_marking_states then [ResJIT.LEFT q] else []
+         | Formula.Atom (`Right, b, q) ->
+             Formula.of_bool(b == (StateSet.mem q s2)),
+             if b && StateSet.mem q auto.topdown_marking_states then [ResJIT.RIGHT q] else []
+
+         | Formula.Or(f1, f2) ->
+             let b1, i1 = loop f1 in
+             let b2, i2 = loop f2 in
+             Formula.or_pred b1 b2, i1 @ i2
+         | Formula.And(f1, f2) ->
+             let b1, i1 = loop f1 in
+             let b2, i2 = loop f2 in
+             Formula.and_pred b1 b2, i1 @ i2
+      in
+      loop f
+
+
+    let eval_trans auto s1 s2 trans =
+      Translist.fold
+       (fun t ((a_st, a_op, a_todo) as acc)->
+          let q, _, m, f = Transition.node t in
+          let form, ops = eval_form auto s1 s2 f in
+          match Formula.expr form with
+            | Formula.True ->
+              StateSet.add q a_st,
+              (q, (if m then (ResJIT.SELF() :: ops) else ops)):: a_op,
+              a_todo
+            | Formula.False -> acc
+            | Formula.Pred p -> a_st, a_op,
+              (p.Tree.Predicate.node, q, [(q,(if m then (ResJIT.SELF() :: ops) else ops))]) :: a_todo
+            | _ -> assert false
+       ) trans (StateSet.empty, [], [])
+
+
+
+    module L3JIT =
+      struct
+
+       type opcode = (t -> t -> t -> Tree.t -> Tree.node -> StateSet.t * t)
+
+       type t = opcode Cache.t Cache.t Cache.t
+
+       let dummy _ _ _ _ _ = failwith "Uninitialized L3JIT"
+
+       let create () = Cache.Lvl3.create 1024 dummy
+
+       let stats fmt d =
+         let d = Cache.Lvl3.to_array d in
+         let len = Array.fold_left
+           (fun acc a ->
+              Array.fold_left (fun acc2 a2 -> Array.length a2 + acc2) acc a) 0 d
+         in
+
+         let lvl1 =
+           Array.fold_left
+             (fun acc a -> if Array.length a == 0 then acc else acc+1) 0 d in
+         let lvl2 = Array.fold_left
+           (fun acc a ->
+              Array.fold_left (fun acc2 a2 -> if Array.length a2 == 0 then acc2 else acc2+1)
+                acc a) 0 d
+         in
+         let lvl3 = Array.fold_left
+           (fun acc a ->
+              Array.fold_left (fun acc2 a2 ->
+                                 Array.fold_left
+                                   (fun acc3 a3 -> if a3 == dummy then acc3 else acc3+1) acc2 a2)
+                acc a) 0 d
+      in
+       fprintf fmt "L3JIT Statistics:
+\t%i entries
+\t%i used L1 lines
+\t%i used L2 lines
+\t%i used L3 lines
+\ttable size: %ikb\n"
+         len lvl1 lvl2 lvl3 (Ocaml.size_kb d)
+
+       let find t tlist s1 s2 =
+         Cache.Lvl3.find t
+           (Uid.to_int tlist.Translist.Node.id)
+           (Uid.to_int s1.StateSet.Node.id)
+           (Uid.to_int s2.StateSet.Node.id)
+
+       let add t tlist s1 s2 v =
+         Cache.Lvl3.add t
+           (Uid.to_int tlist.Translist.Node.id)
+           (Uid.to_int s1.StateSet.Node.id)
+           (Uid.to_int s2.StateSet.Node.id)
+           v
+
+       let compile auto trl s1 s2 =
+         let orig_s1, orig_s2 =
+           Translist.fold (fun t (a1, a2) ->
+                         let _, _, _, f = Transition.node t in
+                         let (_, _, fs1), (_, _, fs2) = Formula.st f in
+                           (StateSet.union s1 fs1, StateSet.union s2 fs2)
+                      ) trl (StateSet.empty, StateSet.empty)
+         in
+         let ns1 = StateSet.inter s1 orig_s1
+         and ns2 = StateSet.inter s2 orig_s2 in
+         let res, ops, todo = eval_trans auto ns1 ns2 trl in
+         let code, not_marking = ResJIT.compile ops in
+         let todo_code, todo_notmarking =
+           List.fold_left (fun (l, b) (p, q, o) -> let c, b' = ResJIT.compile o in
+                                        (p, q, c)::l, b && b')
+             ([], not_marking) todo
+         in
+         let opcode = res, code, todo_notmarking, todo_code in
+         opcode
+
+       let gen_code auto tlist s1 s2 =
+         let res, code, not_marking, todo_code = compile auto tlist s1 s2 in
+         let f =
+           if todo_code == [] then
+             if not_marking then begin fun empty_slot sl1 sl2 _ node ->
+               let slot1_empty = sl1 == empty_slot
+               and slot2_empty = sl2 == empty_slot in
+               if slot1_empty && slot2_empty then res,sl2
+               else
+                 let sl =
+                   if slot2_empty then
+                     if slot1_empty then
+                       Array.copy empty_slot
+                     else sl1
+                   else sl2
+                 in
+                 U.exec sl sl1 sl2 node code;
+                 res, sl
+             end
+             else (* marking *) begin fun empty_slot sl1 sl2 _ node ->
+               let sl =
+                 if sl2 == empty_slot  then
+                   if sl1 == empty_slot then
+                     Array.copy empty_slot
+                   else sl1
+                 else sl2
+               in
+               U.exec sl sl1 sl2 node code;
+               res, sl
+             end
+             else (* todo != [] *)
+             begin fun empty_slot sl1 sl2 tree node ->
+               let sl =
+                 if sl2 == empty_slot  then
+                   if sl1 == empty_slot then
+                     Array.copy empty_slot
+                   else sl1
+                 else sl2
+               in
+               U.exec sl sl1 sl2 node code;
+               List.fold_left
+                 (fun ares (p, q, code) ->
+                   if !p tree node then begin
+                     if code != ResJIT.Nil then U.exec sl sl1 sl2 node code;
+                     StateSet.add q ares
+                   end
+                   else ares) res todo_code, sl
+
+             end
+         in
+         f
+
+       let cache_apply cache auto tlist s1 s2 =
+         let f = gen_code auto tlist s1 s2 in
+         add cache tlist s1 s2 f; f
+      end
+
+DEFINE LOOP (t, states, ctx) = (
+  let _t = (t) in
+  if _t == Tree.nil then nil_res
+  else
+    let tag = Tree.tag tree _t in
+      l2jit_dispatch
+        _t tag (states) (ctx) (L2JIT.find cache2 tag (states))
+)
+
+DEFINE LOOP_TAG (t, states, tag, ctx) = (
+  let _t = (t) in (* to avoid duplicating expression t *)
+  if _t == Tree.nil then nil_res
+  else
+    l2jit_dispatch
+      _t (tag) (states) (ctx) (L2JIT.find cache2 (tag) (states)))
+
+    let top_down_run auto tree root states ctx =
+      let res_len = (StateSet.max_elt auto.states) + 1 in
+      let empty_slot = Array.create res_len U.NS.empty in
+      let nil_res = auto.bottom_states, empty_slot in
+      let cache3 = L3JIT.create () in
+
+      let l3jit_dispatch trl s1 s2 t sl1 sl2 =
+       let f = L3JIT.find cache3 trl s1 s2 in
+       if f == L3JIT.dummy then (L3JIT.cache_apply cache3 auto trl s1 s2) empty_slot sl1 sl2 tree t
+       else f empty_slot sl1 sl2 tree t
+
+      in
+      let cache2 = L2JIT.create () in
+
+      let () = D_TRACE_(at_exit (fun () -> L2JIT.stats Format.err_formatter cache2)) in
+
+      let rec l2jit_dispatch t tag states ctx opcode =
+       match opcode with
+         | L2JIT.RETURN () -> nil_res
+         | L2JIT.CACHE () ->
+             let opcode = L2JIT.compile cache2 auto tree tag states in
+               l2jit_dispatch t tag states ctx opcode
+
+         | L2JIT.LEFT (tr_list, instr) ->
+             let res1, slot1 =
+               l2jit_dispatch_instr t tag states (Tree.closing tree t) instr true
+             in
+               l3jit_dispatch tr_list res1 auto.bottom_states t slot1 empty_slot
+
+         | L2JIT.RIGHT (tr_list, instr) ->
+           let res2, slot2 = l2jit_dispatch_instr t tag states ctx instr false in
+             l3jit_dispatch tr_list auto.bottom_states res2 t empty_slot slot2
+
+         | L2JIT.BOTH (tr_list, instr1, instr2) ->
+             let res1, slot1 =
+               l2jit_dispatch_instr t tag states (Tree.closing tree t) instr1 true
+             in
+             let res2, slot2 = l2jit_dispatch_instr t tag states ctx instr2 false in
+               l3jit_dispatch tr_list res1 res2 t slot1 slot2
+
+    and l2jit_dispatch_instr t tag states ctx instr _left =
+      match instr with
+       | L2JIT.NOP () -> nil_res
+       | L2JIT.FIRST_CHILD s -> LOOP ((Tree.first_child tree t), s, ctx)
+       | L2JIT.NEXT_SIBLING s -> LOOP ((Tree.next_sibling tree t), s, ctx)
+
+       | L2JIT.FIRST_ELEMENT s -> LOOP ((Tree.first_element tree t), s, ctx)
+       | L2JIT.NEXT_ELEMENT s -> LOOP ((Tree.next_element tree t), s, ctx)
+
+       | L2JIT.TAGGED_DESCENDANT (s, tag) ->
+           LOOP_TAG ((Tree.tagged_descendant tree t tag), s, tag, ctx)
+
+       | L2JIT.TAGGED_FOLLOWING (s, tag) ->
+           LOOP_TAG((Tree.tagged_following_before tree t tag ctx), s, tag, ctx)
+
+       | L2JIT.SELECT_DESCENDANT (s, _, us) ->
+           LOOP((Tree.select_descendant tree t us), s, ctx)
+
+       | L2JIT.SELECT_FOLLOWING (s, pt, us) ->
+           LOOP ((Tree.select_following_before tree t us ctx), s, ctx)
+
+       | L2JIT.TAGGED_CHILD (s, tag) ->
+           LOOP_TAG((Tree.tagged_child tree t tag), s, tag, ctx)
+
+       | L2JIT.TAGGED_FOLLOWING_SIBLING (s, tag) ->
+           LOOP_TAG((Tree.tagged_following_sibling tree t tag), s, tag, ctx)
+
+       | L2JIT.SELECT_CHILD (s, _, us) ->
+           LOOP ((Tree.select_child tree t us), s, ctx)
+
+       | L2JIT.SELECT_FOLLOWING_SIBLING (s, _, us) ->
+           LOOP ((Tree.select_following_sibling tree t us), s, ctx)
+
+       | L2JIT.TAGGED_SUBTREE(s, tag) ->
+
+         let count = U.NS.subtree_tags tree t tag in
+               if count != U.NS.empty then
+                 let r = Array.copy empty_slot in
+                   r.(auto.last) <- count;
+                   s,r
+               else
+                 s,empty_slot
+
+       | L2JIT.ELEMENT_SUBTREE(s) ->
+
+             let count = U.NS.subtree_elements tree t in
+               if count != U.NS.empty then
+                 let r = Array.copy empty_slot in
+                   r.(auto.last) <- count;
+                   s,r
+               else
+                 s,empty_slot
+
+    in
+      LOOP (root, states, ctx)
+
+    let full_top_down_run auto states tree root =
+      (*Ata.init (); *)
+      top_down_run auto tree root states (Tree.closing tree root)
+
+    let top_down_run auto tree root =
+      (*Ata.init (); *)
+      let res, slot = full_top_down_run auto auto.init tree root in
+      slot.(StateSet.min_elt auto.topdown_marking_states)
+
+
+    (*** Bottom-up evaluation function **)
+
+    let ns_print fmt t =
+      Format.fprintf fmt "{ ";
+      U.NS.iter begin fun node ->
+       Format.fprintf fmt "%a " Node.print node;
+      end t;
+      Format.fprintf fmt "}"
+
+    let slot_print fmt t =
+      Array.iteri begin fun state ns ->
+       Format.eprintf "%a -> %a\n" State.print state ns_print ns;
+      end t
+
+
+    let eval_trans auto tree parent res1 res2 = assert false
+
+
+    let bottom_up_run auto tree (query, pat) =
+      let leaves = Array.to_list (Tree.full_text_query query tree pat) in
+      let states = auto.states in
+      let res_len = (StateSet.max_elt states) + 1 in
+      let empty_slot = Array.create res_len U.NS.empty in
+      let nil_res = auto.bottom_states, empty_slot in
+      let cache = Cache.Lvl3.create 1024 L3JIT.dummy in
+      let rec loop_leaves l acc =
+       match l with
+           [] -> acc
+         | node :: ll ->
+           let res, lll = bottom_up_next node ll Tree.nil in
+           if (lll <> []) then Printf.eprintf "Leftover elements\n%!";
+           res
+
+      and bottom_up_next node rest stop =
+       let fs = Tree.first_child tree node in
+       let res1 =
+         if fs == Tree.nil then nil_res
+         else full_top_down_run auto states tree fs
+       in
+       move_up node res1 true rest stop
+
+      and move_up node res is_left rest stop =
+       if node == stop then res, rest
+       else
+         let prev_sibling = Tree.prev_sibling tree node in
+         let is_left' = prev_sibling == Tree.nil in
+         let real_parent = Tree.parent tree node in
+         let parent =
+           if is_left' then real_parent else max (Tree.first_child tree real_parent) stop
+         in
+         (* let parent = if is_left' then Tree.parent tree node else prev_sibling in *)
+         let (s1, sl1), (s2, sl2), rest' =
+           if is_left then match rest with
+               [] -> res, nil_res, rest
+             | next :: rest' ->
+               if Tree.is_right_descendant tree node next
+               then
+                 let res2, rest' = bottom_up_next next rest' node in
+                 res, res2, rest'
+               else res, nil_res, rest
+           else
+             nil_res, res, rest
+         in
+         let tag = Tree.tag tree node in
+         let id1 = Uid.to_int s1.StateSet.Node.id in
+         let id2 = Uid.to_int s2.StateSet.Node.id in
+         let code =
+           let code = Cache.Lvl3.find cache tag id1 id2 in
+           if code == L3JIT.dummy then
+             let trl =
+               StateSet.fold
+                 (fun q acc ->
+                   List.fold_left (fun acc' (labels, tr) ->
+                     if labels == TagSet.any || TagSet.mem tag labels
+                     then Translist.cons tr acc' else acc')
+                     acc
+                     (Hashtbl.find auto.trans q)
+                 )
+                 states
+                 Translist.nil
+             in
+             let code = L3JIT.gen_code auto trl s1 s2 in
+             Cache.Lvl3.add cache tag id1 id2 code; code
+           else code
+         in
+         let res' = code empty_slot sl1 sl2 tree node in
+         move_up parent res' is_left' rest' stop
+      in
+      let _, slot = loop_leaves leaves (nil_res) in
+      slot.(StateSet.min_elt auto.topdown_marking_states)
+
+
+  end
+