-
Notifications
You must be signed in to change notification settings - Fork 5
/
instr.ml
542 lines (487 loc) · 13.3 KB
/
instr.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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
module Variable = String
module Label = String
module Address : sig
type t = private int
val compare : t -> t -> int
val fresh : unit -> t
end = struct
type t = int
let compare (x : int) y = Pervasives.compare x y
let counter = ref (-1)
let fresh () = incr counter; !counter
end
type variable = Variable.t
type label = Label.t
module VarSet = Set.Make(Variable)
module LabelSet = Set.Make(Label)
module Identifier = struct
type t = string
let compare = String.compare
end
type identifier = Identifier.t
module Pc = struct
type t = int
let compare (x : int) y = Pervasives.compare x y
end
type pc = Pc.t
type address = Address.t
type 'a position = {
func : label;
version : label;
pos : 'a;
}
type instructions = instruction array
and instruction =
| Decl_var of variable * expression
| Decl_array of variable * array_def
| Call of label * variable * expression * (argument list)
| Return of expression
| Drop of variable
| Assign of variable * expression
| Array_assign of variable * expression * expression
| Read of variable
| Branch of expression * label * label
| Label of label_type
| Goto of label
| Print of expression
| Assert of expression
| Stop of expression
| Guard_hint of expression list
| Assume of {
label : label;
guards : expression list;
target : label position;
varmap : varmap;
extra_frames : extra_frame list; }
| Comment of string
and label_type =
| MergeLabel of label
| BranchLabel of label
| BailoutLabel of label
and array_def =
| Length of expression
| List of expression list
and varmap = (variable * expression) list
and extra_frame = {
varmap : varmap;
cont_pos : label position;
cont_res : variable; }
and argument = expression
and expression =
| Simple of simple_expression
| Unop of unop * simple_expression
| Binop of binop * simple_expression * simple_expression
| Array_index of variable * simple_expression
| Array_length of simple_expression
and simple_expression =
| Constant of value
| Var of variable
and value =
| Nil
| Bool of bool
| Int of int
| String of string
| Fun_ref of string
| Array of address
and unop =
| Neg
| Not
and binop =
| Eq
| Neq
| Lt
| Lte
| Gt
| Gte
| Plus
| Sub
| Mult
| Div
| Mod
| And
| Or
let negate = function
| Simple (Var x) -> Some (Unop (Not, (Var x)))
| Simple (Constant (Bool false)) -> Some (Simple (Constant (Bool true)))
| Simple (Constant (Bool true)) -> Some (Simple (Constant (Bool false)))
| Unop (Not, se) -> Some (Simple se)
| Binop ((Neq|Eq|Gte|Gt|Lte|Lt) as op, e1, e2) ->
let nop = begin match[@warning "-4"] op with
| Eq -> Neq
| Neq -> Eq
| Lt -> Gte
| Lte -> Gt
| Gt -> Lte
| Gte -> Lt
| _ -> assert(false)
end in
Some (Binop (nop, e1, e2))
| Simple (Constant (Nil| Int _|Fun_ref _|Array _|String _))
| Binop ((Plus|Sub|Mult|Div|Mod), _, _)
| Binop ((And|Or), _, _) (* TODO *)
| Unop (Neg, _)
| Array_index _
| Array_length _ -> None
type scope_annotation =
| ExactScope of VarSet.t
| AtLeastScope of VarSet.t
type inferred_scope =
| DeadScope
| Scope of VarSet.t
type annotations = scope_annotation option array
type formal_parameter = Param of variable
type version = {
label : label;
instrs : instructions;
annotations : annotations option;
}
type afunction = {
name : identifier;
formals : formal_parameter list;
body : version list;
}
type program = {
main : afunction;
functions : afunction list;
}
type heap_value =
| Undefined
| Value of value
| Block of value array
type binding =
| Val of value
| Ref of address
exception Unbound_label of label_type
exception Unbound_bailout_label of label
let resolve_by pred code =
let rec loop i =
if i >= Array.length code then raise Not_found
else match pred code.(i) with
| Some o -> i+o
| None -> loop (i + 1)
in loop 0
let resolve code l =
let pred = function[@warning "-4"]
| Label label when label = l -> Some 0
| _ -> None
in
try resolve_by pred code
with Not_found -> raise (Unbound_label l)
let resolve_bailout code l =
let pred = function[@warning "-4"]
| Assume {label} when label = l -> Some 0
| Call (label, _,_,_) when label = l -> Some 1
| Label (BailoutLabel label) when label = l -> Some 1
| _ -> None
in
try resolve_by pred code
with Not_found -> raise (Unbound_bailout_label l)
let resolver_by indexer code =
let tbl = Hashtbl.create 42 in
let add pc instr = match indexer instr with
| None -> ()
| Some (idx, off) ->
assert (not (Hashtbl.mem tbl idx));
Hashtbl.add tbl idx (pc+off)
in
Array.iteri add code;
fun index -> Hashtbl.find tbl index
let resolver code =
let indexer = function[@warning "-4"]
| Label l -> Some (l, 0)
| _ -> None
in
let resolver = resolver_by indexer code in
fun l ->
try resolver l
with Not_found -> raise (Unbound_label l)
let resolver_bailout code =
let indexer = function[@warning "-4"]
| Assume {label} -> Some (label, 0)
| Call (label, _,_,_) -> Some (label, 1)
| Label (BailoutLabel label) -> Some (label, 1)
| _ -> None
in
let resolver = resolver_by indexer code in
fun l ->
try resolver l
with Not_found -> raise (Unbound_bailout_label l)
let simple_expr_vars = function
| Var x -> VarSet.singleton x
| Constant _ -> VarSet.empty
let expr_vars = function
| Simple e -> simple_expr_vars e
| Unop (_op, o) ->
simple_expr_vars o
| Binop (_op, l, r) ->
VarSet.union (simple_expr_vars l) (simple_expr_vars r)
| Array_index (o, i) ->
VarSet.union (VarSet.singleton o) (simple_expr_vars i)
| Array_length e ->
simple_expr_vars e
let arg_vars = expr_vars
let list_vars ls =
let vs = List.map expr_vars ls in
List.fold_left VarSet.union VarSet.empty vs
let declared_vars = function
| Assign _ | Read _ -> VarSet.empty
| Call (_, x, _, _)
| Decl_var (x, _) -> VarSet.singleton x
| Decl_array (x, _) -> VarSet.singleton x
| ( Drop _
| Array_assign _
| Return _
| Branch _
| Label _
| Guard_hint _
| Goto _
| Print _
| Assert _
| Assume _
| Comment _
| Stop _) -> VarSet.empty
let defined_vars = function
| Assign (x ,_)
| Read x -> VarSet.singleton x
| ( Call _
| Decl_var _
| Decl_array _
| Return _
| Drop _
| Branch _
| Label _
| Goto _
| Guard_hint _
| Comment _
| Print _
| Assert _
| Assume _
| Array_assign _ (* The array has to be defined already. *)
| Stop _
) as e -> declared_vars e
let dropped_vars = function
| Drop x -> VarSet.singleton x
| Return _
| Call _
| Decl_var _
| Decl_array _
| Assign _
| Array_assign _
| Read _
| Branch _
| Guard_hint _
| Label _
| Goto _
| Comment _
| Print _
| Assert _
| Assume _
| Stop _ -> VarSet.empty
(* Which variables need to be defined
* Producer: defined_vars *)
let used_vars = function
| Assign (_, e) -> expr_vars e
| Read x -> VarSet.empty
| Call (_, _x, f, es) ->
let v = expr_vars f in
let vs = List.map arg_vars es in
List.fold_left VarSet.union v vs
| Stop e
| Return e -> expr_vars e
| Decl_var (_x, e) -> expr_vars e
| Decl_array (_, Length e) -> expr_vars e
| Decl_array (_, List es) ->
List.map expr_vars es |> List.fold_left VarSet.union VarSet.empty
(* the assignee is only required to be in scope, but not used! *)
| Branch (e, _, _)
| Print e
| Assert e
-> expr_vars e
| Array_assign (x, i, e) ->
VarSet.singleton x
|> VarSet.union (expr_vars i)
|> VarSet.union (expr_vars e)
| Drop _
| Label _
| Goto _
| Comment _
| Guard_hint _
-> VarSet.empty
| Assume {guards; varmap; extra_frames} ->
let fold_cond used guard = VarSet.union used (expr_vars guard) in
let from_cond = List.fold_left fold_cond VarSet.empty guards in
let map_vars map = list_vars (List.map (fun (_, e) -> e) map) in
let fold_map used {varmap} =
VarSet.union used (map_vars varmap) in
let from_map = List.fold_left fold_map (map_vars varmap) extra_frames in
VarSet.union from_cond from_map
(* Which variables need to be in scope
* Producer: declared_vars *)
let required_vars = function
| (Assign (x, _)
| Read x
| Drop x
) as e -> VarSet.add x (used_vars e)
| ( Call _
| Stop _
| Return _
| Decl_var _
| Decl_array _
| Array_assign _
| Branch _
| Guard_hint _
| Label _
| Goto _
| Comment _
| Print _
| Assert _
| Assume _
) as e -> used_vars e
let changed_vars = function
| Call (_, x, _, _)
| Decl_var (x, _) -> VarSet.singleton x
| Assign (x ,_)
| Array_assign (x ,_ , _)
| Drop x
| Decl_array (x, _)
| Read x -> VarSet.singleton x
| Return _
| Branch _
| Guard_hint _
| Label _
| Goto _
| Comment _
| Print _
| Assert _
| Assume _
| Stop _ -> VarSet.empty
exception FunctionDoesNotExist of identifier
let lookup_fun (prog : program) (f : identifier) : afunction =
if f = "main" then prog.main else
try List.find (fun {name} -> name = f) prog.functions with
| Not_found -> raise (FunctionDoesNotExist f)
let replace_fun ({main; functions} as prog : program) (func : afunction) : program =
let rec replace_fun' rest =
match rest with
| [] -> []
| func' :: rest' ->
if func'.name = func.name then func :: rest'
else func' :: replace_fun' rest'
in
if (func.name = main.name) then {prog with main = func}
else {prog with functions = replace_fun' functions}
let get_version (func : afunction) (v : label) : version =
List.find (fun {label} -> label = v) func.body
let active_version (func : afunction) : version =
(List.hd func.body)
let replace_active_version (func : afunction) (repl : version) : afunction =
{ func with
body = repl :: (List.tl func.body); }
let checkpoint_prefix = "cp_"
let checkpoint_label pc =
checkpoint_prefix ^ (string_of_int pc)
let pcs (instrs : instructions) : pc array =
Array.mapi (fun pc _ -> pc) instrs
let is_checkpoint = function[@warning "-4"]
| Assume _ -> true
| _ -> false
let checkpoints (instrs : instructions) =
let pcs = Array.to_list (pcs instrs) in
List.filter (fun pc -> is_checkpoint instrs.(pc)) pcs
let has_checkpoint =
Array.exists is_checkpoint
let independent instr exp =
VarSet.is_empty (VarSet.inter (changed_vars instr) (expr_vars exp))
class map = object (m)
method variable_use x = x
method variable_assign x = x
method binder x = x
method varmap_binder x = x
method value v = v
method simple_expression = function
| Constant v ->
Constant (m#value v)
| Var x ->
Var (m#variable_use x)
method expression = function
| Simple e ->
Simple (m#simple_expression e)
| Unop (op, o) ->
Unop (op, m#simple_expression o)
| Binop (op, l, r) ->
Binop (op, m#simple_expression l, m#simple_expression r)
| Array_index (o, i) ->
Array_index (m#variable_use o, m#simple_expression i)
| Array_length e ->
Array_length (m#simple_expression e)
method goto_label l = l
method branch_label l = l
method bailout_label l = l
method instruction = function
| Decl_var (x, e) ->
Decl_var (m#binder x, m#expression e)
| Decl_array (x, def) ->
Decl_array (m#binder x, m#array_def def)
| Call (l, x, e, args) ->
Call (m#bailout_label l, m#binder x, m#expression e, List.map m#argument args)
| Return e ->
Return (m#expression e)
| Drop x ->
Drop (m#variable_assign x)
| Assign (x, e) ->
Assign (m#variable_assign x, m#expression e)
| Array_assign (x, e, idx) ->
Array_assign (m#variable_assign x, m#expression e, m#expression idx)
| Read x ->
Read (m#variable_assign x)
| Branch (e, l1, l2) ->
Branch (m#expression e, m#branch_label l1, m#branch_label l2)
| Label (MergeLabel l) ->
Label (MergeLabel (m#goto_label l))
| Label (BranchLabel l) ->
Label (BranchLabel (m#branch_label l))
| Label (BailoutLabel l) ->
Label (BailoutLabel (m#bailout_label l))
| Goto l ->
Goto (m#goto_label l)
| Print e ->
Print (m#expression e)
| Assert e ->
Assert (m#expression e)
| Guard_hint es ->
Guard_hint (List.map m#expression es)
| Stop e ->
Stop (m#expression e)
| Assume {label; guards; target; varmap; extra_frames} ->
Assume {
label = m#bailout_label label;
guards = List.map m#expression guards;
target = m#osr_target target;
varmap = m#osr_varmap varmap;
extra_frames = List.map m#extra_frame extra_frames;
}
| Comment s ->
Comment s
method argument e = m#expression e
method array_def = function
| Length e ->
Length (m#expression e)
| List es ->
List (List.map m#expression es)
method osr_func_label l = l
method osr_version_label l = l
method osr_target_label l = l
method osr_target {func; version; pos} = {
func = m#osr_func_label func;
version = m#osr_version_label version;
pos = m#osr_target_label pos }
method frame_cont_res res = res
method extra_frame {varmap; cont_pos; cont_res} = {
varmap = m#osr_varmap varmap;
cont_pos = m#osr_target cont_pos;
cont_res = m#frame_cont_res cont_res;
}
method osr_varmap = List.map m#osr_varmap_entry
method osr_varmap_entry = function
| x, e -> m#varmap_binder x, m#expression e
end