forked from ocaml-flambda/flambda-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlift_let_to_initialize_symbol.ml
300 lines (288 loc) · 11.1 KB
/
lift_let_to_initialize_symbol.ml
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Pierre Chambart, OCamlPro *)
(* Mark Shinwell and Leo White, Jane Street Europe *)
(* *)
(* Copyright 2013--2016 OCamlPro SAS *)
(* Copyright 2014--2016 Jane Street Group LLC *)
(* *)
(* 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. *)
(* *)
(**************************************************************************)
[@@@ocaml.warning "+a-4-9-30-40-41-42-66"]
open! Int_replace_polymorphic_compare
type ('a, 'b) kind =
| Initialisation of (Symbol.t * Tag.t * Flambda.t list)
| Effect of 'b
let should_copy (named:Flambda.named) =
match named with
| Symbol _ | Read_symbol_field _ | Const _ -> true
| _ -> false
type extracted =
| Expr of Variable.t * Flambda.t
| Exprs of Variable.t list * Flambda.t
| Block of Variable.t * Tag.t * Variable.t list
type accumulated = {
copied_lets : (Variable.t * Flambda.named) list;
extracted_lets : extracted list;
terminator : Flambda.expr;
}
let rec accumulate ~substitution ~copied_lets ~extracted_lets
(expr : Flambda.t) =
match expr with
| Let { var; body = Var var'; _ } | Let_rec ([var, _], Var var')
when Variable.equal var var' ->
{ copied_lets; extracted_lets;
terminator = Flambda_utils.toplevel_substitution substitution expr;
}
(* If the pattern is what lifting let_rec generates, prevent it from being
lifted again. *)
| Let_rec (defs,
Let { var; body = Var var';
defining_expr = Prim (Pmakeblock _, fields, _); })
when
Variable.equal var var'
&& List.for_all (fun field ->
List.exists (fun (def_var, _) -> Variable.equal def_var field) defs)
fields ->
{ copied_lets; extracted_lets;
terminator = Flambda_utils.toplevel_substitution substitution expr;
}
| Let { var; defining_expr = Expr (Var alias); body; _ }
| Let_rec ([var, Expr (Var alias)], body) ->
let alias =
match Variable.Map.find alias substitution with
| exception Not_found -> alias
| original_alias -> original_alias
in
accumulate
~substitution:(Variable.Map.add var alias substitution)
~copied_lets
~extracted_lets
body
| Let { var; defining_expr = named; body; _ }
| Let_rec ([var, named], body)
when should_copy named ->
accumulate body
~substitution
~copied_lets:((var, named)::copied_lets)
~extracted_lets
| Let { var; defining_expr = named; body; _ } ->
let extracted =
let renamed = Variable.rename var in
match named with
| Prim (Pmakeblock (tag, (Immutable | Immutable_unique),
_value_kind, Alloc_heap),
args, _dbg) ->
let tag = Tag.create_exn tag in
let args =
List.map (fun v ->
try Variable.Map.find v substitution
with Not_found -> v)
args
in
Block (var, tag, args)
| named ->
let expr =
Flambda_utils.toplevel_substitution substitution
(Flambda.create_let renamed named (Var renamed))
in
Expr (var, expr)
in
accumulate body
~substitution
~copied_lets
~extracted_lets:(extracted::extracted_lets)
| Let_rec ([var, named], body) ->
let renamed = Variable.rename var in
let def_substitution = Variable.Map.add var renamed substitution in
let expr =
Flambda_utils.toplevel_substitution def_substitution
(Let_rec ([renamed, named], Var renamed))
in
let extracted = Expr (var, expr) in
accumulate body
~substitution
~copied_lets
~extracted_lets:(extracted::extracted_lets)
| Let_rec (defs, body) ->
let renamed_defs, def_substitution =
List.fold_right (fun (var, def) (acc, substitution) ->
let new_var = Variable.rename var in
(new_var, def) :: acc,
Variable.Map.add var new_var substitution)
defs ([], substitution)
in
let extracted =
let expr =
let name = Internal_variable_names.lifted_let_rec_block in
Flambda_utils.toplevel_substitution def_substitution
(Let_rec (renamed_defs,
Flambda_utils.name_expr ~name
(Prim (Pmakeblock (0, Immutable, None, Lambda.alloc_heap),
List.map fst renamed_defs,
Debuginfo.none))))
in
Exprs (List.map fst defs, expr)
in
accumulate body
~substitution
~copied_lets
~extracted_lets:(extracted::extracted_lets)
| _ ->
{ copied_lets;
extracted_lets;
terminator = Flambda_utils.toplevel_substitution substitution expr;
}
let rebuild_expr
~(extracted_definitions : (Symbol.t * int list) Variable.Map.t)
~(copied_definitions : Flambda.named Variable.Map.t)
~(substitute : bool)
(expr : Flambda.t) =
let expr_with_read_symbols =
Flambda_utils.substitute_read_symbol_field_for_variables
extracted_definitions expr
in
let free_variables = Flambda.free_variables expr_with_read_symbols in
let substitution =
if substitute then
Variable.Map.of_set (fun x -> Variable.rename x) free_variables
else
Variable.Map.of_set (fun x -> x) free_variables
in
let expr_with_read_symbols =
Flambda_utils.toplevel_substitution substitution
expr_with_read_symbols
in
Variable.Map.fold (fun var declaration body ->
let definition = Variable.Map.find var copied_definitions in
Flambda.create_let declaration definition body)
substitution expr_with_read_symbols
let rebuild (used_variables:Variable.Set.t) (accumulated:accumulated) =
let copied_definitions = Variable.Map.of_list accumulated.copied_lets in
let accumulated_extracted_lets =
List.map (fun decl ->
match decl with
| Block (var, _, _) | Expr (var, _) ->
Symbol_utils.Flambda.for_variable (Variable.rename var), decl
| Exprs _ ->
let name = Internal_variable_names.lifted_let_rec_block in
let var = Variable.create name in
Symbol_utils.Flambda.for_variable var, decl)
accumulated.extracted_lets
in
let extracted_definitions =
(* Blocks are lifted to direct top-level Initialize_block:
accessing the value be done directly through the symbol.
Other let bound variables are initialized inside a size
one static block:
accessing the value is done directly through the field 0
of the symbol.
let rec of size more than one is represented as a block of
all the bound variables allocated inside a size one static
block:
accessing the value is done directly through the right
field of the field 0 of the symbol. *)
List.fold_left (fun map (symbol, decl) ->
match decl with
| Block (var, _tag, _fields) ->
Variable.Map.add var (symbol, []) map
| Expr (var, _expr) ->
Variable.Map.add var (symbol, [0]) map
| Exprs (vars, _expr) ->
let map, _ =
List.fold_left (fun (map, field) var ->
Variable.Map.add var (symbol, [field; 0]) map,
field + 1)
(map, 0) vars
in
map)
Variable.Map.empty accumulated_extracted_lets
in
let extracted =
List.map (fun (symbol, decl) ->
match decl with
| Expr (var, decl) ->
let expr =
rebuild_expr ~extracted_definitions ~copied_definitions
~substitute:true decl
in
if Variable.Set.mem var used_variables then
Initialisation
(symbol,
Tag.create_exn 0,
[expr])
else
Effect expr
| Exprs (_vars, decl) ->
let expr =
rebuild_expr ~extracted_definitions ~copied_definitions
~substitute:true decl
in
Initialisation (symbol, Tag.create_exn 0, [expr])
| Block (_var, tag, fields) ->
let fields =
List.map (fun var ->
rebuild_expr ~extracted_definitions ~copied_definitions
~substitute:true (Var var))
fields
in
Initialisation (symbol, tag, fields))
accumulated_extracted_lets
in
let terminator =
(* We don't need to substitute the variables in the terminator, we
suppose that we did for every other occurrence. Avoiding this
substitution allows this transformation to be idempotent. *)
rebuild_expr ~extracted_definitions ~copied_definitions
~substitute:false accumulated.terminator
in
List.rev extracted, terminator
let introduce_symbols expr =
let accumulated =
accumulate expr
~substitution:Variable.Map.empty
~copied_lets:[] ~extracted_lets:[]
in
let used_variables = Flambda.used_variables expr in
let extracted, terminator = rebuild used_variables accumulated in
extracted, terminator
let add_extracted introduced program =
List.fold_right (fun extracted program ->
match extracted with
| Initialisation (symbol, tag, def) ->
Flambda.Initialize_symbol (symbol, tag, def, program)
| Effect effect ->
Flambda.Effect (effect, program))
introduced program
let rec split_program (program : Flambda.program_body) : Flambda.program_body =
match program with
| End s -> End s
| Let_symbol (s, def, program) ->
Let_symbol (s, def, split_program program)
| Let_rec_symbol (defs, program) ->
Let_rec_symbol (defs, split_program program)
| Effect (expr, program) ->
let program = split_program program in
let introduced, expr = introduce_symbols expr in
add_extracted introduced (Flambda.Effect (expr, program))
| Initialize_symbol (symbol, tag, ((_::_::_) as fields), program) ->
(* CR-someday pchambart: currently the only initialize_symbol with more
than 1 field is the module block. This could evolve, in that case
this pattern should be handled properly. *)
Initialize_symbol (symbol, tag, fields, split_program program)
| Initialize_symbol (sym, tag, [], program) ->
Let_symbol (sym, Block (tag, []), split_program program)
| Initialize_symbol (symbol, tag, [field], program) ->
let program = split_program program in
let introduced, field = introduce_symbols field in
add_extracted introduced
(Flambda.Initialize_symbol (symbol, tag, [field], program))
let lift ~backend:_ (program : Flambda.program) =
{ program with
program_body = split_program program.program_body;
}