libqbe/tools/mgen/cgen.ml

421 lines
11 KiB
OCaml
Raw Normal View History

open Match
type options =
{ pfx: string
; static: bool
; oc: out_channel }
type side = L | R
type id_pred =
| InBitSet of Int64.t
| Ge of int
| Eq of int
and id_test =
| Pred of (side * id_pred)
| And of id_test * id_test
type case_code =
| Table of ((int * int) * int) list
| IfThen of
{ test: id_test
; cif: case_code
; cthen: case_code option }
| Return of int
type case =
{ swap: bool
; code: case_code }
let cgen_case tmp nstates map =
let cgen_test ids =
match ids with
| [id] -> Eq id
| _ ->
let min_id =
List.fold_left min max_int ids in
if List.length ids = nstates - min_id
then Ge min_id
else begin
assert (nstates <= 64);
InBitSet
(List.fold_left (fun bs id ->
Int64.logor bs
(Int64.shift_left 1L id))
0L ids)
end
in
let symmetric =
let inverse ((l, r), x) = ((r, l), x) in
setify map = setify (List.map inverse map) in
let map =
let ordered ((l, r), _) = r <= l in
if symmetric then
List.filter ordered map
else map
in
let exception BailToTable in
try
let st =
match setify (List.map snd map) with
| [st] -> st
| _ -> raise BailToTable
in
(* the operation considered can only
* generate a single state *)
let pairs = List.map fst map in
let ls, rs = List.split pairs in
let ls = setify ls and rs = setify rs in
if List.length ls > 1 && List.length rs > 1 then
raise BailToTable;
{ swap = symmetric
; code =
let pl = Pred (L, cgen_test ls)
and pr = Pred (R, cgen_test rs) in
IfThen
{ test = And (pl, pr)
; cif = Return st
; cthen = Some (Return tmp) } }
with BailToTable ->
{ swap = symmetric
; code = Table map }
let show_op (_cls, op) =
"O" ^ show_op_base op
let indent oc i =
Printf.fprintf oc "%s" (String.sub "\t\t\t\t\t" 0 i)
let emit_swap oc i =
let pf m = Printf.fprintf oc m in
let pfi n m = indent oc n; pf m in
pfi i "if (l < r)\n";
pfi (i+1) "t = l, l = r, r = t;\n"
let gen_tables oc tmp pfx nstates (op, c) =
let i = 1 in
let pf m = Printf.fprintf oc m in
let pfi n m = indent oc n; pf m in
let ntables = ref 0 in
(* we must follow the order in which
* we visit code in emit_case, or
* else ntables goes out of sync *)
let base = pfx ^ show_op op in
let swap = c.swap in
let rec gen c =
match c with
| Table map ->
let name =
if !ntables = 0 then base else
base ^ string_of_int !ntables
in
assert (nstates <= 256);
if swap then
let n = nstates * (nstates + 1) / 2 in
pfi i "static uchar %stbl[%d] = {\n" name n
else
pfi i "static uchar %stbl[%d][%d] = {\n"
name nstates nstates;
for l = 0 to nstates - 1 do
pfi (i+1) "";
for r = 0 to nstates - 1 do
if not swap || r <= l then
begin
pf "%d"
(try List.assoc (l,r) map
with Not_found -> tmp);
pf ",";
end
done;
pf "\n";
done;
pfi i "};\n"
| IfThen {cif; cthen} ->
gen cif;
Option.iter gen cthen
| Return _ -> ()
in
gen c.code
let emit_case oc pfx no_swap (op, c) =
let fpf = Printf.fprintf in
let pf m = fpf oc m in
let pfi n m = indent oc n; pf m in
let rec side oc = function
| L -> fpf oc "l"
| R -> fpf oc "r"
in
let pred oc (s, pred) =
match pred with
| InBitSet bs -> fpf oc "BIT(%a) & %#Lx" side s bs
| Eq id -> fpf oc "%a == %d" side s id
| Ge id -> fpf oc "%d <= %a" id side s
in
let base = pfx ^ show_op op in
let swap = c.swap in
let ntables = ref 0 in
let rec code i c =
match c with
| Return id -> pfi i "return %d;\n" id
| Table map ->
let name =
if !ntables = 0 then base else
base ^ string_of_int !ntables
in
incr ntables;
if swap then
pfi i "return %stbl[(l + l*l)/2 + r];\n" name
else pfi i "return %stbl[l][r];\n" name
| IfThen ({test = And (And (t1, t2), t3)} as r) ->
code i @@ IfThen
{r with test = And (t1, And (t2, t3))}
| IfThen {test = And (Pred p, t); cif; cthen} ->
pfi i "if (%a)\n" pred p;
code i (IfThen {test = t; cif; cthen})
| IfThen {test = Pred p; cif; cthen} ->
pfi i "if (%a) {\n" pred p;
code (i+1) cif;
pfi i "}\n";
Option.iter (code i) cthen
in
pfi 1 "case %s:\n" (show_op op);
if not no_swap && c.swap then
emit_swap oc 2;
code 2 c.code
let emit_list
?(limit=60) ?(cut_before_sep=false)
~col ~indent:i ~sep ~f oc l =
let sl = String.length sep in
let rstripped_sep, rssl =
if sep.[sl - 1] = ' ' then
String.sub sep 0 (sl - 1), sl - 1
else sep, sl
in
let lstripped_sep, lssl =
if sep.[0] = ' ' then
String.sub sep 1 (sl - 1), sl - 1
else sep, sl
in
let rec line col acc = function
| [] -> (List.rev acc, [])
| s :: l ->
let col = col + sl + String.length s in
let no_space =
if cut_before_sep || l = [] then
col > limit
else
col + rssl > limit
in
if no_space then
(List.rev acc, s :: l)
else
line col (s :: acc) l
in
let rec go col l =
if l = [] then () else
let ll, l = line col [] l in
Printf.fprintf oc "%s" (String.concat sep ll);
if l <> [] && cut_before_sep then begin
Printf.fprintf oc "\n";
indent oc i;
Printf.fprintf oc "%s" lstripped_sep;
go (8*i + lssl) l
end else if l <> [] then begin
Printf.fprintf oc "%s\n" rstripped_sep;
indent oc i;
go (8*i) l
end else ()
in
go col (List.map f l)
let emit_numberer opts n =
let pf m = Printf.fprintf opts.oc m in
let tmp = (atom_state n Tmp).id in
let con = (atom_state n AnyCon).id in
let nst = Array.length n.states in
let cases =
StateMap.by_ops n.statemap |>
List.map (fun (op, map) ->
(op, cgen_case tmp nst map))
in
let all_swap =
List.for_all (fun (_, c) -> c.swap) cases in
(* opn() *)
if opts.static then pf "static ";
pf "int\n";
pf "%sopn(int op, int l, int r)\n" opts.pfx;
pf "{\n";
cases |> List.iter
(gen_tables opts.oc tmp opts.pfx nst);
if List.exists (fun (_, c) -> c.swap) cases then
pf "\tint t;\n\n";
if all_swap then emit_swap opts.oc 1;
pf "\tswitch (op) {\n";
cases |> List.iter
(emit_case opts.oc opts.pfx all_swap);
pf "\tdefault:\n";
pf "\t\treturn %d;\n" tmp;
pf "\t}\n";
pf "}\n\n";
(* refn() *)
if opts.static then pf "static ";
pf "int\n";
pf "%srefn(Ref r, Num *tn, Con *con)\n" opts.pfx;
pf "{\n";
let cons =
List.filter_map (function
| (Con c, s) -> Some (c, s.id)
| _ -> None)
n.atoms
in
if cons <> [] then
pf "\tint64_t n;\n\n";
pf "\tswitch (rtype(r)) {\n";
pf "\tcase RTmp:\n";
if tmp <> 0 then begin
assert
(List.exists (fun (_, s) ->
s.id = 0
) n.atoms &&
(* no temp should ever get state 0 *)
List.for_all (fun (a, s) ->
s.id <> 0 ||
match a with
| AnyCon | Con _ -> true
| _ -> false
) n.atoms);
pf "\t\tif (!tn[r.val].n)\n";
pf "\t\t\ttn[r.val].n = %d;\n" tmp;
end;
pf "\t\treturn tn[r.val].n;\n";
pf "\tcase RCon:\n";
if cons <> [] then begin
pf "\t\tif (con[r.val].type != CBits)\n";
pf "\t\t\treturn %d;\n" con;
pf "\t\tn = con[r.val].bits.i;\n";
cons |> inverse |> group_by_fst
|> List.iter (fun (id, cs) ->
pf "\t\tif (";
emit_list ~cut_before_sep:true
~col:20 ~indent:2 ~sep:" || "
~f:(fun c -> "n == " ^ Int64.to_string c)
opts.oc cs;
pf ")\n";
pf "\t\t\treturn %d;\n" id
);
end;
pf "\t\treturn %d;\n" con;
pf "\tdefault:\n";
pf "\t\treturn INT_MIN;\n";
pf "\t}\n";
pf "}\n\n";
(* match[]: patterns per state *)
if opts.static then pf "static ";
pf "bits %smatch[%d] = {\n" opts.pfx nst;
n.states |> Array.iteri (fun sn s ->
let tops =
List.filter_map (function
| Top ("$" | "%") -> None
| Top r -> Some ("BIT(P" ^ r ^ ")")
| _ -> None) s.point |> setify
in
if tops <> [] then
pf "\t[%d] = %s,\n"
sn (String.concat " | " tops);
);
pf "};\n\n"
let var_id vars f =
List.mapi (fun i x -> (x, i)) vars |>
List.assoc f
let compile_action vars act =
let pcs = Hashtbl.create 100 in
let rec gen pc (act: Action.t) =
try
[10 + Hashtbl.find pcs act.id]
with Not_found ->
let code =
match act.node with
| Action.Stop ->
[0]
| Action.Push (sym, k) ->
let c = if sym then 1 else 2 in
[c] @ gen (pc + 1) k
| Action.Set (v, {node = Action.Pop k; _})
| Action.Set (v, ({node = Action.Stop; _} as k)) ->
let v = var_id vars v in
[3; v] @ gen (pc + 2) k
| Action.Set _ ->
(* for now, only atomic patterns can be
* tied to a variable, so Set must be
* followed by either Pop or Stop *)
assert false
| Action.Pop k ->
[4] @ gen (pc + 1) k
| Action.Switch cases ->
let cases =
inverse cases |> group_by_fst |>
List.sort (fun (_, cs1) (_, cs2) ->
let n1 = List.length cs1
and n2 = List.length cs2 in
compare n2 n1)
in
(* the last case is the one with
* the max number of entries *)
let cases = List.rev (List.tl cases)
and last = fst (List.hd cases) in
let ncases =
List.fold_left (fun n (_, cs) ->
List.length cs + n)
0 cases
in
let body_off = 2 + 2 * ncases + 1 in
let pc, tbl, body =
List.fold_left
(fun (pc, tbl, body) (a, cs) ->
let ofs = body_off + List.length body in
let case = gen pc a in
let pc = pc + List.length case in
let body = body @ case in
let tbl =
List.fold_left (fun tbl c ->
tbl @ [c; ofs]
) tbl cs
in
(pc, tbl, body))
(pc + body_off, [], [])
cases
in
let ofs = body_off + List.length body in
let tbl = tbl @ [ofs] in
assert (2 + List.length tbl = body_off);
[5; ncases] @ tbl @ body @ gen pc last
in
if act.node <> Action.Stop then
Hashtbl.replace pcs act.id pc;
code
in
gen 0 act
let emit_matchers opts ms =
let pf m = Printf.fprintf opts.oc m in
if opts.static then pf "static ";
pf "uchar *%smatcher[] = {\n" opts.pfx;
List.iter (fun (vars, pname, m) ->
pf "\t[P%s] = (uchar[]){\n" pname;
pf "\t\t";
let bytes = compile_action vars m in
emit_list
~col:16 ~indent:2 ~sep:","
~f:string_of_int opts.oc bytes;
pf "\n";
pf "\t},\n")
ms;
pf "};\n\n"
let emit_c opts n =
emit_numberer opts n