@@ -344,3 +344,143 @@ let deps (r : reg) =
344344 in (r, vs)
345345 )
346346 |> List. sort (fun (r1 , _ ) (r2 , _ ) -> compare r1 r2)
347+
348+ (* -------------------------------------------------------------------- *)
349+ (* SERIALIZATION *)
350+ (* Return map of indice renaming + list of and gates (increasing order) + (max variable index, and gate count, input gate count) *)
351+ let aiger_preprocess ~(input_count : int ) (r : reg ) : (node -> int) * (node list) * (int * int * int) =
352+ let cache : (int, int) Hashtbl.t = Hashtbl. create 0 in
353+ let count_and = ref 0 in
354+ let and_gates = ref [] in
355+
356+ let rec doit (n : node ) : unit =
357+ match Hashtbl. find_option cache (abs n.id) with
358+ | Some v -> ()
359+ | None ->
360+ let value = doit_force n in
361+ Hashtbl. add cache (abs n.id) value
362+
363+ and doit_force (n : node ) =
364+ match n.gate with
365+ | False -> 0
366+ | Input (v , i ) -> 64 * v + i
367+ | And (n1 , n2 ) ->
368+ doit n1; doit n2;
369+ incr count_and;
370+ and_gates := n::(! and_gates);
371+ ! count_and
372+ in
373+
374+ List. iter doit r;
375+ let and_cnt = ! count_and in
376+ let inp_cnt = input_count in
377+ let id_map =
378+ Hashtbl. to_seq cache |> Map. of_seq
379+ in
380+ let id_map = (function
381+ | { gate = False ; id } -> (if 0 < id then 0 else 1 )
382+ | { gate = And _ ; id } -> ((Map. find (abs id) id_map) + inp_cnt) lsl 1 + (if 0 < id then 0 else 1 )
383+ | { gate = Input _ ; id } -> (Map. find (abs id) id_map) lsl 1 + (if 0 < id then 0 else 1 )
384+ ) in
385+ id_map,
386+ List. sort (fun n1 n2 -> compare (id_map n1) (id_map n2)) ! and_gates,
387+ (and_cnt + inp_cnt, and_cnt, inp_cnt)
388+
389+ let aiger_serialize_int (id : int ) : string =
390+ assert (id > 0 );
391+ let mask = 0x7f in
392+ let rec doit (id : int ) : int list =
393+ if id < 0x80 then
394+ [id]
395+ else
396+ ((id land mask) lor (0x80 ))::(doit (id lsr 7 ))
397+ in
398+
399+ List. fold_left (fun acc id -> (Format. sprintf " %c" (char_of_int id)) ^ acc) " " (List. rev (doit id))
400+
401+ let pp_aiger_int fmt (id : int ) : unit =
402+ Format. fprintf fmt " %s" (aiger_serialize_int id)
403+
404+ let pp_aiger_and fmt ((gid , id1 , id2 ): int * int * int ) : unit =
405+ if not (gid > id1 && id1 > id2) then Format. eprintf " gid : %d | id1: %d | id2: %d@." gid id1 id2;
406+ assert (gid > id1 && id1 > id2);
407+ let delta0 = gid - id1 in
408+ let delta1 = id1 - id2 in
409+ assert (delta0 > 0 && delta1 > 0 );
410+ assert (id1 = gid - delta0);
411+ assert (gid - delta0 - delta1 = id2);
412+ Format. fprintf fmt " %a%a" pp_aiger_int (gid - id1) pp_aiger_int (id1 - id2)
413+
414+ (*
415+ mvi -> Max Variable Index
416+ agc -> And Gate Count
417+ igc -> Input Gate Count
418+ lgc -> Latch Gate Count
419+ ogc -> Output Gate Count
420+ *)
421+ let write_aiger_bin
422+ ~(input_count : int )
423+ ?(inp_name_map : int -> string = fun (i: int) -> "inp" ^ (string_of_int i) )
424+ oc
425+ (r : reg ) =
426+ let aiger_id_of_node, and_gates, (mvi, agc, igc) = aiger_preprocess ~input_count r in
427+
428+ let ogc = List. length r in
429+ let lgc = 0 in
430+ Printf. fprintf oc " aig %d %d %d %d %d\n " mvi igc lgc ogc agc;
431+ List. iter (fun n -> Printf. fprintf oc " %d\n " (aiger_id_of_node n)) r;
432+ List. iter (function
433+ | { gate = And (n1 , n2 ); } as n ->
434+ let id = aiger_id_of_node n in
435+ let id1 = aiger_id_of_node n1 in
436+ let id2 = aiger_id_of_node n2 in
437+ let id = id - (id land 1 ) in
438+ let id1, id2 = if id1 > id2 then id1, id2 else id2, id1 in
439+ Printf. fprintf oc " %s" (Format. asprintf " %a" pp_aiger_and (id, id1, id2))
440+ | _ -> assert false ) and_gates;
441+ for i = 0 to igc-1 do
442+ Printf. fprintf oc " i%d %s@\n " i (inp_name_map i)
443+ done
444+
445+ let write_aiger_bin_temp
446+ ~(input_count : int )
447+ ?(inp_name_map : (int -> string) option )
448+ ?(name : string = "circuit" )
449+ (r : reg ) =
450+ let tf_name, tf_oc = Filename. open_temp_file ~mode: [Open_binary ] name " .aig" in
451+ let tf_oc = BatIO. output_channel ~cleanup: true tf_oc in
452+ write_aiger_bin ~input_count ?inp_name_map tf_oc r;
453+ tf_name
454+
455+ (* Assumes inputs are already matched *)
456+ let abc_check_equiv
457+ ?(r1_name = " r1" )
458+ ?(r2_name = " r2" )
459+ ~(input_count : int )
460+ ?(inp_name_map : (int -> string) option )
461+ (r1 : reg ) (r2 : reg ) : bool =
462+
463+ let tf1_name, tf1_oc = Filename. open_temp_file ~mode: [Open_binary ] r1_name " .aig" in
464+ let tf2_name, tf2_oc = Filename. open_temp_file ~mode: [Open_binary ] r2_name " .aig" in
465+ Format. eprintf " Created temp files (%s) (%s)!@." tf1_name tf2_name;
466+ let tf1_oc = BatIO. output_channel ~cleanup: true tf1_oc in
467+ let tf2_oc = BatIO. output_channel ~cleanup: true tf2_oc in
468+ write_aiger_bin ~input_count ?inp_name_map tf1_oc r1;
469+ write_aiger_bin ~input_count ?inp_name_map tf2_oc r2;
470+ Format. eprintf " Wrote aig files!@." ;
471+ BatIO. close_out tf1_oc; BatIO. close_out tf2_oc;
472+ let abc_command = Format. sprintf " cec %s %s" tf1_name tf2_name in
473+ Format. eprintf " Abc command: %s@." abc_command;
474+ let abc_output_c, abc_in = Unix. open_process " abc" in
475+ (* let abc_in = BatIO.output_channel ~cleanup:true abc_in in *)
476+ BatIO. write_string abc_in (abc_command ^ " \n " );
477+ BatIO. close_out abc_in;
478+ (* let abc_output_c = BatIO.input_channel ~autoclose:true ~cleanup:true abc_output_c in *)
479+ (* FIXME: Get the actual output in all cases from abc *)
480+ let re = Str. regexp {|.* Networks are equivalent.*| } in
481+ Format. eprintf " Before read@." ;
482+ let abc_output = BatIO. read_all abc_output_c in
483+ Format. eprintf " ====== BEGIN ABC OUTPUT =====@.%s@.======= END ABC OUTPUT =====@." abc_output;
484+ let abc_output = String. replace_chars (function | '\n' -> " |" | c -> String. of_char c) abc_output in
485+ if Str. string_match re abc_output 0 then true else false
486+
0 commit comments