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

Flambda: No unboxing of float in simple local recursive function #7289

Closed
vicuna opened this issue Jul 12, 2016 · 8 comments
Closed

Flambda: No unboxing of float in simple local recursive function #7289

vicuna opened this issue Jul 12, 2016 · 8 comments
Assignees
Milestone

Comments

@vicuna
Copy link

vicuna commented Jul 12, 2016

Original bug ID: 7289
Reporter: ybarnoy
Assigned to: @chambart
Status: closed (set by @mshinwell on 2017-03-07T11:45:24Z)
Resolution: not a bug
Priority: normal
Severity: minor
Version: 4.03.0
Target version: 4.05.0 +dev/beta1/beta2/beta3/rc1
Category: back end (clambda to assembly)
Monitored by: @gasche

Bug description

With 4.03+flambda, -O3, in

let _ =
let f = 100. in
let [@inline always] rec loop x =
if x <= 0. then 0.
else loop (x -. 5.)
in
loop f

I would expect x to be unboxed, but it isn't. Instead I get

(function camlTest__loop_4 (x_6/1203: val)
(if (<=f (load float64u x_6/1203) 0.) "camlTest__const_float_39"
(app{test.ml:5,9-23} "camlTest__loop_4"
(alloc 1277 (-f (load float64u x_6/1203) 5.)) val)))

@vicuna
Copy link
Author

vicuna commented Jul 12, 2016

Comment author: @alainfrisch

  1. Floats are always passed in boxed form across function boundaries. See [patch] Avoid boxing float/int32/int64 when doing direct call #5894 for a proposal by Pierre Chambart to improve this.

  2. As far as I know, flambda never turns loops implemented with recursion into local loops (that could benefit from normal unboxing). My understanding is that Extended static exceptions for 4.05 #524 is a step into this direction.

@vicuna
Copy link
Author

vicuna commented Jul 14, 2016

Comment author: @gasche

This issue is also discussed at:

OCamlPro/flambda-task-force#158

Quoting Mark Shinwell's reply below:

Something enabling that (not only when inlined in fact)
has been attempted before, see
https://github.com/chambart/ocaml/tree/float_args

This will definitely be done at some point, but needs
some big changes in the backend first:
OCamlPro/flambda-task-force#153

@vicuna
Copy link
Author

vicuna commented Jul 15, 2016

Comment author: ybarnoy

By definition, there will always be limits to the FLambda inlining approach, since the more is inlined, the fewer GC safe-spots there will be.

As an example, if the recursive function allocates inside, I don't think it can be inlined, since allocation could cause GC calls, which will not have a safe-spot. (Let me know if I'm wrong about this logic).

I therefore thing it would be nice to use a universal approach for passing unboxed floats and ints.

@vicuna
Copy link
Author

vicuna commented Jul 15, 2016

Comment author: @alainfrisch

(Let me know if I'm wrong about this logic).

Indeed, I think you are :-)

(1) There is really no relation between inlining and "GC safe-spots".

(2) A function that allocates can certainly be inlined.

(3) Turning a recursive function into a loop is not really "inlining" (or a very special form of it).

@vicuna
Copy link
Author

vicuna commented Jul 19, 2016

Comment author: ybarnoy

Alain, can you explain how GC safe-spots work, then?

A function may have pointers inside not pointing to a header. Additionally, it may have unboxed ints and floats. If the GC is called in these situations, it would fail. So how do we handle it?

@vicuna
Copy link
Author

vicuna commented Jul 19, 2016

Comment author: @alainfrisch

A place in the machine code is GC safe if it is safe to trigger the GC at this place, i.e. the GC must have enough information to track all heap-allocated OCaml values and update them if they are moved. The compiler arrange so that whenever the GC can run, it knows this information. Given the current design of the OCaml GC, this means for instance that no pointer can point to the middle of a block at such place.

In practice, the compiler produces data structures (frame descriptors) that tell the GC about which stack slots and machine register hold OCaml values.

A function can indeed hold unboxed floats and untagged ints. The compiler simply does not insert the location that hold these values (stack/register) in the frame descriptor. Pointers (that will be follower later) never point to the middle of a block (except very locally, but certainly not when the GC can run, and in particular not before a function call).

But all that has really nothing to do with inlining, which happens at a higher level (symbolic rewriting of expressions to replace a reference to an identifier with its identifier, and to reduce a function immediately applied to its arguments). In a first approximation, inlining could be expressed as source-to-source translation, and of course, any valid source code can be compiled to machine code "where the GC only runs when this is safe".

@vicuna
Copy link
Author

vicuna commented Jul 19, 2016

Comment author: ybarnoy

I see. So really, the only places boxing is absolutely required from the GC's perspective is inside heap data structures. Everything else can be removed from the GC's view, and boxing is only required for generic functions (either the OCaml ones or primitives).

@vicuna
Copy link
Author

vicuna commented Mar 7, 2017

Comment author: @mshinwell

I don't think this is a bug.

We have a roadmap for a comprehensive treatment of unboxing (including unboxed floats being passed as function arguments, for what it's worth) in forthcoming Flambda versions.

@vicuna vicuna closed this as completed Mar 7, 2017
@vicuna vicuna added this to the 4.05.0 milestone Mar 14, 2019
@vicuna vicuna added the bug label Mar 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants