-
Notifications
You must be signed in to change notification settings - Fork 2
/
generate.ml
751 lines (707 loc) · 25.8 KB
/
generate.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
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
(**************************************************************************)
(* *)
(* ldrgen, a generator of random C programs *)
(* Copyright (C) 2017-2024, Gergö Barany <[email protected]> *)
(* *)
(* This program is free software: you can redistribute it and/or modify *)
(* it under the terms of the GNU General Public License as published by *)
(* the Free Software Foundation, either version 3 of the License, or *)
(* (at your option) any later version. *)
(* *)
(* This program is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU General Public License for more details. *)
(* *)
(* You should have received a copy of the GNU General Public License *)
(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)
(* *)
(**************************************************************************)
open Cil
open Cil_types
open Cil_datatype
module LvalSet = struct
include LvalStructEq.Set
let add_vi vi = add (Cil.var vi)
let singleton_vi vi = singleton (Cil.var vi)
end
let loc = Location.unknown
(* The function we're building. *)
let fundec = ref (Cil.emptyFunction "fn1")
(* "Parameter lvalues" are lvalues that may be used in expressions and are
based on function parameters. If the parameter is a pointer, the
corresponding lvalue in this set is dereferenced appropriately. *)
let param_lvals = ref (LvalSet.empty)
(* Global variable for modeling the sizes of arrays. *)
let array_size_var = ref None
let array_size () =
match !array_size_var with
| Some vi -> Cil.evar vi
| None ->
let vi = Cil.makeGlobalVar "N" Cil.uintType in
vi.vstorage <- Extern;
array_size_var := Some vi;
Cil.evar vi
(* Build an assignment of the value of [exp] to [lval]. The [exp] is
automatically cast to the [lval]'s type if needed. *)
let assign lval exp =
let typ = Cil.typeOfLval lval in
let exp = Cil.mkCast ~force:false exp ~newt:typ in
Set (lval, exp, loc)
let gen_type () =
let open Options in
let types = [intType; uintType; longType; ulongType;
charType; scharType; ucharType;
TInt (IShort, []); TInt (IUShort, [])]
in
let fp_types =
if Options.Float.get () then [floatType; doubleType] else [doubleType]
in
let types =
if FloatingPoint.get () then fp_types @ types else types
in
let types =
if LongLong.get () then [longLongType; ulongLongType] @ types else types
in
let types =
if FloatingPointOnly.get () then fp_types else types
in
let types =
(* There is a conflict between [-int-only] and [-float-only]; the former
wins. There's no very good way to warn about this. *)
if IntOnly.get () then begin
LongLong.set false; (* don't want [long long] to appear in modulo *)
[intType; uintType]
end else types
in
Utils.random_select types
let gen_compound_type ~base_type =
let rec gen_multidim_array ~dim ~typ =
if dim = 0 then
typ
else
let typ =
let length = Random.int (Options.MaxArrayLengthPerDim.get ()) + 1 in
TArray
(typ,
Some (Cil.integer ~loc length),
[])
in
gen_multidim_array ~dim:(dim - 1) ~typ
in
let types =
if Options.PointerArgs.get () then
[TPtr (base_type, [])]
else
[]
in
let types =
if Options.Arrays.get () then
let dim = Random.int (Options.MaxArrayDim.get ()) + 1 in
gen_multidim_array ~dim ~typ:base_type :: types
else
types
in
Utils.random_select types
let gen_local_var ?basename typ =
let name = match basename with Some n -> n | None -> "v" in
Cil.makeLocalVar !fundec name typ
let gen_formal_var ?basename typ =
(* Only actually generate a new formal if we haven't exhausted the maximum
number of arguments. *)
if List.length !fundec.sformals < Options.MaxArgs.get () then begin
let name = match basename with Some n -> n | None -> "p" in
let vi = Cil.makeFormalVar !fundec name typ in
if Cil.isPointerType typ then
let mem = (Mem (Cil.evar vi), NoOffset) in
param_lvals := LvalSet.add mem !param_lvals
else
param_lvals := LvalSet.add_vi vi !param_lvals;
vi
end else
Utils.random_select !fundec.sformals
let gen_var ?basename ?(local_only=false) typ =
let gen_funcs =
if local_only then [gen_local_var] else [gen_local_var; gen_formal_var]
in
let f = Utils.random_select gen_funcs in
let vi = f ?basename typ in
(* Use the [vreferenced] field to indicate whether this variable is ever
used. The [gen_lval_use] function below sets it. It is never reset. *)
vi.vreferenced <- false;
vi
let gen_vars n =
let rec loop i acc =
if i > 0 then
let vi = gen_var ~local_only:true (gen_type ()) in
loop (i - 1) (LvalSet.add_vi vi acc)
else
acc
in
loop n LvalSet.empty
let gen_return ?typ () =
let typ = match typ with Some t -> t | None -> gen_type () in
let var = gen_local_var ~basename:"result" typ in
let live = LvalSet.singleton_vi var in
(live, Cil.mkStmt ~valid_sid:true (Return (Some (Cil.evar var), loc)))
(* For integer or floating-point types, generate a random number (possibly
negative) of the appropriate type. For other types, just generate a zero
of the appropriate type. *)
let gen_const typ =
let sign = if Random.bool () then 1 else -1 in
match typ with
| TInt (ikind, _) ->
let i = Random.bits () in
(* Make small-ish constants more likely. *)
let i = if Random.bool () then i mod (1 lsl 16) else i in
Cil.kinteger ~loc ikind (sign * i)
| TFloat (fkind, _) ->
let max = Floating_point.max_single_precision_float in
(* Try to allow generation of small constants; without something like
this, almost all floating point constants would be in the 1e37-1e38
order of magnitude. *)
let bound = Utils.random_select [1e3; 1e10; max] in
Cil.kfloat ~loc fkind (float_of_int sign *. Random.float bound)
| _ ->
Cil.mkCast ~force:false (Cil.zero ~loc) ~newt:typ
let gen_array_elt (lhost, offset as lval) =
let rec gen_offset = function
| TInt _ | TFloat _ ->
NoOffset
| TArray (t, e, _) ->
Index
(Cil.(integer ~loc (Random.int (lenOfArray e))),
gen_offset t)
| _ ->
assert false
in
match Cil.typeOfLval lval with
| TArray _ as typ ->
assert (offset = NoOffset); (* By construction. See [new_lval]. *)
let offset = gen_offset typ in
lhost, offset
| typ ->
let err_msg =
Format.asprintf
"Expected lvalue of array type, got `%a'."
Cil_printer.pp_typ typ
in
raise (Invalid_argument err_msg)
let gen_lval_occurrence lval =
if Cil.(isArrayType (typeOfLval lval)) then
(* Arrays are generated as a single lvalue with array type. Here we need to
actually pick an element of the array [lval]. *)
gen_array_elt lval
else
lval
(* Initialize a local variable to a constant or a parameter. *)
let gen_local_init lval =
let gen_param_use typ =
if not (LvalSet.is_empty !param_lvals) then
let lval = Utils.random_select_from_set !param_lvals in
let lval = gen_lval_occurrence lval in
let e = Cil.new_exp ~loc (Lval lval) in
Cil.mkCast ~force:false e ~newt:typ
else
(* OK, fall back to constants. *)
gen_const typ
in
let generators = [gen_const; gen_param_use] in
let f = Utils.random_select generators in
let assign = assign lval (f (Cil.typeOfLval lval)) in
Cil.mkStmtOneInstr ~valid_sid:true assign
let new_lval () =
let typ = gen_type () in
let vi =
if List.length !fundec.sformals < Options.MaxArgs.get () &&
(Options.PointerArgs.get () || Options.Arrays.get ()) &&
Random.float 1.0 < 0.1
then
(* Make a parameter of pointer or array type. *)
let ptr_or_array_typ = gen_compound_type ~base_type:typ in
gen_formal_var ptr_or_array_typ
else
(* Make a plain variable. *)
let vi = gen_var typ in
vi.vreferenced <- true;
vi
in
if Cil.isPointerType vi.vtype then
(Mem (Cil.evar vi), NoOffset)
else
(Var vi, NoOffset)
let gen_lval_use ~num_live () =
(* Select a known local var or parameter of the function, or generate a
new variable. *)
let select_or_new vars =
if vars <> [] then begin
let vi = Utils.random_select vars in
vi.vreferenced <- true;
(Var vi, NoOffset)
end else
new_lval ()
in
let use_param () =
if LvalSet.is_empty !param_lvals then
new_lval ()
else
Utils.random_select_from_set !param_lvals
in
let use_local () = select_or_new !fundec.slocals in
(* Try to generate new variables if we don't have many live ones; pick
existing ones if enough are live. *)
let lval =
if Random.int (Options.MaxLive.get ()) > num_live then
new_lval ()
else
let f = Utils.random_select [use_param; use_local] in
f ()
in
let lval = gen_lval_occurrence lval in
(* We never want to generate assignments to the function's formal
parameters. This is easy: We simply never put them into the live set,
which is where variables to assign to are selected from.
[Utils.free_vars] only selects locals, not formals. *)
let exp = Cil.new_exp ~loc (Lval lval) in
let live = LvalSet.of_list (Utils.free_vars exp) in
(live, exp)
let gen_leaf_exp ~depth ~num_live () =
ignore depth;
let gen_const ~num_live () =
ignore num_live;
let typ = gen_type () in
(LvalSet.empty, gen_const typ)
in
(* Prefer variables over constants to discourage constant folding and
propagation, i.e., to avoid making the compiler's job too easy. *)
let f = Utils.random_select [gen_const; gen_lval_use; gen_lval_use] in
f ~num_live ()
(* Make sure that either both expressions are integers or both are
floating-point values (picking randomly, if originally one is integer and
the other floating). Return a pair of the two expressions with
appropriate casts. *)
let gen_common_type_exprs expr1 expr2 =
let is_int e = Cil.(isIntegralType (typeOf e)) in
let is_float e = Cil.(isFloatingType (typeOf e)) in
if is_int expr1 && is_int expr2 || is_float expr1 && is_float expr2 then
(expr1, expr2)
else
let typ = Utils.random_select [Cil.typeOf expr1; Cil.typeOf expr2] in
let cast_to_common_typ e = Cil.mkCast ~force:false e ~newt:typ in
(cast_to_common_typ expr1, cast_to_common_typ expr2)
let choose_binop typ =
let binops =
if Cil.isIntegralType typ then
(* Prefer arithmetic over bitwise operations; prefer other bitwise
operations over shifts. *)
let bitwise =
if Options.Bitwise.get () then
let shift = Utils.random_select [Shiftlt; Shiftrt] in
let bitwise = Utils.random_select [shift; BAnd; BXor; BOr] in
[bitwise]
else
[]
in
bitwise @ [PlusA; MinusA; Mult]
else
[PlusA; MinusA; Mult]
in
let binops =
if Options.DivMod.get () then
if Cil.isIntegralType typ then
[Div] @ (if Options.Mod.get () then [Mod] else []) @ binops
else
[Div] @ binops
else
binops
in
Utils.random_select binops
(* For some operations, it's best to patch the RHS operand to avoid some
common problems. *)
let fixup_binop_rexp op lexp rexp =
match op with
| Shiftlt | Shiftrt ->
(* Generate a bit mask expression to transform this operand into the
legal range. *)
let lhs_bitsize = Cil.bitsSizeOf (Cil.typeOf lexp) in
let mask = Cil.integer ~loc (lhs_bitsize - 1) in
Cil.mkBinOp ~loc BAnd rexp mask
| Div | Mod ->
let rexp = Cil.constFold true rexp in
begin match Cil.isInteger rexp with
| Some n when not (Integer.equal Integer.zero n) ->
(* Division by a nonzero integer. OK, keep it. *)
rexp
| _ ->
(* Something other than a nonzero integer. Add a small random
integer, just to be on the safe side; it's unlikely that this
produces a constant zero. *)
let random_num = Cil.integer ~loc (Random.int 1024 + 1) in
Cil.mkBinOp ~loc PlusA rexp random_num
end
| _ -> rexp
let gen_binop_for (llive, lexp) (rlive, rexp) =
let lexp, rexp = gen_common_type_exprs lexp rexp in
let op = choose_binop (Cil.typeOf lexp) in
let rexp' = fixup_binop_rexp op lexp rexp in
let live = LvalSet.union llive rlive in
(live, Cil.mkBinOp ~loc op lexp rexp')
let rec gen_exp ~depth ~num_live () =
let generators =
if depth <= Options.ExprDepth.get () then
(* Prefer binary operations over all other kinds. *)
[gen_leaf_exp; gen_binop; gen_binop; gen_unop]
else
[gen_leaf_exp]
in
let f = Utils.random_select generators in
let live, exp = f ~depth ~num_live () in
(* We sometimes generate expressions that constant fold to floating point
infinities. These would get printed as "inff" and cause compiler
errors. In such cases, try again. *)
if Utils.is_infinity exp then
gen_exp ~depth ~num_live ()
else
live, exp
and gen_binop ~depth ~num_live () =
let depth = depth + 1 in
let llive, lexp = gen_exp ~depth ~num_live () in
let rlive, rexp = gen_exp ~depth ~num_live () in
gen_binop_for (llive, lexp) (rlive, rexp)
and gen_unop ~depth ~num_live () =
let depth = depth + 1 in
let live, exp = gen_exp ~depth ~num_live () in
let typ = Cil.typeOf exp in
let unops =
if Cil.isIntegralType typ && Options.Bitwise.get () then
[Neg; BNot; LNot]
else
[Neg]
in
let op = Utils.random_select unops in
(live, Cil.new_exp ~loc (UnOp (op, exp, typ)))
let gen_exp ~num_live ?typ () =
let live, exp = gen_exp ~depth:1 ~num_live () in
match typ with
| Some typ -> (live, Cil.mkCast ~force:false exp ~newt:typ)
| None -> (live, exp)
let gen_cond ~num_live () =
(* Make sure a condition always uses at least one variable. *)
let rec make_nonconst_exprs () =
let llive, lexp = gen_exp ~num_live () in
let rlive, rexp = gen_exp ~num_live () in
let live = LvalSet.union llive rlive in
if LvalSet.is_empty live then
make_nonconst_exprs ()
else
let lexp, rexp = gen_common_type_exprs lexp rexp in
(live, lexp, rexp)
in
let live, lexp, rexp = make_nonconst_exprs () in
let comparisons = [Lt; Gt; Le; Ge; Eq; Ne] in
let cmp = Utils.random_select comparisons in
(live, Cil.mkBinOp ~loc cmp lexp rexp)
let gen_assignment_to lval ~depth ~live () =
ignore depth;
let num_live = LvalSet.cardinal live in
let typ = Cil.typeOfLval lval in
let (new_live_vars, exp) = gen_exp ~num_live ~typ () in
let live =
LvalSet.union (LvalSet.remove lval live) new_live_vars
in
let assign = assign lval exp in
let stmt = Cil.mkStmtOneInstr ~valid_sid:true assign in
(live, stmt)
let gen_assignment ~depth ~live () =
let vi = Utils.random_select_from_set live in
gen_assignment_to vi ~depth ~live ()
let rec gen_stmt ~depth ~live () =
let generators =
if depth < Options.StmtDepth.get () then
let gens = [gen_assignment; gen_if_stmt] in
if Options.Loops.get () then [gen_loop] @ gens else gens
else
[gen_assignment]
in
let f = Utils.random_select generators in
f ~depth ~live ()
and gen_if_stmt ~depth ~live () =
let depth = depth + 1 in
let (true_live, true_stmts) = gen_stmts ~depth ~live ~tail:[] () in
let (false_live, false_stmts) = gen_stmts ~depth ~live ~tail:[] () in
let body_live = LvalSet.union true_live false_live in
let num_live = LvalSet.cardinal body_live in
let (cond_new_live, cond) = gen_cond ~num_live () in
let live = LvalSet.union body_live cond_new_live in
let if_stmt =
Cil.mkStmt ~valid_sid:true
(If (cond, Cil.mkBlock true_stmts, Cil.mkBlock false_stmts, loc))
in
(live, if_stmt)
and gen_loop ~depth ~live () =
let while_loop =
if Options.WhileLoops.get () then [gen_while_loop] else []
in
(* We can only generate a for loop if we can generate a corresponding
array argument. *)
let generators =
if Options.ForLoops.get () &&
List.length !fundec.sformals < Options.MaxArgs.get () then
[gen_for_loop] @ while_loop
else
while_loop
in
let generators =
(* We may have found a combination of command line arguments and program
state that doesn't allow us to generate a loop after all. In that
case, try some other statement. *)
if generators = [] then [gen_stmt] else generators
in
let generator = Utils.random_select generators in
generator ~depth ~live ()
and gen_for_loop ~depth ~live () =
ignore depth;
(* loop counter *)
let i = gen_local_var ~basename:"i" Cil.uintType in
(* array parameter *)
let elem_typ = gen_type () in
let ptr_typ = TPtr (elem_typ, []) in
let a = gen_formal_var ~basename:"arr" ptr_typ in
(* Hack: [gen_formal_var] put [a] into the set of usable formals, but we
only ever want to use this array in the context of this loop, so remove
it from there. *)
param_lvals := LvalSet.remove (Var a, NoOffset) !param_lvals;
(* The loop body will be of the form
x = arr[i];
r = r op exp(x);
for some random expression [exp] and a random binary operator [op].
That is, we map [exp] over the array, then reduce (fold) with [op], and
put the result in [r]. *)
(* Result of the loop computation. *)
let r = Utils.random_select_from_set live in
let live' = LvalSet.remove r live in
let num_live = LvalSet.cardinal live' in
(* The last statement of the block is an assignment to this result
variable, combining its old value with some new, nontrivial expression
that will be made to use a reference to [a[i]]. *)
let rec gen_nonconst_rexp () =
let (rlive, rexp) = gen_exp ~num_live ~typ:(Cil.typeOfLval r) () in
if Utils.free_vars rexp = [] then
gen_nonconst_rexp ()
else
(rlive, rexp)
in
let (rlive, rexp) = gen_nonconst_rexp () in
let (llive, lexp) = (LvalSet.empty, Cil.new_exp ~loc (Lval r)) in
let assign_live, assign_exp = gen_binop_for (llive, lexp) (rlive, rexp) in
let assign_stmt =
Cil.mkStmtOneInstr ~valid_sid:true (assign r assign_exp)
in
let use_var = Utils.random_select (Utils.free_vars rexp) in
let use_stmt =
let array_elem =
Cil.mkMem
~addr:(Cil.mkBinOp ~loc PlusPI (Cil.evar a) (Cil.evar i))
~off:NoOffset
in
let array_elem_exp = Cil.new_exp ~loc (Lval array_elem) in
Cil.mkStmtOneInstr ~valid_sid:true (assign use_var array_elem_exp)
in
(* The body consists of these two assignments. *)
let use_live = LvalSet.remove r assign_live in
let (body_live, body) = (use_live, [use_stmt; assign_stmt]) in
(* Finally, make a loop for i from 0 to N over this body. *)
let for_stmt_list =
Cil.mkForIncr
~iter:i
~first:(Cil.mkCast ~force:true (Cil.zero ~loc) ~newt:Cil.uintType)
~stopat:(array_size ())
~incr:(Cil.one ~loc)
~body ()
in
let for_stmt =
Cil.mkStmt ~valid_sid:true (Block (Cil.mkBlock for_stmt_list))
in
(* Now, the variables live before the loop are the variables live into the
body, as well as [r], since it must be initialized. And, of course,
whatever is live after the body. *)
let live = LvalSet.union (LvalSet.add r body_live) live' in
(live, for_stmt)
and gen_while_loop ~depth ~live () =
let depth = depth + 1 in
(* Save the variables that are live after the loop. They may be defined in
the loop body, but must nevertheless still be live before the loop. *)
let live_out = live in
let num_live = LvalSet.cardinal live in
let (cond_new_live, cond) = gen_cond ~num_live () in
(* Generate a set of newly used variables in the loop. Some of these will
be loop-carried dependences. *)
let num_live = num_live + LvalSet.cardinal cond_new_live in
let n =
if num_live >= Options.MaxLive.get () then
Random.int 2 + 1
else
Random.int ((Options.MaxLive.get () + 1 - num_live) / 2) + 1
in
let body_new_live = gen_vars n in
let body_live_out =
LvalSet.union live_out
(LvalSet.union cond_new_live body_new_live)
in
let live = body_live_out in
(* Try to generate an assignment to one of the variables in the condition.
This is meant to simulate some kind of progress towards termination of
the loop. *)
let (live, tail) =
let cond_vars = Utils.free_vars cond in
if cond_vars <> [] then
let vi = Utils.random_select cond_vars in
let (live, stmt) = gen_assignment_to vi ~depth ~live () in
(live, [stmt])
else
(live, [])
in
let (body_live_in, stmts) = gen_stmts ~depth ~live ~tail () in
(* Now we must ensure that every variable in [body_new_live] does actually
have a use before a possible redefinition in the loop. If there is a
redefinition but no use before it, the variable is not live into the
body. Otherwise, the variable is not used at all. Both conditions are
checked here. *)
let referenced_vi = function
| Var vi, _ -> vi.vreferenced
| _ -> false
in
let need_use =
LvalSet.filter
(fun b -> not (LvalSet.mem b body_live_in) || not (referenced_vi b))
body_new_live
in
let (body_live_in', stmts') =
if LvalSet.is_empty need_use then
(body_live_in, stmts)
else begin
(* Make an assignment using all the variables in [need_use]. *)
let (newly_live, exp) =
LvalSet.fold
(fun lval acc ->
let exp = Cil.new_exp ~loc (Lval lval) in
gen_binop_for (LvalSet.singleton lval, exp) acc)
need_use
(LvalSet.empty, gen_const (gen_type ()))
in
assert (LvalSet.equal newly_live need_use);
(* For the LHS choose a currently live variable. If there is none,
things get a bit more annoying. *)
let (stmts, lval) =
if LvalSet.is_empty body_live_in then begin
(* The only way everything could have been killed is through an
assignment to some variable. Pick that variable and remove the
assignment. *)
match stmts with
| { skind = Instr (Set (lval, _exp, _)); _ } :: stmts ->
(stmts, lval)
| _ -> assert false
end else
(stmts, Utils.random_select_from_set body_live_in)
in
let body_live_in' =
LvalSet.union (LvalSet.remove lval body_live_in) newly_live
in
let assign = assign lval exp in
let assign_stmt = Cil.mkStmtOneInstr ~valid_sid:true assign in
(body_live_in', assign_stmt :: stmts)
end
in
let loop = Cil.mkLoop ~sattr:[] ~guard:cond ~body:stmts' () in
let live =
LvalSet.union
(LvalSet.union cond_new_live body_live_in')
live_out
in
(live, Cil.mkStmt ~valid_sid:true (Block (Cil.mkBlock loop)))
and gen_stmts ?n ~depth ~live ~tail () =
let rec rec_gen_stmts n ~live ~tail =
(* Stop when we have generated [n] statements or when we have no more
live variables to define. *)
if n < 1 || LvalSet.is_empty live then
(live, tail)
else
let (live, stmt) = gen_stmt ~depth ~live () in
rec_gen_stmts (n-1) ~live ~tail:(stmt :: tail)
in
let n =
match n with
| Some n -> n
| None -> Options.BlockLength.get () - List.length tail
in
rec_gen_stmts n ~live ~tail
let gen_function () =
let typ = gen_type () in
Cil.setReturnType !fundec typ;
let (live, return) = gen_return ~typ () in
let (live', stmts) = gen_stmts ~depth:1 ~live ~tail:[return] () in
!fundec.sbody.bstmts <- stmts;
(* Initialize all live non-formal local variables. *)
let glob_or_formal_vi = function
| Var vi, _ -> vi.vglob || vi.vformal
| _ -> false
in
LvalSet.iter
(fun vi ->
if not (glob_or_formal_vi vi) then
let init = gen_local_init vi in
!fundec.sbody.bstmts <- init :: !fundec.sbody.bstmts)
live';
(* Register the function definition as global. *)
!fundec.svar.vdefined <- true;
Globals.Functions.replace_by_definition !fundec.sspec !fundec loc;
(* Compute the control-flow graph of the function to ensure a well-formed AST.
This is typically needed when several analyses are chained in Frama-C. *)
Cfg.prepareCFG !fundec;
Cfg.cfgFun !fundec;
let f = GFun (!fundec, loc) in
f
let gen_random_function ast =
let f = gen_function () in
(* Set the function's list of formal arguments to its list of formal
arguments. This is silly, but it ensures that we generate a proper
prototype: If the list of formals is empty, the argument list will be
(void) instead of empty (). *)
begin match f with
| GFun (fdec, _) -> Cil.setFormals fdec fdec.sformals
| _ -> ()
end;
let global_array_size_var =
match !array_size_var with
| Some vi ->
Globals.Vars.add vi { init = None };
[GVarDecl (vi, loc)]
| None -> []
in
ast.globals <- global_array_size_var @ f :: ast.globals
let gen_header ast =
let header = Format.asprintf
"// Generated by ldrgen\n\
// Seed: %d\n\
// Command line arguments:%a"
(Options.Seed.get ()) Utils.print_command_line_args ()
in
ast.globals <- GText header :: ast.globals
let run () =
if Options.Run.get () then begin
Options.initialize ();
let prj = Project.create_by_copy ~last:true "ldrgen" in
Project.on
prj
(fun () ->
let ast = Ast.get () in
gen_random_function ast;
gen_header ast;
if Options.CheckAst.get () then
Filecheck.check_ast ~is_normalized:true ~ast "ldrgen";
Ast.mark_as_grown ();
Format.printf "%a" Printer.pp_file ast;
let selection = State_selection.with_dependencies Options.Run.self in
Project.clear ~selection ~project:prj ())
()
end