~ubuntu-branches/debian/sid/frama-c/sid

« back to all changes in this revision

Viewing changes to cil/src/rmtmps.ml

  • Committer: Bazaar Package Importer
  • Author(s): Mehdi Dogguy
  • Date: 2009-06-03 08:19:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090603081925-kihvxvt0wy3zc4ar
Tags: upstream-20081201.dfsg
ImportĀ upstreamĀ versionĀ 20081201.dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(**************************************************************************)
 
2
(*                                                                        *)
 
3
(*  Copyright (C) 2001-2003,                                              *)
 
4
(*   George C. Necula    <necula@cs.berkeley.edu>                         *)
 
5
(*   Scott McPeak        <smcpeak@cs.berkeley.edu>                        *)
 
6
(*   Wes Weimer          <weimer@cs.berkeley.edu>                         *)
 
7
(*   Ben Liblit          <liblit@cs.berkeley.edu>                         *)
 
8
(*  All rights reserved.                                                  *)
 
9
(*                                                                        *)
 
10
(*  Redistribution and use in source and binary forms, with or without    *)
 
11
(*  modification, are permitted provided that the following conditions    *)
 
12
(*  are met:                                                              *)
 
13
(*                                                                        *)
 
14
(*  1. Redistributions of source code must retain the above copyright     *)
 
15
(*  notice, this list of conditions and the following disclaimer.         *)
 
16
(*                                                                        *)
 
17
(*  2. Redistributions in binary form must reproduce the above copyright  *)
 
18
(*  notice, this list of conditions and the following disclaimer in the   *)
 
19
(*  documentation and/or other materials provided with the distribution.  *)
 
20
(*                                                                        *)
 
21
(*  3. The names of the contributors may not be used to endorse or        *)
 
22
(*  promote products derived from this software without specific prior    *)
 
23
(*  written permission.                                                   *)
 
24
(*                                                                        *)
 
25
(*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   *)
 
26
(*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     *)
 
27
(*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS     *)
 
28
(*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE        *)
 
29
(*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   *)
 
30
(*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  *)
 
31
(*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      *)
 
32
(*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      *)
 
33
(*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT    *)
 
34
(*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN     *)
 
35
(*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *)
 
36
(*  POSSIBILITY OF SUCH DAMAGE.                                           *)
 
37
(*                                                                        *)
 
38
(*  File modified by CEA (Commissariat ļæ½ l'ļæ½nergie Atomique).             *)
 
39
(**************************************************************************)
 
40
 
 
41
(* rmtmps.ml *)
 
42
(* implementation for rmtmps.mli *)
 
43
 
 
44
open Pretty
 
45
open Cil_types
 
46
open Cil
 
47
module H = Hashtbl
 
48
module E = Errormsg
 
49
module U = Cilutil
 
50
 
 
51
(* Set on the command-line: *)
 
52
let keepUnused = ref false
 
53
let rmUnusedInlines = ref false
 
54
let rmUnusedStatic = ref false
 
55
let rmEmptyInlines = ref true
 
56
 
 
57
let trace = Trace.trace "rmtmps"
 
58
 
 
59
 
 
60
 
 
61
(***********************************************************************
 
62
 *
 
63
 *  Clearing of "referenced" bits
 
64
 *
 
65
 *)
 
66
 
 
67
 
 
68
let clearReferencedBits file =
 
69
  let considerGlobal global =
 
70
    match global with
 
71
    | GType (info, _) ->
 
72
        (*trace (dprintf "clearing mark: %a\n" d_shortglobal global);*)
 
73
        info.treferenced <- false
 
74
 
 
75
    | GEnumTag (info, _)
 
76
    | GEnumTagDecl (info, _) ->
 
77
        (*trace (dprintf "clearing mark: %a\n" d_shortglobal global);*)
 
78
        info.ereferenced <- false
 
79
 
 
80
    | GCompTag (info, _)
 
81
    | GCompTagDecl (info, _) ->
 
82
        (*trace (dprintf "clearing mark: %a\n" d_shortglobal global);*)
 
83
        info.creferenced <- false
 
84
 
 
85
    | GVar ({vname = _name} as info, _, _)
 
86
    | GVarDecl (_,({vname = _name} as info), _) ->
 
87
        (*trace (dprintf "clearing mark: %a\n" d_shortglobal global);*)
 
88
        info.vreferenced <- false
 
89
 
 
90
    | GFun ({svar = info} as func, _) ->
 
91
(*      trace (dprintf "clearing mark: %a\n" d_shortglobal global);*)
 
92
        info.vreferenced <- false;
 
93
        let clearMark local =
 
94
          (*trace (dprintf "clearing mark: local %s\n" local.vname);*)
 
95
          local.vreferenced <- false
 
96
        in
 
97
        List.iter clearMark func.slocals
 
98
 
 
99
    | _ ->
 
100
        ()
 
101
  in
 
102
  iterGlobals file considerGlobal
 
103
 
 
104
 
 
105
(***********************************************************************
 
106
 *
 
107
 *  Scanning and categorization of pragmas
 
108
 *
 
109
 *)
 
110
 
 
111
 
 
112
(* collections of names of things to keep *)
 
113
type collection = (string, unit) H.t
 
114
type keepers = {
 
115
    typedefs : collection;
 
116
    enums : collection;
 
117
    structs : collection;
 
118
    unions : collection;
 
119
    defines : collection;
 
120
  }
 
121
 
 
122
 
 
123
(* rapid transfer of control when we find a malformed pragma *)
 
124
exception Bad_pragma
 
125
 
 
126
let ccureddeepcopystring = "ccureddeepcopy"
 
127
(* Save this length so we don't recompute it each time. *)
 
128
let ccureddeepcopystring_length = String.length ccureddeepcopystring
 
129
 
 
130
(* CIL and CCured define several pragmas which prevent removal of
 
131
 * various global varinfos.  Here we scan for those pragmas and build
 
132
 * up collections of the corresponding varinfos' names.
 
133
 *)
 
134
 
 
135
let categorizePragmas file =
 
136
 
 
137
  (* names of things which should be retained *)
 
138
  let keepers = {
 
139
    typedefs = H.create 0;
 
140
    enums = H.create 0;
 
141
    structs = H.create 0;
 
142
    unions = H.create 0;
 
143
    defines = H.create 1
 
144
  } in
 
145
 
 
146
  (* populate these name collections in light of each pragma *)
 
147
  let considerPragma =
 
148
 
 
149
    let badPragma location pragma =
 
150
      ignore (warnLoc location "Invalid argument to pragma %s" pragma)
 
151
    in
 
152
 
 
153
    function
 
154
      | GPragma (Attr ("cilnoremove" as directive, args), location) ->
 
155
          (* a very flexible pragma: can retain typedefs, enums,
 
156
           * structs, unions, or globals (functions or variables) *)
 
157
          begin
 
158
            let processArg arg =
 
159
              try
 
160
                match arg with
 
161
                | AStr specifier ->
 
162
                    (* isolate and categorize one varinfo name *)
 
163
                    let collection, name =
 
164
                      (* Two words denotes a typedef, enum, struct, or
 
165
                       * union, as in "type foo" or "enum bar".  A
 
166
                       * single word denotes a global function or
 
167
                       * variable. *)
 
168
                      let whitespace = Str.regexp "[ \t]+" in
 
169
                      let words = Str.split whitespace specifier in
 
170
                      match words with
 
171
                      | ["type"; name] ->
 
172
                          keepers.typedefs, name
 
173
                      | ["enum"; name] ->
 
174
                          keepers.enums, name
 
175
                      | ["struct"; name] ->
 
176
                          keepers.structs, name
 
177
                      | ["union"; name] ->
 
178
                          keepers.unions, name
 
179
                      | [name] ->
 
180
                          keepers.defines, name
 
181
                      | _ ->
 
182
                          raise Bad_pragma
 
183
                    in
 
184
                    H.add collection name ()
 
185
                | _ ->
 
186
                    raise Bad_pragma
 
187
              with Bad_pragma ->
 
188
                badPragma location directive
 
189
            in
 
190
            List.iter processArg args
 
191
          end
 
192
      | GVarDecl (_,v, _) -> begin
 
193
          (* Look for alias attributes, e.g. Linux modules *)
 
194
          match filterAttributes "alias" v.vattr with
 
195
              [] -> ()  (* ordinary prototype. *)
 
196
            | [Attr("alias", [AStr othername])] ->
 
197
                H.add keepers.defines othername ()
 
198
            | _ ->
 
199
                E.s
 
200
                  (error "Bad alias attribute at %a" d_loc (CurrentLoc.get ()))
 
201
        end
 
202
 
 
203
      (*** Begin CCured-specific checks:  ***)
 
204
      (* these pragmas indirectly require that we keep the function named in
 
205
          -- the first arguments of boxmodelof and ccuredwrapperof, and
 
206
          -- the third argument of ccureddeepcopy*. *)
 
207
      | GPragma (Attr("ccuredwrapper" as directive, attribute :: _), location) ->
 
208
          begin
 
209
            match attribute with
 
210
            | AStr name ->
 
211
                H.add keepers.defines name ()
 
212
            | _ ->
 
213
                badPragma location directive
 
214
          end
 
215
      | GPragma (Attr("ccuredvararg", _funcname :: (ASizeOf t) :: _), _location) ->
 
216
          begin
 
217
            match t with
 
218
            | TComp(c,_) when c.cstruct -> (* struct *)
 
219
                H.add keepers.structs c.cname ()
 
220
            | TComp(c,_) -> (* union *)
 
221
                H.add keepers.unions c.cname ()
 
222
            | TNamed(ti,_) ->
 
223
                H.add keepers.typedefs ti.tname ()
 
224
            | TEnum(ei, _) ->
 
225
                H.add keepers.enums ei.ename ()
 
226
            | _ ->
 
227
                ()
 
228
          end
 
229
      | GPragma (Attr(directive, _ :: _ :: attribute :: _), location)
 
230
           when String.length directive > ccureddeepcopystring_length
 
231
               && (Str.first_chars directive ccureddeepcopystring_length)
 
232
                   = ccureddeepcopystring ->
 
233
          begin
 
234
            match attribute with
 
235
            | AStr name ->
 
236
                H.add keepers.defines name ()
 
237
            | _ ->
 
238
                badPragma location directive
 
239
          end
 
240
      (** end CCured-specific stuff **)
 
241
      | _ ->
 
242
          ()
 
243
  in
 
244
  iterGlobals file considerPragma;
 
245
  keepers
 
246
 
 
247
 
 
248
 
 
249
(***********************************************************************
 
250
 *
 
251
 *  Function body elimination from pragmas
 
252
 *
 
253
 *)
 
254
 
 
255
 
 
256
(* When performing global slicing, any functions not explicitly marked
 
257
 * as pragma roots are reduced to mere declarations.  This leaves one
 
258
 * with a reduced source file that still compiles to object code, but
 
259
 * which contains the bodies of only explicitly retained functions.
 
260
 *)
 
261
 
 
262
let amputateFunctionBodies keptGlobals file =
 
263
  let considerGlobal = function
 
264
    | GFun ({svar = {vname = name} as info}, location)
 
265
      when not (H.mem keptGlobals name) ->
 
266
        trace (dprintf "slicing: reducing to prototype: function %s\n" name);
 
267
        GVarDecl (empty_funspec(),info, location)
 
268
    | other ->
 
269
        other
 
270
  in
 
271
  mapGlobals file considerGlobal
 
272
 
 
273
 
 
274
 
 
275
(***********************************************************************
 
276
 *
 
277
 *  Root collection from pragmas
 
278
 *
 
279
 *)
 
280
 
 
281
 
 
282
let isPragmaRoot keepers = function
 
283
  | GType ({tname = name}, _) ->
 
284
      H.mem keepers.typedefs name
 
285
  | GEnumTag ({ename = name}, _)
 
286
  | GEnumTagDecl ({ename = name}, _) ->
 
287
      H.mem keepers.enums name
 
288
  | GCompTag ({cname = name; cstruct = structure}, _)
 
289
  | GCompTagDecl ({cname = name; cstruct = structure}, _) ->
 
290
      let collection = if structure then keepers.structs else keepers.unions in
 
291
      H.mem collection name
 
292
  | GVar ({vname = name; vattr = attrs}, _, _)
 
293
  | GVarDecl (_,{vname = name; vattr = attrs}, _)
 
294
  | GFun ({svar = {vname = name; vattr = attrs}}, _) ->
 
295
      H.mem keepers.defines name ||
 
296
      hasAttribute "used" attrs
 
297
  | _ ->
 
298
      false
 
299
 
 
300
 
 
301
 
 
302
(***********************************************************************
 
303
 *
 
304
 *  Common root collecting utilities
 
305
 *
 
306
 *)
 
307
 
 
308
 
 
309
let traceRoot _reason _global =
 
310
(*  trace (dprintf "root (%s): %a@!" reason d_shortglobal global);*)
 
311
  true
 
312
 
 
313
 
 
314
let traceNonRoot _reason _global =
 
315
(*  trace (dprintf "non-root (%s): %a@!" reason d_shortglobal global);*)
 
316
  false
 
317
 
 
318
 
 
319
let hasExportingAttribute funvar =
 
320
  let rec isExportingAttribute = function
 
321
    | Attr ("constructor", []) -> true
 
322
    | Attr ("destructor", []) -> true
 
323
    | _ -> false
 
324
  in
 
325
  List.exists isExportingAttribute funvar.vattr
 
326
 
 
327
 
 
328
 
 
329
(***********************************************************************
 
330
 *
 
331
 *  Root collection from external linkage
 
332
 *
 
333
 *)
 
334
 
 
335
 
 
336
(* Exported roots are those global varinfos which are visible to the
 
337
 * linker and dynamic loader.  For variables, this consists of
 
338
 * anything that is not "static".  For functions, this consists of:
 
339
 *
 
340
 * - functions bearing a "constructor" or "destructor" attribute
 
341
 * - functions declared extern but not inline
 
342
 * - functions declared neither inline nor static
 
343
 *
 
344
 * gcc incorrectly (according to C99) makes inline functions visible to
 
345
 * the linker.  So we can only remove inline functions on MSVC.
 
346
 *)
 
347
 
 
348
let isExportedRoot global =
 
349
  let result, _reason = match global with
 
350
  | GVar ({vstorage = Static}, _, _) ->
 
351
      false, "static variable"
 
352
  | GVar _ ->
 
353
      true, "non-static variable"
 
354
  | GFun ({svar = v}, _) -> begin
 
355
      if hasExportingAttribute v then
 
356
        true, "constructor or destructor function"
 
357
      else if v.vstorage = Static then
 
358
        not !rmUnusedStatic, "static function"
 
359
      else if v.vinline && v.vstorage != Extern
 
360
              && (theMachine.msvcMode || !rmUnusedInlines) then
 
361
        false, "inline function"
 
362
      else
 
363
        true, "other function"
 
364
  end
 
365
  | GVarDecl(_,v,_) when hasAttribute "alias" v.vattr ->
 
366
      true, "has GCC alias attribute"
 
367
(*  | GVarDecl(spec,_,_) when not (Cil.is_empty_funspec spec) ->
 
368
      true, "has formal spec"
 
369
*)
 
370
  | GAnnot _ -> true, "global annotation"
 
371
  | _ ->
 
372
      false, "neither function nor variable nor annotation"
 
373
  in
 
374
(*  trace (dprintf "isExportedRoot %a -> %b, %s@!"
 
375
           d_shortglobal global result reason);*)
 
376
  result
 
377
 
 
378
 
 
379
 
 
380
(***********************************************************************
 
381
 *
 
382
 *  Root collection for complete programs
 
383
 *
 
384
 *)
 
385
 
 
386
 
 
387
(* Exported roots are "main()" and functions bearing a "constructor"
 
388
 * or "destructor" attribute.  These are the only things which must be
 
389
 * retained in a complete program.
 
390
 *)
 
391
 
 
392
let isCompleteProgramRoot global =
 
393
  let result = match global with
 
394
  | GFun ({svar = {vname = "main"; vstorage = vstorage}}, _) ->
 
395
      vstorage <> Static
 
396
  | GFun (fundec, _)
 
397
    when hasExportingAttribute fundec.svar ->
 
398
      true
 
399
  | _ ->
 
400
      false
 
401
  in
 
402
(*  trace (dprintf "complete program root -> %b for %a@!" result d_shortglobal global);*)
 
403
  result
 
404
 
 
405
 
 
406
(***********************************************************************
 
407
 *
 
408
 *  Transitive reachability closure from roots
 
409
 *
 
410
 *)
 
411
 
 
412
 
 
413
(* This visitor recursively marks all reachable types and variables as used. *)
 
414
class markReachableVisitor
 
415
    ((globalMap: (string, Cil_types.global) H.t),
 
416
     (currentFunc: Cil_types.fundec option ref)) = object (self)
 
417
  inherit nopCilVisitor
 
418
 
 
419
  method vglob = function
 
420
    | GType (typeinfo, _) ->
 
421
        typeinfo.treferenced <- true;
 
422
        DoChildren
 
423
    | GCompTag (compinfo, _)
 
424
    | GCompTagDecl (compinfo, _) ->
 
425
        compinfo.creferenced <- true;
 
426
        DoChildren
 
427
    | GEnumTag (enuminfo, _)
 
428
    | GEnumTagDecl (enuminfo, _) ->
 
429
        enuminfo.ereferenced <- true;
 
430
        DoChildren
 
431
    | GVar (varinfo, _, _)
 
432
    | GVarDecl (_,varinfo, _)
 
433
    | GFun ({svar = varinfo}, _) ->
 
434
        varinfo.vreferenced <- true;
 
435
        DoChildren
 
436
    | GAnnot _ -> DoChildren
 
437
    | _ ->
 
438
        SkipChildren
 
439
 
 
440
  method vinst = function
 
441
      Asm (_, tmpls, _, _, _, _) when theMachine.msvcMode ->
 
442
          (* If we have inline assembly on MSVC, we cannot tell which locals
 
443
           * are referenced. Keep thsem all *)
 
444
        (match !currentFunc with
 
445
          Some fd ->
 
446
            List.iter (fun v ->
 
447
              let vre = Str.regexp_string (Str.quote v.vname) in
 
448
              if List.exists (fun tmp ->
 
449
                try ignore (Str.search_forward vre tmp 0); true
 
450
                with Not_found -> false)
 
451
                  tmpls
 
452
              then
 
453
                v.vreferenced <- true) fd.slocals
 
454
        | _ -> assert false);
 
455
        DoChildren
 
456
    | Call (None,
 
457
            Lval(Var {vname = name; vinline = true}, NoOffset), args,loc) ->
 
458
        let glob = Hashtbl.find globalMap name in
 
459
          begin
 
460
            match glob with
 
461
            GFun ({sbody = {bstmts = [] | [{skind = Return (None,_)}]}},_)
 
462
                ->
 
463
                  if false then
 
464
                  ChangeTo
 
465
                    [Asm ([],["nop"],[],List.map (fun e -> None,"q",e) args ,[],loc)]
 
466
                  else ChangeTo []
 
467
            | _ -> DoChildren
 
468
        end
 
469
    | _ -> DoChildren
 
470
 
 
471
  method vvrbl v =
 
472
    if not v.vreferenced then
 
473
      begin
 
474
        let name = v.vname in
 
475
        if v.vglob then
 
476
          trace (dprintf "marking transitive use: global %s\n" name)
 
477
        else
 
478
          trace (dprintf "marking transitive use: local %s\n" name);
 
479
 
 
480
        (* If this is a global, we need to keep everything used in its
 
481
         * definition and declarations. *)
 
482
        if v.vglob then
 
483
          begin
 
484
            trace (dprintf "descending: global %s\n" name);
 
485
            let descend global =
 
486
              ignore (visitCilGlobal (self :> cilVisitor) global)
 
487
            in
 
488
            let globals = Hashtbl.find_all globalMap name in
 
489
            List.iter descend globals
 
490
          end
 
491
        else begin
 
492
          v.vreferenced <- true;
 
493
        end
 
494
      end;
 
495
    SkipChildren
 
496
 
 
497
  method vexpr (e: exp) =
 
498
    match e with
 
499
      Const (CEnum {eihost = ei}) -> ei.ereferenced <- true; DoChildren
 
500
    | _ -> DoChildren
 
501
 
 
502
  method vtype typ =
 
503
    let old : bool =
 
504
      let visitAttrs attrs =
 
505
        ignore (visitCilAttributes (self :> cilVisitor) attrs)
 
506
      in
 
507
      let visitType typ =
 
508
        ignore (visitCilType (self :> cilVisitor) typ)
 
509
      in
 
510
      match typ with
 
511
      | TEnum(e, attrs) ->
 
512
          let old = e.ereferenced in
 
513
          if not old then
 
514
            begin
 
515
              trace (dprintf "marking transitive use: enum %s\n" e.ename);
 
516
              e.ereferenced <- true;
 
517
              visitAttrs attrs;
 
518
              visitAttrs e.eattr
 
519
            end;
 
520
          old
 
521
 
 
522
      | TComp(c, attrs) ->
 
523
          let old = c.creferenced in
 
524
          if not old then
 
525
            begin
 
526
              trace (dprintf "marking transitive use: compound %s\n" c.cname);
 
527
              c.creferenced <- true;
 
528
 
 
529
              (* to recurse, we must ask explicitly *)
 
530
              let recurse f = visitType f.ftype in
 
531
              List.iter recurse c.cfields;
 
532
              visitAttrs attrs;
 
533
              visitAttrs c.cattr
 
534
            end;
 
535
          old
 
536
 
 
537
      | TNamed(ti, attrs) ->
 
538
          let old = ti.treferenced in
 
539
          if not old then
 
540
            begin
 
541
              trace (dprintf "marking transitive use: typedef %s\n" ti.tname);
 
542
              ti.treferenced <- true;
 
543
 
 
544
              (* recurse deeper into the type referred-to by the typedef *)
 
545
              (* to recurse, we must ask explicitly *)
 
546
              visitType ti.ttype;
 
547
              visitAttrs attrs
 
548
            end;
 
549
          old
 
550
 
 
551
      | _ ->
 
552
          (* for anything else, just look inside it *)
 
553
          false
 
554
    in
 
555
    if old then
 
556
      SkipChildren
 
557
    else
 
558
      DoChildren
 
559
 
 
560
end
 
561
 
 
562
 
 
563
let markReachable file isRoot =
 
564
  (* build a mapping from global names back to their definitions &
 
565
   * declarations *)
 
566
  let globalMap = Hashtbl.create 137 in
 
567
  let considerGlobal global =
 
568
    match global with
 
569
    | GFun ({svar = info}, _)
 
570
    | GVar (info, _, _)
 
571
    | GVarDecl (_,info, _) ->
 
572
        Hashtbl.add globalMap info.vname global
 
573
    | _ ->
 
574
        ()
 
575
  in
 
576
  iterGlobals file considerGlobal;
 
577
 
 
578
  let currentFunc = ref None in
 
579
 
 
580
  (* mark everything reachable from the global roots *)
 
581
  let visitor = new markReachableVisitor (globalMap, currentFunc) in
 
582
  let visitIfRoot global =
 
583
    if isRoot global then
 
584
      begin
 
585
(*      trace (dprintf "traversing root global: %a\n" d_shortglobal global);*)
 
586
        (match global with
 
587
          GFun(fd, _) -> currentFunc := Some fd
 
588
        | _ -> currentFunc := None);
 
589
        ignore (visitCilGlobal visitor global)
 
590
      end
 
591
    else
 
592
(*      trace (dprintf "skipping non-root global: %a\n" d_shortglobal global)*)
 
593
      ()
 
594
  in
 
595
  iterGlobals file visitIfRoot
 
596
 
 
597
 
 
598
(**********************************************************************
 
599
 *
 
600
 * Marking and removing of unused labels
 
601
 *
 
602
 **********************************************************************)
 
603
 
 
604
(* We keep only one label, preferably one that was not introduced by CIL.
 
605
 * Scan a list of labels and return the data for the label that should be
 
606
 * kept, and the remaining filtered list of labels *)
 
607
let labelsToKeep (ll: label list) : (string * location * bool) * label list =
 
608
  let rec loop (sofar: string * location * bool) = function
 
609
      [] -> sofar, []
 
610
    | l :: rest ->
 
611
        let newlabel, keepl =
 
612
          match l with
 
613
          | Case _ | Default _ -> sofar, true
 
614
          | Label (ln, lloc, isorig) -> begin
 
615
              match isorig, sofar with
 
616
              | false, ("", _, _) ->
 
617
                  (* keep this one only if we have no label so far *)
 
618
                  (ln, lloc, isorig), false
 
619
              | false, _ -> sofar, false
 
620
              | true, (_, _, false) ->
 
621
                  (* this is an original label; prefer it to temporary or
 
622
                   * missing labels *)
 
623
                  (ln, lloc, isorig), false
 
624
              | true, _ -> sofar, false
 
625
          end
 
626
        in
 
627
        let newlabel', rest' = loop newlabel rest in
 
628
        newlabel', (if keepl then l :: rest' else rest')
 
629
  in
 
630
  loop ("", Cilutil.locUnknown, false) ll
 
631
 
 
632
class markUsedLabels (labelMap: (string, unit) H.t) =
 
633
let keep_label dest =
 
634
  let (ln, _, _), _ = labelsToKeep !dest.labels in
 
635
  if ln = "" then
 
636
    (Format.eprintf "@[dest: %a@\n@]@." Cil.d_stmt !dest;
 
637
     E.s (E.bug "rmtmps: destination statement does not have labels"));
 
638
  (* Mark it as used *)
 
639
  H.replace labelMap ln ()
 
640
in
 
641
let keep_label_logic =
 
642
  function LogicLabel _ -> () | StmtLabel dest -> keep_label dest
 
643
in
 
644
object
 
645
  inherit nopCilVisitor
 
646
 
 
647
  method vstmt (s: stmt) =
 
648
    match s.skind with
 
649
      Goto (dest, _) -> keep_label dest; DoChildren
 
650
    | _ -> DoChildren
 
651
 
 
652
  method vterm_node t =
 
653
    begin
 
654
      match t with
 
655
      | Tat (_,lab) -> keep_label_logic lab
 
656
      | Tapp(_,labs,_) ->
 
657
          let labs = snd (List.split labs) in List.iter keep_label_logic labs
 
658
      | _ -> ()
 
659
    end;
 
660
    DoChildren
 
661
 
 
662
 
 
663
  method vtsets_elem t =
 
664
    begin
 
665
      match t with
 
666
      | TSat (_,lab) -> keep_label_logic lab
 
667
      | TSapp(_,labs,_) ->
 
668
          let labs = snd (List.split labs) in List.iter keep_label_logic labs
 
669
      | _ -> ()
 
670
    end;
 
671
    DoChildren
 
672
 
 
673
  method vpredicate t =
 
674
    begin
 
675
      match t with
 
676
      | Pat (_,lab) -> keep_label_logic lab
 
677
      | Papp(_,labs,_) ->
 
678
          let labs = snd (List.split labs) in List.iter keep_label_logic labs
 
679
      | _ -> ()
 
680
    end;
 
681
    DoChildren
 
682
 
 
683
   (* No need to go into expressions or types *)
 
684
  method vexpr _ = SkipChildren
 
685
  method vtype _ = SkipChildren
 
686
                                                        end
 
687
 
 
688
class removeUnusedLabels (labelMap: (string, unit) H.t) = object
 
689
  inherit nopCilVisitor
 
690
 
 
691
  method vstmt (s: stmt) =
 
692
    let (ln, lloc, lorig), lrest = labelsToKeep s.labels in
 
693
    s.labels <-
 
694
       (if ln <> "" && H.mem labelMap ln then (* We had labels *)
 
695
         (Label(ln, lloc, lorig) :: lrest)
 
696
       else
 
697
         lrest);
 
698
    DoChildren
 
699
 
 
700
   (* No need to go into expressions or instructions *)
 
701
  method vexpr _ = SkipChildren
 
702
  method vinst _ = SkipChildren
 
703
  method vtype _ = SkipChildren
 
704
end
 
705
 
 
706
(***********************************************************************
 
707
 *
 
708
 *  Removal of unused varinfos
 
709
 *
 
710
 *)
 
711
 
 
712
 
 
713
(* regular expression matching names of uninteresting locals *)
 
714
let uninteresting =
 
715
  let names = [
 
716
    (* Cil.makeTempVar *)
 
717
    "__cil_tmp";
 
718
 
 
719
    (* sm: I don't know where it comes from but these show up all over. *)
 
720
    (* this doesn't seem to do what I wanted.. *)
 
721
    "iter";
 
722
 
 
723
    (* various macros in glibc's <bits/string2.h> *)
 
724
    "__result";
 
725
    "__s"; "__s1"; "__s2";
 
726
    "__s1_len"; "__s2_len";
 
727
    "__retval"; "__len";
 
728
 
 
729
    (* various macros in glibc's <ctype.h> *)
 
730
    "__c"; "__res";
 
731
 
 
732
    (* We remove the __malloc variables *)
 
733
  ] in
 
734
 
 
735
  (* optional alpha renaming *)
 
736
  let alpha = "\\(___[0-9]+\\)?" in
 
737
 
 
738
  let pattern = "\\(" ^ (String.concat "\\|" names) ^ "\\)" ^ alpha ^ "$" in
 
739
  Str.regexp pattern
 
740
 
 
741
 
 
742
let removeUnmarked isRoot file =
 
743
  let removedLocals = ref [] in
 
744
 
 
745
  let filterGlobal global =
 
746
    (match global with
 
747
         (* unused global types, variables, and functions are simply removed *)
 
748
       | GType ({treferenced = false}, _)
 
749
       | GCompTag ({creferenced = false}, _)
 
750
       | GCompTagDecl ({creferenced = false}, _)
 
751
       | GEnumTag ({ereferenced = false}, _)
 
752
       | GEnumTagDecl ({ereferenced = false}, _)
 
753
       | GVar ({vreferenced = false}, _, _)
 
754
       | GFun ({svar = {vreferenced = false}}, _) ->
 
755
           (*   trace (dprintf "removing global: %a\n" d_shortglobal global);*)
 
756
           false
 
757
       | GVarDecl (_,{vreferenced = false}, _) -> false
 
758
 
 
759
       (* retained functions may wish to discard some unused locals *)
 
760
       | GFun (func, _) ->
 
761
           let rec filterLocal local =
 
762
             if not local.vreferenced then
 
763
               begin
 
764
                 (* along the way, record the interesting locals that were removed *)
 
765
                 let name = local.vname in
 
766
                 trace (dprintf "removing local: %s\n" name);
 
767
                 if not (Str.string_match uninteresting name 0) then
 
768
                   removedLocals := (func.svar.vname ^ "::" ^ name) :: !removedLocals;
 
769
               end;
 
770
             local.vreferenced
 
771
           in
 
772
           func.slocals <- List.filter filterLocal func.slocals;
 
773
           (* We also want to remove unused labels. We do it all here, including
 
774
            * marking the used labels *)
 
775
           let usedLabels:(string, unit) H.t = H.create 13 in
 
776
           ignore (visitCilBlock (new markUsedLabels usedLabels) func.sbody);
 
777
           (* And now we scan again and we remove them *)
 
778
           ignore (visitCilBlock (new removeUnusedLabels usedLabels) func.sbody);
 
779
           true
 
780
 
 
781
    (* all other globals are retained *)
 
782
    | _ ->
 
783
(*      trace (dprintf "keeping global: %a\n" d_shortglobal global);*)
 
784
        true) || (isRoot global)
 
785
  in
 
786
  file.globals <- List.filter filterGlobal file.globals;
 
787
  !removedLocals
 
788
 
 
789
 
 
790
(***********************************************************************
 
791
 *
 
792
 *  Exported interface
 
793
 *
 
794
 *)
 
795
 
 
796
 
 
797
type rootsFilter = global -> bool
 
798
 
 
799
let isDefaultRoot = isExportedRoot
 
800
 
 
801
let rec removeUnusedTemps ?(isRoot : rootsFilter = isDefaultRoot) file =
 
802
  if !keepUnused || Trace.traceActive "disableTmpRemoval" then
 
803
    Trace.trace "disableTmpRemoval" (dprintf "temp removal disabled\n")
 
804
  else
 
805
    begin
 
806
      if !E.verboseFlag then
 
807
        ignore (E.log "Removing unused temporaries\n" );
 
808
 
 
809
      if Trace.traceActive "printCilTree" then
 
810
        dumpFile defaultCilPrinter stdout "stdout" file;
 
811
 
 
812
      (* digest any pragmas that would create additional roots *)
 
813
      let keepers = categorizePragmas file in
 
814
 
 
815
      (* if slicing, remove the bodies of non-kept functions *)
 
816
      if !Cilglobopt.sliceGlobal then
 
817
        amputateFunctionBodies keepers.defines file;
 
818
 
 
819
      (* build up the root set *)
 
820
      let isRoot global =
 
821
        isPragmaRoot keepers global ||
 
822
        isRoot global
 
823
      in
 
824
 
 
825
      (* mark everything reachable from the global roots *)
 
826
      clearReferencedBits file;
 
827
      markReachable file isRoot;
 
828
 
 
829
      (* take out the trash *)
 
830
      let removedLocals = removeUnmarked isRoot file in
 
831
 
 
832
      (* print which original source variables were removed *)
 
833
      if false && removedLocals != [] then
 
834
        let count = List.length removedLocals in
 
835
        if count > 2000 then
 
836
          ignore (E.warn "%d unused local variables removed" count)
 
837
        else
 
838
          ignore (E.warn "%d unused local variables removed:@!%a"
 
839
                    count (docList ~sep:(chr ',' ++ break) text) removedLocals)
 
840
    end