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:
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.
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;.
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
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
).