{"action":"created","comment":{"url":"https://api.github.com/repos/ocaml/ocaml/pulls/comments/55825429","id":55825429,"diff_hunk":"@@ -0,0 +1,1245 @@\n+\\chapter{Optimisation with Flambda}\n+\\pdfchapterfold{-9}{Optimisation with Flambda}\n+%HEVEA\\cutname{flambda.html}\n+\n+\\section{Overview}\n+\n+{\\em Flambda} is the term used to describe a series of optimisation passes\n+provided by the native code compilers as of OCaml 4.03.\n+\n+Flambda aims to make it easier to write idiomatic OCaml code without\n+incurring performance penalties.\n+\n+To use the Flambda optimisers it is necessary to pass the {\\tt -flambda}\n+option to the OCaml {\\tt configure} script.  (There is no support for a\n+single compiler that can operate in both Flambda and non-Flambda modes.)\n+Code compiled with Flambda\n+cannot be linked into the same program as code compiled without Flambda.\n+Attempting to do this will result in a compiler error.\n+\n+Flambda provides full optimisation across different compilation units,\n+so long as the {\\tt .cmx} files for the dependencies of the unit currently\n+being compiled are available.  (A compilation unit corresponds to a\n+single {\\tt .ml} source file.)\n+\n+Optimisation with Flambda is not currently supported when generating\n+bytecode.\n+\n+Consult the {\\em Glossary} at the end of this chapter for definitions of\n+technical terms used below.\n+\n+\\section{Command-line flags}\n+\n+The Flambda optimisers provide a variety of command-line flags that may\n+be used to control their behaviour.  Detailed descriptions of each flag\n+are given in the sections that follow.\n+\n+Commonly-used options:\n+\\begin{options}\n+\\item[\\machine{-O2}] Perform more optimisation than usual.  Compilation\n+times may be lengthened.\n+\\item[\\machine{-O3}] Perform even more optimisation than usual, possibly\n+including unrolling of recursive functions.  Compilation times may be\n+significantly lengthened.\n+\\item[\\machine{-Oclassic}] Make inlining decisions at the point of\n+definition of a function rather than at the call site(s).  This mirrors\n+the behaviour of OCaml compilers not using Flambda.  Compared to compilation\n+using the new Flambda inlining heuristics (for example at {\\tt -O2}) it\n+produces\n+smaller {\\tt .cmx} files, shorter compilation times and code that probably\n+runs rather slower.  When using {\\tt -Oclassic}, only the following options\n+described in this section are relevant: {\\tt -inlining-report} and\n+{\\tt -inline}.\n+\\item[\\machine{-inlining-report}] Emit {\\tt .inlining} files (one per\n+round of optimisation) showing all of the inliner's decisions.\n+\\end{options}\n+\n+Less commonly-used options:\n+\\begin{options}\n+\\item[\\machine{-remove-unused-arguments}] Remove unused function arguments\n+even when the argument is not specialised.\n+\\item[\\machine{-unbox-closures}] Pass free variables via specialised arguments\n+rather than closures (an optimisation for reducing allocation).\n+\\end{options}\n+\n+Advanced options:\n+\\begin{options}\n+\\item[\\machine{-inline}] The behaviour depends on whether {\\tt -Oclassic}\n+is used.\n+\\begin{itemize}\n+\\item When not in {\\tt -Oclassic} mode, {\\tt -inline} limits the total\n+size of functions considered for inlining during any speculative inlining\n+search.  (See the sections below for full details.)  Note that \n+this parameter does\n+{\\bf not} control the assessment as to whether any particular function may\n+be inlined.  Raising it to excessive amounts will not necessarily cause\n+more functions to be inlined.\n+\\item When in {\\tt -Oclassic} mode, {\\tt -inline} behaves as in\n+previous versions of the compiler: it is the maximum size of function to\n+be considered for inlining.\n+\\end{itemize}\n+\\item[\\machine{-inline-toplevel}] The equivalent of {\\tt -inline} but used\n+when speculative inlining starts at toplevel.  (Not used in\n+{\\tt -Oclassic} mode.)\n+\\item[\\machine{-inline-branch-factor}] Controls how the inliner assesses\n+whether a code path is likely to be hot or cold.\n+\\item[\\machine{-inline-alloc-cost},\n+  \\machine{-inline-branch-cost},\n+  \\machine{-inline-call-cost}] Controls how the inliner assesses the runtime\n+  performance penalties associated with various operations.\n+\\item[\\machine{-inline-indirect-cost},\n+  \\machine{-inline-prim-cost}] Likewise.\n+\\item[\\machine{-inline-lifting-benefit}] Controls inlining of functors\n+at toplevel.\n+\\item[\\machine{-inline-max-depth}] The maximum depth of any\n+speculative inlining search.\n+\\item[\\machine{-inline-max-unroll}] The maximum depth of any unrolling of\n+recursive functions during any speculative inlining search.\n+\\item[\\machine{-no-unbox-free-vars-of-closures}] %\n+Do not unbox closure variables.\n+\\item[\\machine{-no-unbox-specialised-args}] %\n+Do not unbox arguments to which functions have been specialised.\n+\\item[\\machine{-rounds}] How many rounds of optimisation to perform.\n+\\item[\\machine{-unbox-closures-factor}] Scaling factor for benefit\n+calculation when using {\\tt -unbox-closures}.\n+\\end{options}\n+\n+\\paragraph{Notes}\n+\\begin{itemize}\n+\\item The set of command line flags relating to optimisation should typically\n+be specified to be the same across an entire project.  Flambda does not\n+currently record the requested flags in the {\\tt .cmx} files.  As such,\n+inlining of functions from previously-compiled units will subject their code\n+to the optimisation parameters of the unit currently being compiled, rather\n+than those specified when they were previously compiled.  It is hoped to\n+rectify this deficiency in the future.\n+\n+\\item Flambda-specific flags are silently accepted even when the\n+{\\tt -flambda} option was not provided to the {\\tt configure} script.\n+This is intended to make it more\n+straightforward to run benchmarks with and without the Flambda optimisers\n+in effect.\n+\\item Some of the Flambda flags may be subject to change in future\n+releases.\n+\\end{itemize}\n+\n+\\subsection{Specification of optimisation parameters by round}\n+\n+Flambda operates in {\\em rounds}: one round consists of a certain sequence\n+of transformations that may then be repeated in order to achieve more\n+satisfactory results.  The number of rounds can be set manually using the\n+{\\tt -rounds} parameter (although this is not necessary when using\n+predefined optimisation levels such as with {\\tt -O2} and {\\tt -O3}).\n+For high optimisation the number of rounds might be set at 3 or 4.\n+\n+Command-line flags that may apply per round, for example those with\n+{\\tt \"-cost\"} in the name, accept arguments of the form:\n+\\begin{center}\n+{\\em n}{\\tt\\ |\\ }{\\em round}{\\tt =}{\\em n}[{\\tt,}...]\n+\\end{center}\n+\\begin{itemize}\n+\\item If the first form is used, the value will apply to all rounds.\n+\\item If the second form is used, zero-based {\\em round} integers specify\n+values which are to be used only for those rounds.\n+\\end{itemize}\n+\n+The flags {\\tt -Oclassic}, {\\tt -O2} and {\\tt -O3} are applied before all\n+other flags, meaning that certain parameters may be overridden without\n+having to specify every parameter usually invoked by the given optimisation\n+level.\n+\n+\\section{Inlining}\n+\n+{\\em Inlining} refers to the copying of the code of a function to a\n+place where the function is called.\n+The code of the function will be surrounded by bindings of its parameters\n+to the corresponding arguments.\n+\n+The aims of inlining are:\n+\\begin{itemize}\n+\\item to reduce the runtime overhead caused by function calls (including\n+setting up for such calls and returning afterwards);\n+\\item to reduce instruction cache misses by expressing frequently-taken\n+paths through the program using fewer machine instructions; and\n+\\item to reduce the amount of allocation (especially of closures).\n+\\end{itemize}\n+These goals are often reached not just by inlining itself but also by\n+other optimisations that the compiler is able to perform as a result of\n+inlining.\n+\n+When a recursive call to a function (within the definition of that function\n+or another in the same mutually-recursive group) is inlined, the procedure is\n+also known as {\\em unrolling}.  This is somewhat akin to loop peeling.\n+For example, given the following code:\n+\\begin{verbatim}\n+let rec fact x =\n+  if x = 0 then\n+    1\n+  else\n+    x * fact (x - 1)\n+\n+let n = fact 4\n+\\end{verbatim}\n+unrolling once at the call site {\\tt fact 4} produces (with the body of\n+{\\tt fact} unchanged):\n+\\begin{verbatim}\n+let n =\n+  if 4 = 0 then\n+    1\n+  else\n+    4 * fact (4 - 1)\n+\\end{verbatim}\n+This simplifies to:\n+\\begin{verbatim}\n+let n = 4 * fact 3\n+\\end{verbatim}\n+\n+%% CR pchambart: A specific section for unrolling might be worth (telling\n+%% when this is beneficial)\n+\n+Flambda provides significantly enhanced inlining capabilities relative to\n+previous versions of the compiler.\n+\n+\\subsubsection{Aside: when inlining is performed}\n+\n+Inlining is performed together with all of the other Flambda optimisation\n+passes, that is to say, after closure conversion.  This has three particular\n+advantages over a potentially more straightforward implementation prior to\n+closure conversion:\n+\\begin{itemize}\n+\\item It permits higher-order inlining, for example when a non-inlinable\n+function always returns the same function yet with different environments\n+of definition.  Not all such cases are supported yet, but it is intended\n+that such support will be improved in future.\n+\\item It is easier to integrate with cross-module optimisation, since\n+imported information about other modules is already in the correct\n+intermediate language.\n+\\item It becomes more straightforward to optimise closure allocations since\n+the layout of closures does not have to be estimated in any way: it is\n+known.  Similarly,\n+it becomes more straightforward to control which variables end up\n+in which closures, helping to avoid closure bloat.\n+\\end{itemize}\n+\n+\\subsection{Classic inlining heuristic}\n+\n+In {\\tt -Oclassic} mode the behaviour of the Flambda inliner\n+mimics previous versions\n+of the compiler.  (Code may still be subject to further optimisations not\n+performed by previous versions of the compiler: functors may be inlined,\n+constants are lifted and dead code is eliminated all as described elsewhere\n+in this chapter.)\n+At the definition site of a function, the body of the\n+function is measured.  It will then be marked as eligible for inlining\n+(and hence inlined at every direct call site) if:\n+\\begin{itemize}\n+\\item the measured size (in unspecified units) is smaller than that of a\n+function call plus the argument of the {\\tt -inline} command-line flag; and\n+\\item the function is not recursive.\n+\\end{itemize}\n+\n+Non-Flambda versions of the compiler cannot inline functions that\n+contain a definition of another function.  However {\\tt -Oclassic} does\n+permit this.  Further, non-Flambda versions also cannot inline functions\n+that are only themselves exposed as a result of a previous pass of inlining,\n+but again this is permitted by {\\tt -Oclassic}.\n+For example:\n+\\begin{verbatim}\n+module M : sig\n+  val i : int\n+end = struct\n+  let f x =\n+    let g y = x + y in\n+    g\n+  let h = f 3\n+  let i = h 4  (* h is correctly discovered to be g and inlined *)\n+end\n+\\end{verbatim}\n+\n+All of this contrasts with the normal Flambda mode, that is to say\n+without {\\tt -Oclassic}, where:\n+\\begin{itemize}\n+\\item the inlining decision is made at the {\\bf call site}; and\n+\\item recursive functions can be handled, by {\\em specialisation} (see\n+below).\n+\\end{itemize}\n+The Flambda mode is described in the next section.\n+\n+\\subsection{Overview of ``Flambda'' inlining heuristics}\n+\n+The Flambda inlining heuristics, used whenever the compiler is configured\n+for Flambda and {\\tt -Oclassic} was not specified, make inlining decisions\n+at call sites.  This helps in situations where the context is important.\n+For example:\n+\\begin{verbatim}\n+let f b x =\n+  if b then\n+    x\n+  else\n+    ... big expression ...\n+\n+let g x = f true x\n+\\end{verbatim}\n+In this case, we would like to inline {\\tt f} into {\\tt g}, because a\n+conditional jump can be eliminated and the code size should reduce.  If the\n+inlining decision has been made after the declaration of {\\tt f} without\n+seeing the use, its size would have probably made it ineligible for\n+inlining; but at the call site, its final size can be known.  Further,\n+this function should probably not be inlined systematically: if {\\tt b}\n+is unknown, or indeed {\\tt false}, there is little benefit to trade off\n+against a large increase in code size.  In the existing non-Flambda inliner\n+this isn't a great problem because chains of inlining were cut off farly\n+quickly.  However it has led to excessive use of overly-large inlining\n+parameters such as {\\tt -inline 10000}.\n+\n+In more detail, at each call site the following procedure is followed:\n+\\begin{itemize}\n+\\item Determine whether it is clear that inlining would be beneficial\n+without, for the moment, doing any inlining within the function itself.\n+(The exact assessment of {\\em benefit} is described below.)  If so, the\n+function is inlined.\n+\\item If inlining the function is not clearly beneficial, then inlining\n+will be performed {\\em speculatively} inside the function itself.  The\n+search for speculative inlining possibilities is controlled by two\n+parameters: the {\\em inlining threshold} and the {\\em inlining depth}.\n+(These are described in more detail below.)\n+\\begin{itemize}\n+\\item If such speculation shows that performing some inlining inside the\n+function would be beneficial, then such inlining is performed and the\n+resulting function inlined at the original call site.\n+\\item Otherwise, nothing happens.\n+\\end{itemize}\n+\\end{itemize}\n+Inlining within recursive functions of calls to other\n+functions in the same mutually-recursive group is kept in check by\n+an {\\em unrolling depth}, described below.  This ensures that functions are\n+not unrolled to excess.  (Unrolling is only enabled\n+if {\\tt -O3} optimisation level is selected and/or the\n+{\\tt -inline-max-unroll}\n+flag is passed with an argument greater than zero.)\n+\n+\\subsection{Handling of specific language constructs}\n+\n+\\subsubsection{Functors}\n+\n+There is nothing particular about functors that inhibits inlining compared\n+to normal functions.  To the inliner, these both look the same, except\n+that functors are marked as such.\n+\n+Applications of functors at toplevel are biased in favour of inlining.\n+(This bias may be adjusted:\n+see the documentation for {\\tt -inline-lifting-benefit} below.)\n+\n+Applications of functors not at toplevel, for example in a local module\n+inside some other expression, are treated by the inliner identically to\n+normal function calls.\n+\n+\\subsubsection{First-class modules}\n+\n+The inliner will be able to consider inlining a call to a function in a first\n+class module if it knows which particular function is going to be called.\n+The presence of the first-class module record that wraps the set of functions\n+in the module does not per se inhibit inlining.\n+\n+\\subsubsection{Objects}\n+\n+Method calls to objects are not at present inlined by Flambda.\n+\n+\\subsection{Inlining reports}\n+\n+If the {\\tt -inlining-report} option is provided to the compiler then a file\n+will be emitted corresponding to each round of optimisation.  For the\n+OCaml source file {\\em basename}{\\tt .ml} the files\n+are named {\\em basename}{\\tt .}{\\em round}{\\tt.inlining.org},\n+with {\\em round} a\n+zero-based integer.  Inside the files, which are formatted as ``org mode'',\n+will be found English prose describing the decisions that the inliner took.\n+\n+\\subsection{Assessment of inlining benefit}\n+\n+Inlining typically\n+results in an increase in code size, which if left unchecked, may not only\n+lead to grossly large executables and excessive compilation times but also\n+a decrease in performance due to worse locality.  As such, the\n+Flambda inliner trades off the change in code size against\n+the expected runtime performance benefit, with the benefit being computed\n+based on the number of operations that the compiler observes may be removed\n+as a result of inlining.\n+\n+For example given the following code:\n+\\begin{verbatim}\n+let f b x =\n+  if b then\n+    x\n+  else\n+    ... big expression ...\n+\n+let g x = f true x\n+\\end{verbatim}\n+it would be observed that inlining of {\\tt f} would remove:\n+\\begin{itemize}\n+\\item one direct call;\n+\\item one conditional branch.\n+\\end{itemize}\n+\n+Formally, an estimate of runtime performance benefit is computed by\n+first summing\n+the cost of the operations that are known to be removed as a result of the\n+inlining and subsequent simplification of the inlined body.\n+The individual costs for the various kinds of operations may be adjusted\n+using the various {\\tt -inline-...-cost} flags:\n+\\begin{options}\n+\\item[\\machine{-inline-alloc-cost}] The cost of an allocation.\n+\\item[\\machine{-inline-branch-cost}] The cost of a branch.\n+\\item[\\machine{-inline-call-cost}] The cost of a direct function call.\n+\\item[\\machine{-inline-indirect-cost}] The cost of an indirect function call.\n+\\item[\\machine{-inline-prim-cost}] The cost of a {\\em primitive}.  Primitives\n+encompass operations including arithmetic and memory access.\n+\\end{options}\n+(Default values are described in the {\\em Default settings of parameters}\n+section below.)\n+\n+The initial benefit value is then scaled by a factor that attempts to\n+compensate for the fact that the current point in the code, if under some\n+number of conditional branches, may be cold.  (Flambda does not currently\n+compute hot and cold paths.)  The factor---the estimated probability that\n+the inliner really is on a {\\em hot} path---is calculated as\n+$(\\frac{1}{1 + f})^{d}$, where $f$ is set by\n+{\\tt -inline-branch-factor} and $d$ is the nesting depth of branches\n+at the current point.  As the inliner descends into more deeply-nested\n+branches, the benefit of inlining thus lessens.\n+\n+The resulting benefit value is known as the {\\em estimated benefit}.\n+\n+The change in code size is also estimated: morally speaking it should be the\n+change in machine code size, but since that is not available to the inliner,\n+an approximation is used.\n+\n+If the estimated benefit exceeds the increase in code size then the inlined\n+version of the function will be kept.  Otherwise the function will not be\n+inlined.\n+\n+Applications of functors at toplevel will be given\n+an additional benefit (which may be controlled by the\n+{\\tt -inline-lifting-benefit} flag) to bias inlining in such situations\n+towards keeping the inlined version.\n+\n+\\subsection{Control of speculation}\n+\n+As described above, there are three parameters that restrict the search\n+for inlining opportunities during speculation:\n+\\begin{itemize}\n+\\item the {\\em inlining threshold};\n+\\item the {\\em inlining depth};\n+\\item the {\\em unrolling depth}.\n+\\end{itemize}\n+These parameters are ultimately bounded by the arguments provided to\n+the corresponding command-line flags (or their default values):\n+\\begin{itemize}\n+\\item {\\tt -inline} (or, if the call site that triggered speculation is\n+at toplevel, {\\tt -inline-toplevel});\n+\\item {\\tt -inline-max-depth};\n+\\item {\\tt -inline-max-unroll}.\n+\\end{itemize}\n+{\\bf Note in particular} that {\\tt -inline} does not have the meaning that\n+it has in the previous compiler or in {\\tt -Oclassic} mode.  In both of those\n+situations {\\tt -inline} was effectively some kind of basic assessment of\n+inlining benefit.  However in Flambda inlining mode it corresponds to a\n+constraint on the search; the assessment of benefit is independent, as\n+described above.\n+\n+When speculation starts the inlining threshold starts at the value set\n+by {\\tt -inline} (or {\\tt -inline-toplevel} if appropriate, see above).\n+Upon making a speculative inlining decision the\n+threshold is reduced by the code size of the function being inlined.\n+If the threshold becomes exhausted, at or below zero, no further speculation\n+will be performed.\n+\n+The inlining depth starts at zero\n+and is increased by one every time the inliner\n+descends into another function.  It is then decreased by one every time the\n+inliner leaves such function.  If the depth exceeds the value set by\n+{\\tt -inline-max-depth} then speculation stops.  This parameter is intended\n+as a general backstop for situations where the inlining\n+threshold does not control the search sufficiently.\n+\n+The unrolling depth applies to calls within the same mutually-recursive\n+group of functions.  Each time an inlining of such a call is performed\n+the depth is incremented by one when examining the resulting body.  If the\n+depth reaches the limit set by {\\tt -inline-max-unroll} then speculation\n+stops.\n+\n+\\section{Specialisation}\n+\n+The inliner may discover a call site to a recursive function where\n+something is known about the arguments: for example, they may be equal to\n+some other variables currently in scope.  In this situation it may be\n+beneficial to {\\em specialise} the function to those arguments.  This is\n+done by copying the declaration of the function (and any others involved\n+in any same mutually-recursive declaration) and noting the extra information\n+about the arguments.  The arguments augmented by this information are known\n+as {\\em specialised arguments}.  In order to try to ensure that specialisation\n+is not performed uselessly, arguments are only specialised if it can be shown\n+that they are {\\em invariant}: in other words, during the execution of the\n+recursive function(s) themselves, the arguments never change.\n+\n+Unless overridden by an attribute (see below), specialisation of a function\n+will not be attempted if:\n+\\begin{itemize}\n+\\item the compiler is in {\\tt -Oclassic} mode;\n+\\item the function is not obviously recursive;\n+\\item the function is not closed.\n+\\end{itemize}\n+\n+The compiler can prove invariance of function arguments across multiple\n+functions within a recursive group (although this has some limitations,\n+as shown by the example below).\n+\n+It should be noted that the {\\em unboxing of closures} pass (see below)\n+can introduce specialised arguments on non-recursive functions.  (No other\n+place in the compiler currently does this.)\n+\n+\\paragraph{Example: the well-known {\\tt List.iter} function}\n+This function might be written like so:\n+\\begin{verbatim}\n+let rec iter f l =\n+  match l with\n+  | [] -> ()\n+  | h :: t ->\n+    f h;\n+    iter f t\n+\\end{verbatim}\n+and used like this:\n+\\begin{verbatim}\n+let do_iter foo =\n+  iter foo [1; 2; 3]\n+\\end{verbatim}\n+The argument {\\tt f} to {\\tt iter} is invariant so the function may be\n+specialised:\n+\\begin{verbatim}\n+let do_iter foo =\n+  let rec iter' f l =\n+    (* The compiler knows: f holds the same value as foo throughout iter'. *)\n+    match l with\n+    | [] -> ()\n+    | h :: t ->\n+      f h;\n+      iter' f t\n+  in\n+  iter' foo [1; 2; 3]\n+\\end{verbatim}\n+The compiler notes down that for the function {\\tt iter'}, the argument\n+{\\tt f} is specialised to {\\tt foo}.  In particular, this will enable it\n+to transform the call {\\tt f h} within the body of {\\tt iter'} into a\n+call to {\\tt foo}.  (In turn, if something becomes known about {\\tt foo},\n+the call might be transformed into a direct call.)\n+\n+\\paragraph{Aside on invariant parameters.} The compiler cannot currently\n+detect invariance in cases such as the following.\n+\\begin{verbatim}\n+let rec iter_swap f g l =\n+  match l with\n+  | [] -> ()\n+  | 0 :: t ->\n+    iter_swap g f l\n+  | h :: t ->\n+    f h;\n+    iter_swap f g t\n+\\end{verbatim}\n+\n+\\subsection{Assessment of specialisation benefit}\n+\n+The benefit of specialisation is assessed in a similar way as for inlining.\n+Specialised argument information may mean that the body of the function\n+being specialised can be simplified: the removed operations are accumulated\n+into a benefit.  This, together with the size of the duplicated (specialised)\n+function declaration, is then assessed against the size of the call to the\n+original function.\n+\n+\\section{Default settings of parameters}\n+\n+The default settings (when not using {\\tt -Oclassic}) are for one\n+round of optimisation using the following parameters.\n+\n+\\begin{tableau}{|l|l|}{Parameter}{Setting}\n+\\entree{{\\tt -inline}}{10}\n+\\entree{{\\tt -inline-branch-factor}}{0.1}\n+\\entree{{\\tt -inline-alloc-cost}}{7}\n+\\entree{{\\tt -inline-branch-cost}}{5}\n+\\entree{{\\tt -inline-call-cost}}{5}\n+\\entree{{\\tt -inline-indirect-cost}}{4}\n+\\entree{{\\tt -inline-prim-cost}}{3}\n+\\entree{{\\tt -inline-lifting-benefit}}{1300}\n+\\entree{{\\tt -inline-toplevel}}{160}\n+\\entree{{\\tt -inline-max-depth}}{1}\n+\\entree{{\\tt -inline-max-unroll}}{0}\n+\\entree{{\\tt -unbox-closures-factor}}{10}\n+\\end{tableau}\n+\n+\\subsection{Settings at -O2 optimisation level}\n+\n+When {\\tt -O2} is specified two rounds of optimisation are performed.\n+The first round uses the default parameters (see above).  The second uses\n+the following parameters.\n+\n+\\begin{tableau}{|l|l|}{Parameter}{Setting}\n+\\entree{{\\tt -inline}}{25}\n+\\entree{{\\tt -inline-branch-factor}}{Same as default}\n+\\entree{{\\tt -inline-alloc-cost}}{Double the default}\n+\\entree{{\\tt -inline-branch-cost}}{Double the default}\n+\\entree{{\\tt -inline-call-cost}}{Double the default}\n+\\entree{{\\tt -inline-indirect-cost}}{Double the default}\n+\\entree{{\\tt -inline-prim-cost}}{Double the default}\n+\\entree{{\\tt -inline-lifting-benefit}}{Same as default}\n+\\entree{{\\tt -inline-toplevel}}{400}\n+\\entree{{\\tt -inline-max-depth}}{2}\n+\\entree{{\\tt -inline-max-unroll}}{Same as default}\n+\\entree{{\\tt -unbox-closures-factor}}{Same as default}\n+\\end{tableau}\n+\n+\\subsection{Settings at -O3 optimisation level}\n+\n+When {\\tt -O3} is specified three rounds of optimisation are performed.\n+The first two rounds are as for {\\tt -O2}.  The third round uses\n+the following parameters.\n+\n+\\begin{tableau}{|l|l|}{Parameter}{Setting}\n+\\entree{{\\tt -inline}}{50}\n+\\entree{{\\tt -inline-branch-factor}}{Same as default}\n+\\entree{{\\tt -inline-alloc-cost}}{Triple the default}\n+\\entree{{\\tt -inline-branch-cost}}{Triple the default}\n+\\entree{{\\tt -inline-call-cost}}{Triple the default}\n+\\entree{{\\tt -inline-indirect-cost}}{Triple the default}\n+\\entree{{\\tt -inline-prim-cost}}{Triple the default}\n+\\entree{{\\tt -inline-lifting-benefit}}{Same as default}\n+\\entree{{\\tt -inline-toplevel}}{800}\n+\\entree{{\\tt -inline-max-depth}}{3}\n+\\entree{{\\tt -inline-max-unroll}}{1}\n+\\entree{{\\tt -unbox-closures-factor}}{Same as default}\n+\\end{tableau}\n+\n+\\section{Manual control of inlining and specialisation}\n+\n+Should the inliner prove recalcitrant and refuse to inline a particular\n+function, or if the observed inlining decisions are not to the programmer's\n+satisfaction for some other reason, inlining behaviour can be dictated by the\n+programmer directly in the source code.\n+One example where this might be appropriate is when the programmer,\n+but not the compiler, knows that a particular function call is on a cold\n+code path.  It might be desirable to prevent inlining of the function so\n+that the code size along the hot path is kept smaller, so as to increase\n+locality.\n+\n+The inliner is directed using attributes.\n+For non-recursive functions (and one-step unrolling of recursive functions,\n+although {\\tt \\@unroll} is more clear for this purpose)\n+the following are supported:\n+\\begin{options}\n+\\item[{\\machine{\\@\\@inline always}} or {\\machine{\\@\\@inline never}}] Attached\n+to a {\\em declaration} of a function or functor, these direct the inliner to\n+either\n+always or never inline, irrespective of the size/benefit calculation.  (If\n+the function is recursive then the body is substituted and no special\n+action is taken for the recursive call site(s).)\n+{\\machine{\\@\\@inline}} with no argument is equivalent to\n+{\\machine{\\@\\@inline always}}.\n+\\item[{\\machine{\\@inlined always}} or {\\machine{\\@inlined never}}] Attached\n+to a function {\\em application}, these direct the inliner likewise.  These\n+attributes at call sites override any other attribute that may be present\n+on the corresponding declaration.\n+{\\machine{\\@inlined}} with no argument is equivalent to\n+{\\machine{\\@inlined always}}.\n+\\end{options}\n+\n+For recursive functions the relevant attributes are:\n+\\begin{options}\n+\\item[{\\machine{\\@\\@specialise always}} or {\\machine{\\@\\@specialise never}}]%\n+Attached to a declaration of a function\n+or functor, this directs the inliner to either always or never\n+specialise the function so\n+long as it has appropriate contextual knowledge, irrespective of the\n+size/benefit calculation.\n+{\\machine{\\@\\@specialise}} with no argument is equivalent to\n+{\\machine{\\@\\@specialise always}}.\n+\\item[{\\machine{\\@specialised always}} or {\\machine{\\@specialised never}}]%\n+Attached to a function application, this\n+directs the inliner likewise.  This attribute at a call site overrides any\n+other attribute that may be present on the corresponding declaration.\n+(Note that the function will still only be specialised if there exist\n+one or more invariant parameters whose values are known.)\n+{\\machine{\\@specialised}} with no argument is equivalent to\n+{\\machine{\\@specialised always}}.\n+\\item[{\\machine{\\@unrolled }}$n$] This attribute is attached to a function\n+application and takes an integer argument.  Each time the inliner sees\n+the attribute it behaves as follows:\n+\\begin{itemize}\n+\\item If $n$ is zero or less, nothing happens.\n+\\item Otherwise the function being called is substituted at the call site\n+with its body having been rewritten such that \n+any recursive calls to that function {\\em or\n+any others in the same mutually-recursive group} are annotated with the\n+attribute {\\tt unrolled(}$n - 1${\\tt )}.  Inlining may continue on that body.\n+\\end{itemize}\n+As such, $n$ behaves as the ``maximum depth of unrolling''.\n+\\end{options}\n+\n+A compiler warning will be emitted if it was found impossible to obey an\n+annotation from an {\\tt \\@inlined} or {\\tt \\@specialised} attribute.\n+\n+\\paragraph{Example showing correct placement of attributes}\n+\\begin{verbatim}\n+module F (M : sig type t end) = struct\n+  let bar x =\n+    x * 3\n+  [@@inline never]\n+\n+  let foo x =\n+    (bar [@inlined]) (42 + x)\n+end [@@inline never]\n+\n+module X = F [@inlined] (struct type t = int end)\n+\\end{verbatim}\n+\n+\\section{Simplification}\n+\n+Simplification, which is run in conjunction with inlining,\n+propagates information (known as {\\em approximations}) about which\n+variables hold what values at runtime.  Certain relationships between\n+variables and symbols are also tracked: for example, some variable may be\n+known to always hold the same value as some other variable; or perhaps\n+some variable may be known to always hold the value pointed to by some\n+symbol.\n+\n+The propagation can help to eliminate allocations in cases such as:\n+\\begin{verbatim}\n+let f x y =\n+  ...\n+  let p = x, y in\n+  ...\n+  ... (fst p) ... (snd p) ...\n+\\end{verbatim}\n+The projections from {\\tt p} may be replaced by uses of the variables\n+{\\tt x} and {\\tt y}, potentially meaning that {\\tt p} becomes unused.\n+\n+The propagation performed by the simplification pass is also important for\n+discovering which functions flow to indirect call sites.  This can enable\n+the transformation of such call sites into direct call sites, which makes\n+them eligible for an inlining transformation.\n+\n+\\section{Other code motion transformations}\n+\n+\\subsection{Lifting of constants}\n+\n+Expressions found to be constant will be lifted to symbol\n+bindings---that is to say, they will be statically allocated in the\n+object file---when\n+they evaluate to boxed values.  Such constants may be straightforward numeric\n+constants, such as the floating-point number {\\tt 42.0}, or more complicated\n+values such as constant closures.\n+\n+Lifting of constants to toplevel reduces allocation at runtime.\n+\n+The compiler aims to share constants lifted to toplevel such that there\n+are no duplicate definitions.  However if {\\tt .cmx} files are hidden\n+from the compiler then maximal sharing may not be possible.\n+\n+\\paragraph{Notes about float arrays} %\n+The following language semantics apply specifically to constant float arrays.\n+(By ``constant float array'' is meant an array consisting entirely of floating\n+point numbers that are known at compile time.  A common case is a literal\n+such as {\\tt [| 42.0; 43.0; |]}.\n+\\begin{itemize}\n+\\item Constant float arrays at the toplevel are mutable and never shared.\n+(That is to say, for each\n+such definition there is a distinct symbol in the data section of the object\n+file pointing at the array.)\n+\\item Constant float arrays not at toplevel are mutable and are created each\n+time the expression is evaluated.  This can be thought of as an operation that\n+takes an immutable array (which in the source code has no associated name; let\n+us call it the {\\em initialising array}) and\n+duplicates it into a fresh mutable array.\n+\\begin{itemize}\n+\\item If the array is of size four or less, the expression will create a\n+fresh block and write the values into it one by one.  There is no reference\n+to the initialising array as a whole.\n+\n+\\item Otherwise, the initialising array is lifted out and subject to the\n+normal constant sharing procedure;\n+creation of the array consists of bulk copying the initialising array\n+into a fresh value on the OCaml heap.\n+\\end{itemize}\n+\\end{itemize}\n+\n+\\subsection{Lifting of toplevel let bindings}\n+\n+Toplevel {\\tt let}-expressions may be lifted to symbol bindings to ensure\n+that the corresponding bound variables are not captured by closures.  If the\n+defining expression of a given binding is found to be constant, it is bound\n+as such (the technical term is a {\\em let-symbol} binding).\n+\n+Otherwise, the symbol is bound to a (statically-allocated)\n+{\\em preallocated block} containing one field.  At runtime, the defining\n+expression will be evaluated and the first field of the block filled with\n+the resulting value.  This {\\em initialise-symbol} binding\n+causes one extra indirection but ensures, by\n+virtue of the symbol's address being known at compile time, that uses of the\n+value are not captured by closures.\n+\n+It should be noted that the blocks corresponding to initialise-symbol\n+bindings are kept alive forever, by virtue of them occurring in a static\n+table of GC roots within the object file.  This extended lifetime of\n+expressions may on occasion be surprising.\n+\n+\\section{Unboxing transformations}\n+\n+The transformations in this section relate to the splitting apart of\n+{\\em boxed} (that is to say, non-immediate) values.  They are largely\n+intended to reduce allocation, which tends to result in a runtime\n+performance profile with lower variance and smaller tails.\n+\n+\\subsection{Unboxing of closure variables}\n+\n+This transformation is enabled unless\n+{\\tt -no-unbox-free-vars-of-closures} is provided.\n+\n+Variables that appear in closure environments may themselves be boxed\n+values.  As such, they may be split into further closure variables, each\n+of which corresponds to some projection from the original closure variable(s).\n+This transformation is called {\\em unboxing of closure variables} or\n+{\\em unboxing of free variables of closures}.  It is only applied when\n+there is\n+reasonable certainty that there are no uses of the boxed free variable itself\n+within the corresponding function bodies.\n+% CR-someday mshinwell: Actually, we probably don't check this carefully\n+% enough.  It needs a global analysis in case there is an out-of-scope\n+% projection.\n+\n+\\paragraph{Example:} In the following code, the compiler observes that\n+the closure returned from the function {\\tt f} contains a variable {\\tt pair}\n+(free in the body of {\\tt f}) that may be split into two separate variables.\n+\\begin{verbatim}\n+let f x0 x1 =\n+  let pair = x0, x1 in\n+  Printf.printf \"foo\\n\";\n+  fun y ->\n+    fst pair + snd pair + y\n+\\end{verbatim}\n+After some simplification one obtains:\n+\\begin{verbatim}\n+let f x0 x1 =\n+  let pair_0 = x0 in\n+  let pair_1 = x1 in\n+  Printf.printf \"foo\\n\";\n+  fun y ->\n+    pair_0 + pair_1 + y\n+\\end{verbatim}\n+and then:\n+\\begin{verbatim}\n+let f x0 x1 =\n+  Printf.printf \"foo\\n\";\n+  fun y ->\n+    x0 + x1 + y\n+\\end{verbatim}\n+The allocation of the pair has been eliminated.\n+\n+This transformation does not operate if it would cause the closure to\n+contain more than twice as many closure variables as it did beforehand.\n+\n+\\subsection{Unboxing of specialised arguments}\n+\n+This transformation is enabled unless\n+{\\tt -no-unbox-specialised-args} is provided.\n+\n+It may become the case during compilation that one or more invariant arguments\n+to a function become specialised to a particular value.  When such values are\n+themselves boxed the corresponding specialised arguments may be split into\n+more specialised arguments corresponding to the projections out of the boxed\n+value that occur within the function body.  This transformation is called\n+{\\em unboxing of specialised arguments}.  It is only applied when there is\n+reasonable certainty that the boxed argument itself is unused within the\n+function.\n+\n+If the function in question is involved in a recursive group then unboxing\n+of specialised arguments may be immediately replicated across the group\n+based on the dataflow between invariant arguments.\n+\n+\\paragraph{Example:} Having been given the following code, the compiler\n+will inline {\\tt loop} into {\\tt f}, and then observe {\\tt env}\n+being invariant and always the pair formed by adding {\\tt 42} and {\\tt 43}\n+to the argument {\\tt x} of the function {\\tt f}.\n+\\begin{verbatim}\n+let rec loop inv xs =\n+  match xs with\n+  | [] -> fst inv + snd inv\n+  | x::xs -> x + loop2 xs inv\n+and loop2 ys inv =\n+  match ys with\n+  | [] -> 4\n+  | y::ys -> y - loop inv ys\n+\n+let f x =\n+  Printf.printf \"%d\\n\" (loop (x + 42, x + 43) [1; 2; 3])\n+\\end{verbatim}\n+Since the functions have sufficiently few arguments, more specialised\n+arguments will be added.  After some simplification one obtains:\n+\\begin{verbatim}\n+let f x =\n+  let rec loop' xs inv_0 inv_1 =\n+    match xs with\n+    | [] -> inv_0 + inv_1\n+    | x::xs -> x + loop2' xs inv_0 inv_1\n+  and loop2' ys inv_0 inv_1 =\n+    match ys with\n+    | [] -> 4\n+    | y::ys -> y - loop' ys inv_0 inv_1\n+  in\n+  Printf.printf \"%d\\n\" (loop' (x + 42) (x + 43) [1; 2; 3])\n+\\end{verbatim}\n+The allocation of the pair within {\\tt f} has been removed.  (Since the\n+two closures for {\\tt loop'} and {\\tt loop2'} are constant they will also be\n+lifted to toplevel with no runtime allocation penalty.  This\n+would also happen without having run the transformation to unbox\n+specialise arguments.)\n+\n+The transformation to unbox specialised arguments never introduces extra\n+allocation.\n+\n+The transformation will not unbox arguments if it would result in the\n+original function having sufficiently many arguments so as to inhibit\n+tail-call optimisation.\n+\n+The transformation is implemented by creating a wrapper function that\n+accepts the original arguments.  Meanwhile, the original function is renamed\n+and extra arguments are added corresponding to the unboxed specialised\n+arguments; this new function\n+is called from the wrapper.  The wrapper will then be inlined\n+at direct call sites.  Indeed, all call sites will be direct unless\n+{\\tt -unbox-closures} is being used, since they will have been generated\n+by the compiler when originally specialising the function.  (In the case\n+of {\\tt -unbox-closures} other functions may appear with specialised\n+arguments; in this case there may be indirect calls and these will incur\n+a small penalty owing to having to bounce through the wrapper.  The technique\n+of {\\em direct call surrogates} used for {\\tt -unbox-closures} is not\n+used by the transformation to unbox specialised arguments.)\n+\n+\\subsection{Unboxing of closures}\n+\n+This transformation is {\\em not} enabled by default.  It may be enabled\n+using the {\\tt -unbox-closures} flag.\n+\n+The transformation replaces closure variables by specialised arguments.\n+The aim is to cause more closures to become closed.  It is particularly\n+applicable, as a means of reducing allocation, where the function concerned\n+cannot be inlined or specialised.  For example, some non-recursive function\n+might be too large to inline; or some recursive function might offer\n+no opportunities for specialisation perhaps because its only argument is\n+one of type {\\tt unit}.\n+\n+At present there may be a small penalty in terms of actual runtime\n+performance when this transformation is enabled, although more stable\n+performance may be obtained due to reduced allocation.  It is recommended\n+that developers experiment to determine whether the option is beneficial\n+for their code.  (It is expected that in the future it will be possible\n+for the performance degradation to be removed.)\n+\n+\\paragraph{Simple example:} In the following code (which might typically\n+occur when {\\tt g} is too large to inline) the value of {\\tt x} would usually\n+be communicated to the application of the {\\tt +} function via the closure\n+of {\\tt g}.\n+\\begin{verbatim}\n+let f x =\n+  let g y =\n+    x + y\n+  in\n+  (g [@inlined never]) 42\n+\\end{verbatim}\n+Unboxing of the closure causes the value for {\\tt x} inside {\\tt g} to\n+be passed as an argument to {\\tt g} rather than through its closure.  This\n+means that the closure of {\\tt g} becomes constant and may be lifted to\n+toplevel, eliminating the runtime allocation.\n+\n+The transformation is implemented by adding a new wrapper function in the\n+manner of that used when unboxing specialised arguments.  The closure\n+variables are still free in the wrapper, but the intention is that when\n+the wrapper is inlined at direct call sites, the relevant values are\n+passed directly to the main function via the new specialised arguments.\n+\n+Adding such a wrapper will penalise indirect calls to the function\n+(which might exist in arbitrary places; remember that this transformation\n+is not for example applied only on functions the compiler has produced\n+as a result of specialisation) since such calls will bounce through\n+the wrapper.  To\n+mitigate this, if a function is small enough when weighed up against\n+the number of free variables being removed, it will be duplicated by the\n+transformation to obtain two versions: the original (used for indirect calls,\n+since we can do no better) and the wrapper/rewritten function pair as\n+described in the previous paragraph.  The wrapper/rewritten function pair\n+will only be used at direct call sites of the function.  (The wrapper in\n+this case is known as a {\\em direct call surrogate}, since\n+it takes the place of another function---the unchanged version used for\n+indirect calls---at direct call sites.)\n+\n+The {\\tt -unbox-closures-factor} command line flag, which takes an\n+integer, may be used to adjust the point at which a function is deemed\n+large enough to be ineligible for duplication.  The benefit of\n+duplication is scaled by the integer before being evaluated against the\n+size.\n+\n+\\paragraph{Harder example:} In the following code, there are two closure\n+variables that would typically cause closure allocations.  One is called\n+{\\tt fv} and occurs inside the function {\\tt baz}; the other is called\n+{\\tt z} and occurs inside the function {\\tt bar}.\n+In this toy (yet sophisticated) example we again use an attribute to\n+simulate the typical situation where the first argument of {\\tt baz} is\n+too large to inline.\n+\\begin{verbatim}\n+let foo c =\n+  let rec bar zs fv =\n+    match zs with\n+    | [] -> []\n+    | z::zs ->\n+      let rec baz f = function\n+        | [] -> []\n+        | a::l -> let r = fv + ((f [@inlined never]) a) in r :: baz f l\n+      in\n+      (map2 (fun y -> z + y) [z; 2; 3; 4]) @ bar zs fv\n+  in\n+  Printf.printf \"%d\" (List.length (bar [1; 2; 3; 4] c))\n+\\end{verbatim}\n+The code resulting from applying {\\tt -O3 -unbox-closures} to this code\n+passes the free variables via function arguments in\n+order to eliminate all closure allocation in this example (aside from any\n+that might be performed inside {\\tt printf}).\n+\n+\\section{Removal of unused code and values}\n+\n+\\subsection{Removal of redundant let expressions}\n+\n+The simplification pass removes unused {\\tt let} bindings so long as\n+their corresponding defining expressions have ``no effects''.  See\n+the section ``Treatment of effects'' below for the precise definition of\n+this term.\n+\n+\\subsection{Removal of redundant program constructs}\n+\n+This transformation is analogous to the removal of {\\tt let}-expressions\n+whose defining expressions have no effects.  It operates instead on symbol\n+bindings, removing those that have no effects.\n+\n+\\subsection{Removal of unused arguments}\n+\n+This transformation is only enabled by default for specialised arguments.\n+It may be enabled for all arguments using the {\\tt -remove-unused-arguments}\n+flag.\n+\n+The pass analyses functions to determine which arguments are unused.\n+Removal is effected by creating a wrapper function, which will be inlined\n+at every direct call site, that accepts the original arguments and then\n+discards the unused ones before calling the original function.  As a\n+consequence, this transformation may be detrimental if the original\n+function is usually indirectly called, since such calls will now bounce\n+through the wrapper.  (The technique of {\\em direct call surrogates} used\n+to reduce this penalty during unboxing of closure variables (see above)\n+does not yet apply to the pass that removes unused arguments.)\n+\n+\\subsection{Removal of unused closure variables}\n+\n+This transformation performs an analysis across\n+the whole compilation unit to determine whether there exist closure variables\n+that are never used.  Such closure variables are then eliminated.  (Note that\n+this has to be a whole-unit analysis because a projection of a closure\n+variable from some particular closure may have propagated to an arbitrary\n+location within the code due to inlining.)\n+\n+\\section{Other code transformations}\n+\n+\\subsection{Transformation of non-escaping references into mutable variables}\n+\n+Flambda performs a simple analysis analogous to that performed elsewhere\n+in the compiler that can transform {\\tt ref}s into mutable variables\n+that may then be held in registers (or on the stack as appropriate) rather\n+than being allocated on the OCaml heap.  This only happens so long as the\n+reference concerned can be shown to not escape from its defining scope.\n+\n+\\subsection{Substitution of closure variables for specialised arguments}\n+\n+This transformation discovers closure variables that are known to be\n+equal to specialised arguments.  Such closure variables are replaced by\n+the specialised arguments; the closure variables may then be removed by\n+the ``removal of unused closure variables'' pass (see below).\n+\n+\\section{Treatment of effects}\n+\n+The Flambda optimisers classify expressions in order to determine whether\n+an expression:\n+\\begin{itemize}\n+\\item does not need to be evaluated at all; and/or\n+\\item may be duplicated.\n+\\end{itemize}\n+\n+This is done by forming judgements on the {\\em effects} and the {\\em coeffects}\n+that might be performed were the expression to be executed.  Effects talk\n+about how the expression might affect the world; coeffects talk about how\n+the world might affect the expression.\n+\n+Effects are classified as follows:\n+\\begin{options}\n+\\item[{\\bf No effects:}] The expression does not change the observable state\n+of the world.  For example, it must not write to any mutable storage,\n+call arbitrary external functions or change control flow (e.g. by raising\n+an exception).  Note that allocation is {\\em not} classed as having\n+``no effects'' (see below).\n+\\begin{itemize}\n+\\item It is assumed in the compiler that expressions with no\n+effects, whose results are not used, may be eliminated.  (This typically\n+happens where the expression in question is the defining expression of a\n+{\\tt let}; in such cases the {\\tt let}-expression will be\n+eliminated.) It is further\n+assumed that such expressions with no effects may be\n+duplicated (and thus possibly executed more than once).\n+\\item (Exceptions arising from allocation points, for example\n+``out of memory'' or\n+exceptions propagated from finalizers or signal handlers, are treated as\n+``effects out of the ether'' and thus ignored for our determination here\n+of effectfulness.  The same goes for floating point operations that may\n+cause hardware traps on some platforms.)\n+\\end{itemize}\n+\\item[{\\bf Only generative effects:}] The expression does not change the\n+observable state of the world save for possibly affecting the state of\n+the garbage collector by performing an allocation.  Expressions\n+that only have generative effects and whose results are unused\n+may be eliminated by the compiler.  However, unlike expressions with\n+``no effects'', such expressions will never be eligible for duplication.\n+\\item[{\\bf Arbitrary effects:}] All other expressions.\n+\\end{options}\n+\n+There is a single classification for coeffects:\n+\\begin{options}\n+\\item[{\\bf No coeffects:}] The expression does not observe the effects (in\n+the sense described above) of other expressions.  For example, it must not\n+read from any mutable storage or call arbitrary external functions.\n+\\end{options}\n+\n+It is assumed in the compiler that, subject to data dependencies,\n+expressions with neither effects nor coeffects may be reordered with\n+respect to other expressions.\n+\n+\\section{Compilation of statically-allocated modules}\n+\n+Compilation of modules that are able to be statically allocated (for example,\n+the module corresponding to an entire compilation unit, as opposed to a first\n+class module dependent on values computed at runtime) initially follows the\n+strategy used for bytecode.  A sequence of {\\tt let}-bindings, which may be\n+interspersed with arbitrary effects, surrounds a record creation that becomes\n+the module block.  The Flambda-specific transformation follows: these bindings\n+are lifted to toplevel symbols, as described above.\n+\n+\\section{Inhibition of optimisation}\n+\n+Especially when writing benchmarking suites that run non-side-effecting\n+algorithms in loops, it may be found that the optimiser entirely\n+elides the code being benchmarked.  This behaviour can be prevented by\n+using the {\\tt Sys.opaque\\_identity} function (which indeed behaves as a\n+normal OCaml function and does not possess any ``magic'' semantics).  The\n+documentation of the {\\tt Sys} module should be consulted for further details.\n+\n+\\section{Use of unsafe operations}","path":"manual/manual/cmds/flambda.etex","position":1146,"original_position":1146,"commit_id":"21a9ad0ad390a1c3629945627f76d71395130b09","original_commit_id":"21a9ad0ad390a1c3629945627f76d71395130b09","user":{"login":"edwintorok","id":721894,"avatar_url":"https://avatars.githubusercontent.com/u/721894?v=3","gravatar_id":"","url":"https://api.github.com/users/edwintorok","html_url":"https://github.com/edwintorok","followers_url":"https://api.github.com/users/edwintorok/followers","following_url":"https://api.github.com/users/edwintorok/following{/other_user}","gists_url":"https://api.github.com/users/edwintorok/gists{/gist_id}","starred_url":"https://api.github.com/users/edwintorok/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edwintorok/subscriptions","organizations_url":"https://api.github.com/users/edwintorok/orgs","repos_url":"https://api.github.com/users/edwintorok/repos","events_url":"https://api.github.com/users/edwintorok/events{/privacy}","received_events_url":"https://api.github.com/users/edwintorok/received_events","type":"User","site_admin":false},"body":"This section should be more prominent, to make sure people read it and don't write code that Flambda would break. Maybe mention it in the `Overview` and add a link to the section?\r\n","created_at":"2016-03-11T12:51:55Z","updated_at":"2016-03-11T12:51:55Z","html_url":"https://github.com/ocaml/ocaml/pull/503#discussion_r55825429","pull_request_url":"https://api.github.com/repos/ocaml/ocaml/pulls/503","_links":{"self":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/comments/55825429"},"html":{"href":"https://github.com/ocaml/ocaml/pull/503#discussion_r55825429"},"pull_request":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/503"}}},"pull_request":{"url":"https://api.github.com/repos/ocaml/ocaml/pulls/503","id":62525648,"html_url":"https://github.com/ocaml/ocaml/pull/503","diff_url":"https://github.com/ocaml/ocaml/pull/503.diff","patch_url":"https://github.com/ocaml/ocaml/pull/503.patch","issue_url":"https://api.github.com/repos/ocaml/ocaml/issues/503","number":503,"state":"open","locked":false,"title":"Flambda manual chapter","user":{"login":"mshinwell","id":1315488,"avatar_url":"https://avatars.githubusercontent.com/u/1315488?v=3","gravatar_id":"","url":"https://api.github.com/users/mshinwell","html_url":"https://github.com/mshinwell","followers_url":"https://api.github.com/users/mshinwell/followers","following_url":"https://api.github.com/users/mshinwell/following{/other_user}","gists_url":"https://api.github.com/users/mshinwell/gists{/gist_id}","starred_url":"https://api.github.com/users/mshinwell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mshinwell/subscriptions","organizations_url":"https://api.github.com/users/mshinwell/orgs","repos_url":"https://api.github.com/users/mshinwell/repos","events_url":"https://api.github.com/users/mshinwell/events{/privacy}","received_events_url":"https://api.github.com/users/mshinwell/received_events","type":"User","site_admin":false},"body":"User documentation about Flambda for the manual.\r\nIf you would like to read this without building the manual, there is a temporary link here:\r\nhttps://ocaml.janestreet.com/ocaml-core/flambda_manual/\r\n\r\nThis doesn't constitute developer documentation.  That is intended to be provided by the source code itself and the comments in that code.  We will try to do some more improving of comments before the 4.03 release.\r\n\r\nIn this GPR there is a hack in fix_index.sh (so it would work for me) which I shall revert before merging this.","created_at":"2016-03-11T10:42:53Z","updated_at":"2016-03-11T12:51:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"da7743ca080244216d0e9276a565813532db02d6","assignee":null,"milestone":{"url":"https://api.github.com/repos/ocaml/ocaml/milestones/1","html_url":"https://github.com/ocaml/ocaml/milestones/4.03.0","labels_url":"https://api.github.com/repos/ocaml/ocaml/milestones/1/labels","id":1464887,"number":1,"title":"4.03.0","description":"","creator":{"login":"damiendoligez","id":19104,"avatar_url":"https://avatars.githubusercontent.com/u/19104?v=3","gravatar_id":"","url":"https://api.github.com/users/damiendoligez","html_url":"https://github.com/damiendoligez","followers_url":"https://api.github.com/users/damiendoligez/followers","following_url":"https://api.github.com/users/damiendoligez/following{/other_user}","gists_url":"https://api.github.com/users/damiendoligez/gists{/gist_id}","starred_url":"https://api.github.com/users/damiendoligez/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/damiendoligez/subscriptions","organizations_url":"https://api.github.com/users/damiendoligez/orgs","repos_url":"https://api.github.com/users/damiendoligez/repos","events_url":"https://api.github.com/users/damiendoligez/events{/privacy}","received_events_url":"https://api.github.com/users/damiendoligez/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":64,"state":"open","created_at":"2015-12-17T12:42:44Z","updated_at":"2016-03-11T10:42:53Z","due_on":"2016-02-07T23:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/ocaml/ocaml/pulls/503/commits","review_comments_url":"https://api.github.com/repos/ocaml/ocaml/pulls/503/comments","review_comment_url":"https://api.github.com/repos/ocaml/ocaml/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ocaml/ocaml/issues/503/comments","statuses_url":"https://api.github.com/repos/ocaml/ocaml/statuses/21a9ad0ad390a1c3629945627f76d71395130b09","head":{"label":"mshinwell:flambda_doc","ref":"flambda_doc","sha":"21a9ad0ad390a1c3629945627f76d71395130b09","user":{"login":"mshinwell","id":1315488,"avatar_url":"https://avatars.githubusercontent.com/u/1315488?v=3","gravatar_id":"","url":"https://api.github.com/users/mshinwell","html_url":"https://github.com/mshinwell","followers_url":"https://api.github.com/users/mshinwell/followers","following_url":"https://api.github.com/users/mshinwell/following{/other_user}","gists_url":"https://api.github.com/users/mshinwell/gists{/gist_id}","starred_url":"https://api.github.com/users/mshinwell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mshinwell/subscriptions","organizations_url":"https://api.github.com/users/mshinwell/orgs","repos_url":"https://api.github.com/users/mshinwell/repos","events_url":"https://api.github.com/users/mshinwell/events{/privacy}","received_events_url":"https://api.github.com/users/mshinwell/received_events","type":"User","site_admin":false},"repo":{"id":8355887,"name":"ocaml","full_name":"mshinwell/ocaml","owner":{"login":"mshinwell","id":1315488,"avatar_url":"https://avatars.githubusercontent.com/u/1315488?v=3","gravatar_id":"","url":"https://api.github.com/users/mshinwell","html_url":"https://github.com/mshinwell","followers_url":"https://api.github.com/users/mshinwell/followers","following_url":"https://api.github.com/users/mshinwell/following{/other_user}","gists_url":"https://api.github.com/users/mshinwell/gists{/gist_id}","starred_url":"https://api.github.com/users/mshinwell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mshinwell/subscriptions","organizations_url":"https://api.github.com/users/mshinwell/orgs","repos_url":"https://api.github.com/users/mshinwell/repos","events_url":"https://api.github.com/users/mshinwell/events{/privacy}","received_events_url":"https://api.github.com/users/mshinwell/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mshinwell/ocaml","description":"Read-only mirror of INRIA SVN","fork":true,"url":"https://api.github.com/repos/mshinwell/ocaml","forks_url":"https://api.github.com/repos/mshinwell/ocaml/forks","keys_url":"https://api.github.com/repos/mshinwell/ocaml/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mshinwell/ocaml/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mshinwell/ocaml/teams","hooks_url":"https://api.github.com/repos/mshinwell/ocaml/hooks","issue_events_url":"https://api.github.com/repos/mshinwell/ocaml/issues/events{/number}","events_url":"https://api.github.com/repos/mshinwell/ocaml/events","assignees_url":"https://api.github.com/repos/mshinwell/ocaml/assignees{/user}","branches_url":"https://api.github.com/repos/mshinwell/ocaml/branches{/branch}","tags_url":"https://api.github.com/repos/mshinwell/ocaml/tags","blobs_url":"https://api.github.com/repos/mshinwell/ocaml/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mshinwell/ocaml/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mshinwell/ocaml/git/refs{/sha}","trees_url":"https://api.github.com/repos/mshinwell/ocaml/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mshinwell/ocaml/statuses/{sha}","languages_url":"https://api.github.com/repos/mshinwell/ocaml/languages","stargazers_url":"https://api.github.com/repos/mshinwell/ocaml/stargazers","contributors_url":"https://api.github.com/repos/mshinwell/ocaml/contributors","subscribers_url":"https://api.github.com/repos/mshinwell/ocaml/subscribers","subscription_url":"https://api.github.com/repos/mshinwell/ocaml/subscription","commits_url":"https://api.github.com/repos/mshinwell/ocaml/commits{/sha}","git_commits_url":"https://api.github.com/repos/mshinwell/ocaml/git/commits{/sha}","comments_url":"https://api.github.com/repos/mshinwell/ocaml/comments{/number}","issue_comment_url":"https://api.github.com/repos/mshinwell/ocaml/issues/comments{/number}","contents_url":"https://api.github.com/repos/mshinwell/ocaml/contents/{+path}","compare_url":"https://api.github.com/repos/mshinwell/ocaml/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mshinwell/ocaml/merges","archive_url":"https://api.github.com/repos/mshinwell/ocaml/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mshinwell/ocaml/downloads","issues_url":"https://api.github.com/repos/mshinwell/ocaml/issues{/number}","pulls_url":"https://api.github.com/repos/mshinwell/ocaml/pulls{/number}","milestones_url":"https://api.github.com/repos/mshinwell/ocaml/milestones{/number}","notifications_url":"https://api.github.com/repos/mshinwell/ocaml/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mshinwell/ocaml/labels{/name}","releases_url":"https://api.github.com/repos/mshinwell/ocaml/releases{/id}","deployments_url":"https://api.github.com/repos/mshinwell/ocaml/deployments","created_at":"2013-02-22T11:07:50Z","updated_at":"2014-06-06T12:45:32Z","pushed_at":"2016-03-11T11:01:57Z","git_url":"git://github.com/mshinwell/ocaml.git","ssh_url":"git@github.com:mshinwell/ocaml.git","clone_url":"https://github.com/mshinwell/ocaml.git","svn_url":"https://github.com/mshinwell/ocaml","homepage":null,"size":200675,"stargazers_count":2,"watchers_count":2,"language":"OCaml","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":2,"default_branch":"trunk"}},"base":{"label":"ocaml:4.03","ref":"4.03","sha":"e329753384ac24b0313f280292eaa0f1e16936ce","user":{"login":"ocaml","id":1841483,"avatar_url":"https://avatars.githubusercontent.com/u/1841483?v=3","gravatar_id":"","url":"https://api.github.com/users/ocaml","html_url":"https://github.com/ocaml","followers_url":"https://api.github.com/users/ocaml/followers","following_url":"https://api.github.com/users/ocaml/following{/other_user}","gists_url":"https://api.github.com/users/ocaml/gists{/gist_id}","starred_url":"https://api.github.com/users/ocaml/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ocaml/subscriptions","organizations_url":"https://api.github.com/users/ocaml/orgs","repos_url":"https://api.github.com/users/ocaml/repos","events_url":"https://api.github.com/users/ocaml/events{/privacy}","received_events_url":"https://api.github.com/users/ocaml/received_events","type":"Organization","site_admin":false},"repo":{"id":6786166,"name":"ocaml","full_name":"ocaml/ocaml","owner":{"login":"ocaml","id":1841483,"avatar_url":"https://avatars.githubusercontent.com/u/1841483?v=3","gravatar_id":"","url":"https://api.github.com/users/ocaml","html_url":"https://github.com/ocaml","followers_url":"https://api.github.com/users/ocaml/followers","following_url":"https://api.github.com/users/ocaml/following{/other_user}","gists_url":"https://api.github.com/users/ocaml/gists{/gist_id}","starred_url":"https://api.github.com/users/ocaml/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ocaml/subscriptions","organizations_url":"https://api.github.com/users/ocaml/orgs","repos_url":"https://api.github.com/users/ocaml/repos","events_url":"https://api.github.com/users/ocaml/events{/privacy}","received_events_url":"https://api.github.com/users/ocaml/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ocaml/ocaml","description":"The core OCaml system: compilers, runtime system, base libraries","fork":false,"url":"https://api.github.com/repos/ocaml/ocaml","forks_url":"https://api.github.com/repos/ocaml/ocaml/forks","keys_url":"https://api.github.com/repos/ocaml/ocaml/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ocaml/ocaml/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ocaml/ocaml/teams","hooks_url":"https://api.github.com/repos/ocaml/ocaml/hooks","issue_events_url":"https://api.github.com/repos/ocaml/ocaml/issues/events{/number}","events_url":"https://api.github.com/repos/ocaml/ocaml/events","assignees_url":"https://api.github.com/repos/ocaml/ocaml/assignees{/user}","branches_url":"https://api.github.com/repos/ocaml/ocaml/branches{/branch}","tags_url":"https://api.github.com/repos/ocaml/ocaml/tags","blobs_url":"https://api.github.com/repos/ocaml/ocaml/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ocaml/ocaml/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ocaml/ocaml/git/refs{/sha}","trees_url":"https://api.github.com/repos/ocaml/ocaml/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ocaml/ocaml/statuses/{sha}","languages_url":"https://api.github.com/repos/ocaml/ocaml/languages","stargazers_url":"https://api.github.com/repos/ocaml/ocaml/stargazers","contributors_url":"https://api.github.com/repos/ocaml/ocaml/contributors","subscribers_url":"https://api.github.com/repos/ocaml/ocaml/subscribers","subscription_url":"https://api.github.com/repos/ocaml/ocaml/subscription","commits_url":"https://api.github.com/repos/ocaml/ocaml/commits{/sha}","git_commits_url":"https://api.github.com/repos/ocaml/ocaml/git/commits{/sha}","comments_url":"https://api.github.com/repos/ocaml/ocaml/comments{/number}","issue_comment_url":"https://api.github.com/repos/ocaml/ocaml/issues/comments{/number}","contents_url":"https://api.github.com/repos/ocaml/ocaml/contents/{+path}","compare_url":"https://api.github.com/repos/ocaml/ocaml/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ocaml/ocaml/merges","archive_url":"https://api.github.com/repos/ocaml/ocaml/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ocaml/ocaml/downloads","issues_url":"https://api.github.com/repos/ocaml/ocaml/issues{/number}","pulls_url":"https://api.github.com/repos/ocaml/ocaml/pulls{/number}","milestones_url":"https://api.github.com/repos/ocaml/ocaml/milestones{/number}","notifications_url":"https://api.github.com/repos/ocaml/ocaml/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ocaml/ocaml/labels{/name}","releases_url":"https://api.github.com/repos/ocaml/ocaml/releases{/id}","deployments_url":"https://api.github.com/repos/ocaml/ocaml/deployments","created_at":"2012-11-20T22:18:22Z","updated_at":"2016-03-11T07:22:44Z","pushed_at":"2016-03-11T11:01:58Z","git_url":"git://github.com/ocaml/ocaml.git","ssh_url":"git@github.com:ocaml/ocaml.git","clone_url":"https://github.com/ocaml/ocaml.git","svn_url":"https://github.com/ocaml/ocaml","homepage":"","size":227067,"stargazers_count":694,"watchers_count":694,"language":"OCaml","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":227,"mirror_url":null,"open_issues_count":73,"forks":227,"open_issues":73,"watchers":694,"default_branch":"trunk"}},"_links":{"self":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/503"},"html":{"href":"https://github.com/ocaml/ocaml/pull/503"},"issue":{"href":"https://api.github.com/repos/ocaml/ocaml/issues/503"},"comments":{"href":"https://api.github.com/repos/ocaml/ocaml/issues/503/comments"},"review_comments":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/503/comments"},"review_comment":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/503/commits"},"statuses":{"href":"https://api.github.com/repos/ocaml/ocaml/statuses/21a9ad0ad390a1c3629945627f76d71395130b09"}}}}