forked from ocaml-flambda/flambda-backend
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtmc.mli
81 lines (71 loc) · 3.37 KB
/
tmc.mli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Frédéric Bour *)
(* Gabriel Scherer, projet Partout, INRIA Saclay *)
(* Basile Clément, projet Cambium, INRIA Paris *)
(* *)
(* Copyright 2020 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
(** Tail-modulo-cons optimization.
{b Warning:} this module is unstable and part of
{{!Compiler_libs}compiler-libs}.
*)
(** TMC (Tail Modulo Cons) is a code transformation that
rewrites transformed functions in destination-passing-style, in
such a way that certain calls that were not in tail position in the
original program become tail-calls in the transformed program.
As a classic example, the following program
{|
let[@tail_mod_cons] rec map f = function
| [] -> []
| x :: xs ->
let y = f x in
y :: map f xs
|}
becomes (expressed in almost-source-form; the translation is in
fact at the Lambda-level)
{|
let rec map f = function
| [] -> []
| x :: xs ->
let y = f x in
let dst = y :: Placeholder in
map_dps dst 1 f xs; dst
and map_dps dst offset f = function
| [] ->
dst.offset <- []
| x :: xs ->
let y = f x in
let dst' = y :: Placeholder in
dst.offset <- dst';
map_dps dst 1 f fx
|}
In this example, the expression (y :: map f xs) had a call in
non-tail-position, and it gets rewritten into tail-calls. TMC
handles all such cases where the continuation of the call
(what needs to be done after the return) is a "construction", the
creation of a (possibly nested) data block.
The code transformation generates two versions of the
input function, the "direct" version with the same type and
behavior as the original one (here just [map]), and
the "destination-passing-style" version (here [map_dps]).
Any call to the original function from outside the let..rec
declaration gets transformed into a call into the direct version,
which will itself call the destination-passing-style versions on
recursive calls that may benefit from it (they are in tail-position
modulo constructors).
Because of this inherent code duplication, the transformation may
not always improve performance. In this implementation, TMC is
opt-in, we only transform functions that the user has annotated
with an attribute to request the transformation.
*)
open Lambda
val rewrite : lambda -> lambda