aboutsummaryrefslogtreecommitdiffstats
path: root/cil/src/ext/simplify.ml
blob: 776d491633f8288f3bd3b061adbf2720cf28d7d6 (plain)
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
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
(*
 *
 * Copyright (c) 2001-2002, 
 *  George C. Necula    <necula@cs.berkeley.edu>
 *  Scott McPeak        <smcpeak@cs.berkeley.edu>
 *  Wes Weimer          <weimer@cs.berkeley.edu>
 *  Sumit Gulwani       <gulwani@cs.berkeley.edu>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. The names of the contributors may not be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *)

(* This module simplifies the expressions in a program in the following ways:
 
1. All expressions are either 

 basic::=
    Const _ 
    Addrof(Var v, NoOffset)
    StartOf(Var v, NoOffset)
    Lval(Var v, off), where v is a variable whose address is not taken
                      and off contains only "basic"

 exp::=
    basic
    Lval(Mem basic, NoOffset)
    BinOp(bop, basic, basic)
    UnOp(uop, basic)
    CastE(t, basic)
   
 lval ::= 
    Mem basic, NoOffset
    Var v, off, where v is a variable whose address is not taken and off
                contains only "basic"

 - all sizeof and alignof are turned into constants
 - accesses to variables whose address is taken is turned into "Mem" accesses
 - same for accesses to arrays
 - all field and index computations are turned into address arithmetic, 
   including bitfields.

*)


open Pretty
open Cil
module E = Errormsg
module H = Hashtbl

type taExp = exp (* Three address expression *)
type bExp = exp  (* Basic expression *)

let debug = true

(* Whether to split structs *)
let splitStructs = ref true

let onlyVariableBasics = ref false
let noStringConstantsBasics = ref false

exception BitfieldAccess

(* Turn an expression into a three address expression (and queue some 
 * instructions in the process) *)
let rec makeThreeAddress 
    (setTemp: taExp -> bExp) (* Given an expression save it into a temp and 
                              * return that temp *)
    (e: exp) : taExp = 
  match e with 
    SizeOf _ | SizeOfE _ | AlignOf _ |  AlignOfE _ | SizeOfStr _ -> 
      constFold true e
  | Const _ -> e
  | AddrOf (Var _, NoOffset) -> e
  | Lval lv -> Lval (simplifyLval setTemp lv)
  | BinOp(bo, e1, e2, tres) -> 
      BinOp(bo, makeBasic setTemp e1, makeBasic setTemp e2, tres)
  | UnOp(uo, e1, tres) -> 
      UnOp(uo, makeBasic setTemp e1, tres)
  | CastE(t, e) -> 
      CastE(t, makeBasic setTemp e)
  | AddrOf lv -> begin
      match simplifyLval setTemp lv with 
        Mem a, NoOffset -> a
      | _ -> (* This is impossible, because we are taking the address 
          * of v and simplifyLval should turn it into a Mem, except if the 
          * sizeof has failed.  *)
          E.s (bug "Simplify: makeThreeAddress for AddrOf(LV=%a, LVT=%a)"
              d_lval lv d_type (typeOfLval lv))
  end
  | StartOf lv -> 
      makeThreeAddress setTemp (AddrOf (addOffsetLval (Index(zero, NoOffset))
                                          lv))

(* Make a basic expression *)      
and makeBasic (setTemp: taExp -> bExp) (e: exp) : bExp = 
  let dump = false (* !currentLoc.line = 395 *) in
  if dump then
    ignore (E.log "makeBasic %a\n" d_plainexp e);
  (* Make it a three address expression first *)
  let e' = makeThreeAddress setTemp e in
  if dump then 
    ignore (E.log "   e'= %a\n" d_plainexp e);
  (* See if it is a basic one *)
  match e' with 
  | Lval (Var _, _) -> e'
  | Const _ | AddrOf (Var _, NoOffset) | StartOf (Var _, NoOffset) ->
      if !onlyVariableBasics then setTemp e' else e'
  | SizeOf _ | SizeOfE _ | AlignOf _ |  AlignOfE _ | SizeOfStr _ -> 
      E.s (bug "Simplify: makeBasic found SizeOf: %a" d_exp e')

   (* We cannot make a function to be Basic, unless it actually is a variable 
    * already. If this is a function pointer the best we can do is to make 
    * the address of the function basic *)
  | Lval (Mem a, NoOffset) when isFunctionType (typeOf e') -> 
      if dump then 
        ignore (E.log "  a function type\n");
      let a' = makeBasic setTemp a in
      Lval (Mem a', NoOffset)

  | _ -> setTemp e' (* Put it into a temporary otherwise *)


and simplifyLval 
    (setTemp: taExp -> bExp) 
    (lv: lval) : lval = 
  (* Add, watching for a zero *)
  let add (e1: exp) (e2: exp) = 
    if isZero e2 then e1 else BinOp(PlusA, e1, e2, !upointType) 
  in
  (* Convert an offset to an integer, and possibly a residual bitfield offset*)
  let rec offsetToInt 
      (t: typ) (* The type of the host *)
      (off: offset) : exp * offset = 
    match off with 
      NoOffset -> zero, NoOffset
    | Field(fi, off') -> begin
        let start = 
          try 
            let start, _ = bitsOffset t (Field(fi, NoOffset)) in
            start
          with SizeOfError (whystr, t') -> 
            E.s (E.bug "%a: Cannot compute sizeof: %s: %a"
                   d_loc !currentLoc whystr d_type t')
        in
        if start land 7 <> 0 then begin
          (* We have a bitfield *)
          assert (off' = NoOffset);
          zero, Field(fi, off')
        end else begin
          let next, restoff = offsetToInt fi.ftype off' in
          add (integer (start / 8)) next,  restoff
        end
    end
    | Index(ei, off') -> begin
        let telem = match unrollType t with 
          TArray(telem, _, _) -> telem
        | _ -> E.s (bug "Simplify: simplifyLval: index on a non-array")
        in
        let next, restoff = offsetToInt telem off' in
        add 
          (BinOp(Mult, ei, SizeOf telem, !upointType)) 
          next,
        restoff
    end
  in
  let tres = TPtr(typeOfLval lv, []) in
  match lv with 
    Mem a, off -> 
      let offidx, restoff = offsetToInt (typeOfLval (Mem a, NoOffset)) off in
      let a' = 
        if offidx <> zero then 
          add (mkCast a !upointType) offidx
        else
          a
      in
      let a' = makeBasic setTemp a' in
      Mem (mkCast a' tres), restoff

  | Var v, off when v.vaddrof -> (* We are taking this variable's address *)
      let offidx, restoff = offsetToInt v.vtype off in
      (* We cannot call makeBasic recursively here, so we must do it 
       * ourselves *)
      let a = mkAddrOrStartOf (Var v, NoOffset) in
      let a' = 
        if offidx = zero then a else 
        add (mkCast a !upointType) (makeBasic setTemp offidx) 
      in
      let a' = setTemp a' in
      Mem (mkCast a' tres), restoff

  | Var v, off -> 
      (Var v, simplifyOffset setTemp off)


(* Simplify an offset and make sure it has only three address expressions in 
 * indices *)
and simplifyOffset (setTemp: taExp -> bExp) = function
    NoOffset -> NoOffset
  | Field(fi, off) -> Field(fi, simplifyOffset setTemp off)
  | Index(ei, off) -> 
      let ei' = makeBasic setTemp ei in
      Index(ei', simplifyOffset setTemp off)




(** This is a visitor that will turn all expressions into three address code *)
class threeAddressVisitor (fi: fundec) = object (self)
  inherit nopCilVisitor

  method private makeTemp (e1: exp) : exp = 
    let t = makeTempVar fi (typeOf e1) in
    (* Add this instruction before the current statement *)
    self#queueInstr [Set(var t, e1, !currentLoc)];
    Lval(var t)

      (* We'll ensure that this gets called only for top-level expressions 
       * inside functions. We must turn them into three address code. *)
  method vexpr (e: exp) = 
    let e' = makeThreeAddress self#makeTemp e in
    ChangeTo e'


     (** We want the argument in calls to be simple variables *)
  method vinst (i: instr) =
    match i with 
      Call (someo, f, args, loc) -> 
        let someo' = 
          match someo with 
            Some lv -> Some (simplifyLval self#makeTemp lv)
          | _ -> None
        in
        let f' = makeBasic self#makeTemp f in
        let args' = List.map (makeBasic self#makeTemp) args in 
        ChangeTo [ Call (someo', f', args', loc) ]
  | _ -> DoChildren

      (* This method will be called only on top-level "lvals" (those on the 
       * left of assignments and function calls) *)
  method vlval (lv: lval) = 
    ChangeTo (simplifyLval self#makeTemp lv)
end

(********************
  Next is an old version of the code that was splitting structs into 
 * variables. It was not working on variables that are arguments or returns 
 * of function calls. 
(** This is a visitor that splits structured variables into separate 
 * variables. *)
let isStructType (t: typ): bool = 
  match unrollType t with
    TComp (ci, _)  -> ci.cstruct
  | _ -> false

(* Keep track of how we change the variables. For each variable id we keep a 
 * hash table that maps an offset (a sequence of fieldinfo) into a 
 * replacement variable. We also keep track of the splittable vars: those 
 * with structure type but whose address is not take and which do not appear 
 * as the argument to a Return *)
let splittableVars: (int, unit) H.t = H.create 13
let replacementVars: (int * offset, varinfo) H.t = H.create 13

let findReplacement (fi: fundec) (v: varinfo) (off: offset) : varinfo = 
  try
    H.find replacementVars (v.vid, off)
  with Not_found -> begin
    let t = typeOfLval (Var v, off) in
    (* make a name for this variable *)
    let rec mkName = function
      | Field(fi, off) -> "_" ^ fi.fname ^ mkName off
      | _ -> ""
    in
    let v' = makeTempVar fi ~name:(v.vname ^ mkName off ^ "_") t in
    H.add replacementVars (v.vid, off) v';
    if debug then
      ignore (E.log "Simplify: %s (%a) replace %a with %s\n"
                fi.svar.vname
                d_loc !currentLoc
                d_lval (Var v, off)
                v'.vname);
    v'
  end

      (* Now separate the offset into a sequence of field accesses and the 
      * rest of the offset *)
let rec separateOffset (off: offset): offset * offset = 
  match off with
    NoOffset -> NoOffset, NoOffset
  | Field(fi, off') when fi.fcomp.cstruct -> 
      let off1, off2 = separateOffset off' in
      Field(fi, off1), off2
  | _ -> NoOffset, off


class splitStructVisitor (fi: fundec) = object (self) 
  inherit nopCilVisitor

  method vlval (lv: lval) = 
    match lv with 
      Var v, off when H.mem splittableVars v.vid ->
        (* The type of this lval better not be a struct *)
        if isStructType (typeOfLval lv) then 
          E.s (unimp "Simplify: found lval of struct type %a : %a\n"
                 d_lval lv d_type (typeOfLval lv));
        let off1, restoff = separateOffset off in
        let lv' = 
          if off1 <> NoOffset then begin
            (* This is a splittable variable and we have an offset that makes 
            * it a scalar. Find the replacement variable for this *)
            let v' = findReplacement fi v off1 in
            if restoff = NoOffset then 
              Var v', NoOffset
            else (* We have some more stuff. Use Mem *)
              Mem (mkAddrOrStartOf (Var v', NoOffset)), restoff
          end else begin (* off1 = NoOffset *)
            if restoff = NoOffset then 
              E.s (bug "Simplify: splitStructVisitor:lval")
            else
              simplifyLval 
                (fun e1 -> 
                  let t = makeTempVar fi (typeOf e1) in
                  (* Add this instruction before the current statement *)
                  self#queueInstr [Set(var t, e1, !currentLoc)];
                  Lval(var t)) 
                (Mem (mkAddrOrStartOf (Var v, NoOffset)), restoff)
          end
        in
        ChangeTo lv'

    | _ -> DoChildren

  method vinst (i: instr) = 
    (* Accumulate to the list of instructions a number of assignments of 
     * non-splittable lvalues *)
    let rec accAssignment (ci: compinfo) (dest: lval) (what: lval) 
                         (acc: instr list) : instr list = 
      List.fold_left
        (fun acc f -> 
          let dest' = addOffsetLval (Field(f, NoOffset)) dest in
          let what' = addOffsetLval (Field(f, NoOffset)) what in
          match unrollType f.ftype with 
            TComp(ci, _) when ci.cstruct -> 
              accAssignment ci dest' what' acc
          | TArray _ -> (* We must copy the array *)
              (Set((Mem (AddrOf dest'), NoOffset),
                   Lval (Mem (AddrOf what'), NoOffset), !currentLoc)) :: acc
          | _ -> (* If the type of f is not a struct then leave this alone *)
              (Set(dest', Lval what', !currentLoc)) :: acc)
        acc
        ci.cfields
    in
    let doAssignment (ci: compinfo) (dest: lval) (what: lval) : instr list = 
      let il' = accAssignment ci dest what [] in
      List.concat (List.map (visitCilInstr (self :> cilVisitor)) il')
    in
    match i with 
      Set(((Var v, off) as lv), what, _) when H.mem splittableVars v.vid ->
        let off1, restoff = separateOffset off in
        if restoff <> NoOffset then (* This means that we are only assigning 
                                     * part of a replacement variable. Leave 
                                     * this alone because the vlval will take 
                                     * care of it *)
          DoChildren
        else begin
          (* The type of the replacement has to be a structure *)
          match unrollType (typeOfLval lv) with
            TComp (ci, _) when ci.cstruct -> 
              (* The assigned thing better be an lvalue *)
              let whatlv = 
                match what with 
                  Lval lv -> lv
                | _ -> E.s (unimp "Simplify: assigned struct is not lval")
              in
              ChangeTo (doAssignment ci (Var v, off) whatlv)
              
          | _ -> (* vlval will take care of it *)
              DoChildren
        end

    | Set(dest, Lval (Var v, off), _) when H.mem splittableVars v.vid -> 
        let off1, restoff = separateOffset off in
        if restoff <> NoOffset then (* vlval will do this *)
          DoChildren
        else begin
          (* The type of the replacement has to be a structure *)
          match unrollType (typeOfLval dest) with
            TComp (ci, _) when ci.cstruct -> 
              ChangeTo (doAssignment ci dest (Var v, off))
              
          | _ -> (* vlval will take care of it *)
              DoChildren
        end
          
    | _ -> DoChildren
        
end
*)

(* Whether to split the arguments of functions *)
let splitArguments = true

(* Whether we try to do the splitting all in one pass. The advantage is that 
 * it is faster and it generates nicer names *)
let lu = locUnknown

(* Go over the code and split some temporary variables of stucture type into 
 * several separate variables. The hope is that the compiler will have an 
 * easier time to do standard optimizations with the resulting scalars *)
(* Unfortunately, implementing this turns out to be more complicated than I 
 * thought *)

(** Iterate over the fields of a structured type. Returns the empty list if 
 * no splits. The offsets are in order in which they appear in the structure 
 * type. Along with the offset we pass a string that identifies the 
 * meta-component, and the type of that component. *)
let rec foldRightStructFields
    (doit: offset -> string -> typ -> 'a) (* Invoked on non-struct fields *)
    (off: offset)
    (post: 'a list) (** A suffix to what you compute *)
    (fields: fieldinfo list) : 'a list = 
  List.fold_right
    (fun f post -> 
      let off' = addOffset (Field(f, NoOffset)) off in 
      match unrollType f.ftype with 
        TComp (comp, _) when comp.cstruct -> (* struct type: recurse *)
          foldRightStructFields doit off' post comp.cfields
      | _ -> 
          (doit off' f.fname f.ftype) :: post)
    fields
    post
  

let rec foldStructFields
    (t: typ) 
    (doit: offset -> string -> typ -> 'a) 
    : 'a list = 
  match unrollType t with 
    TComp (comp, _) when comp.cstruct -> 
      foldRightStructFields doit NoOffset [] comp.cfields
  | _ -> []
      
      
(* Map a variable name to a list of component variables, along with the 
 * accessor offset. The fields are in the order in which they appear in the 
 * structure. *)
let newvars : (string, (offset * varinfo) list) H.t = H.create 13

(* Split a variable and return the replacements, in the proper order. If this 
 * variable is not split, then return just the variable. *)
let splitOneVar (v: varinfo) 
                (mknewvar: string -> typ -> varinfo) : varinfo list = 
  try 
    (* See if we have already split it *)
    List.map snd (H.find newvars v.vname)
  with Not_found -> begin
    let vars: (offset * varinfo) list = 
      foldStructFields v.vtype 
        (fun off n t -> (* make a new one *)
          let newname = v.vname ^ "_" ^ n in 
          let v'= mknewvar newname t in
          (off, v'))
    in
    if vars = [] then
      [ v ]
    else begin
      (* Now remember the newly created vars *)
      H.add newvars v.vname vars;
      List.map snd vars (* Return just the vars *)
    end
  end


(* A visitor that finds all locals that appear in a call or have their 
 * address taken *)
let dontSplitLocals : (string, bool) H.t = H.create 111
class findVarsCantSplitClass : cilVisitor = object (self) 
  inherit nopCilVisitor
        
        (* expressions, to see the address being taken *)
  method vexpr (e: exp) : exp visitAction =
    match e with 
      AddrOf (Var v, NoOffset) -> 
        H.add dontSplitLocals v.vname true; SkipChildren
      (* See if we take the address of the "_ms" field in a variable *)
    | _ -> DoChildren


          (* variables involved in call instructions *)
  method vinst (i: instr) : instr list visitAction = 
    match i with 
      Call (res, f, args, _) -> 
        (match res with 
          Some (Var v, NoOffset) -> H.add dontSplitLocals v.vname true
        | _ -> ());
        if not splitArguments then 
          List.iter (fun a -> 
            match a with
              Lval (Var v, NoOffset) -> H.add dontSplitLocals v.vname true
            | _ -> ()) args; 
        (* Now continue the visit *)
        DoChildren

    | _ -> DoChildren

          (* Variables used in return should not be split *)
  method vstmt (s: stmt) : stmt visitAction = 
    match s.skind with 
      Return (Some (Lval (Var v, NoOffset)), _) -> 
        H.add dontSplitLocals v.vname true; DoChildren
    | Return (Some e, _) -> 
        DoChildren
    | _ -> DoChildren

  method vtype t = SkipChildren

end
let findVarsCantSplit = new findVarsCantSplitClass

let isVar lv =
  match lv with 
      (Var v, NoOffset) -> true
    | _ -> false


class splitVarVisitorClass(func:fundec option) : cilVisitor = object (self)
  inherit nopCilVisitor

  method private makeTemp (e1: exp) : exp = 
    let fi:fundec = match func with
        Some f -> f
      | None -> 
          E.s (bug "You can't create a temporary if you're not in a function.")
    in
    let t = makeTempVar fi (typeOf e1) in
    (* Add this instruction before the current statement *)
    self#queueInstr [Set(var t, e1, !currentLoc)];
    Lval(var t)


  (* We must process the function types *)
  method vtype t = 
    (* We invoke the visitor first and then we fix it *)
    let postProcessFunType (t: typ) : typ = 
      match t with 
        TFun(rt, Some params, isva, a) -> 
          let rec loopParams = function
              [] -> []
            | ((pn, pt, pa) :: rest) as params -> 
                let rest' = loopParams rest in
                let res: (string * typ * attributes) list = 
                  foldStructFields pt
                    (fun off n t -> 
                      (* Careful with no-name parameters, or we end up with 
                       * many parameters named _p ! *)
                      ((if pn <> "" then pn ^ n else ""), t, pa)) 
                in
                if res = [] then (* Not a fat *)
                  if rest' == rest then 
                    params (* No change at all. Try not to reallocate so that 
                            * the visitor does not allocate. *)
                  else
                    (pn, pt, pa) :: rest'
                else (* Some change *)
                  res @ rest'
          in
          let params' = loopParams params in
          if params == params' then 
            t
          else
            TFun(rt, Some params', isva, a)
          
      | t -> t
    in
    if splitArguments then 
      ChangeDoChildrenPost(t, postProcessFunType)
    else
      SkipChildren

      (* Whenever we see a variable with a field access we try to replace it 
       * by its components *)
  method vlval ((b, off) : lval) : lval visitAction = 
    try
      match b, off with
        Var v, (Field _ as off) ->
          (* See if this variable has some splits.Might throw Not_found *)
          let splits = H.find newvars v.vname in
          (* Now find among the splits one that matches this offset. And 
           * return the remaining offset *)
          let rec find = function
              [] -> 
                E.s (E.bug "Cannot find component %a of %s\n" 
                       (d_offset nil) off v.vname)
            | (splitoff, splitvar) :: restsplits -> 
                let rec matches = function 
                    Field(f1, rest1), Field(f2, rest2) 
                      when f1.fname = f2.fname -> 
                        matches (rest1, rest2)
                  | off, NoOffset -> 
                      (* We found a match *)
                      (Var splitvar, off)
                  | NoOffset, restoff -> 
                      ignore (warn "Found aggregate lval %a\n" 
                                d_lval (b, off));
                      find restsplits

                  | _, _ -> (* We did not match this one; go on *)
                      find restsplits
                in
                matches (off, splitoff)
          in
          ChangeTo (find splits)
      | _ -> DoChildren
    with Not_found -> DoChildren

        (* Sometimes we pass the variable as a whole to a function or we 
         * assign it to something *)
  method vinst (i: instr) : instr list visitAction = 
    match i with
      (* Split into several instructions and then do children inside
       * the rhs.  Howver, v might appear in the rhs and if we
       * duplicate the instruction we might get bad
       * results. (e.g. test/small1/simplify_Structs2.c). So first copy
       * the rhs to temp variables, then to v. 
       *
       * Optimization: if the rhs is a variable, skip the temporary vars.
       * Either the rhs = lhs, in which case this is all a nop, or it's not, 
       * in which case the rhs and lhs don't overlap.*)

      Set ((Var v, NoOffset), Lval lv, l) when H.mem newvars v.vname -> begin
        let needTemps = not (isVar lv) in
        let vars4v = H.find newvars v.vname in
        if vars4v = [] then E.s (errorLoc l "No fields in split struct");
        ChangeTo 
          (List.map 
             (fun (off, newv) ->  
                let lv' = 
                  visitCilLval (self :> cilVisitor)
                    (addOffsetLval off lv) in
                (* makeTemp creates a temp var and puts (Lval lv') in it,
                   before any instructions in this ChangeTo list are handled.*)
                let lv_tmp = if needTemps then
                               self#makeTemp (Lval lv') 
                             else 
                               (Lval lv')
                in
                Set((Var newv, NoOffset), lv_tmp, l))
             vars4v)
      end 
 
      | Set (lv, Lval (Var v, NoOffset), l) when H.mem newvars v.vname -> begin
          (* Split->NonSplit assignment.  no overlap between lhs and rhs 
             is possible*)
          let vars4v = H.find newvars v.vname in
          if vars4v = [] then E.s (errorLoc l "No fields in split struct");
          ChangeTo  
            (List.map 
               (fun (off, newv) -> 
                  let lv' = 
                    visitCilLval (self :> cilVisitor)
                      (addOffsetLval off lv) in
                  Set(lv', Lval (Var newv, NoOffset), l))
               vars4v)
        end 

        (* Split all function arguments in calls *)
      | Call (ret, f, args, l) when splitArguments ->
          (* Visit the children first and then see if we must change the 
           * arguments *)
          let finishArgs = function
              [Call (ret', f', args', l')] as i' -> 
                let mustChange = ref false in
                let newargs = 
                  (* Look for opportunities to split arguments. If we can
                   * split, we must split the original argument (in args).
                   * Otherwise, we use the result of processing children
                   * (in args'). *)
                  List.fold_right2
                    (fun a a' acc -> 
                      match a with
                        Lval (Var v, NoOffset) when H.mem newvars v.vname -> 
                          begin
                            mustChange := true;
                            (List.map 
                               (fun (_, newv) -> 
                                 Lval (Var newv, NoOffset)) 
                               (H.find newvars v.vname))
                            @ acc
                          end
                      | Lval lv  -> begin
                          let newargs = 
                            foldStructFields (typeOfLval lv)
                              (fun off n t ->
                                 let lv' = addOffsetLval off lv in
                                 Lval lv') in
                          if newargs = [] then
                            a' :: acc (* not a split var *)
                          else begin
                            mustChange := true;
                            newargs @ acc
                          end
                        end
                      | _ -> (* only lvals are split, right? *)
                          a' :: acc)
                    args args'
                    []
                in
                if !mustChange then 
                  [Call (ret', f', newargs, l')]
                else
                  i'
            | _ -> E.s (E.bug "splitVarVisitorClass: expecting call")
          in
          ChangeDoChildrenPost ([i], finishArgs)

      | _ -> DoChildren

        
  method vfunc (func: fundec) : fundec visitAction = 
    H.clear newvars;
    H.clear dontSplitLocals;
    (* Visit the type of the function itself *)
    if splitArguments then 
      func.svar.vtype <- visitCilType (self :> cilVisitor) func.svar.vtype;

    (* Go over the block and find the candidates *)
    ignore (visitCilBlock findVarsCantSplit func.sbody);

    (* Now go over the formals and create the splits *)
    if splitArguments then begin
      (* Split all formals because we will split all arguments in function 
       * types *)
      let newformals = 
        List.fold_right 
          (fun form acc -> 
            (* Process the type first *)
            form.vtype <- 
               visitCilType (self : #cilVisitor :> cilVisitor) form.vtype;
            let form' = 
              splitOneVar form 
                          (fun s t -> makeLocalVar func ~insert:false s t)
            in
            (* Now it is a good time to check if we actually can split this 
             * one *)
            if List.length form' > 1 &&
               H.mem dontSplitLocals form.vname then
              ignore (warn "boxsplit: can't split formal \"%s\" in %s. Make sure you never take the address of a formal.\n"
                     form.vname func.svar.vname);
            form' @ acc)
          func.sformals [] 
      in
      (* Now make sure we fix the type.  *)
      setFormals func newformals
    end;
    (* Now go over the locals and create the splits *)
    List.iter 
      (fun l ->
        (* Process the type of the local *)
        l.vtype <- visitCilType (self :> cilVisitor) l.vtype;
        (* Now see if we must split it *)
        if not (H.mem dontSplitLocals l.vname) then begin
          ignore (splitOneVar l (fun s t -> makeTempVar func ~name:s t))
        end) 
      func.slocals;
    (* Now visit the body and change references to these variables *)
    ignore (visitCilBlock (self :> cilVisitor) func.sbody);
    H.clear newvars;
    H.clear dontSplitLocals;
    SkipChildren  (* We are done with this function *)

  (* Try to catch the occurrences of the variable in a sizeof expression *)
  method vexpr (e: exp) = 
    match e with 
    | SizeOfE (Lval(Var v, NoOffset)) -> begin
        try
          let splits =  H.find newvars v.vname in
          (* We cound here on no padding between the elements ! *)
          ChangeTo
            (List.fold_left
               (fun acc (_, thisv) -> 
                 BinOp(PlusA, SizeOfE(Lval(Var thisv, NoOffset)), 
                       acc, uintType))
               zero
               splits)
        with Not_found -> DoChildren
    end
    | _ -> DoChildren
end

let doGlobal = function 
    GFun(fi, _) ->  
      (* Visit the body and change all expressions into three address code *)
      let v = new threeAddressVisitor fi in
      fi.sbody <- visitCilBlock v fi.sbody;
      if !splitStructs then begin
        H.clear dontSplitLocals;
        let splitVarVisitor = new splitVarVisitorClass (Some fi) in    
        ignore (visitCilFunction splitVarVisitor fi);
      end
  | GVarDecl(vi, _) when isFunctionType vi.vtype ->
      (* we might need to split the args/return value in the function type. *)
      if !splitStructs then begin
        H.clear dontSplitLocals;
        let splitVarVisitor = new splitVarVisitorClass None in    
        ignore (visitCilVarDecl splitVarVisitor vi);
      end
  | _ -> ()
      
let feature : featureDescr = 
  { fd_name = "simplify";
    fd_enabled = ref false;
    fd_description = "compiles CIL to 3-address code";
    fd_extraopt = [
      ("--no-split-structs", Arg.Unit (fun _ -> splitStructs := false),
                    "do not split structured variables"); 
    ];
    fd_doit = (function f -> iterGlobals f doGlobal);
    fd_post_check = true;
}