10000 Move symbol initialization up in `to_cmm` by Gbury · Pull Request #4238 · oxcaml/oxcaml · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Move symbol initialization up in to_cmm #4238

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Gbury
Copy link
Contributor
@Gbury Gbury commented Jun 30, 2025

This PR adds to to_cmm code to lift/move some symbol initialization upwards. The intent of that move is to (as much as possible and reasonable) have symbol initializations right after the definitions for the variable/value that is written in the symbol field. This should reduce the lifetime of some of these values, which should reduce the pressure on the register allocator (and allow for less spilling).

This is particularly useful for symbols that have a lot of fields that are not static (i.e. whose value is computed during module initialization), which is often the case in classic mode for most modules (where the lifting and static allocation is limited), but can also occur even with Simplify depending on the shape of the code.

Let's consider the following code:

let[@inline never] foo () = 42

let field1 = foo ()
let field2 = foo ()
let field3 = foo ()
let field4 = foo ()

Before this PR (i.e. on current main), this is the cmm code generated for the module initialization in classic mode:

(function no_cse reduce_code_size linscan camlFoo__entry ()
 (catch
   (let
     (field1/295 (app{../flambda2/foo.ml:4,13-19} G:"camlFoo__foo_0_0" 1 int)
      field2/296 (app{../flambda2/foo.ml:5,13-19} G:"camlFoo__foo_0_0" 1 int)
      field3/297 (app{../flambda2/foo.ml:6,13-19} G:"camlFoo__foo_0_0" 1 int)
      field4/298 (app{../flambda2/foo.ml:7,13-19} G:"camlFoo__foo_0_0" 1 int))
     (extcall "caml_initialize" (+a L:"camlFoo__s1" 8) field1/295 ->unit) 1
     (extcall "caml_initialize" (+a L:"camlFoo__s1" 16) field2/296 ->unit) 1
     (extcall "caml_initialize" (+a L:"camlFoo__s1" 24) field3/297 ->unit) 1
     (extcall "caml_initialize" (+a L:"camlFoo__s1" 32) field4/298 ->unit) 1
     (extcall "caml_initialize" G:"camlFoo" G:"camlFoo__s0" ->unit) 1
     (extcall "caml_initialize" (+a G:"camlFoo" 8)
       (load val (+a L:"camlFoo__s1" 8)) ->unit)
     1
     (extcall "caml_initialize" (+a G:"camlFoo" 16)
       (load val (+a L:"camlFoo__s1" 16)) ->unit)
     1
     (extcall "caml_initialize" (+a G:"camlFoo" 24)
       (load val (+a L:"camlFoo__s1" 24)) ->unit)
     1
     (extcall "caml_initialize" (+a G:"camlFoo" 32)
       (load val (+a L:"camlFoo__s1" 32)) ->unit)
     1 (exit 1 G:"camlFoo"))
 with(1 *ret*/292: val) 1))

After this PR, we now generate the following:

(function no_cse reduce_code_size linscan camlFoo__entry ()
 (catch
   (let field1/295 (app{foo.ml:4,13-19} G:"camlFoo__foo_0_0" 1 int)
     (extcall "caml_initialize" (+a L:"camlFoo__s1" 8) field1/295 ->unit)
     (let field2/296 (app{foo.ml:5,13-19} G:"camlFoo__foo_0_0" 1 int)
       (extcall "caml_initialize" (+a L:"camlFoo__s1" 16) field2/296 ->unit)
       (let field3/297 (app{foo.ml:6,13-19} G:"camlFoo__foo_0_0" 1 int)
         (extcall "caml_initialize" (+a L:"camlFoo__s1" 24) field3/297
           ->unit)
         (let field4/298 (app{foo.ml:7,13-19} G:"camlFoo__foo_0_0" 1 int)
           (extcall "caml_initialize" (+a L:"camlFoo__s1" 32) field4/298
             ->unit)
           (extcall "caml_initialize" G:"camlFoo" G:"camlFoo__s0" ->unit)
           (extcall "caml_initialize" (+a G:"camlFoo" 8)
             (load val (+a L:"camlFoo__s1" 8)) ->unit)
           (extcall "caml_initialize" (+a G:"camlFoo" 16)
             (load val (+a L:"camlFoo__s1" 16)) ->unit)
           (extcall "caml_initialize" (+a G:"camlFoo" 24)
             (load val (+a L:"camlFoo__s1" 24)) ->unit)
           (extcall "caml_initialize" (+a G:"camlFoo" 32)
             (load val (+a L:"camlFoo__s1" 32)) ->unit)
           (exit 1 G:"camlFoo")))))
 with(1 *ret*/292: val) 1))

Notice how the calls to caml_initialize are now spread out and occur immediately after the loads or function calls that create the value to be used, instead of all bunched up. This makes it so that the lifetime of the field values are now much shorter.

Finally, this effect of moving symbol initializations up is limited by the current implement, which only allows to move them up to the closest environment flush (or in flambda2 terms, roughly a the top of the current continuation's handler), so code with complex control flow might now get as optimized (one solution for this would be to disallow inlining of functions at top-level, thus ensuring a simple workflow).

@Gbury Gbury added flambda2 Prerequisite for, or part of, flambda2 flambda2 classic mode labels Jun 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flambda2 classic mode flambda2 Prerequisite for, or part of, flambda2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
0