From a19d5ab3d64999405053cffe19db84822d1b581c Mon Sep 17 00:00:00 2001 From: Kait Lam Date: Wed, 24 Jul 2024 14:24:13 +1000 Subject: [PATCH] cpp backend (#62) Offline lifter backend for C++, including IBI instantiation to emit LLVM IR * offline-cpp: create directory. * add boilerplate cpp files * working on cpp backend. to do: implement f_gen_* functions. * stashing work to add types rt_expr and rt_label. * stub all f_ functions. * implement template-based generation * update stubs with new interface * fix some bigint/bits bugs and clean up includes. * fix cmake, do not inherit. * add gitignore for cpp * fix bits/bigint oopsie * stub v_ variables * implement v_ variable prefixing (we compile now!!) * use traits class * temp * stub implementations * compiling fr now * fix typo-ed names and change to _impl.hpp * temporarily do the scoping thing :( * disable scoping hack * working on f_gen methods * starting restructure for compile speed * work on explicit instantiation * refactor to cpp_fun_sig * clean up and fix. split compilation working now! * restore aslp_lifter_impl.hpp * collate decode_tests and split llvm traits * generating basic llvm? * touch * typename * implement missing registers and intrinsics for non-float ops. * fix with new branch intrinsics. * restructure * restructuring aslp-lifter-gen now works with pkg-config maybe * meson build system * merge to aslp-lifter project, distrubute src files, remove old cmakes * compile speedups with clang+lld * ci * Update build.sh * Update test.yml * use absolute paths in ci CXX CXX_LD * use llvm-15 * Update test.yml * fix important warnings * fix ci nix overrides CXX variable when entering shell, predictably * no ci * fix uninit warnings * add minimal cmakelists for non-installed use only * update to generate in external repository * copy support files when generating cpp lifter this allows keeping versioning the lifter_interface alongside the backend code which uses it. * add cpp build files into repository * do not copy headers and fix mkdir_p * add warning to user to copy c++ build files. * update cpp readme to suit in-repository location * set default cpp gen dir to within subprojects * generate meson file * use %blob in cpp_backend * update comment in cpp_backend * add cpp build instructions to cpp readme * add gitignore to offlineasl-cpp * add check.py to perform syntax-checking of generated cpp it would be nice to run this in ci. note llvm 17 dependency * meson: restrict to llvm 17 * chdir in check.py --- .gitignore | 3 + bin/asli.ml | 13 +- libASL/cpp_backend.ml | 570 +++++++++++ libASL/cpu.ml | 2 +- libASL/dune | 11 +- libASL/utils.ml | 7 +- offlineASL-cpp/.gitignore | 15 + offlineASL-cpp/CMakeLists.txt | 54 + offlineASL-cpp/Doxyfile | 960 ++++++++++++++++++ offlineASL-cpp/README.md | 127 +++ offlineASL-cpp/check.py | 57 ++ offlineASL-cpp/meson.build | 18 + offlineASL-cpp/meson.options | 5 + .../subprojects/aslp-lifter-demo/meson.build | 25 + .../aslp-lifter-demo/meson.options | 2 + .../subprojects/aslp-lifter-demo/src/main.cpp | 39 + .../subprojects/aslp-lifter-llvm/.gitkeep | 0 .../aslp-lifter-llvm/CMakeLists.txt | 22 + .../include/aslp/llvm_interface.hpp | 475 +++++++++ .../include/aslp/llvm_lifter_traits.hpp | 26 + .../subprojects/aslp-lifter-llvm/meson.build | 38 + .../aslp-lifter-llvm/pch/cpp_pch.hpp | 8 + .../aslp-lifter-llvm/src/cpp_pch.hpp | 0 .../subprojects/aslp-lifter/dummy.cpp | 0 .../aslp-lifter/include/aslp/aslp_lifter.hpp | 1 + .../include/aslp/aslp_lifter_impl.hpp | 1 + .../include/aslp/generated/A64_decoder.hpp | 22 + .../include/aslp/generated/aslp_lifter.hpp | 24 + .../aslp/generated/aslp_lifter_impl.hpp | 15 + .../include/aslp/generated/decode_tests.hpp | 15 + .../aslp-lifter/include/aslp/interface.hpp | 181 ++++ .../subprojects/aslp-lifter/meson.build | 43 + .../subprojects/aslp-lifter/meson.build.in | 40 + .../aslp-lifter/src/generated/A64_decoder.cpp | 18 + .../src/generated/decode_tests.cpp | 18 + 35 files changed, 2844 insertions(+), 11 deletions(-) create mode 100644 libASL/cpp_backend.ml create mode 100644 offlineASL-cpp/.gitignore create mode 100644 offlineASL-cpp/CMakeLists.txt create mode 100644 offlineASL-cpp/Doxyfile create mode 100644 offlineASL-cpp/README.md create mode 100755 offlineASL-cpp/check.py create mode 100644 offlineASL-cpp/meson.build create mode 100644 offlineASL-cpp/meson.options create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-demo/meson.build create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-demo/meson.options create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-demo/src/main.cpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-llvm/.gitkeep create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-llvm/CMakeLists.txt create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_interface.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_lifter_traits.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-llvm/meson.build create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-llvm/pch/cpp_pch.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter-llvm/src/cpp_pch.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/dummy.cpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter_impl.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/A64_decoder.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter_impl.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/decode_tests.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/include/aslp/interface.hpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/meson.build create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/meson.build.in create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/src/generated/A64_decoder.cpp create mode 100644 offlineASL-cpp/subprojects/aslp-lifter/src/generated/decode_tests.cpp diff --git a/.gitignore b/.gitignore index d57eafcf..bc24e96c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,7 @@ scripts/output.txt scripts/cntlm.bir scripts/total.txt +.cache/ +build/ + offlineASL/aarch64_* diff --git a/bin/asli.ml b/bin/asli.ml index 771742e0..792c767a 100644 --- a/bin/asli.ml +++ b/bin/asli.ml @@ -57,7 +57,7 @@ let help_msg = [ (** supported backends for :gen and their default output directories *) let gen_backends = [ ("ocaml", (Cpu.Ocaml, "offlineASL")); - ("cpp", (Cpu.Cpp, "offlineASL-cpp")); + ("cpp", (Cpu.Cpp, "offlineASL-cpp/subprojects/aslp-lifter")); ] let () = Random.self_init () @@ -198,17 +198,18 @@ let rec process_command (tcenv: TC.Env.t) (cpu: Cpu.cpu) (fname: string) (input0 (Dis.dis_decode_entry cpu.env cpu.denv decoder op); Option.iter close_out chan_opt | ":gen" :: iset :: id :: rest when List.length rest <= 2 -> - let backend = Option.value List.(nth_opt rest 0) ~default:"ocaml" in - Printf.printf "Generating lifter for %s %s using %s backend\n" iset id backend; + let backend_str = Option.value List.(nth_opt rest 0) ~default:"ocaml" in + Printf.printf "Generating lifter for %s %s using %s backend\n" iset id backend_str; - let (backend, default_dir) = match List.assoc_opt backend gen_backends with + let (backend, default_dir) = match List.assoc_opt backend_str gen_backends with | Some x -> x | None -> invalid_arg @@ Printf.sprintf "unknown backend %s (supported: %s)" - backend (String.concat ", " List.(map fst gen_backends)) in + backend_str (String.concat ", " List.(map fst gen_backends)) in let dir = Option.value List.(nth_opt rest 1) ~default:default_dir in let cpu' = Cpu.mkCPU cpu.env cpu.denv in - cpu'.gen iset id backend dir + cpu'.gen iset id backend dir; + Printf.printf "Done generating %s lifter into '%s'.\n" backend_str dir; | ":dump" :: iset :: opcode :: rest -> let fname = (match rest with diff --git a/libASL/cpp_backend.ml b/libASL/cpp_backend.ml new file mode 100644 index 00000000..386e1536 --- /dev/null +++ b/libASL/cpp_backend.ml @@ -0,0 +1,570 @@ +open Asl_ast +open Asl_utils + +(**************************************************************** + * Write State + ****************************************************************) + +type st = { + mutable depth : int; + mutable skip_seq : bool; + file: string; + oc : out_channel; + mutable ref_vars : IdentSet.t; + + (* symbols declared within semantics, e.g. non-global variables and other generated functions *) + mutable genvars : ident list; +} + +let inc_depth st = + st.depth <- st.depth + 2 + +let dec_depth st = + st.depth <- st.depth - 2 + +let is_ref_var v st = + IdentSet.mem v st.ref_vars + +let clear_ref_vars st = + st.ref_vars <- IdentSet.empty + +let add_ref_var v st = + st.ref_vars <- IdentSet.add v st.ref_vars + +(**************************************************************** + * String Utils + ****************************************************************) + +let replace s = + let s = + String.fold_left (fun acc c -> + if c = '.' then acc ^ "_" + else if c = '#' then acc ^ "HASH" + else acc ^ (String.make 1 c)) "" s in + s + +let name_of_ident v = + let s = (match v with + | Ident n -> "v_" ^ n + | FIdent (n,0) -> "f_" ^ n + | FIdent (n,i) -> "f_" ^ n ^ "_" ^ (string_of_int i)) in + replace s + +let prefixed_name_of_ident st v = + let name = name_of_ident v in + match v with + (* non-generated functions and variables are translated to methods on an interface object. *) + | FIdent _ when not (List.mem v st.genvars) -> "iface." ^ name + | Ident _ when not (List.mem v st.genvars) -> "iface." ^ name ^ "()" + | _ -> name + +let rec name_of_lexpr l = + match l with + | LExpr_Var v -> name_of_ident v + | LExpr_Field (l, f) -> + let l = name_of_lexpr l in + let f = name_of_ident f in + l ^ "." ^ f + | LExpr_Wildcard -> "_" + | _ -> failwith @@ "name_of_lexpr: " ^ (pp_lexpr l) + +(**************************************************************** + * File IO + ****************************************************************) + +let write_preamble opens ?(header = true) ?(exports = []) st = + Printf.fprintf st.oc "/* AUTO-GENERATED LIFTER FILE */\n\n"; + if header then Printf.fprintf st.oc "#pragma once\n"; + List.iter (fun s -> + Printf.fprintf st.oc "#include <%s>\n" s) opens; + List.iter (fun s -> + Printf.fprintf st.oc "#include <%s> // IWYU pragma: export\n" s) exports; + Printf.fprintf st.oc "\n"; + Printf.fprintf st.oc "namespace aslp {\n\n" + +let write_epilogue fid st = + Printf.fprintf st.oc "\n} // namespace aslp" + +let write_line s st = + let padding = String.concat "" (List.init st.depth (fun _ -> " ")) in + output_string st.oc padding; + output_string st.oc s + +let write_seq st = + if st.skip_seq then + st.skip_seq <- false + else Printf.fprintf st.oc ";\n" + +let write_nl st = + Printf.fprintf st.oc "\n" + +(**************************************************************** + * Expr Printing + ****************************************************************) + +let rec prints_expr e st = + match e with + (* Boolean Expressions *) + | Expr_Var(Ident "TRUE") -> "true" + | Expr_Var(Ident "FALSE") -> "false" + | Expr_TApply(FIdent("and_bool", 0), [], [a;b]) -> + Printf.sprintf "(%s) && (%s)" (prints_expr a st) (prints_expr b st) + | Expr_TApply(FIdent("or_bool", 0), [], [a;b]) -> + Printf.sprintf "(%s) || (%s)" (prints_expr a st) (prints_expr b st) + | Expr_TApply(FIdent("implies_bool", 0), [], [a;b]) -> + Printf.sprintf "(%s) ? (%s) : true" (prints_expr a st) (prints_expr b st) + | Expr_TApply(FIdent("not_bool", 0), [], [a]) -> + "! (" ^ prints_expr a st ^ ")" + + (* State Accesses *) + | Expr_Var(v) -> + let n = prefixed_name_of_ident st v in + if is_ref_var v st then "" ^ n else n + | Expr_Field(e, f) -> + prints_expr e st ^ "." ^ name_of_ident f + | Expr_Array(a,i) -> + Printf.sprintf "List.nth (%s) (%s)" (prints_expr a st) (prints_expr i st) + + (* Int Expressions *) + | Expr_LitInt i -> + Printf.sprintf "iface.bigint_lit(\"%s\")" i + | Expr_TApply(FIdent("add_int", 0), [], [a;b]) -> + Printf.sprintf "(%s) + (%s)" (prints_expr a st) (prints_expr b st) + | Expr_TApply(FIdent("sub_int", 0), [], [a;b]) -> + Printf.sprintf "(%s) - (%s)" (prints_expr a st) (prints_expr b st) + | Expr_TApply(FIdent("mul_int", 0), [], [a;b]) -> + Printf.sprintf "(%s) * (%s)" (prints_expr a st) (prints_expr b st) + | Expr_TApply(FIdent("frem_int", 0), [], [a;b]) -> + Printf.sprintf "(%s) %% (%s)" (prints_expr a st) (prints_expr b st) + + (* Other operations *) + | Expr_LitBits b -> + let len = String.length b in + Printf.sprintf "iface.bits_lit(%dU, \"%s\")" len b + | Expr_Slices(e,[Slice_LoWd(i,w)]) -> + let e = prints_expr e st in + let i = prints_expr i st in + let w = prints_expr w st in + Printf.sprintf "iface.extract_bits(%s, /*lo*/ %s, /*wd*/ %s)" e i w + | Expr_TApply(f, targs, args) -> + let f = prefixed_name_of_ident st f in + (* let args = List.map (fun e -> prints_expr e st) (targs @ args) in *) + let args = List.map (fun e -> prints_expr e st) ([] @ args) in + f ^ "(" ^ (String.concat ", " args) ^ ")" + + | Expr_LitString s -> "\"" ^ s ^ "\"" + | Expr_Tuple(es) -> "std::make_tuple(" ^ (String.concat "," (List.map (fun e -> prints_expr e st) es)) ^ ")" + | Expr_Unknown(ty) -> default_value ty st + + | _ -> failwith @@ "prints_expr: " ^ pp_expr e + +and default_value t st = + match t with + | Type_Bits w -> + Printf.sprintf "iface.bits_zero(%s)" (prints_expr w st) + | Type_Constructor (Ident "boolean") -> "true" + | Type_Constructor (Ident "integer") -> "iface.bigint_zero()" + | Type_Constructor (Ident "rt_label") -> "typename Traits::rt_label{}" + | Type_Constructor (Ident "rt_expr") -> "typename Traits::rt_expr{}" + | Type_Array(Index_Range(lo, hi),ty) -> + let lo = prints_expr lo st in + let hi = prints_expr hi st in + let d = default_value ty st in + Printf.sprintf "std::vector{(%s)-(%s), %s}" hi lo d + | _ -> failwith @@ "Unknown type for default value: " ^ (pp_type t) + +let prints_type t = + match t with + | Type_Constructor (Ident "boolean") -> "bool" + | Type_Bits _ -> "typename Traits::bits" + | Type_Constructor (Ident "integer") -> "typename Traits::bigint" + | Type_Constructor (Ident "rt_label") -> "typename Traits::rt_label" + | Type_Constructor (Ident "rt_expr") -> "typename Traits::rt_expr" + | Type_Constructor (Ident "rt_sym") -> "typename Traits::rt_lexpr" + | _ -> failwith @@ Asl_utils.pp_type t + +let prints_ret_type = Option.fold ~none:"void" ~some:prints_type + +(**************************************************************** + * Prim Printing + ****************************************************************) + +let write_fun_return e st = + let s = Printf.sprintf "return (%s)" e in + write_line s st + +let write_proc_return st = + write_line "return" st + +let write_assert s st = + let s = Printf.sprintf "assert(%s)" s in + write_line s st + +let write_unsupported st = + write_line {|throw std::runtime_error{"aslp_lifter: unsupported! " + std::string{__func__} + " @ " + std::string{__FILE__} + ":" + std::to_string(__LINE__)}|} st + +let write_call f targs args st = + let f = prefixed_name_of_ident st f in + let args = [] @ args in + let call = f ^ "(" ^ (String.concat ", " args) ^ ")" in + write_line call st + +let write_ref ty v e st = + (* let t = prints_type ty in *) + let t = "auto" in + let name = prefixed_name_of_ident st v in + let s = Printf.sprintf "%s %s = %s" t name e in + write_line s st; + add_ref_var v st + +let write_let ty v e st = + (* let t = prints_type ty in *) + let t = "auto" in + let v = prefixed_name_of_ident st v in + let s = Printf.sprintf "const %s %s = %s" t v e in + write_line s st + +let write_if_start c st = + let s = Printf.sprintf "if (%s) {\n" c in + write_line s st + +let write_if_elsif c st = + let s = Printf.sprintf "} else if (%s) {\n" c in + write_line s st + +let write_if_else st = + write_line "} else {\n" st + +let write_if_end st = + write_line "} // if\n" st; + st.skip_seq <- true + +(**************************************************************** + * Stmt Printing + ****************************************************************) + +let rec write_lexpr v st = + match v with + | LExpr_Wildcard -> + "std::ignore" + + | LExpr_Var v -> + name_of_ident v + + | LExpr_Array (LExpr_Var v, i) -> + let i = prints_expr i st in + let v = name_of_ident v in + Printf.sprintf "%s.at(%s)" v i + + | LExpr_Field (l, f) -> + let v = name_of_lexpr l in + Printf.sprintf "%s" v + + | LExpr_Tuple (ls) -> + let vars = List.map (fun l -> write_lexpr l st) ls in + let v = String.concat "," vars in + Printf.sprintf "std::tie(%s)" v + + | _ -> failwith @@ "write_assign: " ^ (pp_lexpr v) + +let rec write_stmt s st = + match s with + | Stmt_VarDeclsNoInit(ty, vs, loc) -> + let e = default_value ty st in + st.genvars <- vs @ st.genvars; + List.iter (fun v -> write_ref ty v e st) vs + + | Stmt_VarDecl(ty, v, e, loc) -> + let e = prints_expr e st in + st.genvars <- v :: st.genvars; + write_ref ty v e st + + | Stmt_ConstDecl(ty, v, e, loc) -> + let e = prints_expr e st in + st.genvars <- v :: st.genvars; + write_let ty v e st + + | Stmt_Assign(l, r, loc) -> + let e = prints_expr r st in + let l = write_lexpr l st in + write_line (Printf.sprintf "%s = %s" l e) st + + | Stmt_TCall(f, tes, es, loc) -> + let tes = List.map (fun e -> prints_expr e st) tes in + let es = List.map (fun e -> prints_expr e st) es in + write_call f tes es st + + | Stmt_FunReturn(e, loc) -> + write_fun_return (prints_expr e st) st + + | Stmt_ProcReturn(loc) -> + write_proc_return st + + | Stmt_Assert(e, loc) -> + write_assert (prints_expr e st) st + + | Stmt_Throw _ -> + write_unsupported st + + | Stmt_If(c, t, els, f, loc) -> + let rec iter = function + | S_Elsif_Cond(c,b)::xs -> + write_if_elsif (prints_expr c st) st; + write_stmts b st; + iter xs + | [] -> () in + write_if_start (prints_expr c st) st; + write_stmts t st; + iter els; + if f <> [] then (write_if_else st; write_stmts f st); + write_if_end st + + | _ -> failwith @@ "write_stmt: " ^ (pp_stmt s); + +and write_stmts s st = + inc_depth st; + match s with + | [] -> + write_proc_return st; + write_seq st; + dec_depth st + | x::xs -> + write_stmt x st; + write_seq st; + List.iter (fun s -> + write_stmt s st; + write_seq st; + ) xs; + dec_depth st; + assert (not st.skip_seq) + +(* XXX: assumes all function arguments are bits. *) +let build_args prefix targs args = + let inner = String.concat " " @@ + List.map + (fun s -> prefix ^ "bits " ^ name_of_ident s) + (targs@args) in + "(" ^ inner ^ ")" + +let typenames = ["bits"; "bigint"; "rt_expr"; "rt_lexpr"; "rt_label"] +let template_header = "template \n" +let template_args = "" + +(** tuple of return type, function name, function arguments (parenthesised) *) +type cpp_fun_sig = { + rty: string; + prefix: string; + name: ident; + args: string; + file: string; +} + +let write_fn name (ret_tyo,_,targs,args,_,body) st : cpp_fun_sig = + clear_ref_vars st; + let oldvars = st.genvars in + st.genvars <- (targs @ args) @ oldvars; + + let prefix = "aslp_lifter" ^ template_args ^ "::" in + let fname = name_of_ident name in + let args = build_args "typename Traits::" targs args in + let ret = prints_ret_type ret_tyo in + + write_line template_header st; + Printf.fprintf st.oc "%s %s%s%s {\n" ret prefix fname args; + write_stmts body st; + Printf.fprintf st.oc "\n} // %s\n\n" fname; + + st.genvars <- oldvars; + { rty = ret; prefix; name; args; file = st.file; } + +(**************************************************************** + * Directory Setup + ****************************************************************) + +let init_st (genfns: cpp_fun_sig list) prefix file = + let genvars = List.map (fun {name;_} -> name) genfns in + let path = Filename.concat prefix file in + Utils.mkdir_p (Filename.dirname path); + let oc = open_out path in + + { depth = 0; skip_seq = false; file; oc; ref_vars = IdentSet.empty ; + genvars; } + +(* prefix used to access all generated header files. *) +let export_prefix = "aslp/generated" +(* directory for generated template headers. *) +let gen_dir = "include" +(* directory for generated source files for explicit instantiation. *) +let instantiate_dir = "src/generated" +(* headers required by all files. note aslp/interface.hpp is NOT generated. *) +let stdlib_deps = ["cassert"; "tuple"; "variant"; "vector"; "stdexcept"; "aslp/interface.hpp"] +(* headers required by instruction semantics files. + includes forward declaration of lifter class. *) +let global_deps = stdlib_deps @ [export_prefix^"/aslp_lifter.hpp"] + + +(** Write an instruction file, containing just the behaviour of one instructions *) +let write_instr_file fn fnsig prefix dir = + let m = name_of_FIdent fn in + let path = dir ^ "/" ^ m ^ ".hpp" in + let st = init_st [] prefix path in + write_preamble global_deps st; + let gen = write_fn fn fnsig st in + write_epilogue () st; + close_out st.oc; + gen + +(* Write the test file, containing all decode tests *) +let write_test_file tests prefix dir = + let m = "decode_tests" in + let path = dir ^ "/" ^ m ^ ".hpp" in + let st = init_st [] prefix path in + write_preamble global_deps st; + let gens = List.map (fun (i,s) -> write_fn i s st) @@ Bindings.bindings tests in + write_epilogue () st; + close_out st.oc; + gens + +(* Write the decoder file *) +let write_decoder_file fn fnsig genfns prefix dir = + let m = name_of_FIdent fn in + let path = dir ^ "/" ^ m ^ ".hpp" in + let st = init_st genfns prefix path in + write_preamble (global_deps@[export_prefix^"/decode_tests.hpp"]) st; + let gen = write_fn fn fnsig st in + write_epilogue fn st; + close_out st.oc; + gen + + +(* Write the public-facing header file. For compilation speed, this declares but does not define. *) +let write_header_file fn fnsig semfns testfns prefix dir = + let name = "aslp_lifter" in + let path = dir ^ "/" ^ name ^ ".hpp" in + let st = init_st [] prefix path in + write_preamble stdlib_deps st; + + write_line template_header st; + write_line "class aslp_lifter {\n" st; + + inc_depth st; + write_line ("public: using interface = lifter_interface" ^ template_args ^ ";\n") st; + write_line "private: interface& iface;\n" st; + write_line "public:\n" st; + write_line "aslp_lifter(interface& iface) : iface{iface} { }\n" st; + + write_line "/* generated semantics */\n" st; + List.iter + (fun {rty; name; args; _} -> write_line (rty ^ " " ^ name_of_ident name ^ args ^ ";\n") st) + semfns; + write_line "/* generated decode test conditions */\n" st; + List.iter + (fun {rty; name; args; _} -> write_line (rty ^ " " ^ name_of_ident name ^ args ^ ";\n") st) + testfns; + + dec_depth st; + write_line "};\n" st; + + write_epilogue fn st; + close_out st.oc; + (name, semfns @ testfns) + +(** Writes the template implementation file. If needed, this can be used to instantiate the entire lifter. + However, it is fairly slow. *) +let write_impl_file allfns prefix dir = + let name = "aslp_lifter_impl" in + let path = dir ^ "/" ^ name ^ ".hpp" in + let st = init_st [] prefix path in + let exports = Utils.nub @@ List.map (fun {file;_} -> file) allfns in + write_preamble stdlib_deps ~exports st; + + write_epilogue () st; + close_out st.oc; + name + +(* Creates a directory of explicit instantiations, supporting parallel compilation. *) +let write_explicit_instantiations cppfuns prefix dir = + let write_instantiation file (cppfuns : cpp_fun_sig list) = + let dep = file in + let file = Filename.(chop_extension (basename file)) in + let path = dir ^ "/" ^ file ^ ".cpp" in + let st = init_st [] prefix path in + + write_preamble ~header:false stdlib_deps ~exports:[dep] st; + + write_line "#ifdef ASLP_LIFTER_INSTANTIATE\n" st; + write_line "using Traits = ASLP_LIFTER_INSTANTIATE;\n" st; + List.iter + (fun {rty; name; args; _} -> + let fname = name_of_ident name in + let s = Printf.sprintf "template %s %s%s::%s%s;\n" rty "aslp_lifter" template_args fname args in + write_line s st) + cppfuns; + write_line "#endif\n" st; + + write_epilogue () st; + close_out st.oc; + (path, cppfuns) + in + (* group by the .hpp file where each template is defined. *) + let files = Utils.nub @@ List.map (fun x -> x.file) cppfuns in + List.map + (fun file -> + write_instantiation file (List.filter (fun x -> x.file = file) cppfuns)) + files + + +(* Finalises the generation by writing build-system files. *) +let write_build_files instdir funs dir = + let meson_template = [%blob "../offlineASL-cpp/subprojects/aslp-lifter/meson.build.in"] in + let interface_file = [%blob "../offlineASL-cpp/subprojects/aslp-lifter/include/aslp/interface.hpp"] in + + close_out @@ open_out_bin @@ dir ^ "/dummy.cpp"; + + let headers_dir = dir ^ "/include/aslp" in + Utils.mkdir_p headers_dir; + let exported = ["aslp_lifter.hpp"; "aslp_lifter_impl.hpp"] in + List.iter (fun h -> + let f = open_out_bin @@ headers_dir ^ "/" ^ h in + Printf.fprintf f {|#include "generated/%s" // IWYU pragma: export%s|} h "\n"; + close_out f + ) exported; + + let f = open_out_bin @@ headers_dir ^ "/interface.hpp" in + output_string f interface_file; + close_out f; + + let re = Str.regexp_string {|["SRCFILES"]|} in + let instfiles = + List.map fst funs + |> Utils.nub + |> List.map (fun file -> Printf.sprintf {| '%s/%s',%s|} instdir file "\n") in + let srcfiles = "[\n" ^ String.concat "" instfiles ^ "]\n" in + let f = open_out_bin @@ dir ^ "/meson.build" in + output_string f @@ Str.global_replace re srcfiles meson_template; + close_out f + +(* Write all of the above, generating all necessary files for a minimal meson project *) +let run dfn dfnsig tests fns root = + + let genprefix = root ^ "/" ^ gen_dir in + let instprefix = root ^ "/" ^ instantiate_dir in + + let semfns = Bindings.fold (fun fn fnsig acc -> (write_instr_file fn fnsig genprefix export_prefix)::acc) fns [] in + let testfns = write_test_file tests genprefix export_prefix in + let allfns = semfns @ testfns in + + let dfn = write_decoder_file dfn dfnsig allfns genprefix export_prefix in + let allfns = dfn :: allfns in + + let _header = write_header_file dfn dfnsig (dfn :: semfns) testfns genprefix export_prefix in + let explicits = write_explicit_instantiations allfns instprefix "." in + + let _impl = write_impl_file allfns genprefix export_prefix in + + let () = write_build_files instantiate_dir explicits root in + + if not (Sys.file_exists (root ^ "/meson.build")) then + Printf.eprintf "Warning: cpp gen directory '%s' is missing build system files. These might need to be copied manually.\n\n" root; + + () diff --git a/libASL/cpu.ml b/libASL/cpu.ml index f536fa92..a5b06260 100644 --- a/libASL/cpu.ml +++ b/libASL/cpu.ml @@ -69,7 +69,7 @@ let mkCPU (env : Eval.Env.t) (denv: Dis.env): cpu = let run_gen_backend : gen_function = match backend with | Ocaml -> Ocaml_backend.run - | Cpp -> failwith "cpp backend not yet implemented" in + | Cpp -> Cpp_backend.run in (* Build backend program *) run_gen_backend decoder_id decoder_fnsig tests instrs dir diff --git a/libASL/dune b/libASL/dune index cee63844..341e074d 100644 --- a/libASL/dune +++ b/libASL/dune @@ -39,12 +39,19 @@ lexer lexersupport loadASL monad primops rws symbolic tcheck testing transforms value symbolic_lifter decoder_program call_graph req_analysis offline_transform ocaml_backend dis_tc offline_opt - arm_env pretransforms flags + cpp_backend arm_env pretransforms flags ) - (preprocessor_deps (alias ../asl_files)) + (preprocessor_deps (alias ../asl_files) (alias cpp_backend_files)) (preprocess (pps ppx_blob)) (libraries libASL_stage0 libASL_support str)) + +(alias + (name cpp_backend_files) + (deps + ../offlineASL-cpp/subprojects/aslp-lifter/meson.build.in + ../offlineASL-cpp/subprojects/aslp-lifter/include/aslp/interface.hpp)) + (library (name libASL_virtual) (public_name asli.libASL-virtual) diff --git a/libASL/utils.ml b/libASL/utils.ml index ef16c906..b0e08dcb 100644 --- a/libASL/utils.ml +++ b/libASL/utils.ml @@ -11,9 +11,12 @@ let rec mkdir_p p = let open Filename in if Sys.file_exists p then () - else + else begin (* make parents, then make final directory. *) - (mkdir_p (dirname p); Sys.mkdir p 0o755) + mkdir_p (dirname p); + if Sys.file_exists p then () + else Sys.mkdir p 0o755 + end (**************************************************************** diff --git a/offlineASL-cpp/.gitignore b/offlineASL-cpp/.gitignore new file mode 100644 index 00000000..ced121b0 --- /dev/null +++ b/offlineASL-cpp/.gitignore @@ -0,0 +1,15 @@ +!.gitkeep +build + +.cache +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps diff --git a/offlineASL-cpp/CMakeLists.txt b/offlineASL-cpp/CMakeLists.txt new file mode 100644 index 00000000..eaabc77f --- /dev/null +++ b/offlineASL-cpp/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.20) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +project(offlineasl) + +set(ASLP_LIFTER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/subprojects/aslp-lifter") +set(ASLP_LIFTER_LLVM_DIR "${CMAKE_CURRENT_SOURCE_DIR}/subprojects/aslp-lifter-instantiate") + +## dependencies ## + +if (NOT LLVM_FOUND) + find_package(LLVM REQUIRED CONFIG) +else() + message(STATUS "... LLVM already found") +endif() +message(STATUS "Found LLVM: " ${LLVM_DIR} ", tools: " ${LLVM_TOOLS_BINARY_DIR}) +get_target_property(LLVM_CONFIG llvm-config LOCATION) + +message(STATUS "llvm-config: ${LLVM_CONFIG}") + +### aslp-lifter ### +# provides templates to instantiate the lifter with generic targets + +add_library(aslp-lifter INTERFACE) +target_include_directories(aslp-lifter INTERFACE "${ASLP_LIFTER_DIR}/include") + +target_compile_options(aslp-lifter INTERFACE -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=unused-but-set-variable) +target_compile_options(aslp-lifter INTERFACE -g -O0) + +### aslp-lifter-llvm ### +# instantiates the aslp-lifter with a LLVM target + +file(GLOB_RECURSE GEN_INSTANTIATE "${ASLP_LIFTER_DIR}/src/*.cpp") +add_library(aslp-lifter-llvm SHARED ${GEN_INSTANTIATE}) + +target_include_directories(aslp-lifter-llvm PUBLIC "${ASLP_LIFTER_LLVM_DIR}/include") + +execute_process(COMMAND "${LLVM_CONFIG}" --libs support core irreader bitwriter + COMMAND_ERROR_IS_FATAL ANY + OUTPUT_VARIABLE llvm_libs OUTPUT_STRIP_TRAILING_WHITESPACE) +message(STATUS "llvm_libs: ${llvm_libs}") +separate_arguments(llvm_libs UNIX_COMMAND "${llvm_libs}") +target_link_options(aslp-lifter-llvm PUBLIC "${llvm_libs}") +target_link_directories(aslp-lifter-llvm PUBLIC ${LLVM_LIBRARY_DIR}) +target_include_directories(aslp-lifter-llvm PUBLIC ${LLVM_INCLUDE_DIRS}) + +target_link_libraries(aslp-lifter-llvm PUBLIC aslp-lifter) + +# define macros for the explicit instantiation +target_compile_options(aslp-lifter-llvm PRIVATE "-includeaslp/llvm_lifter_traits.hpp") +target_compile_definitions(aslp-lifter-llvm PRIVATE "ASLP_LIFTER_INSTANTIATE=llvm_lifter_traits") + diff --git a/offlineASL-cpp/Doxyfile b/offlineASL-cpp/Doxyfile new file mode 100644 index 00000000..0f7df0f0 --- /dev/null +++ b/offlineASL-cpp/Doxyfile @@ -0,0 +1,960 @@ +# Doxyfile 1.10.0 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. + + +DOXYFILE_ENCODING = UTF-8 + + +PROJECT_NAME = "aslp-lifter-cpp" + + +PROJECT_NUMBER = + + +PROJECT_BRIEF = + + +PROJECT_LOGO = + + +PROJECT_ICON = + + +OUTPUT_DIRECTORY = + + +CREATE_SUBDIRS = NO + + +CREATE_SUBDIRS_LEVEL = 8 + + +ALLOW_UNICODE_NAMES = NO + + +OUTPUT_LANGUAGE = English + + +BRIEF_MEMBER_DESC = YES + + +REPEAT_BRIEF = YES + + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + + +ALWAYS_DETAILED_SEC = NO + + +INLINE_INHERITED_MEMB = NO + + +FULL_PATH_NAMES = YES + + +STRIP_FROM_PATH = + + +STRIP_FROM_INC_PATH = + + +SHORT_NAMES = NO + + +JAVADOC_AUTOBRIEF = NO + + +JAVADOC_BANNER = NO + + +QT_AUTOBRIEF = NO + + +MULTILINE_CPP_IS_BRIEF = NO + + +PYTHON_DOCSTRING = YES + + +INHERIT_DOCS = YES + + +SEPARATE_MEMBER_PAGES = NO + + +TAB_SIZE = 4 + + +ALIASES = + + +OPTIMIZE_OUTPUT_FOR_C = NO + + +OPTIMIZE_OUTPUT_JAVA = NO + + +OPTIMIZE_FOR_FORTRAN = NO + + +OPTIMIZE_OUTPUT_VHDL = NO + + +OPTIMIZE_OUTPUT_SLICE = NO + + +EXTENSION_MAPPING = + + +MARKDOWN_SUPPORT = YES + + +TOC_INCLUDE_HEADINGS = 5 + + +MARKDOWN_ID_STYLE = DOXYGEN + + +AUTOLINK_SUPPORT = YES + + +BUILTIN_STL_SUPPORT = NO + + +CPP_CLI_SUPPORT = NO + + +SIP_SUPPORT = NO + + +IDL_PROPERTY_SUPPORT = YES + + +DISTRIBUTE_GROUP_DOC = NO + + +GROUP_NESTED_COMPOUNDS = NO + + +SUBGROUPING = YES + + +INLINE_GROUPED_CLASSES = NO + + +INLINE_SIMPLE_STRUCTS = NO + + +TYPEDEF_HIDES_STRUCT = NO + + +LOOKUP_CACHE_SIZE = 0 + + +NUM_PROC_THREADS = 1 + + +TIMESTAMP = NO + + + +EXTRACT_ALL = YES + + +EXTRACT_PRIVATE = NO + + +EXTRACT_PRIV_VIRTUAL = NO + + +EXTRACT_PACKAGE = NO + + +EXTRACT_STATIC = NO + + +EXTRACT_LOCAL_CLASSES = YES + + +EXTRACT_LOCAL_METHODS = NO + + +EXTRACT_ANON_NSPACES = NO + + +RESOLVE_UNNAMED_PARAMS = YES + + +HIDE_UNDOC_MEMBERS = NO + + +HIDE_UNDOC_CLASSES = NO + + +HIDE_FRIEND_COMPOUNDS = NO + + +HIDE_IN_BODY_DOCS = NO + + +INTERNAL_DOCS = NO + + +CASE_SENSE_NAMES = SYSTEM + + +HIDE_SCOPE_NAMES = NO + + +HIDE_COMPOUND_REFERENCE= NO + + +SHOW_HEADERFILE = YES + + +SHOW_INCLUDE_FILES = YES + + +SHOW_GROUPED_MEMB_INC = NO + + +FORCE_LOCAL_INCLUDES = NO + + +INLINE_INFO = YES + + +SORT_MEMBER_DOCS = YES + + +SORT_BRIEF_DOCS = NO + + +SORT_MEMBERS_CTORS_1ST = NO + + +SORT_GROUP_NAMES = NO + + +SORT_BY_SCOPE_NAME = NO + + +STRICT_PROTO_MATCHING = NO + + +GENERATE_TODOLIST = YES + + +GENERATE_TESTLIST = YES + + +GENERATE_BUGLIST = YES + + +GENERATE_DEPRECATEDLIST= YES + + +ENABLED_SECTIONS = + + +MAX_INITIALIZER_LINES = 30 + + +SHOW_USED_FILES = YES + + +SHOW_FILES = YES + + +SHOW_NAMESPACES = YES + + +FILE_VERSION_FILTER = + + +LAYOUT_FILE = + + +CITE_BIB_FILES = + + + +QUIET = NO + + +WARNINGS = YES + + +WARN_IF_UNDOCUMENTED = YES + + +WARN_IF_DOC_ERROR = YES + + +WARN_IF_INCOMPLETE_DOC = YES + + +WARN_NO_PARAMDOC = NO + + +WARN_IF_UNDOC_ENUM_VAL = NO + + +WARN_AS_ERROR = NO + + +WARN_FORMAT = "$file:$line: $text" + + +WARN_LINE_FORMAT = "at line $line of file $file" + + +WARN_LOGFILE = + + + +INPUT = README.md subprojects/aslp-lifter subprojects/aslp-lifter-llvm + + +INPUT_ENCODING = UTF-8 + + +INPUT_FILE_ENCODING = + + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.ccm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + + +RECURSIVE = YES + + +EXCLUDE = + + +EXCLUDE_SYMLINKS = NO + + +EXCLUDE_PATTERNS = + + +EXCLUDE_SYMBOLS = + + +EXAMPLE_PATH = + + +EXAMPLE_PATTERNS = * + + +EXAMPLE_RECURSIVE = NO + + +IMAGE_PATH = + + +INPUT_FILTER = + + +FILTER_PATTERNS = + + +FILTER_SOURCE_FILES = NO + + +FILTER_SOURCE_PATTERNS = + + +USE_MDFILE_AS_MAINPAGE = README.md + + +FORTRAN_COMMENT_AFTER = 72 + + + +SOURCE_BROWSER = YES + + +INLINE_SOURCES = NO + + +STRIP_CODE_COMMENTS = YES + + +REFERENCED_BY_RELATION = NO + + +REFERENCES_RELATION = NO + + +REFERENCES_LINK_SOURCE = YES + + +SOURCE_TOOLTIPS = YES + + +USE_HTAGS = NO + + +VERBATIM_HEADERS = YES + + + +ALPHABETICAL_INDEX = YES + + +IGNORE_PREFIX = + + + +GENERATE_HTML = YES + + +HTML_OUTPUT = html + + +HTML_FILE_EXTENSION = .html + + +HTML_HEADER = + + +HTML_FOOTER = + + +HTML_STYLESHEET = + + +HTML_EXTRA_STYLESHEET = + + +HTML_EXTRA_FILES = + + +HTML_COLORSTYLE = AUTO_LIGHT + + +HTML_COLORSTYLE_HUE = 220 + + +HTML_COLORSTYLE_SAT = 100 + + +HTML_COLORSTYLE_GAMMA = 80 + + +HTML_DYNAMIC_MENUS = YES + + +HTML_DYNAMIC_SECTIONS = NO + + +HTML_CODE_FOLDING = YES + + +HTML_COPY_CLIPBOARD = YES + + +HTML_PROJECT_COOKIE = + + +HTML_INDEX_NUM_ENTRIES = 100 + + +GENERATE_DOCSET = NO + + +DOCSET_FEEDNAME = "Doxygen generated docs" + + +DOCSET_FEEDURL = + + +DOCSET_BUNDLE_ID = org.doxygen.Project + + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + + +DOCSET_PUBLISHER_NAME = Publisher + + +GENERATE_HTMLHELP = NO + + +CHM_FILE = + + +HHC_LOCATION = + + +GENERATE_CHI = NO + + +CHM_INDEX_ENCODING = + + +BINARY_TOC = NO + + +TOC_EXPAND = NO + + +SITEMAP_URL = + + +GENERATE_QHP = NO + + +QCH_FILE = + + +QHP_NAMESPACE = org.doxygen.Project + + +QHP_VIRTUAL_FOLDER = doc + + +QHP_CUST_FILTER_NAME = + + +QHP_CUST_FILTER_ATTRS = + + +QHP_SECT_FILTER_ATTRS = + + +QHG_LOCATION = + + +GENERATE_ECLIPSEHELP = NO + + +ECLIPSE_DOC_ID = org.doxygen.Project + + +DISABLE_INDEX = NO + + +GENERATE_TREEVIEW = NO + + +FULL_SIDEBAR = NO + + +ENUM_VALUES_PER_LINE = 4 + + +TREEVIEW_WIDTH = 250 + + +EXT_LINKS_IN_WINDOW = NO + + +OBFUSCATE_EMAILS = YES + + +HTML_FORMULA_FORMAT = png + + +FORMULA_FONTSIZE = 10 + + +FORMULA_MACROFILE = + + +USE_MATHJAX = NO + + +MATHJAX_VERSION = MathJax_2 + + +MATHJAX_FORMAT = HTML-CSS + + +MATHJAX_RELPATH = + + +MATHJAX_EXTENSIONS = + + +MATHJAX_CODEFILE = + + +SEARCHENGINE = YES + + +SERVER_BASED_SEARCH = NO + + +EXTERNAL_SEARCH = NO + + +SEARCHENGINE_URL = + + +SEARCHDATA_FILE = searchdata.xml + + +EXTERNAL_SEARCH_ID = + + +EXTRA_SEARCH_MAPPINGS = + + + +GENERATE_LATEX = YES + + +LATEX_OUTPUT = latex + + +LATEX_CMD_NAME = + + +MAKEINDEX_CMD_NAME = makeindex + + +LATEX_MAKEINDEX_CMD = makeindex + + +COMPACT_LATEX = NO + + +PAPER_TYPE = a4 + + +EXTRA_PACKAGES = + + +LATEX_HEADER = + + +LATEX_FOOTER = + + +LATEX_EXTRA_STYLESHEET = + + +LATEX_EXTRA_FILES = + + +PDF_HYPERLINKS = YES + + +USE_PDFLATEX = YES + + +LATEX_BATCHMODE = NO + + +LATEX_HIDE_INDICES = NO + + +LATEX_BIB_STYLE = plain + + +LATEX_EMOJI_DIRECTORY = + + + +GENERATE_RTF = NO + + +RTF_OUTPUT = rtf + + +COMPACT_RTF = NO + + +RTF_HYPERLINKS = NO + + +RTF_STYLESHEET_FILE = + + +RTF_EXTENSIONS_FILE = + + + +GENERATE_MAN = NO + + +MAN_OUTPUT = man + + +MAN_EXTENSION = .3 + + +MAN_SUBDIR = + + +MAN_LINKS = NO + + + +GENERATE_XML = NO + + +XML_OUTPUT = xml + + +XML_PROGRAMLISTING = YES + + +XML_NS_MEMB_FILE_SCOPE = NO + + + +GENERATE_DOCBOOK = NO + + +DOCBOOK_OUTPUT = docbook + + + +GENERATE_AUTOGEN_DEF = NO + + + +GENERATE_SQLITE3 = NO + + +SQLITE3_OUTPUT = sqlite3 + + +SQLITE3_RECREATE_DB = YES + + + +GENERATE_PERLMOD = NO + + +PERLMOD_LATEX = NO + + +PERLMOD_PRETTY = YES + + +PERLMOD_MAKEVAR_PREFIX = + + + +ENABLE_PREPROCESSING = YES + + +MACRO_EXPANSION = NO + + +EXPAND_ONLY_PREDEF = NO + + +SEARCH_INCLUDES = YES + + +INCLUDE_PATH = + + +INCLUDE_FILE_PATTERNS = + + +PREDEFINED = + + +EXPAND_AS_DEFINED = + + +SKIP_FUNCTION_MACROS = YES + + + +TAGFILES = + + +GENERATE_TAGFILE = + + +ALLEXTERNALS = NO + + +EXTERNAL_GROUPS = YES + + +EXTERNAL_PAGES = YES + + + +HIDE_UNDOC_RELATIONS = YES + + +HAVE_DOT = NO + + +DOT_NUM_THREADS = 0 + + +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" + + +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" + + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + + +DOT_FONTPATH = + + +CLASS_GRAPH = YES + + +COLLABORATION_GRAPH = YES + + +GROUP_GRAPHS = YES + + +UML_LOOK = NO + + +UML_LIMIT_NUM_FIELDS = 10 + + +DOT_UML_DETAILS = NO + + +DOT_WRAP_THRESHOLD = 17 + + +TEMPLATE_RELATIONS = NO + + +INCLUDE_GRAPH = YES + + +INCLUDED_BY_GRAPH = YES + + +CALL_GRAPH = NO + + +CALLER_GRAPH = NO + + +GRAPHICAL_HIERARCHY = YES + + +DIRECTORY_GRAPH = YES + + +DIR_GRAPH_MAX_DEPTH = 1 + + +DOT_IMAGE_FORMAT = png + + +INTERACTIVE_SVG = NO + + +DOT_PATH = + + +DOTFILE_DIRS = + + +DIA_PATH = + + +DIAFILE_DIRS = + + +PLANTUML_JAR_PATH = + + +PLANTUML_CFG_FILE = + + +PLANTUML_INCLUDE_PATH = + + +DOT_GRAPH_MAX_NODES = 50 + + +MAX_DOT_GRAPH_DEPTH = 0 + + +DOT_MULTI_TARGETS = NO + + +GENERATE_LEGEND = YES + + +DOT_CLEANUP = YES + + +MSCGEN_TOOL = + + +MSCFILE_DIRS = diff --git a/offlineASL-cpp/README.md b/offlineASL-cpp/README.md new file mode 100644 index 00000000..9f7bba1b --- /dev/null +++ b/offlineASL-cpp/README.md @@ -0,0 +1,127 @@ +# aslp-lifter-cpp +This directory contains source files for an arm lifter, generated by [aslp]'s offline partial evaluation. +Most of the files here are manually written, with the exception of those in the `generated` +subfolders within [subprojects/aslp-lifter][]. +The `:gen` command is designed to output its generated into the correct locations within +this folder structure. + +[aslp]: https://github.com/UQ-PAC/aslp +[subprojects/aslp-lifter]: subprojects/aslp-lifter + +Within Git, the directory contains a generated lifter that supports no instructions. +The following commands should generate and compile a functioning lifter: +```bash +# in 'aslp' repository root +echo ':gen A64 .* cpp' | OCAMLRUNPARAM=b dune exec asli +cd offlineASL-cpp +CXX=clang++ meson setup --reconfigure build +meson compile -C build -j16 +build/subprojects/aslp-lifter-demo/aslp-lifter-demo 0xaa0203e1 +``` +To reset this directory to its clean state, use: +```bash +rm -rf offlineASL-cpp +git checkout -- offlineASL-cpp +``` + +## lifter + +The generated [`aslp_lifter`][] is a polymorphic C++ class +with a type parameter to support retargeting the lifter to emit different IR targets (e.g. LLVM IR). +The methods which a backend interface must provide are declared in the [`lifter_interface`][] +class and further explained in the next section. + +[`aslp_lifter`]: subprojects/aslp-lifter/include/aslp/generated/aslp_lifter.hpp +[`lifter_interface`]: subprojects/aslp-lifter/include/aslp/interface.hpp + +The entry point of the lifter is `f_A64_decoder` which decodes the given opcode +and calls a lifter function for that particular encoding (e.g. `f_aarch64_branch_unconditional_immediate`). +Within this lifter function, methods from the lifter interface are called to implement the IR generation. + +### interface + +The [`lifter_interface`][] is the interface which the generated [`aslp_lifter`][] interacts with. +The [`Traits`][] parameter to lifter_interface defines the fundamental types which the interface operates with. +By providing different implementations of the interface and its traits, the lifter can be _retargeted_ +to interpret the semantics in different ways. +For example, [`llvm_interface`][] represents the semantics by emitting LLVM IR, and +other interfaces can be conceived to target other intermediate representations or even emulate the semantics in software. + +[`llvm_interface`]: subprojects/aslp-lifter-llvm/include/aslp/llvm_interface.hpp + +Within the interface, there is an important distinction between two different stages of evaluation: +- **lift-time** refers to the execution of the lifter's C++ code. + The [`Traits`][] types concerning lift-time are `::bits` for fixed-length bit-vectors, and `::bigint` for arbitrary-sized integers. + For example, the opcode is given to the lifter functions as a `::bits` value, so the opcode and anything derived from it is said to be known at lift-time. + However, anything concerning machine state must be deferred to run-time via the `f_gen_*` methods of [`lifter_interface`][]. +- **run-time** refers to the interpretation of the semantics produced during lift-time (e.g. LLVM IR). + Here, the important types are prefixed with `rt_`: + `::rt_expr` is the type for ordinary run-time values, `::rt_lexpr` is used for run-time variables, and + `::rt_label` represents a run-time block. + Methods which emit code for run-time begin with `f_gen_`. + +These two stages are performed in sequence, and we must be careful not to mix up the two. +For example, lift-time information is forgotten at run-time unless explicitly converted. +The `rt_` types are also merely a C++ convenience - they need not be distinct, and they +need not precisely correspond to actual run-time types. +Of course, they should satisfy the C++ type checker, but the lifter will not impose additional expectations on them. + +[`Traits`]: subprojects/aslp-lifter/include/aslp/interface.hpp + +[`llvm_lifter_traits`][] and [`llvm_interface`][] can be consulted for examples. +The llvm_interface is divided explicitly into lift-time and run-time components. + +[`llvm_lifter_traits`]: subprojects/aslp-lifter-llvm/include/aslp/llvm_lifter_traits.hpp + +### usage of headers + +The generated lifter template is fairly large. +If included in its entirety, it would take a substantial amount of time and memory to compile. +We alleviate this by splitting the lifter class's header into a forward declaration header, +accompanied with a separate header file for each lifter function definition. + +To make use of the lifter in downstream projects, including [`aslp_lifter`][] is sufficient. +This will need to be linked against a library providing the required symbols. + +For compiling the library, we provide [subprojects/aslp-lifter/src/generated](subprojects/aslp-lifter/src/generated) +which is a folder of C++ files, each instantiating a single lifter function with a particular [`Traits`][] class. +The traits are defined by a compiler macro, `ASLP_LIFTER_INSTANTIATE`, which should expand to the required Traits class name. +Header files can be included with the `-include` compiler argument. + +Examples of this can be seen in the Meson and CMake projects, described below. + +## usage + +To integrate the lifter with other applications, you can make use of the +Meson and CMake files in this repository. + +In Meson (preferred), we define several subprojects: +- _aslp-lifter_ is the main project, containing the lifter definition in header files and + source files to support explicit instantiation of the lifter template. +- _aslp-lifter-llvm_ uses the aslp-lifter sources to build a lifter to LLVM. + It provides a library for users to link against. +- _aslp-lifter-demo_ builds an executable which takes a numeric (big-endian) opcode and + emits its LLVM semantics. This demonstrates use of the preceding two libraries. + +[Meson](https://mesonbuild.com/Running-Meson.html) is very easy to use. +These commands will configure and build the project into a build/ subdirectory. +It is suggested to use Clang, as it is much faster than GCC in our testing. + +```bash +CXX=clang++ meson setup build +meson compile -C build +``` + +The CMakeLists is provided so this repository may be used with FetchContent. +However, it does not make much effort to separate the subprojects, and +it is not installable. + +## generating + +The files are generated by running this command from aslp's directory. +Replace $ASLP_LIFTER_CPP_DIR with this repository's directory: +```bash +echo ':gen A64 .+ cpp $ASLP_LIFTER_CPP_DIR/subprojects/aslp-lifter' | dune exec asli +``` +When commiting to Git, make sure to add the `generated` subfolder changes with `-f`. +They are ignored by default to avoid cluttering git output. diff --git a/offlineASL-cpp/check.py b/offlineASL-cpp/check.py new file mode 100755 index 00000000..59d52caf --- /dev/null +++ b/offlineASL-cpp/check.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +""" +Configures a meson build for syntax-checking only. +To use, run `meson compile -C build` after this script. + +This script works by manually editing the meson-generated +ninja files. This is obviously a bit hacky, but meson does +not yet properly support -fsyntax-only. +""" + +import os +import shutil +import subprocess +from pathlib import Path + +clangpp = shutil.which('clang++') +true = shutil.which('true') +assert clangpp +assert true + +def main(): + assert clangpp and true + + os.chdir(Path(__file__).parent) + + os.environ['CXX'] = clangpp + + subprocess.check_call('meson setup --reconfigure build'.split()) + + with open('build/build.ninja', 'rb') as f: + build = f.readlines() + + bstr = lambda x: x.encode('utf-8') + + # replacement rules, keyed by sentinel line, and values are + # replacements to perform on the NEXT line. + repls = { + b'rule cpp_COMPILER\n': (b' $ARGS ', b' $ARGS -fsyntax-only '), + b'rule cpp_LINKER\n': (bstr(clangpp), bstr(true)), + b'rule cpp_LINKER_RSP\n': (bstr(clangpp), bstr(true)), + } + + with open('build/build.ninja', 'wb') as f: + repl = None + for l in build: + if repl is not None: + assert repl[0] in l, f'replacement failed {repl} in {l!r}' + f.write(l.replace(repl[0], repl[1])) + repl = None + else: + repl = repls.get(l) # must match exactly + f.write(l) + +if __name__ == '__main__': + main() + diff --git a/offlineASL-cpp/meson.build b/offlineASL-cpp/meson.build new file mode 100644 index 00000000..5f8bb78d --- /dev/null +++ b/offlineASL-cpp/meson.build @@ -0,0 +1,18 @@ +project('offlineasl', 'cpp', + version : '0.1', + default_options : [ + 'warning_level=3', + 'cpp_std=c++20', + 'buildtype=debug', + # 'optimization=1', # slow and increases file size? + # 'b_lto=true', # too slow + # 'b_lto_mode=thin', + ] +) + +# debug(get_option('buildtype')) + +# TODO properly use dependency() to support builds against system-installed libraries +subproject('aslp-lifter', required: get_option('aslp-lifter')) +subproject('aslp-lifter-llvm', required: get_option('aslp-lifter-llvm')) +subproject('aslp-lifter-demo', required: get_option('aslp-lifter-demo')) diff --git a/offlineASL-cpp/meson.options b/offlineASL-cpp/meson.options new file mode 100644 index 00000000..b8dbcbfc --- /dev/null +++ b/offlineASL-cpp/meson.options @@ -0,0 +1,5 @@ +# these flags control whether particular subprojects are built +option('aslp-lifter', type : 'feature', value : 'enabled') +option('aslp-lifter-llvm', type : 'feature', value : 'enabled') +option('aslp-lifter-demo', type : 'feature', value : 'enabled') + diff --git a/offlineASL-cpp/subprojects/aslp-lifter-demo/meson.build b/offlineASL-cpp/subprojects/aslp-lifter-demo/meson.build new file mode 100644 index 00000000..d4d6ac18 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-demo/meson.build @@ -0,0 +1,25 @@ +project('aslp-lifter-demo', 'cpp', + version : '0.1', + default_options : ['warning_level=3', 'cpp_std=c++20']) + +incdir = 'include' + +llvm_dep = dependency('llvm') + +aslp_lifter_llvm_dep = dependency('aslp-lifter-llvm', fallback: ['aslp-lifter-llvm', 'dep']) +if get_option('use-libaslp-lifter-llvm').disabled() + aslp_lifter_llvm_dep = declare_dependency( + dependencies: aslp_lifter_llvm_dep.partial_dependency(includes: true), + compile_args: ['-DASLP_LIFTER_IMPL']) +endif + +aslp_lifter_dep = dependency('aslp-lifter', fallback: ['aslp-lifter', 'gen_dep']) + +srcfiles = files('src/main.cpp') + +executable( + 'aslp-lifter-demo', + sources: srcfiles, + dependencies: [ llvm_dep, aslp_lifter_llvm_dep, aslp_lifter_dep ], + install: true) + diff --git a/offlineASL-cpp/subprojects/aslp-lifter-demo/meson.options b/offlineASL-cpp/subprojects/aslp-lifter-demo/meson.options new file mode 100644 index 00000000..651f3c0e --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-demo/meson.options @@ -0,0 +1,2 @@ +option('use-libaslp-lifter-llvm', type : 'feature', value : 'enabled', + description : 'whether to use the separately-compiled aslp-lifter-llvm library') diff --git a/offlineASL-cpp/subprojects/aslp-lifter-demo/src/main.cpp b/offlineASL-cpp/subprojects/aslp-lifter-demo/src/main.cpp new file mode 100644 index 00000000..4b0f9b3d --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-demo/src/main.cpp @@ -0,0 +1,39 @@ +#include + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" + +#include +#ifdef ASLP_LIFTER_IMPL +#include +#endif + +#include + +int main(int argc, char **argv) { + llvm::LLVMContext context; + + llvm::Module module{"aslp_lifter_module", context}; + + auto funty = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false); + auto function = llvm::Function::Create(funty, llvm::GlobalValue::LinkageTypes::ExternalLinkage, "aslp_lifter_fun", module); + auto entry = llvm::BasicBlock::Create(context, "entry", function); + + aslp::llvm_lifter_interface iface{*function}; + aslp::aslp_lifter lifter{iface}; + + assert(argc > 1); + char* endp; + unsigned long long adds = std::strtoull(argv[1], &endp, 0); + assert(endp == argv[1] + strlen(argv[1])); + auto v_enc = llvm::APInt{32, adds}; + lifter.f_A64_decoder(v_enc); + + module.print(llvm::errs(), nullptr); + // module.dump(); + + assert(llvm::verifyModule(module)); + + std::cout << "boop! module validated." << std::endl; +} diff --git a/offlineASL-cpp/subprojects/aslp-lifter-llvm/.gitkeep b/offlineASL-cpp/subprojects/aslp-lifter-llvm/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/offlineASL-cpp/subprojects/aslp-lifter-llvm/CMakeLists.txt b/offlineASL-cpp/subprojects/aslp-lifter-llvm/CMakeLists.txt new file mode 100644 index 00000000..078a161f --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-llvm/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.20) + +project(offlineasl) + +include(../cmake/prelude.cmake) +include(../cmake/llvm.cmake) + +### aslp-lifter-llvm ### +# instantiates the aslp-lifter with a LLVM target + +pkg_check_modules(aslp-lifter-gen REQUIRED aslp-lifter-gen) + +file(GLOB GEN_INSTANTIATE src/generated/*.cpp) +add_library(aslp-lifter-llvm ${GEN_INSTANTIATE}) +target_include_directories(aslp-lifter-llvm PUBLIC include) + +target_link_libraries(aslp-lifter-llvm ${aslp-lifter-gen_LIBRARIES}) +target_include_directories(aslp-lifter-llvm PUBLIC ${aslp-lifter-gen_INCLUDE_DIRS}) +target_compile_options(aslp-lifter-llvm PUBLIC ${aslp-lifter-gen_CFLAGS_OTHER}) + +target_compile_options(aslp-lifter-llvm PRIVATE "-includellvm_lifter_traits.hpp") +target_compile_definitions(aslp-lifter-llvm PRIVATE "ASLP_LIFTER_INSTANTIATE=llvm_lifter_traits") diff --git a/offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_interface.hpp b/offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_interface.hpp new file mode 100644 index 00000000..604d39e9 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_interface.hpp @@ -0,0 +1,475 @@ +#pragma once +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "llvm_lifter_traits.hpp" + +namespace aslp { + +class llvm_lift_time_interface : virtual public lifter_interface { +public: + bits bits_lit(unsigned width, std::string_view str) override { + return llvm::APInt{width, str, (char)2}; + } + bits bits_zero(unsigned width) override { + return llvm::APInt::getZero(width); + } + bigint bigint_lit(std::string_view str) override { + bigint x; + auto result = std::from_chars(str.cbegin(), str.cend(), x); + + if (result.ec == std::errc{} && result.ptr == str.cend()) { + return x; + } else { + throw std::invalid_argument{"invalid bigint literal: " + std::string{str}}; + } + } + bigint bigint_zero() override { + return 0LL; + } + + bits extract_bits(const bits &val, bigint lo, bigint wd) override { + return val.extractBits(wd, lo); + } + bool f_eq_bits(const bits &x, const bits &y) override { + return x == y; + } + bool f_ne_bits(const bits &x, const bits &y) override { + return x != y; + } + bits f_add_bits(const bits &x, const bits &y) override { + return x + y; + } + bits f_sub_bits(const bits &x, const bits &y) override { + return x - y; + } + bits f_mul_bits(const bits &x, const bits &y) override { + return x * y; + } + bits f_and_bits(const bits &x, const bits &y) override { + return x & y; + } + bits f_or_bits(const bits &x, const bits &y) override { + return x | y; + } + bits f_eor_bits(const bits &x, const bits &y) override { + return x ^ y; + } + bits f_not_bits(const bits &x) override { + return ~x; + } + bool f_slt_bits(const bits &x, const bits &y) override { + return x.slt(y); + } + bool f_sle_bits(const bits &x, const bits &y) override { + return x.sle(y); + } + bits f_zeros_bits(bigint n) override { + return llvm::APInt::getZero(n); + } + bits f_ones_bits(bigint n) override { + return llvm::APInt::getAllOnes(n); + } + bits f_replicate_bits(const bits &x, bigint n) override { + assert(n >= 1); + bits original = x; + unsigned wd = x.getBitWidth(); + auto ret = x.zextOrTrunc(wd * n); + for (unsigned i = 1; i < n; i++) { + ret <<= wd; + ret |= original; + } + return ret; + } + bits f_append_bits(const bits &x, const bits &y) override { + auto wd = x.getBitWidth() + y.getBitWidth(); + auto ret = x.zext(wd); + ret <<= y.getBitWidth(); + ret |= y.zext(wd); + return ret; + } + bits f_ZeroExtend(const bits &x, bigint wd) override { + return x.zextOrTrunc(wd); + } + bits f_SignExtend(const bits &x, bigint wd) override { + return x.sextOrTrunc(wd); + } + bits f_lsl_bits(const bits &x, const bits &y) override { + return x << y; + } + bits f_lsr_bits(const bits &x, const bits &y) override { + return x.lshr(y); + } + bits f_asr_bits(const bits &x, const bits &y) override { + return x.ashr(y); + } + bigint f_cvt_bits_uint(const bits &x) override { + return x.getZExtValue(); + } + +}; + + +class llvm_run_time_interface : virtual public lifter_interface { +protected: + llvm::Function &function; + llvm::LLVMContext &context{function.getContext()}; + llvm::Module &module{*function.getParent()}; + + rt_label builder{std::make_shared>(&function.getEntryBlock())}; + + llvm::Function &assertDecl{createAssertDecl(module)}; + + static llvm::Function& createAssertDecl(llvm::Module &module) { + auto& context = module.getContext(); + auto funTy = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {llvm::Type::getInt1Ty(context)}, false); + auto fun = module.getOrInsertFunction("llvm.assert", funTy); + return *llvm::cast(fun.getCallee()); + } + + rt_global getOrInsertGlobal(unsigned count, llvm::Type *type, const std::string_view basename) { + if (count == 0) return {}; + + rt_global ret{std::make_shared(count)}; + for (unsigned i = 0; i < count; i++) { + std::string name{basename}; + if (count > 1) + name += std::to_string(i); + + ret->at(i) = llvm::cast(module.getOrInsertGlobal(name, type)); + } + assert(ret->size() == count); + return ret; + } + + llvm::Type* intty(unsigned width) { + return llvm::Type::getIntNTy(context, width); + } + + bigint int_expr(rt_expr x) { + return llvm::cast(x)->getSExtValue(); + } + rt_expr int_const(unsigned wd, bigint x) { + return llvm::ConstantInt::get(intty(wd), x); + } + + +public: + llvm_run_time_interface(llvm::Function &f) : function{f} {} + + + rt_lexpr v_PSTATE_C() override { + static rt_lexpr x{getOrInsertGlobal(1, intty(1), "C")}; + return x; + }; + rt_lexpr v_PSTATE_Z() override { + static rt_lexpr x{getOrInsertGlobal(1, intty(1), "Z")}; + return x; + }; + rt_lexpr v_PSTATE_V() override { + static rt_lexpr x{getOrInsertGlobal(1, intty(1), "V")}; + return x; + }; + rt_lexpr v_PSTATE_N() override { + static rt_lexpr x{getOrInsertGlobal(1, intty(1), "N")}; + return x; + }; + rt_lexpr v__PC() override { + static rt_lexpr x{getOrInsertGlobal(1, intty(64), "PC")}; + return x; + }; + rt_lexpr v__R() override { + // XXX redirect some of these to FP/LR + static rt_lexpr x{getOrInsertGlobal(31, intty(64), "R")}; + return x; + }; + rt_lexpr v__Z() override { + static rt_lexpr x{getOrInsertGlobal(32, intty(128), "Z")}; + return x; + }; + rt_lexpr v_SP_EL0() override { + static rt_lexpr x{getOrInsertGlobal(1, intty(64), "SP")}; + return x; + }; + rt_lexpr v_FPSR() override { assert(0); }; + rt_lexpr v_FPCR() override { assert(0); }; + + rt_lexpr v_PSTATE_BTYPE() override { assert(0); }; + rt_lexpr v_BTypeCompatible() override { assert(0); }; + rt_lexpr v___BranchTaken() override { assert(0); }; + rt_lexpr v_BTypeNext() override { assert(0); }; + rt_lexpr v___ExclusiveLocal() override { assert(0); }; + + + rt_lexpr f_decl_bv(std::string_view name, bigint width) override { + return builder->CreateAlloca(intty(width), /*addrspace*/0, /*arraysize*/nullptr, name); + } + rt_lexpr f_decl_bool(std::string_view name) override { + return f_decl_bv(name, 1); + } + + void f_switch_context(rt_label label) override { builder.swap(label); } + + std::tuple f_gen_branch(rt_expr cond) override { + auto tcase = llvm::BasicBlock::Create(context, "true", &function); + auto fcase = llvm::BasicBlock::Create(context, "false", &function); + auto join = llvm::BasicBlock::Create(context, "join", &function); + + builder->CreateCondBr(cond, tcase, fcase); + + rt_label tlabel = std::make_shared>(llvm::BranchInst::Create(join, tcase)); + rt_label flabel = std::make_shared>(llvm::BranchInst::Create(join, fcase)); + rt_label jlabel = std::make_shared>(join); + + return std::make_tuple(std::move(tlabel), std::move(flabel), std::move(jlabel)); + } + + rt_label f_true_branch(std::tuple x) override { + return std::get<0>(x); + } + rt_label f_false_branch(std::tuple x) override { + return std::get<1>(x); + } + rt_label f_merge_branch(std::tuple x) override { + return std::get<2>(x); + } + + void f_gen_assert(rt_expr cond) override { + builder->CreateCall(assertDecl.getFunctionType(), &assertDecl, {cond}); + } + rt_expr f_gen_bit_lit(bits bits) override { + return llvm::ConstantInt::get(intty(bits.getBitWidth()), bits); + } + rt_expr f_gen_bool_lit(bool b) override { + return f_gen_bit_lit(b ? llvm::APInt::getAllOnes(1) : llvm::APInt::getZero(1)); + } + rt_expr f_gen_int_lit(bigint i) override { + return llvm::ConstantInt::get(intty(111), i); // XXX hopefully this is never needed + } + + rt_expr f_gen_load(rt_lexpr ptr) override { + return f_gen_array_load(ptr, 0); + } + void f_gen_store(rt_lexpr ptr, rt_expr exp) override { + return f_gen_array_store(ptr, 0, exp); + } + + rt_expr f_gen_array_load(rt_lexpr var, bigint index) override { + llvm::Value* ptr{}; + llvm::Type* ty{}; + + if (auto global = std::get_if(&var); global) { + auto glob = global[0]->at(index); + ptr = glob; + ty = glob->getValueType(); + } else if (auto local = std::get_if(&var); local) { + assert(index == 0 && "non-zero index into rt_local"); + ptr = *local; + ty = local[0]->getAllocatedType(); + } + assert(ptr && ty); + return builder->CreateLoad(ty, ptr); + } + + void f_gen_array_store(rt_lexpr var, bigint index, rt_expr exp) override { + llvm::Value* ptr{}; + + if (auto global = std::get_if(&var); global) { + auto glob = global[0]->at(index); + ptr = glob; + } else if (auto local = std::get_if(&var); local) { + assert(index == 0 && "non-zero index into rt_local"); + ptr = *local; + } + assert(ptr); + builder->CreateStore(exp, ptr); + } + void f_gen_Mem_set(rt_expr addr, rt_expr width, rt_expr acctype, rt_expr exp) override { + auto ptr = builder->CreateIntToPtr(addr, llvm::PointerType::get(context, 0)); + builder->CreateStore(exp, ptr); + } + rt_expr f_gen_Mem_read(rt_expr addr, rt_expr width, rt_expr acctype) override { + auto ptr = builder->CreateIntToPtr(addr, llvm::PointerType::get(context, 0)); + return builder->CreateLoad(intty(int_expr(width)), ptr); + } + void f_gen_AArch64_MemTag_set(rt_expr address, rt_expr acctype, rt_expr value) override { assert(0); } + + void f_AtomicStart() override { assert(0); } + void f_AtomicEnd() override { assert(0); } + + rt_expr f_gen_cvt_bits_uint(rt_expr bits) override { + return bits; + } + rt_expr f_gen_cvt_bool_bv(rt_expr e) override { + assert(e->getType()->getIntegerBitWidth() == 1 && "f_gen_cvt_bool_bv given non-bv1 expression"); + return e; + } + + rt_expr f_gen_not_bool(rt_expr e) override { + return f_gen_not_bits(e); + } + rt_expr f_gen_and_bool(rt_expr x, rt_expr y) override { + return f_gen_and_bits(x, y); + } + rt_expr f_gen_or_bool(rt_expr x, rt_expr y) override { + return f_gen_or_bits(x, y); + } + rt_expr f_gen_eq_enum(rt_expr x, rt_expr y) override { + return f_gen_eq_bits(x, y); + } + + rt_expr f_gen_not_bits(rt_expr x) override { + return builder->CreateNot(x); + } + rt_expr f_gen_eq_bits(rt_expr x, rt_expr y) override { + return builder->CreateICmpEQ(x, y); + } + rt_expr f_gen_ne_bits(rt_expr x, rt_expr y) override { + return builder->CreateICmpNE(x, y); + } + rt_expr f_gen_or_bits(rt_expr x, rt_expr y) override { + return builder->CreateOr(x, y); + } + rt_expr f_gen_eor_bits(rt_expr x, rt_expr y) override { + return builder->CreateXor(x, y); + } + rt_expr f_gen_and_bits(rt_expr x, rt_expr y) override { + return builder->CreateAnd(x, y); + } + + rt_expr f_gen_add_bits(rt_expr x, rt_expr y) override { + return builder->CreateAdd(x, y); + } + rt_expr f_gen_sub_bits(rt_expr x, rt_expr y) override { + return builder->CreateSub(x, y); + } + rt_expr f_gen_sdiv_bits(rt_expr x, rt_expr y) override { + return builder->CreateSDiv(x, y); + } + rt_expr f_gen_sle_bits(rt_expr x, rt_expr y) override { + return builder->CreateICmpSLE(x, y); + } + rt_expr f_gen_slt_bits(rt_expr x, rt_expr y) override { + return builder->CreateICmpSLT(x, y); + } + rt_expr f_gen_mul_bits(rt_expr x, rt_expr y) override { + return builder->CreateMul(x, y); + } + + rt_expr f_gen_append_bits(rt_expr x, rt_expr y) override { + unsigned xwd = x->getType()->getIntegerBitWidth(); + unsigned wd = xwd + y->getType()->getIntegerBitWidth(); + // shift lhs + x = builder->CreateZExtOrBitCast(x, intty(wd)); + x = builder->CreateShl(x, int_const(wd, xwd)); + // union with rhs + y = builder->CreateZExtOrBitCast(y, intty(wd)); + return builder->CreateOr(x, y); + } + rt_expr f_gen_lsr_bits(rt_expr x, rt_expr y) override { + // assert(x->getType() == y->getType()); + auto wd = x->getType()->getIntegerBitWidth(); + auto max = llvm::ConstantInt::get(x->getType(), wd-1); + auto ok = builder->CreateICmpULE(y, max); + auto shift = builder->CreateLShr(x, y); + auto zero = int_const(wd, 0); + return builder->CreateSelect(ok, shift, zero); + } + + rt_expr f_gen_lsl_bits(rt_expr x, rt_expr y) override { + // assert(x->getType() == y->getType()); + auto wd = x->getType()->getIntegerBitWidth(); + auto max = llvm::ConstantInt::get(x->getType(), wd-1); + auto ok = builder->CreateICmpULE(y, max); + auto shift = builder->CreateShl(x, y); + auto zero = int_const(wd, 0); + return builder->CreateSelect(ok, shift, zero); + } + + rt_expr f_gen_asr_bits(rt_expr x, rt_expr y) override { + assert(x->getType() == y->getType()); + auto wd = x->getType()->getIntegerBitWidth(); + auto max = llvm::ConstantInt::get(x->getType(), wd-1); + auto ok = builder->CreateICmpULE(y, max); + auto shift = builder->CreateAShr(x, y); + auto zero = int_const(wd, 0); + return builder->CreateSelect(ok, shift, zero); + } + + rt_expr f_gen_replicate_bits(rt_expr x, rt_expr y) override { + auto count = int_expr(y); + auto basewd = x->getType()->getIntegerBitWidth() ; + auto finalwd = basewd * count; + auto finalty = intty(finalwd); + + auto base = builder->CreateZExtOrBitCast(x, finalty); + auto ret = base; + for (unsigned i = 1; i < count; i++) { + auto shifted = builder->CreateShl(base, int_const(finalwd, basewd * i)); + ret = builder->CreateOr(ret, shifted); + } + return ret; + } + + rt_expr f_gen_ZeroExtend(rt_expr x, rt_expr y) override { + return builder->CreateZExtOrBitCast(x, intty(int_expr(y))); + } + rt_expr f_gen_SignExtend(rt_expr x, rt_expr y) override { + return builder->CreateSExtOrBitCast(x, intty(int_expr(y))); + } + + rt_expr f_gen_slice(rt_expr e, bigint lo, bigint wd) override { + auto shifted = lo != 0 ? builder->CreateLShr(e, llvm::ConstantInt::get(e->getType(), lo)) : e; + return builder->CreateTruncOrBitCast(shifted, intty(wd)); + } + + rt_expr f_gen_FPCompare(rt_expr x, rt_expr y, rt_expr signalnan, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPCompareEQ(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPCompareGE(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPCompareGT(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPAdd(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPSub(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMulAdd(rt_expr addend, rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMulAddH(rt_expr addend, rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMulX(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMul(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPDiv(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMin(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMinNum(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMax(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPMaxNum(rt_expr x, rt_expr y, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPRecpX(rt_expr x, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPSqrt(rt_expr x, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_FPRecipEstimate(rt_expr x, rt_expr fpcr) override { assert(0); } + rt_expr f_gen_BFAdd(rt_expr x, rt_expr y) override { assert(0); } + rt_expr f_gen_BFMul(rt_expr x, rt_expr y) override { assert(0); } + rt_expr f_gen_FPConvertBF(rt_expr x, rt_expr fpcr, rt_expr rounding) override { assert(0); } + rt_expr f_gen_FPRecipStepFused(rt_expr x, rt_expr y) override { assert(0); } + rt_expr f_gen_FPRSqrtStepFused(rt_expr x, rt_expr y) override { assert(0); } + rt_expr f_gen_FPToFixed(rt_expr x, rt_expr fbits, rt_expr isunsigned, rt_expr fpcr, rt_expr rounding) override { assert(0); } + rt_expr f_gen_FixedToFP(rt_expr x, rt_expr fbits, rt_expr isunsigned, rt_expr fpcr, rt_expr rounding) override { assert(0); } + rt_expr f_gen_FPConvert(rt_expr x, rt_expr fpcr, rt_expr rounding) override { assert(0); } + rt_expr f_gen_FPRoundInt(rt_expr x, rt_expr fpcr, rt_expr rounding, rt_expr isexact) override { assert(0); } + rt_expr f_gen_FPRoundIntN(rt_expr x, rt_expr fpcr, rt_expr rounding, rt_expr intsize) override { assert(0); } + rt_expr f_gen_FPToFixedJS_impl(rt_expr x, rt_expr fpcr, rt_expr is64) override { assert(0); } // from override.asl +}; + + +class llvm_lifter_interface : public llvm_lift_time_interface, public llvm_run_time_interface { +public: + llvm_lifter_interface(llvm::Function &f) : llvm_run_time_interface(f) { }; +}; + +static_assert(!std::is_abstract_v); + +} // namespace aslp diff --git a/offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_lifter_traits.hpp b/offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_lifter_traits.hpp new file mode 100644 index 00000000..b84d1e5c --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-llvm/include/aslp/llvm_lifter_traits.hpp @@ -0,0 +1,26 @@ +#pragma once +#include + +#include +#include +#include + +#include + +namespace aslp { + +struct llvm_lifter_traits { + using bits = llvm::APInt; + using bigint = long long; + using rt_expr = llvm::Value *; + + using rt_local = llvm::AllocaInst *; + using rt_global = std::shared_ptr>; + + using rt_lexpr = std::variant; + using rt_label = std::shared_ptr>; +}; + +static_assert(lifter_traits); + +} // namespace aslp diff --git a/offlineASL-cpp/subprojects/aslp-lifter-llvm/meson.build b/offlineASL-cpp/subprojects/aslp-lifter-llvm/meson.build new file mode 100644 index 00000000..f872ed12 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-llvm/meson.build @@ -0,0 +1,38 @@ +project('aslp-lifter-llvm', 'cpp', + version : '0.1', + default_options : [ 'warning_level=3', 'cpp_std=c++20' ]) + +version = meson.project_version() +llvm_dep = dependency('llvm', version: ['>=17', '<18']) +empty_dep = dependency('', required: false) + +lifter_dep = dependency('aslp-lifter', version: version, fallback: ['aslp-lifter', 'gen_dep']) +instantiate_dep = dependency('aslp-lifter-instantiate', version: version, fallback: ['aslp-lifter', 'instantiate_dep']) +instantiate_srcdir = instantiate_dep.get_variable(pkgconfig: 'srcdir', default_value: '') + +if instantiate_srcdir != '' + srcfiles = run_command('find', instantiate_srcdir, '-name', '*.cpp', check : true).stdout().strip().split('\n') + instantiate_dep = declare_dependency( + sources: srcfiles, + dependencies: instantiate_dep, + ) +endif + +incdir = 'include' +install_subdir(incdir, install_dir : get_option('includedir'), strip_directory : true) + +lib = library( + meson.project_name(), + [], + include_directories : incdir, + dependencies: [ llvm_dep, instantiate_dep ], + cpp_args: [ + '-includeaslp/llvm_lifter_traits.hpp', + '-DASLP_LIFTER_INSTANTIATE=llvm_lifter_traits'], + cpp_pch: 'pch/cpp_pch.hpp', + install : true) + +dep = declare_dependency(link_with: lib, include_directories: incdir) + +pkg = import('pkgconfig') +pkg.generate(lib, requires: 'aslp-lifter=' + version, variables: {'llvm_version': llvm_dep.version()}) diff --git a/offlineASL-cpp/subprojects/aslp-lifter-llvm/pch/cpp_pch.hpp b/offlineASL-cpp/subprojects/aslp-lifter-llvm/pch/cpp_pch.hpp new file mode 100644 index 00000000..fabe7098 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter-llvm/pch/cpp_pch.hpp @@ -0,0 +1,8 @@ +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/offlineASL-cpp/subprojects/aslp-lifter-llvm/src/cpp_pch.hpp b/offlineASL-cpp/subprojects/aslp-lifter-llvm/src/cpp_pch.hpp new file mode 100644 index 00000000..e69de29b diff --git a/offlineASL-cpp/subprojects/aslp-lifter/dummy.cpp b/offlineASL-cpp/subprojects/aslp-lifter/dummy.cpp new file mode 100644 index 00000000..e69de29b diff --git a/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter.hpp b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter.hpp new file mode 100644 index 00000000..2a197681 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter.hpp @@ -0,0 +1 @@ +#include "generated/aslp_lifter.hpp" // IWYU pragma: export diff --git a/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter_impl.hpp b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter_impl.hpp new file mode 100644 index 00000000..a7dfaaab --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/aslp_lifter_impl.hpp @@ -0,0 +1 @@ +#include "generated/aslp_lifter_impl.hpp" // IWYU pragma: export diff --git a/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/A64_decoder.hpp b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/A64_decoder.hpp new file mode 100644 index 00000000..0ea8cd24 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/A64_decoder.hpp @@ -0,0 +1,22 @@ +/* AUTO-GENERATED LIFTER FILE */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aslp { + +template +void aslp_lifter::f_A64_decoder(typename Traits::bits v_enc) { + throw std::runtime_error{"aslp_lifter: unsupported! " + std::string{__func__} + " @ " + std::string{__FILE__} + ":" + std::to_string(__LINE__)}; + +} // f_A64_decoder + + +} // namespace aslp \ No newline at end of file diff --git a/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter.hpp b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter.hpp new file mode 100644 index 00000000..bd672528 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter.hpp @@ -0,0 +1,24 @@ +/* AUTO-GENERATED LIFTER FILE */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace aslp { + +template +class aslp_lifter { + public: using interface = lifter_interface; + private: interface& iface; + public: + aslp_lifter(interface& iface) : iface{iface} { } + /* generated semantics */ + void f_A64_decoder(typename Traits::bits v_enc); + /* generated decode test conditions */ +}; + +} // namespace aslp \ No newline at end of file diff --git a/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter_impl.hpp b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter_impl.hpp new file mode 100644 index 00000000..d43b8ce6 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/aslp_lifter_impl.hpp @@ -0,0 +1,15 @@ +/* AUTO-GENERATED LIFTER FILE */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export + +namespace aslp { + + +} // namespace aslp \ No newline at end of file diff --git a/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/decode_tests.hpp b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/decode_tests.hpp new file mode 100644 index 00000000..460d34d5 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/generated/decode_tests.hpp @@ -0,0 +1,15 @@ +/* AUTO-GENERATED LIFTER FILE */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace aslp { + + +} // namespace aslp \ No newline at end of file diff --git a/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/interface.hpp b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/interface.hpp new file mode 100644 index 00000000..2e1438b5 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/include/aslp/interface.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include + +namespace aslp { + +template +concept lifter_traits = requires(T) { + typename T::bits; + typename T::bigint; + typename T::rt_expr; + typename T::rt_lexpr; + typename T::rt_label; +}; + +template class lifter_interface : public Traits { +public: + // bits which are known at lift-time + using typename Traits::bits; + // bigints which are known at lift-time + using typename Traits::bigint; + + // runtime-expression type, i.e. the type of values produced by the semantics + using typename Traits::rt_expr; + using typename Traits::rt_lexpr; + // runtime-label, supports switching blocks during semantics generation + using typename Traits::rt_label; + + // TODO: split lift-time interface from run-time interface + // TODO: more flexible method of adding const-lvalue qualifiers +public: + virtual ~lifter_interface() = default; + + virtual rt_lexpr v_PSTATE_C() = 0; + virtual rt_lexpr v_PSTATE_Z() = 0; + virtual rt_lexpr v_PSTATE_V() = 0; + virtual rt_lexpr v_PSTATE_N() = 0; + virtual rt_lexpr v__PC() = 0; + virtual rt_lexpr v__R() = 0; + virtual rt_lexpr v__Z() = 0; + virtual rt_lexpr v_SP_EL0() = 0; + virtual rt_lexpr v_FPSR() = 0; + virtual rt_lexpr v_FPCR() = 0; + + virtual rt_lexpr v_PSTATE_BTYPE() = 0; + virtual rt_lexpr v_BTypeCompatible() = 0; + virtual rt_lexpr v___BranchTaken() = 0; + virtual rt_lexpr v_BTypeNext() = 0; + virtual rt_lexpr v___ExclusiveLocal() = 0; + + virtual bits bits_lit(unsigned width, std::string_view str) = 0; + virtual bits bits_zero(unsigned width) = 0; + virtual bigint bigint_lit(std::string_view str) = 0; + virtual bigint bigint_zero() = 0; + virtual bits extract_bits(const bits &val, bigint lo, bigint wd) = 0; + + virtual bool f_eq_bits(const bits &x, const bits &y) = 0; + virtual bool f_ne_bits(const bits &x, const bits &y) = 0; + virtual bits f_add_bits(const bits &x, const bits &y) = 0; + virtual bits f_sub_bits(const bits &x, const bits &y) = 0; + virtual bits f_mul_bits(const bits &x, const bits &y) = 0; + virtual bits f_and_bits(const bits &x, const bits &y) = 0; + virtual bits f_or_bits(const bits &x, const bits &y) = 0; + virtual bits f_eor_bits(const bits &x, const bits &y) = 0; + virtual bits f_not_bits(const bits &x) = 0; + virtual bool f_slt_bits(const bits &x, const bits &y) = 0; + virtual bool f_sle_bits(const bits &x, const bits &y) = 0; + virtual bits f_zeros_bits(bigint n) = 0; + virtual bits f_ones_bits(bigint n) = 0; + virtual bits f_replicate_bits(const bits &x, bigint n) = 0; + virtual bits f_append_bits(const bits &x, const bits &y) = 0; + virtual bits f_ZeroExtend(const bits &x, bigint wd) = 0; + virtual bits f_SignExtend(const bits &x, bigint wd) = 0; + virtual bits f_lsl_bits(const bits &x, const bits &y) = 0; + virtual bits f_lsr_bits(const bits &x, const bits &y) = 0; + virtual bits f_asr_bits(const bits &x, const bits &y) = 0; + virtual bigint f_cvt_bits_uint(const bits &x) = 0; + + virtual rt_lexpr f_decl_bv(std::string_view name, bigint width) = 0; + virtual rt_lexpr f_decl_bool(std::string_view name) = 0; + + virtual void f_switch_context(rt_label label) = 0; + virtual rt_label f_true_branch(std::tuple) = 0; + virtual rt_label f_false_branch(std::tuple) = 0; + virtual rt_label f_merge_branch(std::tuple) = 0; + virtual std::tuple + f_gen_branch(rt_expr cond) = 0; + + virtual void f_gen_assert(rt_expr cond) = 0; + virtual rt_expr f_gen_bit_lit(bits bits) = 0; + virtual rt_expr f_gen_bool_lit(bool b) = 0; + virtual rt_expr f_gen_int_lit(bigint i) = 0; + virtual rt_expr f_gen_load(rt_lexpr ptr) = 0; + virtual void f_gen_store(rt_lexpr var, rt_expr exp) = 0; + virtual rt_expr + f_gen_array_load(rt_lexpr array, + bigint index) = 0; // XXX unsure of array ptr type. + virtual void f_gen_array_store(rt_lexpr array, bigint index, rt_expr exp) = 0; + virtual void f_gen_Mem_set(rt_expr ptr, rt_expr width, rt_expr acctype, + rt_expr exp) = 0; + virtual rt_expr f_gen_Mem_read(rt_expr ptr, rt_expr width, + rt_expr acctype) = 0; + virtual void f_gen_AArch64_MemTag_set(rt_expr address, rt_expr acctype, + rt_expr value) = 0; + + virtual void f_AtomicStart() = 0; + virtual void f_AtomicEnd() = 0; + + virtual rt_expr f_gen_cvt_bits_uint(rt_expr bits) = 0; + virtual rt_expr f_gen_cvt_bool_bv(rt_expr e) = 0; + + virtual rt_expr f_gen_not_bool(rt_expr e) = 0; + virtual rt_expr f_gen_and_bool(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_or_bool(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_eq_enum(rt_expr x, rt_expr y) = 0; + + virtual rt_expr f_gen_not_bits(rt_expr x) = 0; + virtual rt_expr f_gen_eq_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_ne_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_or_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_eor_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_and_bits(rt_expr x, rt_expr y) = 0; + + virtual rt_expr f_gen_add_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_sub_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_sdiv_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_sle_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_slt_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_mul_bits(rt_expr x, rt_expr y) = 0; + + virtual rt_expr f_gen_append_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_lsr_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_lsl_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_asr_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_replicate_bits(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_ZeroExtend(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_SignExtend(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_slice(rt_expr e, bigint lo, bigint wd) = 0; + + virtual rt_expr f_gen_FPCompare(rt_expr x, rt_expr y, rt_expr signalnan, + rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPCompareEQ(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPCompareGE(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPCompareGT(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPAdd(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPSub(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMulAdd(rt_expr addend, rt_expr x, rt_expr y, + rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMulAddH(rt_expr addend, rt_expr x, rt_expr y, + rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMulX(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMul(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPDiv(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMin(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMinNum(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMax(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPMaxNum(rt_expr x, rt_expr y, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPRecpX(rt_expr x, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPSqrt(rt_expr x, rt_expr fpcr) = 0; + virtual rt_expr f_gen_FPRecipEstimate(rt_expr x, rt_expr fpcr) = 0; + virtual rt_expr f_gen_BFAdd(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_BFMul(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_FPConvertBF(rt_expr x, rt_expr fpcr, + rt_expr rounding) = 0; + virtual rt_expr f_gen_FPRecipStepFused(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_FPRSqrtStepFused(rt_expr x, rt_expr y) = 0; + virtual rt_expr f_gen_FPToFixed(rt_expr x, rt_expr fbits, rt_expr isunsigned, + rt_expr fpcr, rt_expr rounding) = 0; + virtual rt_expr f_gen_FixedToFP(rt_expr x, rt_expr fbits, rt_expr isunsigned, + rt_expr fpcr, rt_expr rounding) = 0; + virtual rt_expr f_gen_FPConvert(rt_expr x, rt_expr fpcr, + rt_expr rounding) = 0; + virtual rt_expr f_gen_FPRoundInt(rt_expr x, rt_expr fpcr, rt_expr rounding, + rt_expr isexact) = 0; + virtual rt_expr f_gen_FPRoundIntN(rt_expr x, rt_expr fpcr, rt_expr rounding, + rt_expr intsize) = 0; + virtual rt_expr f_gen_FPToFixedJS_impl(rt_expr x, rt_expr fpcr, + rt_expr is64) = 0; // from override.asl +}; + +} // namespace aslp diff --git a/offlineASL-cpp/subprojects/aslp-lifter/meson.build b/offlineASL-cpp/subprojects/aslp-lifter/meson.build new file mode 100644 index 00000000..5d39cc73 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/meson.build @@ -0,0 +1,43 @@ +project('aslp-lifter', 'cpp', + version : '0.1', + default_options : ['warning_level=3', 'cpp_std=c++20']) + +incdir = 'include' +srcdir = 'src' +install_srcdir = get_option('datadir') / 'aslp' + +install_subdir(incdir, install_dir : get_option('includedir'), strip_directory : true) +gen_dep = declare_dependency(include_directories : incdir) + +srcfiles = [ + 'src/generated/./A64_decoder.cpp', +] + + +install_subdir(srcdir, install_dir: install_srcdir) +cxxflags = ['-Wno-unused-parameter'] +instantiate_dep = declare_dependency( + sources: srcfiles, + dependencies: gen_dep, + compile_args: cxxflags) + +# needed to generate compile_commands.json when building this subproject standalone +dummy_lib = library('dummy', 'dummy.cpp', dependencies: gen_dep, install: false) + +pkg = import('pkgconfig') +pkg.generate( + name: meson.project_name(), + description: 'offline aslp lifter library (template headers)', +) + +instantiate_srcfiles = [] +foreach f : srcfiles + instantiate_srcfiles += [get_option('prefix') / install_srcdir / f] +endforeach + +pkg.generate( + name: meson.project_name() + '-instantiate', + description: 'offline aslp lifter library (source files for explicit instantiation)', + extra_cflags: cxxflags, + variables: { 'srcdir': get_option('prefix') / install_srcdir, 'srcfiles': ';'.join(instantiate_srcfiles) }, +) diff --git a/offlineASL-cpp/subprojects/aslp-lifter/meson.build.in b/offlineASL-cpp/subprojects/aslp-lifter/meson.build.in new file mode 100644 index 00000000..2c08b97c --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/meson.build.in @@ -0,0 +1,40 @@ +project('aslp-lifter', 'cpp', + version : '0.1', + default_options : ['warning_level=3', 'cpp_std=c++20']) + +incdir = 'include' +srcdir = 'src' +install_srcdir = get_option('datadir') / 'aslp' + +install_subdir(incdir, install_dir : get_option('includedir'), strip_directory : true) +gen_dep = declare_dependency(include_directories : incdir) + +srcfiles = ["SRCFILES"] + +install_subdir(srcdir, install_dir: install_srcdir) +cxxflags = ['-Wno-unused-parameter'] +instantiate_dep = declare_dependency( + sources: srcfiles, + dependencies: gen_dep, + compile_args: cxxflags) + +# needed to generate compile_commands.json when building this subproject standalone +dummy_lib = library('dummy', 'dummy.cpp', dependencies: gen_dep, install: false) + +pkg = import('pkgconfig') +pkg.generate( + name: meson.project_name(), + description: 'offline aslp lifter library (template headers)', +) + +instantiate_srcfiles = [] +foreach f : srcfiles + instantiate_srcfiles += [get_option('prefix') / install_srcdir / f] +endforeach + +pkg.generate( + name: meson.project_name() + '-instantiate', + description: 'offline aslp lifter library (source files for explicit instantiation)', + extra_cflags: cxxflags, + variables: { 'srcdir': get_option('prefix') / install_srcdir, 'srcfiles': ';'.join(instantiate_srcfiles) }, +) diff --git a/offlineASL-cpp/subprojects/aslp-lifter/src/generated/A64_decoder.cpp b/offlineASL-cpp/subprojects/aslp-lifter/src/generated/A64_decoder.cpp new file mode 100644 index 00000000..b98d4764 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/src/generated/A64_decoder.cpp @@ -0,0 +1,18 @@ +/* AUTO-GENERATED LIFTER FILE */ + +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export + +namespace aslp { + +#ifdef ASLP_LIFTER_INSTANTIATE +using Traits = ASLP_LIFTER_INSTANTIATE; +template void aslp_lifter::f_A64_decoder(typename Traits::bits v_enc); +#endif + +} // namespace aslp \ No newline at end of file diff --git a/offlineASL-cpp/subprojects/aslp-lifter/src/generated/decode_tests.cpp b/offlineASL-cpp/subprojects/aslp-lifter/src/generated/decode_tests.cpp new file mode 100644 index 00000000..db69df11 --- /dev/null +++ b/offlineASL-cpp/subprojects/aslp-lifter/src/generated/decode_tests.cpp @@ -0,0 +1,18 @@ +/* AUTO-GENERATED LIFTER FILE */ + +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export + +namespace aslp { + +#ifdef ASLP_LIFTER_INSTANTIATE +using Traits = ASLP_LIFTER_INSTANTIATE; +template bool aslp_lifter::f_aarch64_memory_vector_multiple_post_inc_decode_test(typename Traits::bits v_enc); +#endif + +} // namespace aslp \ No newline at end of file