Как я могу посетить мои заявления с vstmt_aux в Frama-C - PullRequest
0 голосов
/ 03 мая 2018

Я работаю над плагином для скалярной замены. У меня есть некоторые трудности с заявлением посетителя. Я хотел бы проанализировать инструкции, которые находятся в циклах, для каждой инструкции мне нужно знать, включена ли она в цикл, включен ли сам этот цикл или нет в другой цикл. Вот, например, простой пример моего 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)) = (плавать) ((i * j + 1)% ni) / (плавать) ni; внутри внутренней петли.

Когда я использую DoChildren вместо SkipChildren в случае с шаблоном цикла, все инструкции внутри циклов обнаруживаются более одного раза. Например:

  • инструкция A[i * nk + j] = (float) ((i*j+1) % ni) / ni; обнаруживается три раза:

Найти инструкцию * (A + (i * nk + j)) = (плавать) ((i * j + 1)% ni) / (плавать) ni; внутри внутренней петли.

Найти инструкцию * (A + (i * nk + j)) = (плавать) ((i * j + 1)% ni) / (плавать) ni; внутри внешней петли.

Найти инструкцию * (A + (i * nk + j)) = (плавать) ((i * j + 1)% ni) / (плавать) 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 голосов
/ 03 мая 2018

Я думаю, что самый простой ответ - показать код 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).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...