diff --git a/.Makefile.lint b/.Makefile.lint index c09a0269d79008f3c636c3d1c55df785cee4bc6a..d553a4f87b25859cfc56c8d3f410139bccae5eb5 100644 --- a/.Makefile.lint +++ b/.Makefile.lint @@ -12,8 +12,6 @@ ML_LINT_KO+=src/kernel_internals/runtime/messages.mli ML_LINT_KO+=src/kernel_internals/runtime/special_hooks.ml ML_LINT_KO+=src/kernel_internals/typing/allocates.ml ML_LINT_KO+=src/kernel_internals/typing/asm_contracts.ml -ML_LINT_KO+=src/kernel_internals/typing/cfg.ml -ML_LINT_KO+=src/kernel_internals/typing/cfg.mli ML_LINT_KO+=src/kernel_internals/typing/frontc.mli ML_LINT_KO+=src/kernel_internals/typing/infer_annotations.ml ML_LINT_KO+=src/kernel_internals/typing/logic_builtin.ml diff --git a/src/kernel_internals/typing/cfg.ml b/src/kernel_internals/typing/cfg.ml index b86c46e5e98d087e21d713fa28161daa6f1886bf..1ffc5c6b32b0e4bfa3fff39766b17671e172ba3d 100644 --- a/src/kernel_internals/typing/cfg.ml +++ b/src/kernel_internals/typing/cfg.ml @@ -53,27 +53,27 @@ open Cil_datatype let nodeList : stmt list ref = ref [] class caseLabeledStmtFinder slr = object - inherit nopCilVisitor + inherit nopCilVisitor - method! vstmt s = - if List.exists (fun l -> - match l with | Case(_, _) | Default _ -> true | _ -> false) - s.labels - then begin - slr := s :: (!slr); - match s.skind with - | Switch(_,_,_,_) -> SkipChildren - | _ -> DoChildren - end else match s.skind with - | Switch(_,_,_,_) -> SkipChildren - | _ -> DoChildren + method! vstmt s = + if List.exists (fun l -> + match l with | Case(_, _) | Default _ -> true | _ -> false) + s.labels + then begin + slr := s :: (!slr); + match s.skind with + | Switch(_,_,_,_) -> SkipChildren + | _ -> DoChildren + end else match s.skind with + | Switch(_,_,_,_) -> SkipChildren + | _ -> DoChildren end let findCaseLabeledStmts (b : block) : stmt list = - let slr = ref [] in - let vis = new caseLabeledStmtFinder slr in - ignore(visitCilBlock vis b); - !slr + let slr = ref [] in + let vis = new caseLabeledStmtFinder slr in + ignore(visitCilBlock vis b); + !slr (* we might need to add a Skip statement at the end of a block in order to avoid that a break close too many blocks, in particular when one @@ -144,8 +144,8 @@ and cfgStmts env (ss: stmt list) next break cont = [] -> (); | [s] -> cfgStmt (innermost_last env) s next break cont | hd::tl -> - cfgStmt env hd (Some (List.hd tl)) break cont; - cfgStmts env tl next break cont + cfgStmt env hd (Some (List.hd tl)) break cont; + cfgStmts env tl next break cont (* Fill in the CFG info for the stmts in a block next = succ of the last stmt in this block @@ -172,7 +172,7 @@ and cfgStmt env (s: stmt) next break cont = (* We might have duplicate in succs here. This is important to preserve the invariant that If has exactly two successors (in case of [if(e);L:...], both branches will have [L:] as successor). - *) + *) if not (List.memq s n.preds) then n.preds <- s::n.preds in let addOptionSucc (n: stmt option) = @@ -192,16 +192,16 @@ and cfgStmt env (s: stmt) next break cont = in let cfgCatch env c next break cont = match c with - | Catch_all -> () - | Catch_exn(_,l) -> - let cfg_aux_clause (_,b) = cfgBlock env b next break cont in - List.iter cfg_aux_clause l + | Catch_all -> () + | Catch_exn(_,l) -> + let cfg_aux_clause (_,b) = cfgBlock env b next break cont in + List.iter cfg_aux_clause l in let add_stmt_if_needed env s = if requires_new_stmt env then begin match s with | None -> assert false - (* we have explicitly created a statement as target of break *) + (* we have explicitly created a statement as target of break *) | Some stmt -> match stmt.preds with | [] -> () @@ -214,61 +214,60 @@ and cfgStmt env (s: stmt) next break cont = in let instrFallsThrough (i : instr) : bool = match i with Call (_, {enode = Lval (Var vf, NoOffset)}, _, _) -> - (* See if this has the noreturn attribute *) - not (hasAttribute "noreturn" vf.vattr) + (* See if this has the noreturn attribute *) + not (hasAttribute "noreturn" vf.vattr) | Call (_, f, _, _) -> - not (typeHasAttribute "noreturn" (typeOf f)) + not (typeHasAttribute "noreturn" (typeOf f)) | _ -> true in match s.skind with Instr il -> - if instrFallsThrough il then - addOptionSucc next - else - () + if instrFallsThrough il then + addOptionSucc next + else + () | Return _ | Throw _ -> () | Goto (p,_) when not s.ghost && !p.ghost -> - Kernel.error - "%a:@ '%a' cannot see target label (ghost), removing ghost status of the label." - Location.pretty (Stmt.loc s) Cil_printer.pp_stmt s ; - (!p).ghost <- false ; + Kernel.error ~once:true ~source:(fst (Stmt.loc s)) + "'%a' would jump from normal statement to ghost code" + Cil_printer.pp_stmt s ; addSucc !p | Goto (p,_) -> addSucc !p | Break _ -> addOptionSucc break | Continue _ -> addOptionSucc cont | If (_, blk1, blk2, _) -> - (* The succs of If is [true branch;false branch]. Do the 'else' block - first. *) - addBlockSucc blk2; - addBlockSucc blk1; - cfgBlock env blk1 next break cont; - cfgBlock env blk2 next break cont + (* The succs of If is [true branch;false branch]. Do the 'else' block + first. *) + addBlockSucc blk2; + addBlockSucc blk1; + cfgBlock env blk1 next break cont; + cfgBlock env blk2 next break cont | UnspecifiedSequence seq -> - addBlockSucc (block_from_unspecified_sequence seq); - cfgBlock env (block_from_unspecified_sequence seq) next break cont + addBlockSucc (block_from_unspecified_sequence seq); + cfgBlock env (block_from_unspecified_sequence seq) next break cont | Block b -> - addBlockSucc b; - cfgBlock env b next break cont + addBlockSucc b; + cfgBlock env b next break cont | Switch(_,blk,_l,loc) -> let break = make_break_stmt loc env next in let bl = findCaseLabeledStmts blk in - (* if there's no default, need to connect s->next *) - if not (List.exists - (fun stmt -> List.exists - (function Default _ -> true | _ -> false) - stmt.labels) - bl) - then addOptionSucc next; - (* Then add cases, that will come first in final 'succs' list. bl - is already reversed, so the order is ok. *) - List.iter addSucc bl; - (* we are ready to add a statement when needed. Hence we're not the - last statement of a block with VLA scope. - *) - cfgBlock (innermost_nonlast env) blk next break cont; - add_stmt_if_needed env break; + (* if there's no default, need to connect s->next *) + if not (List.exists + (fun stmt -> List.exists + (function Default _ -> true | _ -> false) + stmt.labels) + bl) + then addOptionSucc next; + (* Then add cases, that will come first in final 'succs' list. bl + is already reversed, so the order is ok. *) + List.iter addSucc bl; + (* we are ready to add a statement when needed. Hence we're not the + last statement of a block with VLA scope. + *) + cfgBlock (innermost_nonlast env) blk next break cont; + add_stmt_if_needed env break; | Loop(_,blk,loc,_,_) -> let break = make_break_stmt loc env next in addBlockSuccFull s blk; @@ -290,14 +289,14 @@ and cfgStmt env (s: stmt) next break cont = goes from the conversion block to the main block of the catch clause *) List.iter (fun (c,b) -> - let n = - match b.bstmts with - | [] -> next - | s::_ -> Some s - in - cfgCatch env c n break cont; cfgBlock env b next break cont) c; + let n = + match b.bstmts with + | [] -> next + | s::_ -> Some s + in + cfgCatch env c n break cont; cfgBlock env b next break cont) c; | TryExcept _ | TryFinally _ -> - Kernel.fatal "try/except/finally" + Kernel.fatal "try/except/finally" (*------------------------------------------------------------*) @@ -323,9 +322,9 @@ let clearCFGinfo ?(clear_id=true) (fd : fundec) = let clearFileCFG ?(clear_id=true) (f : file) = iterGlobals f (fun g -> - match g with - | GFun(fd,_) -> clearCFGinfo ~clear_id fd - | _ -> ()) + match g with + | GFun(fd,_) -> clearCFGinfo ~clear_id fd + | _ -> ()) let clear_sid_info_ref = Extlib.mk_fun "Cfg.clear_sid_info_ref" @@ -334,10 +333,10 @@ let computeFileCFG (f : file) = iterGlobals f (fun g -> match g with GFun(fd,_) -> cfgFun fd | _ -> ()) - (* This alphaTable is used to prevent collision of label names when - transforming switch statements and loops. It uses a *unit* - alphaTableData ref because there isn't any information we need to - carry around. *) +(* This alphaTable is used to prevent collision of label names when + transforming switch statements and loops. It uses a *unit* + alphaTableData ref because there isn't any information we need to + carry around. *) let labelAlphaTable : unit Alpha.alphaTable = Hashtbl.create 11 let freshLabel (base:string) = @@ -347,42 +346,42 @@ let freshLabel (base:string) = let xform_switch_block ?(keepSwitch=false) b = let breaks_stack = Stack.create () in let continues_stack = Stack.create () in - (* NB: these are two stacks of stack, as the scope of - breaks/continues clauses depends on two things: First, - /*@ breaks P */ while(1) {} is not the same thing as - while(1) { /*@ breaks P */ }: - only the latter applies to the break of the current loop. - Second - while(1) { /*@ breaks P1 */ { /*@ breaks P2 */{}}} - requires maintaining an inner stack, since the breaks - of the current loop are under two different, nested, breaks - clauses *) + (* NB: these are two stacks of stack, as the scope of + breaks/continues clauses depends on two things: First, + /*@ breaks P */ while(1) {} is not the same thing as + while(1) { /*@ breaks P */ }: + only the latter applies to the break of the current loop. + Second + while(1) { /*@ breaks P1 */ { /*@ breaks P2 */{}}} + requires maintaining an inner stack, since the breaks + of the current loop are under two different, nested, breaks + clauses *) let () = Stack.push (Stack.create()) breaks_stack in let () = Stack.push (Stack.create()) continues_stack in let assert_of_clause f ca = match ca.annot_content with - | AAssert _ | AInvariant _ | AVariant _ | AAssigns _ | AAllocation _ | APragma _ | AExtended _ -> Logic_const.ptrue - | AStmtSpec (_bhv,s) -> - let open Logic_const in - List.fold_left - (fun acc bhv -> - pand - (acc, - pimplies - (pands - (List.map - (fun p -> + | AAssert _ | AInvariant _ | AVariant _ | AAssigns _ | AAllocation _ | APragma _ | AExtended _ -> Logic_const.ptrue + | AStmtSpec (_bhv,s) -> + let open Logic_const in + List.fold_left + (fun acc bhv -> + pand + (acc, + pimplies + (pands + (List.map + (fun p -> pold ~loc:p.ip_content.pred_loc (Logic_const.pred_of_id_pred p)) - bhv.b_assumes), - pands - (List.fold_left - (fun acc (kind,p) -> + bhv.b_assumes), + pands + (List.fold_left + (fun acc (kind,p) -> if f kind then Logic_const.pred_of_id_pred p :: acc else acc) - [ptrue] bhv.b_post_cond) - ))) - ptrue s.spec_behavior + [ptrue] bhv.b_post_cond) + ))) + ptrue s.spec_behavior in let assert_of_continues ca = assert_of_clause (function Continues -> true | _ -> false) ca @@ -428,293 +427,293 @@ let xform_switch_block ?(keepSwitch=false) b = in let rec xform_switch_stmt stmts break_dest cont_dest label_index popstack = match stmts with - [] -> [] - | s :: rest -> - begin - CurrentLoc.set (Stmt.loc s); - if not keepSwitch then - s.labels <- List.map (fun lab -> match lab with + [] -> [] + | s :: rest -> + begin + CurrentLoc.set (Stmt.loc s); + if not keepSwitch then + s.labels <- List.map (fun lab -> match lab with Label _ -> lab | Case(e,l) -> - let suffix = - match isInteger e with - | Some value -> - if Integer.lt value Integer.zero then - "neg_" ^ Integer.to_string (Integer.neg value) - else - Integer.to_string value - | None -> - "exp" - in - let str = Format.sprintf "switch_%d_%s" label_index suffix in - (Label(freshLabel str,l,false)) + let suffix = + match isInteger e with + | Some value -> + if Integer.lt value Integer.zero then + "neg_" ^ Integer.to_string (Integer.neg value) + else + Integer.to_string value + | None -> + "exp" + in + let str = Format.sprintf "switch_%d_%s" label_index suffix in + (Label(freshLabel str,l,false)) | Default(l) -> Label(freshLabel - (Printf.sprintf "switch_%d_default" label_index), - l, false) + (Printf.sprintf "switch_%d_default" label_index), + l, false) ) s.labels ; - match s.skind with - | Instr (Code_annot (ca,_)) -> - add_clause s ca; - s:: - xform_switch_stmt - rest break_dest cont_dest label_index (popstack+1) - | Instr _ | Return _ | Goto _ | Throw _ -> - popn popstack; - s:: - xform_switch_stmt - rest break_dest cont_dest label_index 0 - | Break(l) -> - if Stack.is_empty breaks_stack then - Kernel.fatal "empty breaks stack"; - s.skind <- Goto(break_dest (),l); - let breaks = Stack.top breaks_stack in - let assertion = ref Logic_const.ptrue in - Stack.iter (fun p -> - assertion := Logic_const.pand (p,!assertion)) breaks; - (match !assertion with - { pred_content = Ptrue } -> - popn popstack; - s :: - xform_switch_stmt - rest break_dest cont_dest label_index 0 - | p -> - let a = - Logic_const.new_code_annotation (AAssert ([], Assert, p)) - in - let assertion = mkStmt (Instr(Code_annot(a,l))) in - popn popstack; - assertion:: s :: - xform_switch_stmt - rest break_dest cont_dest label_index 0) - | Continue(l) -> - if Stack.is_empty continues_stack then - Kernel.fatal "empty continues stack"; - s.skind <- Goto(cont_dest (),l); - let continues = Stack.top continues_stack in - let assertion = ref Logic_const.ptrue in - Stack.iter (fun p -> - assertion := Logic_const.pand(p,!assertion)) continues; - (match !assertion with - { pred_content = Ptrue } -> - popn popstack; - s :: - xform_switch_stmt - rest break_dest cont_dest label_index 0 - | p -> - let a = - Logic_const.new_code_annotation (AAssert ([], Assert, p)) - in - let assertion = mkStmt (Instr(Code_annot(a,l))) in - popn popstack; - assertion :: s :: - xform_switch_stmt - rest break_dest cont_dest label_index 0) - | If(e,b1,b2,l) -> - let b1 = xform_switch_block b1 break_dest cont_dest label_index - in - let b2 = xform_switch_block b2 break_dest cont_dest label_index - in - popn popstack; - s.skind <- If(e,b1,b2,l); - s:: xform_switch_stmt rest break_dest cont_dest label_index 0 - | Switch(e,b,sl,(_, snd_l as l)) -> - let loc = snd_l, snd_l in - if keepSwitch then begin - let label_index = label_index + 1 in - let break_stmt = mkStmt (Instr (Skip loc)) in - break_stmt.labels <- - [Label - (freshLabel - (Printf.sprintf "switch_%d_break" label_index), - l, false)] ; - Stack.push (Stack.create()) breaks_stack; - let b = - xform_switch_block - b (fun () -> ref break_stmt) cont_dest label_index - in - ignore (Stack.pop breaks_stack); - popn popstack; - s.skind <- Switch (e,b,sl,l); - s::break_stmt:: - xform_switch_stmt rest break_dest cont_dest label_index 0 - end else begin - (* change - * switch (se) { - * case 0: s0 ; - * case 1: s1 ; break; - * ... - * } - * - * into: - * - * if (se == 0) goto label_0; - * else if (se == 1) goto label_1; - * ... - * else goto label_break; - * { // body_block - * label_0: s0; - * label_1: s1; goto label_break; - * ... - * } - * label_break: ; // break_stmt - * - *) - let label_index = label_index + 1 in - let break_stmt = mkStmt (Instr (Skip loc)) in - break_stmt.labels <- - [Label(freshLabel - (Printf.sprintf - "switch_%d_break" label_index), l, false)] ; - (* The default case, if present, must be used only if *all* - non-default cases fail [ISO/IEC 9899:1999, §6.8.4.2, ¶5]. As a - result, we sort the order in which we handle the labels (but not the - order in which we print out the statements, so fall-through still - works as expected). *) - let compare_choices s1 s2 = match s1.labels, s2.labels with - | (Default(_) :: _), _ -> 1 - | _, (Default(_) :: _) -> -1 - | _, _ -> 0 - in - let rec handle_choices sl = - match sl with - [] -> - (* If there's no case that matches and no default, - we just skip the entire switch (6.8.4.2.5)*) - Goto (ref break_stmt,l) - | stmt_hd :: stmt_tl -> - let rec handle_labels lab_list = - match lab_list with - [] -> handle_choices stmt_tl - | Case(ce,cl) :: lab_tl -> - (* begin replacement: *) - let pred = - match ce.enode with - Const (CInt64 (z,_,_)) - when Integer.equal z Integer.zero - -> - new_exp ~loc:ce.eloc (UnOp(LNot,e,intType)) - | _ -> - new_exp ~loc:ce.eloc (BinOp(Eq,e,ce,intType)) - in - (* end replacement *) - let then_block = - mkBlock [ mkStmt (Goto(ref stmt_hd,cl)) ] in - let else_block = - mkBlock [ mkStmt (handle_labels lab_tl) ] in - If(pred,then_block,else_block,cl) - | Default(dl) :: lab_tl -> - (* ww: before this was 'if (1) goto label', - but as Ben points out this might confuse - someone down the line who doesn't have special - handling for if(1) into thinking - that there are two paths here. - The simpler 'goto label' is what we want. *) - Block(mkBlock [ mkStmt (Goto(ref stmt_hd,dl)) ; - mkStmt (handle_labels lab_tl) ]) - | Label(_,_,_) :: lab_tl -> handle_labels lab_tl - in - handle_labels stmt_hd.labels - in - let sl = List.sort compare_choices sl in - let ifblock = mkStmt (handle_choices sl) in - Stack.push (Stack.create()) breaks_stack; - let switch_block = - xform_switch_block - b (fun () -> ref break_stmt) cont_dest label_index + match s.skind with + | Instr (Code_annot (ca,_)) -> + add_clause s ca; + s:: + xform_switch_stmt + rest break_dest cont_dest label_index (popstack+1) + | Instr _ | Return _ | Goto _ | Throw _ -> + popn popstack; + s:: + xform_switch_stmt + rest break_dest cont_dest label_index 0 + | Break(l) -> + if Stack.is_empty breaks_stack then + Kernel.fatal "empty breaks stack"; + s.skind <- Goto(break_dest (),l); + let breaks = Stack.top breaks_stack in + let assertion = ref Logic_const.ptrue in + Stack.iter (fun p -> + assertion := Logic_const.pand (p,!assertion)) breaks; + (match !assertion with + { pred_content = Ptrue } -> + popn popstack; + s :: + xform_switch_stmt + rest break_dest cont_dest label_index 0 + | p -> + let a = + Logic_const.new_code_annotation (AAssert ([], Assert, p)) + in + let assertion = mkStmt (Instr(Code_annot(a,l))) in + popn popstack; + assertion:: s :: + xform_switch_stmt + rest break_dest cont_dest label_index 0) + | Continue(l) -> + if Stack.is_empty continues_stack then + Kernel.fatal "empty continues stack"; + s.skind <- Goto(cont_dest (),l); + let continues = Stack.top continues_stack in + let assertion = ref Logic_const.ptrue in + Stack.iter (fun p -> + assertion := Logic_const.pand(p,!assertion)) continues; + (match !assertion with + { pred_content = Ptrue } -> + popn popstack; + s :: + xform_switch_stmt + rest break_dest cont_dest label_index 0 + | p -> + let a = + Logic_const.new_code_annotation (AAssert ([], Assert, p)) + in + let assertion = mkStmt (Instr(Code_annot(a,l))) in + popn popstack; + assertion :: s :: + xform_switch_stmt + rest break_dest cont_dest label_index 0) + | If(e,b1,b2,l) -> + let b1 = xform_switch_block b1 break_dest cont_dest label_index + in + let b2 = xform_switch_block b2 break_dest cont_dest label_index + in + popn popstack; + s.skind <- If(e,b1,b2,l); + s:: xform_switch_stmt rest break_dest cont_dest label_index 0 + | Switch(e,b,sl,(_, snd_l as l)) -> + let loc = snd_l, snd_l in + if keepSwitch then begin + let label_index = label_index + 1 in + let break_stmt = mkStmt (Instr (Skip loc)) in + break_stmt.labels <- + [Label + (freshLabel + (Printf.sprintf "switch_%d_break" label_index), + l, false)] ; + Stack.push (Stack.create()) breaks_stack; + let b = + xform_switch_block + b (fun () -> ref break_stmt) cont_dest label_index + in + ignore (Stack.pop breaks_stack); + popn popstack; + s.skind <- Switch (e,b,sl,l); + s::break_stmt:: + xform_switch_stmt rest break_dest cont_dest label_index 0 + end else begin + (* change + * switch (se) { + * case 0: s0 ; + * case 1: s1 ; break; + * ... + * } + * + * into: + * + * if (se == 0) goto label_0; + * else if (se == 1) goto label_1; + * ... + * else goto label_break; + * { // body_block + * label_0: s0; + * label_1: s1; goto label_break; + * ... + * } + * label_break: ; // break_stmt + * + *) + let label_index = label_index + 1 in + let break_stmt = mkStmt (Instr (Skip loc)) in + break_stmt.labels <- + [Label(freshLabel + (Printf.sprintf + "switch_%d_break" label_index), l, false)] ; + (* The default case, if present, must be used only if *all* + non-default cases fail [ISO/IEC 9899:1999, §6.8.4.2, ¶5]. As a + result, we sort the order in which we handle the labels (but not the + order in which we print out the statements, so fall-through still + works as expected). *) + let compare_choices s1 s2 = match s1.labels, s2.labels with + | (Default(_) :: _), _ -> 1 + | _, (Default(_) :: _) -> -1 + | _, _ -> 0 + in + let rec handle_choices sl = + match sl with + [] -> + (* If there's no case that matches and no default, + we just skip the entire switch (6.8.4.2.5)*) + Goto (ref break_stmt,l) + | stmt_hd :: stmt_tl -> + let rec handle_labels lab_list = + match lab_list with + [] -> handle_choices stmt_tl + | Case(ce,cl) :: lab_tl -> + (* begin replacement: *) + let pred = + match ce.enode with + Const (CInt64 (z,_,_)) + when Integer.equal z Integer.zero + -> + new_exp ~loc:ce.eloc (UnOp(LNot,e,intType)) + | _ -> + new_exp ~loc:ce.eloc (BinOp(Eq,e,ce,intType)) + in + (* end replacement *) + let then_block = + mkBlock [ mkStmt (Goto(ref stmt_hd,cl)) ] in + let else_block = + mkBlock [ mkStmt (handle_labels lab_tl) ] in + If(pred,then_block,else_block,cl) + | Default(dl) :: lab_tl -> + (* ww: before this was 'if (1) goto label', + but as Ben points out this might confuse + someone down the line who doesn't have special + handling for if(1) into thinking + that there are two paths here. + The simpler 'goto label' is what we want. *) + Block(mkBlock [ mkStmt (Goto(ref stmt_hd,dl)) ; + mkStmt (handle_labels lab_tl) ]) + | Label(_,_,_) :: lab_tl -> handle_labels lab_tl in - ignore (Stack.pop breaks_stack); - popn popstack; - s.skind <- Block switch_block; - (match switch_block.bstmts with - ({ skind = Instr(Code_annot _) } as ca):: tl -> - (* We move the annotation outside of the block, since - the \old would otherwise be attached to a label which - by construction is never reached. - *) - switch_block.bstmts <- ca :: ifblock :: tl - | l -> switch_block.bstmts <- ifblock :: l); - s :: break_stmt :: - xform_switch_stmt rest break_dest cont_dest label_index 0 - end - | Loop(a,b,(fst_l, snd_l as l),_,_) -> - let label_index = label_index + 1 in - let loc_break = snd_l, snd_l in - let break_stmt = mkStmt (Instr (Skip loc_break)) in - break_stmt.labels <- - [Label(freshLabel - (Printf.sprintf - "while_%d_break" label_index),l,false)] ; - let cont_loc = fst_l, fst_l in - let cont_stmt = mkStmt (Instr (Skip cont_loc)) in - b.bstmts <- cont_stmt :: b.bstmts ; - let my_break_dest () = ref break_stmt in - let use_continue = ref false in - let my_cont_dest () = - use_continue := true; - ref cont_stmt - in - Stack.push (Stack.create ()) breaks_stack; - Stack.push (Stack.create ()) continues_stack; - let b = - xform_switch_block b my_break_dest my_cont_dest label_index - in - if !use_continue then - cont_stmt.labels <- - [Label - (freshLabel - (Printf.sprintf - "while_%d_continue" label_index),l,false)] ; - s.skind <- Loop(a,b,l,Some(cont_stmt),Some(break_stmt)); - break_stmt.succs <- s.succs ; - ignore (Stack.pop breaks_stack); - ignore (Stack.pop continues_stack); - popn popstack; - s :: break_stmt :: - xform_switch_stmt rest break_dest cont_dest label_index 0 - | Block b -> - let b = xform_switch_block b break_dest cont_dest label_index in - popn popstack; - s.skind <- Block b; - s :: xform_switch_stmt rest break_dest cont_dest label_index 0 - | TryCatch (t,c,l) -> - let t' = xform_switch_block t break_dest cont_dest label_index in - let c' = - List.map - (fun (e,b) -> - (e, xform_switch_block b break_dest cont_dest label_index)) - c - in - s.skind <- TryCatch(t',c',l); - popn popstack; - s :: xform_switch_stmt rest break_dest cont_dest label_index 0 - | UnspecifiedSequence seq -> - let seq = - xform_switch_unspecified seq break_dest cont_dest label_index - in - popn popstack; - s.skind <- UnspecifiedSequence seq; - s :: xform_switch_stmt rest break_dest cont_dest label_index 0 - | TryExcept _ | TryFinally _ -> - Kernel.fatal - "xform_switch_statement: \ - structured exception handling not implemented" - end - and xform_switch_block b break_dest cont_dest label_index = - (* [VP] I fail to understand what link_succs is supposed to do. The head - of the block has as successors all the statements in the block? *) - (* let rec link_succs sl = match sl with - | [] -> () - | hd :: tl -> (if hd.succs = [] then hd.succs <- tl) ; link_succs tl + handle_labels stmt_hd.labels in - link_succs b.bstmts ; *) + let sl = List.sort compare_choices sl in + let ifblock = mkStmt (handle_choices sl) in + Stack.push (Stack.create()) breaks_stack; + let switch_block = + xform_switch_block + b (fun () -> ref break_stmt) cont_dest label_index + in + ignore (Stack.pop breaks_stack); + popn popstack; + s.skind <- Block switch_block; + (match switch_block.bstmts with + ({ skind = Instr(Code_annot _) } as ca):: tl -> + (* We move the annotation outside of the block, since + the \old would otherwise be attached to a label which + by construction is never reached. + *) + switch_block.bstmts <- ca :: ifblock :: tl + | l -> switch_block.bstmts <- ifblock :: l); + s :: break_stmt :: + xform_switch_stmt rest break_dest cont_dest label_index 0 + end + | Loop(a,b,(fst_l, snd_l as l),_,_) -> + let label_index = label_index + 1 in + let loc_break = snd_l, snd_l in + let break_stmt = mkStmt (Instr (Skip loc_break)) in + break_stmt.labels <- + [Label(freshLabel + (Printf.sprintf + "while_%d_break" label_index),l,false)] ; + let cont_loc = fst_l, fst_l in + let cont_stmt = mkStmt (Instr (Skip cont_loc)) in + b.bstmts <- cont_stmt :: b.bstmts ; + let my_break_dest () = ref break_stmt in + let use_continue = ref false in + let my_cont_dest () = + use_continue := true; + ref cont_stmt + in + Stack.push (Stack.create ()) breaks_stack; + Stack.push (Stack.create ()) continues_stack; + let b = + xform_switch_block b my_break_dest my_cont_dest label_index + in + if !use_continue then + cont_stmt.labels <- + [Label + (freshLabel + (Printf.sprintf + "while_%d_continue" label_index),l,false)] ; + s.skind <- Loop(a,b,l,Some(cont_stmt),Some(break_stmt)); + break_stmt.succs <- s.succs ; + ignore (Stack.pop breaks_stack); + ignore (Stack.pop continues_stack); + popn popstack; + s :: break_stmt :: + xform_switch_stmt rest break_dest cont_dest label_index 0 + | Block b -> + let b = xform_switch_block b break_dest cont_dest label_index in + popn popstack; + s.skind <- Block b; + s :: xform_switch_stmt rest break_dest cont_dest label_index 0 + | TryCatch (t,c,l) -> + let t' = xform_switch_block t break_dest cont_dest label_index in + let c' = + List.map + (fun (e,b) -> + (e, xform_switch_block b break_dest cont_dest label_index)) + c + in + s.skind <- TryCatch(t',c',l); + popn popstack; + s :: xform_switch_stmt rest break_dest cont_dest label_index 0 + | UnspecifiedSequence seq -> + let seq = + xform_switch_unspecified seq break_dest cont_dest label_index + in + popn popstack; + s.skind <- UnspecifiedSequence seq; + s :: xform_switch_stmt rest break_dest cont_dest label_index 0 + | TryExcept _ | TryFinally _ -> + Kernel.fatal + "xform_switch_statement: \ + structured exception handling not implemented" + end + and xform_switch_block b break_dest cont_dest label_index = + (* [VP] I fail to understand what link_succs is supposed to do. The head + of the block has as successors all the statements in the block? *) + (* let rec link_succs sl = match sl with + | [] -> () + | hd :: tl -> (if hd.succs = [] then hd.succs <- tl) ; link_succs tl + in + link_succs b.bstmts ; *) { b with bstmts = - xform_switch_stmt b.bstmts break_dest cont_dest label_index 0 } + xform_switch_stmt b.bstmts break_dest cont_dest label_index 0 } and xform_switch_unspecified seq break_dest cont_dest label_index = let treat_one (s,m,w,r,c) = - (* NB: this assumes that we don't have any statement contract in - an unspecified sequence. - *) + (* NB: this assumes that we don't have any statement contract in + an unspecified sequence. + *) let res = xform_switch_stmt [s] break_dest cont_dest label_index 0 in (List.hd res, m,w,r,c) diff --git a/src/kernel_internals/typing/cfg.mli b/src/kernel_internals/typing/cfg.mli index db42387f27ee3e863422f40f1043e8948b50df5e..0cc33e231128e5812b91e775be881bc2885dadfe 100644 --- a/src/kernel_internals/typing/cfg.mli +++ b/src/kernel_internals/typing/cfg.mli @@ -57,7 +57,7 @@ val computeFileCFG: file -> unit val clearFileCFG: ?clear_id:bool -> file -> unit (** Compute a control flow graph for fd. Stmts in fd have preds and succs - filled in *) + filled in *) val cfgFun : fundec -> unit (** clear the sid, succs, and preds fields of each statement in a function *) @@ -76,4 +76,3 @@ val prepareCFG: ?keepSwitch:bool -> fundec -> unit (**/**) val clear_sid_info_ref: (unit -> unit) ref - diff --git a/tests/cil/oracle/ghost_cfg.1.res.oracle b/tests/cil/oracle/ghost_cfg.1.res.oracle index 387a9f72dd7752da8c4012041203541600e520dd..286d75896541aa3b0a9fd374e273cd2323dc2115 100644 --- a/tests/cil/oracle/ghost_cfg.1.res.oracle +++ b/tests/cil/oracle/ghost_cfg.1.res.oracle @@ -1,5 +1,5 @@ [kernel] Parsing tests/cil/ghost_cfg.c (with preprocessing) -[kernel] User Error: tests/cil/ghost_cfg.c:101: - 'goto X;' cannot see target label (ghost), removing ghost status of the label. +[kernel] tests/cil/ghost_cfg.c:101: User Error: + 'goto X;' would jump from normal statement to ghost code [kernel] User Error: Deferred error message was emitted during execution. See above messages for more information. [kernel] Frama-C aborted: invalid user input.