Search code examples
cframa-c

How can I visit my statements with vstmt_aux in Frama-C


I am working on a plugin for scalar replacement. I have some difficulty with the statement visitor. I would like to analyze the instructions that are in loops, for each instruction I need to know if it is included in a loop, if this loop itself is included or not in another loop. Here is for example a simple example of my 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

And a example of code I want to analyze:

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];
    }
}

When I run this script on this c code, only instruction A[i * nk + j] = (float) ((i*j+1) % ni) / ni; is detected.

Find instruction *(A + (i * nk + j)) = (float)((i * j + 1) % ni) / (float)ni; inside inner loop.

When I use DoChildren instead of SkipChildren in Loop pattern case, all instructions inside loops are detected more than one time. For example:

  • instruction A[i * nk + j] = (float) ((i*j+1) % ni) / ni; is detected three times:

Find instruction *(A + (i * nk + j)) = (float)((i * j + 1) % ni) / (float)ni; inside inner loop.

Find instruction *(A + (i * nk + j)) = (float)((i * j + 1) % ni) / (float)ni; inside outer loop.

Find instruction *(A + (i * nk + j)) = (float)((i * j + 1) % ni) / (float)ni;.

where only the first message is true.

  • instruction tmp[i * nj + j] = 0.0; is detected one time but output message is not correct (while this instruction is inside an inner loop)

Find instruction *(tmp + (i * nj + j)) = (float)0.0;.

  • same thing happens for instruction tmp[i * nj + j] += alpha * A[i * nk + k] * B[k * nj + j];.

When I add braces around instructions of each loop plus DoChildren, all instructions are detected by the pattern | Instr(i) -> Format.printf "Find instruction %a. @.\n" Printer.pp_instr i; Cil.SkipChildren.

Do you have any idea how I can solve this problem? Thanks


Solution

  • I think that the easiest answer is to show the code of Kernel_function.find_enclosing_loop, that returns the innermost loop to which a given statement belong (and raise Not_found if said statement is not in any loop). Note that for mainly historical reasons, the original code inherits from Cil.nopCilVisitor, but this does not matter here, and Visitor.frama_c_inplace should be preferred unless you have very specific uses.

    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
    

    Basically, the idea is that you maintain a stack of currently visited loops, and each time you reach a statement of interest, you can check how many elements there are in the stack. When you're done visiting the children of a loop, you just have to pop the stack (hence the DoChildrenPost).