如何在Frama-C中使用vstmt_aux访问我的语句

时间:2018-05-03 14:18:58

标签: c frama-c

我正在开发一个标量替换插件。我对声明访问者有些困难。我想分析循环中的指令,如果它包含在循环中,我需要知道的每条指令,如果这个循环本身包含在另一个循环中。以下是我的vstmt_aux的简单示例:

method! vstmt_aux s = 
match s.skind with
| Loop(_, body, l, _, _) -> begin
  let lp = (fst l).Lexing.pos_lnum in 
  Format.printf "Find outer loop at line %d @.\n" lp;
  let bst = body.bstmts in List.iter(fun vbody -> 
    match vbody.skind with
    | Loop(_, subbody, lsub, _, _) -> begin
      let ls = (fst lsub).Lexing.pos_lnum in 
      Format.printf "Find inner loop at line %d @.\n" ls;
      let sub_st = subbody.bstmts in List.iter(fun ss_body ->
        match ss_body.skind with
        | Instr(ii) -> Format.printf "Find instruction %a inside inner loop. @.\n" Printer.pp_instr ii 
        | Loop(_, l_sub, lss, _, _) -> begin
          let nss = (fst lss).Lexing.pos_lnum in 
          Format.printf "Find inner inner loop at line %d @.\n" nss;
          let ss_st = l_sub.bstmts in List.iter(fun ss ->
            match ss.skind with
            | Instr(iii) -> Format.printf "Find instruction %a inner inner loop. @.\n" Printer.pp_instr iii
            | _ -> ()
          ) ss_st
        end 
        | _ -> ()
      ) sub_st;
    end
    | Instr(iloop) -> Format.printf "Find instruction %a inside outer loop. @.\n" Printer.pp_instr iloop
    | _ -> ()
  ) bst;
  Cil.SkipChildren
end
| Instr(i) -> Format.printf "Find instruction %a. @.\n" Printer.pp_instr i; Cil.SkipChildren
| _ -> Cil.DoChildren

我要分析的代码示例:

void test(int ni, int nj, int nk, float alpha, float *tmp, float *A) {
  int i, j, k;
  for (i = 0; i < ni; i++) 
  for (j = 0; j < nk; j++) 
    A[i * nk + j] = (float) ((i*j+1) % ni) / ni;

  for (i = 0; i < ni; i++) 
    for (j = 0; j < nj; j++) {
      tmp[i * nj + j] = 0.0;
      for (k = 0; k < nk; ++k) 
        tmp[i * nj + j] += alpha * A[i * nk + k] * A[i * nk + k];
    }
}

当我在此c代码上运行此脚本时,仅检测到指令A[i * nk + j] = (float) ((i*j+1) % ni) / ni;

  

查找指令*(A +(i * nk + j))=(float)((i * j + 1)%ni)/(float)ni;内循环内部。

当我在循环模式案例中使用DoChildren而不是SkipChildren时,循环内的所有指令都会被多次检测到。例如:

  • 指令A[i * nk + j] = (float) ((i*j+1) % ni) / ni;被检测到三次:
  

查找指令*(A +(i * nk + j))=(float)((i * j + 1)%ni)/(float)ni;内循环内部。

     

查找指令*(A +(i * nk + j))=(float)((i * j + 1)%ni)/(float)ni;内外循环。

     

查找指令*(A +(i * nk + j))=(float)((i * j + 1)%ni)/(float)ni;。

只有第一条消息为真。

  • 指令tmp[i * nj + j] = 0.0;被检测到一次,但输出消息不正确(当此指令在内循环内时)
  

查找指令*(tmp +(i * nj + j))=(float)0.0;。

  • 同样的事情发生在指令tmp[i * nj + j] += alpha * A[i * nk + k] * B[k * nj + j];上。

当我在每个循环的指令和DoChildren周围添加括号时,所有指令都由模式| Instr(i) -> Format.printf "Find instruction %a. @.\n" Printer.pp_instr i; Cil.SkipChildren检测到。

你知道我怎么能解决这个问题吗? 感谢

1 个答案:

答案 0 :(得分:2)

我认为最简单的答案是显示Kernel_function.find_enclosing_loop的代码,它返回给定语句所属的最内层循环(如果所述语句不在任何循环中,则引发Not_found)。请注意,由于主要是历史原因,原始代码继承自Cil.nopCilVisitor,但这并不重要,除非您有非常具体的用途,否则Visitor.frama_c_inplace应该是首选。

let find_enclosing_loop kf stmt =
  let module Res = struct exception Found of Cil_types.stmt end in
  let vis = object
    inherit Visitor.frama_c_inplace
    val loops = Stack.create ()
    method! vstmt_aux s =
      match s.skind with
        | Loop _ ->
          Stack.push s loops;
          Cil.DoChildrenPost (fun s -> ignore (Stack.pop loops); s)
        | _ when Cil_datatype.Stmt.equal s stmt ->
          raise (Res.Found (Stack.top loops))
        | _ -> Cil.DoChildren
  end
  in
  try
    (match stmt.skind with
      | Loop _ -> stmt
      | _ ->
        ignore
          (Visitor.visitFramacFunction vis (get_definition kf));
        raise Not_found)
  with
    | No_Definition -> raise Not_found (* Not the good kf obviously. *)
    | Stack.Empty -> raise Not_found (* statement outside of a loop *)
    | Res.Found s -> s

基本上,我们的想法是维护一堆当前访问过的循环,每次到达感兴趣的语句时,都可以检查堆栈中有多少元素。当您完成访问循环子节点时,您只需弹出堆栈(因此DoChildrenPost)。