aboutsummaryrefslogtreecommitdiffstats
path: root/cparser/Checks.ml
blob: 507488f2437e6056d92576eabd3ac5c5a5b27963 (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
(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*         Bernhard Schommer, AbsInt Angewandte Informatik GmbH        *)
(*                                                                     *)
(*  Copyright Institut National de Recherche en Informatique et en     *)
(*  Automatique.  All rights reserved.  This file is distributed       *)
(*  under the terms of the GNU Lesser General Public License as        *)
(*  published by the Free Software Foundation, either version 2.1 of   *)
(*  the License, or  (at your option) any later version.               *)
(*  This file is also distributed under the terms of the               *)
(*  INRIA Non-Commercial License Agreement.                            *)
(*                                                                     *)
(* *********************************************************************)

open C
open Diagnostics
open Cutil
open Env

(* AST traversal functions *)

let fold_over_stmt_loc ~(expr: 'a -> location -> exp -> 'a)
    ~(decl: 'a -> location -> decl -> 'a)
    (a: 'a) (s: stmt) : 'a =
  let rec fold a s =
    match s.sdesc with
    | Sskip -> a
    | Sbreak -> a
    | Scontinue -> a
    | Slabeled(_, s1) -> fold a s1
    | Sgoto _ -> a
    | Sreturn None -> a
    | Sreturn (Some e) -> expr a s.sloc e
    | Sasm(_, _, outs, ins, _) -> asm_operands (asm_operands a s.sloc outs) s.sloc ins
    | Sdo e -> expr a s.sloc e
    | Sif (e, s1, s2) -> fold (fold (expr a s.sloc e) s1) s2
    | Sseq (s1, s2) -> fold (fold a s1) s2
    | Sfor (s1, e, s2, s3) -> fold (fold (expr (fold a s1) s.sloc e) s2) s3
    | Swhile(e, s1) -> fold (expr a s.sloc e) s1
    | Sdowhile (s1, e) -> expr (fold a s1) s.sloc e
    | Sswitch (e, s1) -> fold (expr a s.sloc e) s1
    | Sblock sl -> List.fold_left fold a sl
    | Sdecl d -> decl a s.sloc d
  and asm_operands a loc l =
    List.fold_left (fun a (_, _, e) -> expr a loc e) a l
  in fold a s

let iter_over_stmt_loc
    ?(expr = fun loc e -> ())
    ?(decl = fun loc decl -> ())
    (s: stmt) : unit =
  fold_over_stmt_loc ~expr: (fun () loc e -> expr loc e)
    ~decl: (fun () loc d -> decl loc d)
    () s

let fold_over_stmt ~(expr: 'a -> exp -> 'a)
    ~(decl: 'a -> location -> decl -> 'a)
    (a: 'a) (s: stmt) : 'a =
  fold_over_stmt_loc ~expr:(fun a _ e -> expr a e) ~decl:decl a s

let iter_over_stmt ?(expr = fun e -> ())
    ?(decl = fun loc decl -> ())
    (s:stmt) : unit =
  fold_over_stmt_loc ~expr:(fun () _ e -> expr e)
    ~decl:(fun () loc d -> decl loc d) () s

let fold_over_init ~(expr: 'a -> exp -> 'a) (a: 'a) (i: init) : 'a =
  let rec fold a = function
  | Init_single e -> expr a e
  | Init_array il -> List.fold_left fold a il
  | Init_struct (_, sl) -> List.fold_left (fun a (_,i) -> fold a i) a sl
  | Init_union (_, _, ui) -> fold a ui
  in fold a i

let iter_over_init ~(expr: exp -> unit) (i:init) : unit =
  fold_over_init ~expr:(fun () e -> expr e) () i

let fold_over_decl ~(expr: 'a -> exp -> 'a) (a: 'a) loc (sto, id, ty, init) : 'a=
  match init with
  | Some i -> fold_over_init ~expr a i
  | None -> a

let traverse_program
    ?(decl = fun env loc d -> ())
    ?(fundef = fun env loc fd -> ())
    ?(compositedecl = fun env loc su id attr -> ())
    ?(compositedef = fun env loc su id attr fl -> ())
    ?(typedef = fun env loc id ty -> ())
    ?(enum = fun env loc id attr members -> ())
    ?(pragma = fun env loc s -> ())
    p =
  let rec traverse env = function
    | [] -> ()
    | g :: gl ->
      let env =
        match g.gdesc with
        | Gdecl ((sto, id, ty, init) as d) ->
          decl env g.gloc d;
          add_ident env id sto ty
        | Gfundef f ->
          fundef env g.gloc f;
          add_ident env f.fd_name f.fd_storage (fundef_typ f)
        | Gcompositedecl (su,id,attr) ->
          compositedecl env g.gloc su id attr;
          add_composite env id (composite_info_decl su attr)
        | Gcompositedef (su,id,attr,fl) ->
          compositedef env g.gloc su id attr fl;
          add_composite env id (composite_info_def env su attr fl)
        | Gtypedef (id,ty) ->
          typedef env g.gloc id ty;
          add_typedef env id ty
        | Genumdef (id,attr,members) ->
          enum env g.gloc id attr members;
          add_enum env id {ei_members = members; ei_attr = attr}
        | Gpragma s ->
          pragma env g.gloc s;
          env in
      traverse env gl in
  traverse (Env.initial ()) p

(* Unknown attributes warning *)

let unknown_attrs loc attrs =
  let unknown attr =
    let attr_class = class_of_attribute attr in
    if attr_class = Attr_unknown then
      warning loc Unknown_attribute
        "unknown attribute '%s' ignored" (name_of_attribute attr) in
  List.iter unknown attrs

let unknown_attrs_typ env loc ty =
  let attr = attributes_of_type env ty in
  unknown_attrs loc attr

let unknown_attrs_decl env loc (sto, id, ty, init) =
  unknown_attrs_typ env loc ty

let unknown_attrs_stmt env s =
  iter_over_stmt ~decl:(unknown_attrs_decl env) s

let unknown_attrs_program p =
  let decl env loc d =
    unknown_attrs_decl env loc d
  and fundef env loc f =
     List.iter (fun (id,typ) -> unknown_attrs_typ env loc typ) f.fd_params;
     unknown_attrs loc f.fd_attrib;
     unknown_attrs_stmt env f.fd_body;
     List.iter (unknown_attrs_decl env loc) f.fd_locals;
  and compositedecl env loc su id attr =
    unknown_attrs loc attr
  and compositedef env loc su id attr fl =
    unknown_attrs loc attr;
    List.iter (fun fld ->  unknown_attrs_typ env loc fld.fld_typ) fl
  and typedef env loc id ty =
    unknown_attrs_typ env loc ty
  and enum env loc id attr members =
    unknown_attrs loc attr
  in
  traverse_program
    ~decl:decl
    ~fundef:fundef
    ~compositedecl:compositedecl
    ~compositedef:compositedef
    ~typedef:typedef
    ~enum:enum
    p

(* Unused variables and parameters warning *)

let rec vars_used_expr env e =
  match e.edesc with
  | EConst _
  | ESizeof _
  | EAlignof _ -> env
  | EVar id -> IdentSet.add id env
  | ECast (_,e)
  | EUnop (_,e) -> vars_used_expr env e
  | EBinop (_,e1,e2,_) ->
    let env = vars_used_expr env e1 in
    vars_used_expr env e2
  | EConditional (e1,e2,e3) ->
    let env = vars_used_expr env e1 in
    let env = vars_used_expr env e2 in
    vars_used_expr env e3
  | ECompound (_,init) -> vars_used_init env init
  | ECall (e,p) ->
    let env = vars_used_expr env e in
    List.fold_left vars_used_expr env p

and vars_used_init env init =
  fold_over_init ~expr:vars_used_expr env init

let vars_used_stmt env s =
  fold_over_stmt ~expr: vars_used_expr
    ~decl: (fold_over_decl ~expr: vars_used_expr) env s

let unused_variable env used loc (id, ty) =
  let attr = attributes_of_type env ty in
  let unused_attr = find_custom_attributes ["unused";"__unused__"] attr <> [] in
  if not ((IdentSet.mem id used) || unused_attr) then
    warning loc Unused_variable "unused variable '%s'" id.name

let unused_variables_stmt env used s =
  iter_over_stmt ~decl:(fun loc (sto, id, ty, init) -> unused_variable env used loc (id,ty)) s

let unused_variables p =
  let fundef env loc fd =
    let used = vars_used_stmt IdentSet.empty fd.fd_body in
    unused_variables_stmt env used fd.fd_body;
    List.iter (unused_variable env used loc) fd.fd_params in
  traverse_program
    ~fundef:fundef
    p

(* Warning for conditionals that cannot be transformed into linear code *)

(* Compute the set of local variables that do not have their address taken *)

let rec non_stack_locals_expr vars e =
  match e.edesc with
  | ECast (_,e) -> non_stack_locals_expr vars e
  | EUnop (Oaddrof,e) ->
    begin match e.edesc with
    | EVar id ->
      IdentSet.remove id vars
    | _ -> vars
    end
  | EUnop (Oderef, e) ->
    (* Special optimization *(& ...) is removed in SimplExpr *)
    begin match e.edesc with
      | EUnop (Oaddrof,e) -> non_stack_locals_expr vars e
      | _ -> non_stack_locals_expr vars e
    end
  | EUnop (_, e) ->
    non_stack_locals_expr vars e
  | EBinop (_,e1,e2,_) ->
    let vars = non_stack_locals_expr vars e1 in
    non_stack_locals_expr vars e2
  | EConditional (e1,e2,e3) ->
    let vars = non_stack_locals_expr vars e1 in
    let vars = non_stack_locals_expr vars e2 in
    non_stack_locals_expr vars e3
  | ECompound (_,init) -> non_stack_locals_init vars init
  | ECall (e,p) ->
    let vars = non_stack_locals_expr vars e in
    List.fold_left non_stack_locals_expr vars p
  | _ -> vars

and non_stack_locals_init vars init =
  fold_over_init ~expr:non_stack_locals_expr vars init

let add_vars env vars (id,ty) =
  let volatile = List.mem AVolatile (attributes_of_type env ty) in
  if not volatile then
    IdentSet.add id vars
  else
    vars

let non_stack_locals_stmt env vars s =
  let decl vars loc (sto, id, ty, init) =
    let vars = match init with
      | Some init -> non_stack_locals_init vars init
      | None -> vars in
    add_vars env vars (id,ty) in
  fold_over_stmt ~expr:non_stack_locals_expr ~decl:decl
    vars s

(* Check whether an expression is safe and can be always evaluated *)

let safe_cast env tfrom tto =
  match unroll env tfrom, unroll env tto with
  | (TInt _ | TPtr _ | TArray _ | TFun _ | TEnum _),
    (TInt _ | TPtr _ | TEnum _) -> true
  | TFloat _, TFloat _ -> true
  | _, _ -> equal_types env tfrom tto

let safe_expr vars env e =
  let rec expr e =
    match e.edesc with
    | EConst _ | ESizeof _ | EAlignof _   | ECompound _  -> true
    | EVar id -> (IdentSet.mem id vars) || not (is_scalar_type env e.etyp)
    | ECast (ty, e) ->
      safe_cast env e.etyp ty && expr e
    | EUnop (op, e) ->
      unop op  e
    | EBinop (op, e1, e2, ty) ->
      binop op e1  e2
    | EConditional _  -> false
    | ECall _ -> false
  and binop op e1 e2 =
    let is_long_long_type ty =
      match unroll env ty with
      | TInt (ILongLong, _)
      | TInt (IULongLong, _) -> true
      | _ -> false in
    match op with
    | Oadd | Osub | Omul | Oand | Oor | Oxor | Oshl | Oshr ->
      expr e1 && expr e2
    | Oeq | One | Olt | Ogt | Ole | Oge ->
      let not_long_long = not (is_long_long_type e1.etyp) && not (is_long_long_type e2.etyp) in
      not_long_long && expr e1 && expr e2
    | _ -> false
  (* x.f if f has array or struct or union type *)
  and unop  op e =
    match op with
    | Ominus | Onot | Olognot | Oplus -> expr e
    | Oaddrof ->
      begin match e.edesc with
        (* skip &*e *)
        | EUnop (Oderef, e) -> expr e
        (* skip &(e.f) *)
        | EUnop (Odot f, e) -> expr e
        | _ -> expr e
      end
    (* skip *&e *)
    | Oderef ->
      begin match e.edesc with
        | EUnop (Oaddrof,e) -> expr e
        | _ -> false
      end
    (* e.f is okay if f has array or composite type *)
    | Odot m ->
      let fld = field_of_dot_access env e.etyp m in
      (is_array_type env fld.fld_typ || is_composite_type env fld.fld_typ) && expr e
    | _ -> false in
  expr e

(* Check expressions if they contain conditionals that cannot be transformed in
   linear code. The inner_cond parameter is used to mimic the translation of short
   circuit logical or and logical and as well as conditional to if statements in
   SimplExpr. *)

let rec non_linear_cond_expr inner_cond vars env loc e =
  match e.edesc with
  | EConst _ | ESizeof _ | EAlignof _ | EVar _ -> ()
  | ECast (_ , e) | EUnop (_, e)-> non_linear_cond_expr false vars env loc e
  | EBinop (op, e1, e2, ty) ->
    let inner_cond = match op with
      | Ocomma -> inner_cond
      | Ologand | Ologor -> true
      | _ -> false
    in
    non_linear_cond_expr false vars env loc e1;
    non_linear_cond_expr inner_cond vars env loc e2
  | EConditional (c, e1, e2) ->
    let can_cast = safe_cast env e1.etyp e.etyp && safe_cast env e2.etyp e.etyp in
    if not can_cast || inner_cond || not (safe_expr vars env e1) || not (safe_expr vars env e2) then
      warning loc Non_linear_cond_expr "conditional expression may not be linearized";
    non_linear_cond_expr true vars env loc e1;
    non_linear_cond_expr true vars env loc e2;
  | ECompound (ty, init) -> non_linear_cond_init vars env loc init
  | ECall (e, params) ->
    non_linear_cond_expr false vars env loc e;
    List.iter (non_linear_cond_expr false vars env loc) params

and non_linear_cond_init vars env loc init =
  iter_over_init ~expr:(non_linear_cond_expr false vars env loc) init

let non_linear_cond_stmt vars env s =
  let decl loc (sto, id, ty, init) =
    match init with
    | None -> ()
    | Some init -> non_linear_cond_init vars env loc init in
  iter_over_stmt_loc ~expr:(non_linear_cond_expr false vars env) ~decl:decl s

let non_linear_conditional p =
  if active_warning Non_linear_cond_expr && !Clflags.option_Obranchless then begin
    let fundef env loc fd =
      let vars = List.fold_left (add_vars env) IdentSet.empty fd.fd_params in
      let vars = non_stack_locals_stmt env vars fd.fd_body in
      non_linear_cond_stmt vars env fd.fd_body;
    in
    traverse_program
      ~fundef:fundef
      p
  end