diff --git a/src/libraries/utils/wto.ml b/src/libraries/utils/wto.ml
index 07056a2b7f00edc1f7a00a5b171f084d91a4b140..8d71938a308dfe10ffcb3e7653ebb06129af25d7 100644
--- a/src/libraries/utils/wto.ml
+++ b/src/libraries/utils/wto.ml
@@ -153,8 +153,12 @@ module Make(N:sig
         this loop with the lowest level. This vertex is also the deepest in the
         stack and the neareast vertex from the root that is below [vertex] in
         the spanning tree built by the DFS);
-      - [partition] is returned completed. *)
-  let rec visit ~pref state vertex partition =
+      - [partition] is returned completed.
+
+      A tail-recursive implementation of this function is now used to avoid
+      stack overflow errors on very large functions. This function is kept
+      for documentation purposes, as it is simpler and more readable. *)
+  let rec _visit ~pref state vertex partition =
     match DFN.find state.dfn vertex with
     (* The vertex is already in the partition *)
     | Invisible -> NoLoop, partition (* skip it *)
@@ -171,7 +175,7 @@ module Make(N:sig
       (* Visit all its successors *)
       let succs = state.succs vertex in
       let (loop,partition) = List.fold_left (fun (loop,partition) succ ->
-          let (loop',partition) = visit ~pref state succ partition in
+          let (loop',partition) = _visit ~pref state succ partition in
           let loop = min_loop loop loop' in
           (loop,partition)
         ) (NoLoop,partition) succs
@@ -211,7 +215,7 @@ module Make(N:sig
         (* Restart the component analysis *)
         let component = List.fold_left
             (fun component succ ->
-               let (loop,component) = visit ~pref state succ component in
+               let (loop,component) = _visit ~pref state succ component in
                (* Since we reset the component we should have no loop *)
                assert (loop = NoLoop);
                component
@@ -224,12 +228,123 @@ module Make(N:sig
            be added during the second visit of the SCC. *)
         (loop,partition)
 
+
+  (* Types for the tail-recursive function of [visit]. *)
+
+  (* Whether to apply [min_loop] at the end of the visit of a vertex. *)
+  type kind = Default | MinLoop
+
+  (* [visit] contains two recursive calls; its tail-recursive version is split
+     into several steps accordingly. For the visit of a vertex, this type
+     indicates the step that must be done. *)
+  type step =
+    | Start of kind
+    (* Starts the visit of a vertex. If [kind] = [MinLoop], then apply
+       [min_loop] between the previous and the resulting loop. *)
+    | Continue
+    (* Resumes the visit of a vertex after the first recursive call. *)
+    | End of N.t partition
+    (* Ends the visit of a vertex after the second recursive call. *)
+    | Min_loop of loop
+    (* Apply [Min_loop] between [loop] (from the previous result) and the
+       current loop. *)
+
+  (* The list of steps that must be done, with their related vertex. It starts
+     with [init, Start Default] and grows for each original recursive call. *)
+  type continuation = (N.t * step) list
+
+  (** Tail-recursive version of the [visit] function above. *)
+  let tail_recursive_visit ~pref state vertex partition =
+    (* Visit according to the next step to be done. *)
+    let rec visit (cont: continuation) (loop, partition) =
+      match cont with
+      | [] -> loop, partition
+      | (vertex, step) :: cont ->
+        match step with
+        | Start kind ->
+          let cont =
+            if kind = MinLoop then (vertex, Min_loop loop) :: cont else cont
+          in
+          visit_first cont vertex partition
+        | Continue ->
+          visit_second cont vertex loop partition
+        | End partition' ->
+          assert (loop = NoLoop);
+          let r = NoLoop, Component (vertex, partition) :: partition' in
+          visit cont r
+        | Min_loop loop' ->
+          let r = min_loop loop' loop, partition in
+          visit cont r
+
+    (* Visits all successors of [vertex], then continue with [next] and the
+       [continuation]. The status of [vertex] is first set to [status]. *)
+    and visit_succs continuation next (vertex, status) kind acc =
+      let succs = state.succs vertex in
+      DFN.replace state.dfn vertex status;
+      let list = List.map (fun v -> v, Start kind) succs in
+      visit (list @ (vertex, next) :: continuation) (NoLoop, acc)
+
+    and visit_first continuation vertex partition =
+      match DFN.find state.dfn vertex with
+      (* The vertex is already in the partition *)
+      | Invisible ->
+        visit continuation (NoLoop, partition) (* skip it *)
+      (* The vertex have been visited but is not yet in the partition *)
+      | Parent i ->
+        visit continuation (Loop (vertex,i), partition) (* we are in a loop *)
+      (* The vertex have not been visited yet *)
+      | exception Not_found ->
+        (* Put the current vertex into the stack *)
+        Stack.push vertex state.stack;
+        (* Number it and mark it as visited *)
+        let n = state.num + 1 in
+        state.num <- n;
+        (* Visit all its successors. *)
+        visit_succs continuation Continue (vertex, Parent n) MinLoop partition
+
+    and visit_second continuation vertex loop partition =
+      match loop with
+      (* We are not in a loop. Add the vertex to the partition *)
+      | NoLoop ->
+        let _ = Stack.pop state.stack in
+        DFN.replace state.dfn vertex Invisible;
+        visit continuation (NoLoop,Node(vertex)::partition)
+      (* We are in a loop and the current vertex is the head of this loop *)
+      | Loop(head,_) when N.equal head vertex ->
+        (* Unmark all vertices in the loop, and, if pref is given, try to
+           return a better head *)
+        let rec reset_SCC best_head =
+          (** pop until vertex *)
+          let element = Stack.pop state.stack in
+          DFN.remove state.dfn element;
+          if not (N.equal element vertex) then begin
+            (** the strict is important because we are conservative *)
+            let best_head =
+              if pref best_head element < 0 then element else best_head
+            in
+            reset_SCC best_head
+          end
+          else
+            best_head
+        in
+        let best_head = reset_SCC vertex in
+        let vertex = if N.equal best_head vertex then vertex else best_head in
+        (* Makes [vertex] invisible and visits all its successors. *)
+        visit_succs continuation (End partition) (vertex, Invisible) Default []
+      | _ ->
+        (* [vertex] is part of a strongly connected component but is not the
+           head. Do not update partition; the vertex will
+           be added during the second visit of the SCC. *)
+        visit continuation (loop, partition)
+    in
+    visit [vertex, Start Default] (NoLoop, partition)
+
   type pref = N.t -> N.t -> int
 
   let partition ~pref ~init ~succs =
     let state = {dfn = DFN.create 17; num = 0; succs;
                  stack = Stack.create () } in
-    let loop,component = visit ~pref state init [] in
+    let loop,component = tail_recursive_visit ~pref state init [] in
     assert (loop = NoLoop);
     component