c - 如何在 Frama-C 中使用 vstmt_aux 访问我的语句
问题描述
我正在开发一个用于标量替换的插件。我对声明访问者有一些困难。我想分析循环中的指令,对于每条指令,我需要知道它是否包含在循环中,该循环本身是否包含在另一个循环中。例如,这是我的 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;
被检测 3 次:
查找指令 *(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 的指令周围添加大括号时,所有指令都会被 pattern 检测到| Instr(i) -> Format.printf "Find instruction %a. @.\n" Printer.pp_instr i; Cil.SkipChildren
。
你知道我该如何解决这个问题吗?谢谢
解决方案
我认为最简单的答案是显示 的代码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
)。
推荐阅读
- python - 为什么这个内核感知器实现需要无限的时间来运行?
- html - 我找不到解决方案,以便 Angular 可以访问我的一些文件
- r - 使用带有行和列索引的 mutate 和 group by
- php - 我如何从使用 php 中选择的用户输入中计算总价
- python - 将 DateField 添加到 models.py 时出现“函数缺少必需的参数 'month' (pos 2)”错误
- c++ - 为什么打印 c 样式字符串的“索引 n 的地址”会导致子字符串的输出
- swift - UICollectionView 的奇怪自动布局问题
- python - pandas cumsum 基于列的条件
- c# - 控制台:禁用 Shift+箭头键(文本选择模式)
- azure - 失败时的 Azure 数据工厂复制活动 | 未评估表达式