Skip to content

Commit

Permalink
rasl check for load structure and included primops
Browse files Browse the repository at this point in the history
  • Loading branch information
ailrst committed Jan 9, 2025
1 parent dde3c1d commit 8500ec0
Show file tree
Hide file tree
Showing 11 changed files with 266 additions and 25 deletions.
11 changes: 6 additions & 5 deletions bin/asli.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ let opt_print_version = ref false
let opt_no_default_aarch64 = ref false
let opt_export_aarch64_dir = ref ""
let opt_verbose = ref false

let opt_check_rasl = ref false


let () =
Expand Down Expand Up @@ -134,7 +134,7 @@ let rec process_command (tcenv: TC.Env.t) (cpu: Cpu.cpu) (fname: string) (input0

(try
(* Generate and evaluate partially evaluated instruction *)
let disStmts = Dis.dis_decode_entry disEnv lenv decoder op in
let disStmts = Dis.dis_decode_entry disEnv lenv (!opt_check_rasl) decoder op in
List.iter (eval_stmt disEvalEnv) disStmts;

if Eval.Env.compare evalEnv disEvalEnv then
Expand Down Expand Up @@ -191,15 +191,15 @@ let rec process_command (tcenv: TC.Env.t) (cpu: Cpu.cpu) (fname: string) (input0
let cpu' = Cpu.mkCPU cpu.env cpu.denv in
let op = Z.of_string opcode in
Printf.printf "Decoding instruction %s %s\n" iset (Z.format "%x" op);
cpu'.sem iset op
cpu'.sem ~validate_rasl:(!opt_check_rasl) iset op
| ":ast" :: iset :: opcode :: rest when List.length rest <= 1 ->
let op = Z.of_string opcode in
let decoder = Eval.Env.getDecoder cpu.env (Ident iset) in
let chan_opt = Option.map open_out (List.nth_opt rest 0) in
let chan = Option.value chan_opt ~default:stdout in
List.iter
(fun s -> Printf.fprintf chan "%s\n" (Utils.to_string (PP.pp_raw_stmt s)))
(Dis.dis_decode_entry cpu.env cpu.denv decoder op);
(Dis.dis_decode_entry cpu.env cpu.denv (!opt_check_rasl) decoder op);
Option.iter close_out chan_opt
| ":gen" :: iset :: id :: rest when List.length rest <= 3 ->
let pc_option = Option.value List.(nth_opt rest 1) ~default:"false" in
Expand Down Expand Up @@ -230,7 +230,7 @@ let rec process_command (tcenv: TC.Env.t) (cpu: Cpu.cpu) (fname: string) (input0
let cpu' = Cpu.mkCPU (Eval.Env.copy cpu.env) cpu.denv in
let op = Z.of_string opcode in
let decoder = Eval.Env.getDecoder cpu'.env (Ident iset) in
let stmts = Dis.dis_decode_entry cpu'.env cpu.denv decoder op in
let stmts = Dis.dis_decode_entry cpu'.env cpu.denv (!opt_check_rasl) decoder op in
let chan = open_out_bin fname in
Printf.printf "Dumping instruction semantics for %s %s" iset (Z.format "%x" op);
Printf.printf " to file %s\n" fname;
Expand Down Expand Up @@ -302,6 +302,7 @@ let rec repl (tcenv: TC.Env.t) (cpu: Cpu.cpu): unit =

let options = Arg.align ([
( "-x", Arg.Set_int Dis.debug_level, " Partial evaluation debugging (requires debug level argument >= 0)");
( "--check", Arg.Set opt_check_rasl, " Produce an error when rASL does not conform to expected properties");
( "-v", Arg.Set opt_verbose, " Verbose output");
( "--no-aarch64", Arg.Set opt_no_default_aarch64 , " Disable bundled AArch64 semantics");
( "--export-aarch64", Arg.Set_string opt_export_aarch64_dir, " Export bundled AArch64 MRA to the given directory");
Expand Down
2 changes: 1 addition & 1 deletion bin/server.ml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ let eval_instr (opcode: string) : string * string =
let env' = Lazy.force persistent_env in
let lenv = Dis.build_env env' in
let decoder = Eval.Env.getDecoder env' (Ident "A64") in
let (enc,stmts) = Dis.dis_decode_entry_with_inst env' lenv decoder (Z.of_string opcode) in
let (enc,stmts) = Dis.dis_decode_entry_with_inst env' lenv decoder true (Z.of_string opcode) in

let stmts' = List.map pp_raw stmts in
enc, String.concat "\n" stmts'
Expand Down
6 changes: 3 additions & 3 deletions libASL/cpu.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type cpu = {
setPC : Primops.bigint -> unit;
elfwrite : Int64.t -> char -> unit;
opcode : string -> Primops.bigint -> unit;
sem : string -> Primops.bigint -> unit;
sem : validate_rasl:bool -> string -> Primops.bigint -> unit;
gen : string -> string -> bool -> gen_backend -> string -> unit;
}

Expand Down Expand Up @@ -55,11 +55,11 @@ let mkCPU (env : Eval.Env.t) (denv: Dis.env): cpu =
let decoder = Eval.Env.getDecoder env (Ident iset) in
Eval.eval_decode_case AST.Unknown env decoder opcode

and sem (iset: string) (opcode: Primops.bigint): unit =
and sem ~(validate_rasl:bool) (iset: string) (opcode: Primops.bigint): unit =
let decoder = Eval.Env.getDecoder env (Ident iset) in
List.iter
(fun s -> Printf.printf "%s\n" (pp_stmt s))
(Dis.dis_decode_entry env denv decoder opcode)
(Dis.dis_decode_entry env denv validate_rasl decoder opcode)

and gen (iset: string) (pat: string) (include_pc: bool) (backend: gen_backend) (dir: string): unit =
if not (Sys.file_exists dir) then failwith ("Can't find target dir " ^ dir);
Expand Down
2 changes: 1 addition & 1 deletion libASL/cpu.mli
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type cpu = {
setPC : Primops.bigint -> unit;
elfwrite : Int64.t -> char -> unit;
opcode : string -> Primops.bigint -> unit;
sem : string -> Primops.bigint -> unit;
sem : validate_rasl:bool -> string -> Primops.bigint -> unit;
gen : string -> string -> bool -> gen_backend -> string -> unit;
}

Expand Down
34 changes: 22 additions & 12 deletions libASL/dis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ let rec flatten x acc =
type config = {
eval_env: Eval.Env.t;
unroll_bound: Z.t;
validate_rasl: bool;
}

module DisEnv = struct
Expand Down Expand Up @@ -1499,10 +1500,8 @@ let enum_types env i =
| _ -> None

(* Actually perform dis *)
let dis_core (env: Eval.Env.t) (unroll_bound) ((lenv,globals): env) (decode: decode_case) (op: Primops.bigint): string * stmt list =
let dis_core ~(config:config) ((lenv,globals): env) (decode: decode_case) (op: Primops.bigint): string * stmt list =
let DecoderCase_Case (_,_,loc) = decode in
let config = { eval_env = env ; unroll_bound } in

let (enc,lenv',stmts) = (dis_decode_case loc decode op) config lenv in
let varentries = List.(concat @@ map (fun vars -> StringMap.(bindings (map fst vars))) lenv.locals) in
let bindings = Asl_utils.Bindings.of_seq @@ List.to_seq @@ List.map (fun (x,y) -> (Ident x,y)) varentries in
Expand All @@ -1511,7 +1510,7 @@ let dis_core (env: Eval.Env.t) (unroll_bound) ((lenv,globals): env) (decode: dec
let stmts' = Transforms.RemoveUnused.remove_unused globals @@ stmts in
let stmts' = Transforms.RedundantSlice.do_transform Bindings.empty stmts' in
let stmts' = Transforms.FixRedefinitions.run (globals : IdentSet.t) stmts' in
let stmts' = Transforms.StatefulIntToBits.run (enum_types env) stmts' in
let stmts' = Transforms.StatefulIntToBits.run (enum_types config.eval_env) stmts' in
let stmts' = Transforms.IntToBits.ints_to_bits stmts' in
let stmts' = Transforms.CommonSubExprElim.do_transform stmts' in
let stmts' = Transforms.CopyProp.copyProp stmts' in
Expand All @@ -1528,25 +1527,36 @@ let dis_core (env: Eval.Env.t) (unroll_bound) ((lenv,globals): env) (decode: dec
List.iter (fun s -> Printf.printf "%s\n" (pp_stmt s)) stmts';
Printf.printf "===========\n";
end;

(try
let suppress = not config.validate_rasl in
RASL_check.LoadStatementInvariantExc.check_stmts_exc ~suppress stmts';
RASL_check.AllowedIntrinsicsExc.check_stmts_exc ~suppress stmts'
with
| (RASL_check.RASLInvariantFailed _) as ex -> raise (DisTrace (lenv'.trace, ex))
)
;

enc, stmts'


(* Wrapper around the core to attempt loop vectorization, reverting back if this fails.
This is a complete hack, but it is nicer to make the loop unrolling decision during
partial evaluation, rather than having to unroll after we know vectorization failed.
*)
let dis_decode_entry_with_inst (env: Eval.Env.t) ((lenv,globals): env) (decode: decode_case) (op: Primops.bigint): string * stmt list =
let dis_decode_entry_with_inst (env: Eval.Env.t) ((lenv,globals): env) (decode: decode_case) validate_rasl (op: Primops.bigint): string * stmt list =
let max_upper_bound = Z.of_int64 Int64.max_int in
let config = { eval_env = env ; unroll_bound=max_upper_bound ; validate_rasl } in
match !Symbolic.use_vectoriser with
| false -> dis_core env max_upper_bound (lenv,globals) decode op
| false -> dis_core ~config (lenv,globals) decode op
| true ->
let enc,stmts' = dis_core env Z.one (lenv,globals) decode op in
let enc,stmts' = dis_core ~config:{config with unroll_bound= Z.one} (lenv,globals) decode op in
let (res,stmts') = Transforms.LoopClassify.run stmts' env in
if res then (enc,stmts') else
dis_core env max_upper_bound (lenv,globals) decode op
dis_core ~config (lenv,globals) decode op

let dis_decode_entry (env: Eval.Env.t) ((lenv,globals): env) (decode: decode_case) (op: Primops.bigint): stmt list =
snd @@ dis_decode_entry_with_inst env (lenv,globals) decode op
let dis_decode_entry (env: Eval.Env.t) ((lenv,globals): env) validate_rasl (decode: decode_case) (op: Primops.bigint): stmt list =
snd @@ dis_decode_entry_with_inst env (lenv,globals) decode validate_rasl op

let build_env (env: Eval.Env.t): env =
let env = Eval.Env.freeze env in
Expand Down Expand Up @@ -1585,10 +1595,10 @@ let setPC (env: Eval.Env.t) (lenv,g: env) (address: Z.t): env =
let addr = VBits (Primops.mkBits width address) in
LocalEnv.setVar loc (Var(0, pc)) (Val addr) lenv,g

let retrieveDisassembly ?(address:string option) (env: Eval.Env.t) (lenv: env) (opcode: string) : stmt list =
let retrieveDisassembly ?(validate_rasl=false) ?(address:string option) (env: Eval.Env.t) (lenv: env) (opcode: string) : stmt list =
let decoder = Eval.Env.getDecoder env (Ident "A64") in
let DecoderCase_Case (_,_,loc) = decoder in
let lenv = match address with
| Some v -> setPC env lenv (Z.of_string v)
| None -> lenv in
dis_decode_entry env lenv decoder (Z.of_string opcode)
dis_decode_entry env lenv validate_rasl decoder (Z.of_string opcode)
1 change: 1 addition & 0 deletions libASL/dune
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
(:standard -w -27 -cclib -lstdc++ -open LibASL_stage0))
(modules cpu dis elf eval
lexer lexersupport loadASL monad primops rws symbolic tcheck testing transforms value
rASL_check
symbolic_lifter decoder_program call_graph req_analysis
offline_transform dis_tc offline_opt
ocaml_backend
Expand Down
198 changes: 198 additions & 0 deletions libASL/rASL_check.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@


module AST = Asl_ast
module Env = Eval.Env

open AST
open Visitor
open Asl_utils
open Asl_visitor

(****************************************************************
* Check invariants of the reduced asl language
****************************************************************)

type error_info = {
at_statment: stmt option;
violation: [`LoadSingle | `DisallowedIntrinsic of string];
}

let show_violation = function
| `LoadSingle -> "Loads are limited to rhs of constdecl"
| `DisallowedIntrinsic s -> "Illegal intrinsic :'" ^ s ^ "'"

let show_error_info (e: error_info) =
Printf.sprintf "%s at %s" (show_violation e.violation)
((Option.map (fun s -> pp_stmt s) e.at_statment) |> function | Some x -> x | None -> "")

exception RASLInvariantFailed of (error_info list)

let () = Printexc.register_printer (function
| RASLInvariantFailed es -> Some (Printf.sprintf "RASL invariant(s) failed: %s" (String.concat "; " (List.map show_error_info es)))
| _ -> None
)

module type InvChecker = sig
val check_stmts : stmt list -> error_info list
val check_expr : expr -> error_info list
val check_stmt : stmt -> error_info list
end


module type InvCheckerExc = sig
include InvChecker
val check_stmts_exc : ?suppress:bool -> stmt list -> unit
end

module MakeInvCheckerExc(E : InvChecker) : InvCheckerExc = struct
include E

let check_stmts_exc ?(suppress=false) s = if (not suppress) then check_stmts s |>
function
| [] -> ()
| es -> raise (RASLInvariantFailed es)
end

module LoadStatmentInvariant : InvChecker = struct

class single_load (vars) = object (self)
inherit Asl_visitor.nopAslVisitor
(* Ensures loads only appear in statements of the form lhs := Mem_load(v) *)

val mutable violating = []
val mutable curstmt = None
method get_violating () = violating

method!vexpr e = match e with
| Expr_TApply(f, _, _) when (name_of_FIdent f = "Mem.read") -> (
violating <- {at_statment=curstmt; violation=`LoadSingle}::violating ;
SkipChildren
)
| _ -> DoChildren

method!vstmt s =
curstmt <- Some s ;
match s with
| Stmt_ConstDecl(t, ident, Expr_TApply(f, _, _), loc) when (name_of_FIdent f = "Mem.read") -> SkipChildren
| _ -> DoChildren

end

let check_stmts s =
let v = new single_load () in
ignore @@ visit_stmts v s ;
v#get_violating ()

let check_stmt s = check_stmts [s]

let check_expr e =
let v = new single_load () in
ignore @@ visit_expr v e ;
v#get_violating ()

end

module AllowedIntrinsics: InvChecker = struct

(*
Ensure the only intrinsic function calls are appear in the following list.
*)
let allowed_intrinsics =
StringSet.of_list
[
"Mem.read";
"Mem.set";
"Elem.read";
"Elem.set";
"AtomicEnd";
"AtomicStart";
"FPAdd";
"FPDiv";
"FPMul";
"FPMulAdd";
"FPSub";
"FPSqrt";
"FPCompare";
"FixedToFP";
"FPToFixed";
"FPConvert";
"FPRoundInt";
"not_bool";
"and_bool";
"cvt_bool_bv";
"SignExtend";
"ZeroExtend";
"add_bits";
"and_bits";
"append_bits";
"asr_bits";
"lsl_bits";
"lsr_bits";
"cvt_bits_uint";
"eor_bits";
"eq_bits";
"ite";
"mul_bits";
"not_bits";
"or_bits";
"sdiv_bits";
"sub_bits";
"sle_bits";
"slt_bits";
"replicate_bits";
"select_vec";
"ite_vec";
"eq_vec";
"add_vec";
"sub_vec";
"asr_vec";
"trunc_vec";
"zcast_vec";
"shuffle_vec";
"scast_vec";
]

class allowed_intrinsics (vars) = object (self)
inherit Asl_visitor.nopAslVisitor
(* Ensures loads only appear in statements of the form lhs := Mem_load(v) *)

val mutable violating = []
val mutable curstmt = None
method get_violating () = violating

method!vexpr e = match e with
| Expr_TApply(f, _, _) when (not @@ StringSet.mem (name_of_FIdent f) allowed_intrinsics) -> (
let f = (name_of_FIdent f) in
violating <- {at_statment=curstmt; violation=(`DisallowedIntrinsic f)}::violating ;
DoChildren
)
| _ -> DoChildren

method!vstmt s =
curstmt <- Some s ;
match s with
| Stmt_ConstDecl(t, ident, Expr_TApply(f, _, _), loc) when (name_of_FIdent f = "Mem.read") -> SkipChildren
| Stmt_TCall(f, _, _, _) when (not @@ StringSet.mem (name_of_FIdent f) allowed_intrinsics) ->
let f = (name_of_FIdent f) in
violating <- {at_statment=curstmt; violation=(`DisallowedIntrinsic f)}::violating ;
DoChildren
| _ -> DoChildren

end

let check_stmts s =
let v = new allowed_intrinsics () in
ignore @@ visit_stmts v s ;
v#get_violating ()

let check_stmt s = check_stmts [s]

let check_expr e =
let v = new allowed_intrinsics () in
ignore @@ visit_expr v e ;
v#get_violating ()

end

module LoadStatementInvariantExc = MakeInvCheckerExc(LoadStatmentInvariant)
module AllowedIntrinsicsExc = MakeInvCheckerExc(AllowedIntrinsics)
Loading

0 comments on commit 8500ec0

Please sign in to comment.