Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tail-call optimisation not working properly on s390x machines #10857

Closed
icristescu opened this issue Jan 7, 2022 · 2 comments
Closed

Tail-call optimisation not working properly on s390x machines #10857

icristescu opened this issue Jan 7, 2022 · 2 comments

Comments

@icristescu
Copy link

icristescu commented Jan 7, 2022

The following code crashes with Segmentation fault on s390x machines, which is actually a stack overflow issue. The code below prints the size of the stack during a traversal of a very large map. The stack size is normally constant, but on an s309x machine, the stack is gradually growing until it raises the segmentation fault error.

module StepMap = struct
  module X = struct
    type t = string

    let compare = String.compare
  end

  include Map.Make (X)
end

type t = string StepMap.t

let add t step v = StepMap.add step v t
let rcons t s = t @ [ s ]

let stack_size p =
  let stats = Gc.stat () in
  Fmt.epr "stack_size at position %d is %d \n%!" p stats.Gc.stack_size

type ('a, 'r) cont = ('a -> 'r) -> 'r

type ('v, 'acc, 'r) folder =
  path:string list -> 'acc -> int -> 'v -> ('acc, 'r) cont

(* If we remove the [path] or the [d] argument the test passes. *)
let fold : type acc. path:string list -> t -> acc -> acc =
 fun ~path t acc ->
  let counter = ref 0 in
  let rec step : type r. (string * string, acc, r) folder =
   fun ~path:_ acc _d _h k ->
    incr counter;
    if !counter mod 20_000 = 0 then stack_size !counter;
    k acc
  and steps : type r. ((string * string) Seq.t, acc, r) folder =
   fun ~path acc d s k ->
    match s () with
    | Seq.Nil -> (k [@tailcall]) acc
    | Seq.Cons (h, t) ->
        let steps' acc = (steps [@tailcall]) ~path acc d t k in
        (* If we replace the call of [step] with its code, the test passes.*)
        (step [@tailcall]) ~path acc d h steps'
  and map : type r. (t, acc, r) folder =
   fun ~path acc d t k ->
    let bindings = StepMap.to_seq t in
    (steps [@tailcall]) ~path acc d bindings k
  in
  map ~path acc 0 t Fun.id

let test () =
  let size = 830829 in
  let t =
    List.init size string_of_int
    |> List.fold_left (fun acc i -> add acc i i) StepMap.empty
  in
  fold ~path:[] t [] |> ignore

let () = test ()
@icristescu icristescu changed the title Tail-call optimisation not working properly on s309x machines Tail-call optimisation not working properly on s390x machines Jan 7, 2022
@xavierleroy
Copy link
Contributor

Right, that's an old limitation of the OCaml native-code compiler: tail calls with too many arguments are turned into regular calls. "Too many" depends on the target processor, but is quite low for s390x, so this may be a reason why you saw the problem on s390x first.

At any rate, this limitation was lifted in the working sources, see #10595. So, everything should work fine in the next release of OCaml. version 4.14.

samoht added a commit to samoht/opam-repository that referenced this issue Jan 7, 2022
…irmin-pack, irmin-mirage, irmin-mirage-graphql, irmin-mirage-git, irmin-layers, irmin-http, irmin-graphql, irmin-git, irmin-fs, irmin-containers, irmin-chunk and irmin-bench (2.10.0)

CHANGES:

### Fixed

- *irmin*
  - Conversion between proofs and trees are now done in CPS (mirage/irmin#1624, @samoht)
  - Better support for s390x to workaround ocaml/ocaml#10857
    (mirage/irmin#1694, @icristescu)

- *irmin-pack*
  - Fix proofs for large inodes by tracking side-effects reads inside the
    inode implementation (mirage/irmin#1670, @samoht, @Ngoguey42)
  - Flush branch store without calling `Repo.close` (mirage/irmin#1707, @zshipko)

### Added

- **irmin**
  - Add `Tree.produce_proof` and `Tree.verify_proof` to produce and verify
    proofs from complex computations. `produce_proof` and `verify_proof`
    takes a callback over tree and instead of a static list of operations
    -- this now means that the full `Tree` API can now be used in proofs,
    including sub-tree operations, folds and paginated lists
    (mirage/irmin#1625, mirage/irmin#1663, mirage/irmin#1683, @samoht, @Ngoguey42)
  - Add `Tree.produce_stream` and `Tree.verify_stream` to produce and
    verify stream proofs (mirage/irmin#1684, mirage/irmin#1692, mirage/irmin#1691, @samoht, @Ngoguey42, @icristescu)

- **irmin-pack**
  - Verify inode depth invariants (mirage/irmin#1665, @samoht)

- **irmin-unix**
  - Add `tezos` store type for `irmin` command-line (mirage/irmin#1678, @zshipko)

### Changed

- **irmin**
  - Remove `Tree.Proof.of_keys`. Use `Tree.produce_proof` instead
    (mirage/irmin#1625, @samoht)
  - `Tree.empty` now takes a unit argument. (mirage/irmin#1566, @craigfe)
  - `Tree.length` now takes a tree as argument (mirage/irmin#1676, @samoht)
  - `Tree.Proof.t` now uses a more precise datatype to encode value
    invariants (mirage/irmin#1688, @samoht)

- **irmin-pack**
  - irmin-pack: add an option to configure the index function and pick
    the relevant bits in cryptographic a hash by default (mirage/irmin#1677, @samoht)

- **irmin-git**
  -  Require at least `git.3.7.0` in the codebase (mirage/irmin#1637, @dinosaure)
samoht added a commit to samoht/opam-repository that referenced this issue Jan 7, 2022
…irmin-pack, irmin-mirage, irmin-mirage-graphql, irmin-mirage-git, irmin-layers, irmin-http, irmin-graphql, irmin-git, irmin-fs, irmin-containers, irmin-chunk and irmin-bench (2.10.0)

CHANGES:

- *irmin*
  - Conversion between proofs and trees are now done in CPS (mirage/irmin#1624, @samoht)
  - Better support for s390x to workaround ocaml/ocaml#10857
    (mirage/irmin#1694, @icristescu)

- *irmin-pack*
  - Fix proofs for large inodes by tracking side-effects reads inside the
    inode implementation (mirage/irmin#1670, @samoht, @Ngoguey42)
  - Flush branch store without calling `Repo.close` (mirage/irmin#1707, @zshipko)

- **irmin**
  - Add `Tree.produce_proof` and `Tree.verify_proof` to produce and verify
    proofs from complex computations. `produce_proof` and `verify_proof`
    takes a callback over tree and instead of a static list of operations
    -- this now means that the full `Tree` API can now be used in proofs,
    including sub-tree operations, folds and paginated lists
    (mirage/irmin#1625, mirage/irmin#1663, mirage/irmin#1683, @samoht, @Ngoguey42)
  - Add `Tree.produce_stream` and `Tree.verify_stream` to produce and
    verify stream proofs (mirage/irmin#1684, mirage/irmin#1692, mirage/irmin#1691, @samoht, @Ngoguey42, @icristescu)

- **irmin-pack**
  - Verify inode depth invariants (mirage/irmin#1665, @samoht)

- **irmin-unix**
  - Add `tezos` store type for `irmin` command-line (mirage/irmin#1678, @zshipko)

- **irmin**
  - Remove `Tree.Proof.of_keys`. Use `Tree.produce_proof` instead
    (mirage/irmin#1625, @samoht)
  - `Tree.empty` now takes a unit argument. (mirage/irmin#1566, @craigfe)
  - `Tree.length` now takes a tree as argument (mirage/irmin#1676, @samoht)
  - `Tree.Proof.t` now uses a more precise datatype to encode value
    invariants (mirage/irmin#1688, @samoht)

- **irmin-pack**
  - irmin-pack: add an option to configure the index function and pick
    the relevant bits in cryptographic a hash by default (mirage/irmin#1677, @samoht)

- **irmin-git**
  -  Require at least `git.3.7.0` in the codebase (mirage/irmin#1637, @dinosaure)
@icristescu
Copy link
Author

Thank you for the response. I run my example with ocaml#trunk and indeed it works (constant stack size and no seg fault).

icristescu added a commit to icristescu/opam-repository that referenced this issue Jan 10, 2022
…irmin-pack, irmin-mirage, irmin-mirage-graphql, irmin-mirage-git, irmin-layers, irmin-http, irmin-graphql, irmin-git, irmin-fs, irmin-containers, irmin-chunk and irmin-bench (2.9.1)

CHANGES:

### Fixed

- **irmin**
  - Better support for s390x to workaround
    ocaml/ocaml#10857 (mirage/irmin#1694, @icristescu)
icristescu added a commit to icristescu/opam-repository that referenced this issue Jan 10, 2022
…irmin-pack, irmin-mirage, irmin-mirage-graphql, irmin-mirage-git, irmin-layers, irmin-http, irmin-graphql, irmin-git, irmin-fs, irmin-containers, irmin-chunk and irmin-bench (2.9.1)

CHANGES:

- **irmin**
  - Better support for s390x to workaround
    ocaml/ocaml#10857 (mirage/irmin#1694, @icristescu)
icristescu added a commit to icristescu/opam-repository that referenced this issue Jan 10, 2022
…irmin-pack, irmin-mirage, irmin-mirage-graphql, irmin-mirage-git, irmin-layers, irmin-http, irmin-graphql, irmin-git, irmin-fs, irmin-containers, irmin-chunk and irmin-bench (2.9.1)

CHANGES:

- **irmin**
  - Better support for s390x to workaround
    ocaml/ocaml#10857 (mirage/irmin#1694, @icristescu)
icristescu added a commit to icristescu/opam-repository that referenced this issue Jan 10, 2022
…irmin-pack, irmin-mirage, irmin-mirage-graphql, irmin-mirage-git, irmin-layers, irmin-http, irmin-graphql, irmin-git, irmin-fs, irmin-containers, irmin-chunk and irmin-bench (2.9.1)

CHANGES:

- **irmin**
  - Better support for s390x to workaround
    ocaml/ocaml#10857 (mirage/irmin#1694, @icristescu)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants