Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for thread-local variables #6

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 7 additions & 22 deletions bin/asl2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,6 @@
// functions.
:filter_unlisted_functions imports

// Remove global variables corresponding to thread-local variables
// (Thread local variables are listed in a configuration file like this
//
// {{
// "thread_local_state": [
// "GPR",
// "RFLAGS"
// ]
// }}
:filter_listed_variables thread_local_state

// Deleting the ASL definitions of any functions on the import list may
// result in additional dead code (i.e., functions that are only used by
// those functions) so delete any unreachable functions
Expand Down Expand Up @@ -197,10 +186,10 @@
// etc. know what file/line in the ASL file produced each line of C code.
// Use --line-info or --no-line-info to control this.
//
// Optionally, references to thread-local processor state such as registers
// can be accessed via a pointer.
// (This can be useful when modelling multi-processor systems.)
:{generate_c} --output-dir={output_dir} --basename={basename} --num-c-files={num_c_files} {line_info} {thread_local}
// Optionally, global state can be split across multiple structs.
// (This can be useful when modelling multi-processor systems to separate
// thread-local state from global state.)
:{generate_c} --output-dir={output_dir} --basename={basename} --num-c-files={num_c_files} {line_info} {split_state}

:quit
""".strip()
Expand Down Expand Up @@ -304,11 +293,13 @@ def mk_script(args, output_directory):
'line_info': "",
'num_c_files': args.num_c_files,
'output_dir': output_directory,
'split_state': "",
'track_valid': "",
'wrap_variables': "",
}
if args.instrument_unknown: substitutions['track_valid'] = ":xform_valid track-valid"
if args.wrap_variables: substitutions['wrap_variables'] = ":xform_wrap"
if args.split_state: substitutions['split_state'] = "--split-state"
if not args.auto_case_split:
substitutions['auto_case_split'] = '--no-auto-case-split'
else:
Expand All @@ -317,12 +308,6 @@ def mk_script(args, output_directory):
substitutions['line_info'] = '--no-line-info'
else:
substitutions['line_info'] = '--line-info'
if args.thread_local_pointer:
thread_local = f"--thread-local-pointer={args.thread_local_pointer}"
thread_local += f" --thread-local=thread_local_state"
else:
thread_local = ''
substitutions['thread_local'] = thread_local

script = base_script.format(**substitutions)

Expand Down Expand Up @@ -446,7 +431,7 @@ def main() -> int:
parser.add_argument("--num-c-files", help="write functions to N files (default: 1)", metavar="N", type=int, default=1)
parser.add_argument("--auto-case-split", help="generate case split code automatically", action=argparse.BooleanOptionalAction)
parser.add_argument("--line-info", help="insert line directives into C code", action=argparse.BooleanOptionalAction)
parser.add_argument("--thread-local-pointer", help="name of pointer to thread-local processor state", metavar="varname", default=None)
parser.add_argument("--split-state", help="split state into multiple structs", action=argparse.BooleanOptionalAction)
parser.add_argument("--instrument-unknown", help="instrument assignments of UNKNOWN", action=argparse.BooleanOptionalAction)
parser.add_argument("--wrap-variables", help="wrap global variables into functions", action=argparse.BooleanOptionalAction)
parser.add_argument("-O0", help="perform minimal set of transformations", action=argparse.BooleanOptionalAction)
Expand Down
6 changes: 3 additions & 3 deletions demo/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ CC=clang-16 -std=c2x

.PRECIOUS: simulator_%

simulator_% : simulator.c exports.json demo.asl
simulator_% : simulator.c config.json demo.asl
@ if ${HAVE_GNU_AS}; then \
$(ASL2C) --basename=sim --intermediates=log --backend=$* > sim.prj ; \
env ASL_PATH="${ASL_PATH}" $(ASLI) --nobanner --batchmode --project=sim.prj --configuration=exports.json demo.asl ; \
$(ASL2C) --basename=sim --intermediates=log --split-state --backend=$* > sim.prj ; \
env ASL_PATH="${ASL_PATH}" $(ASLI) --nobanner --batchmode --project=sim.prj --configuration=config.json demo.asl ; \
$(CC) ${CFLAGS} simulator.c -o $@ ${LDFLAGS} ; \
else echo ${REPORT_NOT_GAS}; fi

Expand Down
10 changes: 9 additions & 1 deletion demo/exports.json → demo/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
"ASL_WriteReg64",
"ASL_WriteMemory8",
"PrintState"
]
],

"__comment": [
"Split the variables into shared (global) and thread-local."
],
"split_state": {
"global_state": ["__Memory"],
"threadlocal_state" : [".*"]
}
}

16 changes: 16 additions & 0 deletions demo/simulator.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ UNUSED static void set_register(const char* name, uint64_t val)
* Simulator
****************************************************************/

// Storage for the thread-local state in a processor.
// (Note that the variables 'threadlocal_state_ptr' and 'global_state_ptr'
// need to point to these structs and their initializers need to be
// called.)
struct threadlocal_state Processor0;
struct global_state Global;

int main(int argc, const char* argv[])
{
ASL_error_file = stderr;
Expand All @@ -240,6 +247,15 @@ int main(int argc, const char* argv[])
exit(1);
}
exception_clear();

// Initialize all the state structs
ASL_initialize_threadlocal_state(&Processor0);
ASL_initialize_global_state(&Global);

// Set the state pointers
threadlocal_state_ptr = &Processor0;
global_state_ptr = &Global;

ASL_Reset_0();
exception_check("ASL_Reset");

Expand Down
164 changes: 117 additions & 47 deletions libASL/backend_c_new.ml
Original file line number Diff line number Diff line change
Expand Up @@ -198,21 +198,27 @@ let ident (fmt : PP.formatter) (x : Ident.t) : unit =

let semicolon (fmt : PP.formatter) : unit = PP.pp_print_string fmt ";"

(* Support for packing thread-local state (e.g., registers) into a struct that is then
* accessed via the named pointer.
*
* todo: at present, all global variable are treated as thread-local.
* In the future shared state such as memory should be stored separately.
(* Support for distributing state (e.g., registers and memory) into struct that
* are then accessed via a named pointer.
* Typically used to separate state into thread-local, processor-local,
* cluster-local, package-local, etc. structs
*)
let opt_thread_local_pointer : string option ref = ref None
let thread_local_variables : Ident.t list ref = ref []
let var_ptrs : (string * string) Bindings.t ref = ref Bindings.empty

let pointer (fmt : PP.formatter) (x : Ident.t) : unit =
( match !opt_thread_local_pointer with
| Some ptr when List.mem x !thread_local_variables -> PP.fprintf fmt "%s->" ptr
( match Bindings.find_opt x !var_ptrs with
| Some (_, ptr) -> PP.fprintf fmt "%s->" ptr
| _ -> ()
)

(* Any global state can register an initializer to be run on a global variable.
* This is currently only used for RAM blocks.
*)
let initializers : (string * string) list ref = ref []

let add_initializer (struct_name : string) (i : string) : unit =
initializers := !initializers @ [(struct_name, i)]

(* list of all exception tycons - used to decide whether to insert a tag in
* Expr_RecordInit
*)
Expand Down Expand Up @@ -1028,12 +1034,21 @@ let declaration (fmt : PP.formatter) ?(is_extern : bool option) (x : AST.declara
| Type_Constructor (i, [ _ ])
when Ident.equal i Builtin_idents.ram && not is_extern_val ->
let ram fmt v = PP.fprintf fmt "ASL_%a" ident v in
PP.fprintf fmt "struct ASL_ram %a = (struct ASL_ram){ 0 };@," ram v;
varty loc fmt v ty;
PP.fprintf fmt " = &%a;@,@," ram v
( match Bindings.find_opt v !var_ptrs with
| Some (s, ptr) -> (* if RAM is in a struct, initializer function needs to set pointer to the RAM object *)
PP.fprintf fmt "struct ASL_ram %a;@," ram v;
varty loc fmt v ty;
PP.fprintf fmt ";@,";
add_initializer s (PP.asprintf "p->%a = (struct ASL_ram){ 0 };" ram v);
add_initializer s (PP.asprintf "p->%a = &p->%a;" ident v ram v)
| None -> (* if RAM is not in a struct, it can be initialized directly *)
PP.fprintf fmt "struct ASL_ram %a = (struct ASL_ram){ 0 };@," ram v;
varty loc fmt v ty;
PP.fprintf fmt " = &%a;@,@," ram v;
)
| _ ->
varty loc fmt v ty;
PP.fprintf fmt ";@,@,"
PP.fprintf fmt ";@,"
);
| Decl_BuiltinFunction (f, fty, loc) ->
()
Expand Down Expand Up @@ -1129,30 +1144,6 @@ let type_decls (xs : AST.declaration list) : AST.declaration list =
in
List.filter_map mk_type_decl xs

let var_decls (xs : AST.declaration list) : AST.declaration list =
let is_var_decl (x : AST.declaration) : bool =
( match x with
| Decl_Const _
| Decl_Config _
| Decl_Var _
-> true

| Decl_Enum _
| Decl_Record _
| Decl_Exception _
| Decl_Typedef _
| Decl_FunType _
| Decl_FunDefn _
| Decl_BuiltinType _
| Decl_Forward _
| Decl_BuiltinFunction _
| Decl_Operator1 _
| Decl_Operator2 _
-> false
)
in
List.filter is_var_decl xs

let fun_decls (xs : AST.declaration list) : AST.declaration list =
let is_fun_decl (x : AST.declaration) : bool =
( match x with
Expand Down Expand Up @@ -1307,6 +1298,14 @@ let get_rt_header (_ : unit) : string list =
let module Runtime = (val (!runtime) : RuntimeLib) in
Runtime.file_header

(* the name of the pointer to a given struct *)
let struct_ptr (s : string) : string = s ^ "_ptr"

let state_struct (fmt : PP.formatter) (name : string) (vs : AST.declaration list) : unit =
PP.fprintf fmt "struct %s {@." name;
indented fmt (fun _ -> declarations fmt vs);
PP.fprintf fmt "@.};@.@."

let emit_c_header (dirname : string) (basename : string) (f : PP.formatter -> unit) : unit =
let header_suffix = if !is_cxx then ".hpp" else ".h" in
let basename = basename ^ header_suffix in
Expand Down Expand Up @@ -1350,10 +1349,44 @@ let emit_c_source (filename : string) ?(index : int option) (includes : string l
f fmt
)

let regexps_match (res : Str.regexp list) (s : string) : bool =
List.exists (fun re -> Str.string_match re s 0) res

let generate_files (num_c_files : int) (dirname : string) (basename : string)
(ffi_prototypes : PP.formatter -> unit)
(ffi_definitions : PP.formatter -> unit)
(structs : (Str.regexp list * string) list)
(ds : AST.declaration list) : unit =

(* Construct
* - the global map 'var_ptrs' from global variables to the pointer to be used
* to access the variable
* - the association list 'structs' from struct names to variable declarations
* - a list of all declarations of mutable and immutable variables not associated with a struct
*)
let struct_vars = ref (List.map (fun (_, s) -> (s, ref [])) structs) in
let global_vars : AST.declaration list ref = ref [] in
List.iter (fun d ->
( match d with
| AST.Decl_Var (v, _, _) ->
let name = Ident.name v in
let entry = List.find_opt (fun (res, _) -> regexps_match res name) structs in
( match entry with
| Some (_, s) ->
var_ptrs := Bindings.add v (s, struct_ptr s) !var_ptrs;
let ds = List.assoc s !struct_vars in
ds := d :: !ds
| None ->
global_vars := d :: !global_vars
)
| Decl_Const _
| Decl_Config _
->
global_vars := d :: !global_vars
| _ -> ()
))
ds;

let basename_t = basename ^ "_types" in
emit_c_header dirname basename_t (fun fmt ->
type_decls ds |> Asl_utils.topological_sort |> List.rev |> declarations fmt;
Expand All @@ -1367,7 +1400,11 @@ let generate_files (num_c_files : int) (dirname : string) (basename : string)
);
let basename_v = basename ^ "_vars" in
emit_c_header dirname basename_v (fun fmt ->
extern_declarations fmt (var_decls ds)
extern_declarations fmt !global_vars;
List.iter (fun (s, ds) -> state_struct fmt s !ds) !struct_vars;
List.iter
(fun (s, _) -> Format.fprintf fmt "extern struct %s *%s;@," s (struct_ptr s))
!struct_vars;
);

let header_suffix = if !is_cxx then ".hpp" else ".h" in
Expand All @@ -1379,7 +1416,21 @@ let generate_files (num_c_files : int) (dirname : string) (basename : string)
emit_c_source filename_e gen_h_filenames exceptions_init;

let filename_v = Filename.concat dirname basename_v in
emit_c_source filename_v gen_h_filenames (fun fmt -> declarations fmt (var_decls ds));
emit_c_source filename_v gen_h_filenames (fun fmt ->
declarations fmt !global_vars;
List.iter
(fun (s, _) -> Format.fprintf fmt "struct %s *%s;@," s (struct_ptr s))
!struct_vars;
List.iter
(fun (s, _) ->
Format.fprintf fmt "void ASL_initialize_%s(struct %s *p) {@," s s;
List.iter
(fun (s', i) -> if s = s' then Format.fprintf fmt " %s\n" i;)
!initializers;
Format.fprintf fmt "}@,"
)
!struct_vars;
);

let ds = fun_decls ds in
let filename_f = Filename.concat dirname (basename ^ "_funs") in
Expand Down Expand Up @@ -1420,11 +1471,7 @@ let _ =
let opt_dirname = ref "" in
let opt_num_c_files = ref 1 in
let opt_basename = ref "asl2c" in

let add_thread_local_variables (group : string) : unit =
let names = Configuration.get_strings group in
thread_local_variables := !thread_local_variables @ (Ident.mk_idents names)
in
let opt_split_state = ref false in

let cmd (tcenv : Tcheck.Env.t) (cpu : Cpu.cpu) : bool =
let decls = !Commands.declarations in
Expand All @@ -1435,8 +1482,32 @@ let _ =
(* the new FFI doesn't change code if it thinks there are no exports *)
[]
in

(* When splitting global state across structs is enabled,
* the struct map is a file contains entries like
* "global": [ "RAM" ],
* "thread_local": [ ".*" ]
* That is, a list of regular expressions associated with struct names.
*
* This is used to generate structs that contain entries for global
* mutable variables that match the regular expressions.
* And all references to those variables are indirected through a
* global variable that points to structs of that type.
* For example:
* global_ptr->RAM
* thread_local_ptr->RIP
* thread_local_ptr->GPR[i]
* Note that immutable variables are not put in structs
*)
let structs = if !opt_split_state then
let map = Configuration.get_record_entries "split_state" in
List.map (fun (s, res) -> (List.map Str.regexp res), s) map
else
[]
in

let (ffi_protos, ffi_defns) = mk_ffi_exports decls exports in
generate_files !opt_num_c_files !opt_dirname !opt_basename ffi_protos ffi_defns decls;
generate_files !opt_num_c_files !opt_dirname !opt_basename ffi_protos ffi_defns structs decls;
true
in

Expand All @@ -1449,8 +1520,7 @@ let _ =
("--no-new-ffi", Arg.Clear new_ffi, " Do not use new FFI");
("--line-info", Arg.Set include_line_info, " Insert line number information");
("--no-line-info", Arg.Clear include_line_info, " Do not insert line number information");
("--thread-local-pointer", Arg.String (fun s -> opt_thread_local_pointer := Some s), "<varname> Access all thread-local variables through named pointer");
("--thread-local", Arg.String add_thread_local_variables, "<config name> Configuration file group of thread local variable names");
("--split-state", Arg.Set opt_split_state, " Split global variables into structs");
]
in
Commands.registerCommand "generate_c_new" flags [] [] "Generate C (new)" cmd
Expand Down
13 changes: 13 additions & 0 deletions libASL/configuration.ml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ let read_configuration_file (filename : string) : unit =
let get_strings (key : string) : string list =
get_list_by_key key !configurations

(** Read list of strings from all previously read configuration files *)
let get_record_entries (key : string) : (string * string list) list =
let trees = List.filter_map (get_entry key) !configurations in
let keys = List.filter_map (fun tree ->
( match tree with
| `Assoc kvs -> Some (List.map fst kvs)
| _ -> None
))
trees
|> List.concat
in
List.map (fun key -> (key, get_list_by_key key trees)) keys

(****************************************************************
* End
****************************************************************)
Loading
Loading