diff --git a/bin/iic.py b/bin/iic.py index cf4c2112..27e682a9 100755 --- a/bin/iic.py +++ b/bin/iic.py @@ -331,6 +331,7 @@ def mk_script(args, output_directory): if args.O0: script = [] script.append(":filter_unlisted_functions imports") + script.append(":xform_monomorphize") script.append(":filter_reachable_from --no-keep-builtins exports") if args.Obounded: script.append(":xform_bounded") if args.transform_foreign: script.append(":xform_foreign") diff --git a/bin/iii.ml b/bin/iii.ml index a3c1c55c..f0b41bfe 100644 --- a/bin/iii.ml +++ b/bin/iii.ml @@ -199,12 +199,9 @@ let read_foreign_idents (export : bool) (ds : AST.declaration list) : Ident.t list = List.filter_map (fun d -> match d with - | Decl_FunFFI (nm, is_export, f, [], loc) when is_export = export -> Some f - | Decl_FunFFI (nm, is_export, f, ps, loc) when is_export = export -> - let name = Ident.name_with_tag f in - raise (Utils.InternalError - (loc, Format.asprintf "No specialized function '%s' created" name, - (fun fmt -> FMT.declaration fmt d), __LOC__ )) + | Decl_FunFFI (nm, is_export, f, _, loc) when is_export = export -> Some f + | Decl_VarFFI (nm, is_export, f, loc) when is_export = export -> Some f + | Decl_TypeFFI (nm, is_export, f, loc) when is_export = export -> Some f | _ -> None ) ds diff --git a/libISA/backend_c.ml b/libISA/backend_c.ml index 8194f3e2..a81aa68e 100644 --- a/libISA/backend_c.ml +++ b/libISA/backend_c.ml @@ -1552,7 +1552,7 @@ let mk_ffi_conversion (loc : Loc.t) (indirect : bool) (c_name : Ident.t) (asl_na when Ident.equal f cvt_sintN_int -> mk_ffi_convert_large_bits (Z.to_int n.v) | _ -> - let msg = PP.asprintf "Type '%a' cannot be used in functions that are imported or exported between ASL and C" + let msg = PP.asprintf "Type '%a' cannot be used in functions that are imported from or exported to C" FMT.ty asl_type in raise (Error.TypeError (loc, msg)) @@ -1892,26 +1892,63 @@ let mk_ffi_import_wrapper (pp_proto, pp_wrapper) +(* Make list of foreign import/export pairs based either on old-style config files + * or new-style foreign import/export declarations. + *) +let get_ffi (get_exports : bool) (decls : AST.declaration list) : (string * Ident.t) list = + (* old-style imports/exports are specified via a configuration json file *) + let key = if get_exports then "exports" else "imports" in + let raw_exports = if !new_ffi then Configuration.get_strings key else [] in + let old_exports = List.filter_map (fun d -> + ( match d with + | AST.Decl_Record (x, _, _, _) + | AST.Decl_Enum (x, _, _) + | AST.Decl_Var (x, _, _) + | AST.Decl_Config (x, _, _, _) + | AST.Decl_Const (x, _, _, _) + | AST.Decl_FunType (x, _, _) + when List.mem (Ident.name x) raw_exports + -> Some (Ident.name x, x) + | _ + -> None + )) + decls + in + + (* new-style imports/exports are specified using foreign import/export declarations *) + let new_exports = List.filter_map (fun d -> + ( match d with + | AST.Decl_FunFFI (nm, is_export, x, _, _) + | AST.Decl_VarFFI (nm, is_export, x, _) + | AST.Decl_TypeFFI (nm, is_export, x, _) + when is_export == get_exports + -> Some (nm, x) + | _ + -> None + )) decls + in + old_exports @ new_exports + + let mk_ffi_infos (is_import : bool) (decl_map : AST.declaration list Bindings.t) - (c_names : string list) : + (xs : (string * Ident.t) list) : (Ident.t * Ident.t * AST.function_type * Loc.t) list = let missing : string list ref = ref [] in - let infos = List.filter_map (fun c_name -> - let asl_ident = Ident.mk_fident c_name in + let infos = List.filter_map (fun (c_name, isa_ident) -> let c_ident = Ident.mk_ident c_name in - let info = ( match Bindings.find_opt asl_ident decl_map with + let info = ( match Bindings.find_opt isa_ident decl_map with | Some ds -> ( match List.find_opt (function AST.Decl_FunType _ -> true | _ -> false) ds with - | Some (AST.Decl_FunType (_, fty, loc)) -> Some (c_ident, asl_ident, fty, loc) + | Some (AST.Decl_FunType (_, fty, loc)) -> Some (c_ident, isa_ident, fty, loc) | _ -> None ) | _ -> None ) in - if Option.is_none info && not (is_enumerated_type c_ident) then begin + if Option.is_none info && not (is_enumerated_type isa_ident) then begin missing := c_name :: !missing end; info - ) c_names + ) xs in if not (Utils.is_empty !missing) then begin let direction = if is_import then "Import" else "Export" in @@ -1920,32 +1957,6 @@ let mk_ffi_infos (is_import : bool) (decl_map : AST.declaration list Bindings.t) end; infos -let mk_new_ffi_infos (is_import : bool) - (decl_map : AST.declaration list Bindings.t) (ds : AST.declaration list) : - (Ident.t * Ident.t * AST.function_type * Loc.t) list = - List.filter_map (fun x -> - match x with - | AST.Decl_FunFFI (nm, is_export, f, ps, loc) when is_export = not is_import -> - let c_ident = Ident.mk_ident nm in - let info = ( match Bindings.find_opt f decl_map with - | Some ds -> - ( match List.find_opt (function AST.Decl_FunType _ -> true | _ -> false) ds with - | Some (AST.Decl_FunType (_, fty, loc)) -> Some (c_ident, f, fty, loc) - | _ -> None - ) - | _ -> None - ) in - if Option.is_none info then begin - let direction = if is_import then "Import" else "Export" in - let pp fmt = FMT.declaration fmt x in - let fname = Ident.name_with_tag f in - raise (InternalError - (loc, PP.asprintf "%sed function '%s' is not defined" direction fname, pp, __LOC__)) - end; - info - | _ -> None) - ds - (* Build ffi wrapper functions *) let mk_ffi_wrappers (is_import : bool) (infos : (Ident.t * Ident.t * AST.function_type * Loc.t) list) : @@ -1971,12 +1982,16 @@ let mk_ffi_wrappers (is_import : bool) let pp_defns fmt : unit = List.iter (fun f -> f fmt) mk_defns in (pp_protos, pp_defns) -let ffi_track_enums (decl_map : (AST.declaration list) Bindings.t) (exports : string list) : unit = - List.iter (fun c_name -> - let c_ident = Ident.mk_ident c_name in - ( match Bindings.find_opt c_ident decl_map with - | Some (Decl_Enum (tc, es, loc) :: _) -> - add_enumerated_type tc es +let ffi_track_enums (decl_map : (AST.declaration list) Bindings.t) (exports : (string * Ident.t) list) : unit = + List.iter (fun (c_name, isa_ident) -> + ( match Bindings.find_opt isa_ident decl_map with + | Some ds -> + List.iter (fun d -> + ( match d with + | AST.Decl_Enum (tc, es, loc) -> add_enumerated_type tc es + | _ -> () + )) + ds | _ -> () )) @@ -2003,7 +2018,7 @@ let config_getter_prefix = "ASL_get_config_" (* Generate ASL functions for reading/writing configuration variables. * These will then be exported so that C code can read/write the variables. *) -let mk_ffi_config (decls : AST.declaration list) : (string list * AST.declaration list) = +let mk_ffi_config (decls : AST.declaration list) : ((string * Ident.t) list * AST.declaration list) = let configs = List.filter_map (fun x -> ( match x with | AST.Decl_Config (v, ty, i, loc) -> Some (v, ty, loc) @@ -2012,7 +2027,7 @@ let mk_ffi_config (decls : AST.declaration list) : (string list * AST.declaratio decls in - let mk_functions ((v, ty, loc) : (Ident.t * AST.ty * Loc.t)) : (string list * AST.declaration list) = + let mk_functions ((v, ty, loc) : (Ident.t * AST.ty * Loc.t)) : ((string * Ident.t) list * AST.declaration list) = let getter_name = config_getter_prefix ^ Ident.name v in let getter_id = Ident.mk_fident getter_name in let getter_fty : AST.function_type = { @@ -2048,7 +2063,7 @@ let mk_ffi_config (decls : AST.declaration list) : (string list * AST.declaratio let setter_defn = AST.Decl_FunDefn (setter_id, setter_fty, setter_body, loc) in let setter_type = AST.Decl_FunType (setter_id, setter_fty, loc) in - ([getter_name; setter_name], [getter_defn; getter_type; setter_defn; setter_type]) + ([(getter_name, getter_id); (setter_name, setter_id)], [getter_defn; getter_type; setter_defn; setter_type]) in let (names, decls) = List.map mk_functions configs |> List.split in @@ -2259,22 +2274,19 @@ let _ = let (cfg_exports, cfg_funs) = mk_ffi_config decls in let decls' = decls @ cfg_funs in let decl_map = Isa_utils.decls_map_of decls' in - let imports = if !new_ffi then Configuration.get_strings "imports" else [] in - let exports = if !new_ffi then Configuration.get_strings "exports" else [] in + + let imports = get_ffi false decls' in + let exports = get_ffi true decls' in ffi_track_enums decl_map exports; ffi_track_return_types decls'; let ffi_import_infos = mk_ffi_infos true decl_map imports in - let new_ffi_import_infos = mk_new_ffi_infos true decl_map decls' in - let (ffi_import_protos, ffi_import_defns) = - mk_ffi_wrappers true (ffi_import_infos @ new_ffi_import_infos) + let (ffi_import_protos, ffi_import_defns) = mk_ffi_wrappers true ffi_import_infos in let ffi_export_infos = mk_ffi_infos false decl_map (cfg_exports @ exports) in - let new_ffi_export_infos = mk_new_ffi_infos false decl_map decls' in - let (ffi_export_protos, ffi_export_defns) = - mk_ffi_wrappers false (ffi_export_infos @ new_ffi_export_infos) + let (ffi_export_protos, ffi_export_defns) = mk_ffi_wrappers false ffi_export_infos in let ffi_protos (fmt : PP.formatter) : unit = diff --git a/libISA/isa_ast.ml b/libISA/isa_ast.ml index a5d9276e..62d2560c 100644 --- a/libISA/isa_ast.ml +++ b/libISA/isa_ast.ml @@ -193,7 +193,7 @@ declaration = | Decl_FunInstance of Ident.t * (Ident.t * Value.value option) list * Loc.t | Decl_FunFFI of string * bool * Ident.t * (Ident.t * Value.value) list * Loc.t | Decl_VarFFI of string * bool * Ident.t * Loc.t - | Decl_TypeFFI of string * bool * ty * Loc.t + | Decl_TypeFFI of string * bool * Ident.t * Loc.t | Decl_Operator1 of unop * Ident.t list * Loc.t | Decl_Operator2 of binop * Ident.t list * Loc.t | Decl_Config of Ident.t * ty * expr * Loc.t diff --git a/libISA/isa_fmt.ml b/libISA/isa_fmt.ml index 7be127e0..b1ff857d 100644 --- a/libISA/isa_fmt.ml +++ b/libISA/isa_fmt.ml @@ -919,7 +919,7 @@ let declaration ?(short=false) (fmt : PP.formatter) (x : AST.declaration) : unit ffi_direction is_export kw_type nm - ty t + tycon t | Decl_Operator1 (op, fs, loc) -> kw_underscore_operator1 fmt; nbsp fmt; diff --git a/libISA/isa_parser.mly b/libISA/isa_parser.mly index 2ab1533c..0dbef35f 100644 --- a/libISA/isa_parser.mly +++ b/libISA/isa_parser.mly @@ -680,7 +680,7 @@ let ffi_definition := "var"; nm=STRINGLIT; "="; v=path; ";"; { Decl_VarFFI(nm, is_export, v, Range($symbolstartpos, $endpos)) } | "foreign"; is_export=ffi_direction; - "type"; nm=STRINGLIT; "="; t=ty; ";"; + "type"; nm=STRINGLIT; "="; t=path; ";"; { Decl_TypeFFI(nm, is_export, t, Range($symbolstartpos, $endpos)) } let ffi_parameter_value := diff --git a/libISA/isa_utils.ml b/libISA/isa_utils.ml index 151ac987..668b4fed 100644 --- a/libISA/isa_utils.ml +++ b/libISA/isa_utils.ml @@ -513,7 +513,7 @@ let decl_name (x : declaration) : Ident.t option = | Decl_FunInstance (f, _, loc) -> Some f | Decl_FunFFI (nm, is_export, f, _, loc) -> Some f | Decl_VarFFI (nm, is_export, v, loc) -> Some v - | Decl_TypeFFI (nm, is_export, t, loc) -> None + | Decl_TypeFFI (nm, is_export, t, loc) -> Some t | Decl_Operator1 (op, vs, loc) -> None | Decl_Operator2 (op, vs, loc) -> None | Decl_Config (v, ty, e, loc) -> Some v diff --git a/libISA/isa_visitor.ml b/libISA/isa_visitor.ml index 9f2b7f97..83564129 100644 --- a/libISA/isa_visitor.ml +++ b/libISA/isa_visitor.ml @@ -655,9 +655,9 @@ let visit_decl (vis : isaVisitor) (x : declaration) : declaration = | Decl_VarFFI (nm, is_export, v, loc) -> let v' = visit_var vis Definition v in if v == v' then x else Decl_VarFFI (nm, is_export, v', loc) - | Decl_TypeFFI (nm, is_export, t, loc) -> - let t' = visit_type vis t in - if t == t' then x else Decl_TypeFFI (nm, is_export, t', loc) + | Decl_TypeFFI (nm, is_export, tc, loc) -> + let tc' = visit_var vis Definition tc in + if tc == tc' then x else Decl_TypeFFI (nm, is_export, tc', loc) | Decl_Operator1 (op, vs, loc) -> let vs' = mapNoCopy (visit_var vis Definition) vs in if vs == vs' then x else Decl_Operator1 (op, vs', loc) diff --git a/libISA/tcheck.ml b/libISA/tcheck.ml index 6472d0a2..3e6f8ced 100644 --- a/libISA/tcheck.ml +++ b/libISA/tcheck.ml @@ -3271,9 +3271,13 @@ let tc_declaration (env : GlobalEnv.t) (d : AST.declaration) : | Decl_VarFFI (nm, is_export, v, loc) -> let v' = get_var (Env.mkEnv env) loc v in [ Decl_VarFFI (nm, is_export, v'.name, loc) ] - | Decl_TypeFFI (nm, is_export, t, loc) -> - let t' = tc_type (Env.mkEnv env) loc t in - [ Decl_TypeFFI (nm, is_export, t', loc) ] + | Decl_TypeFFI (nm, is_export, tc, loc) -> + let tc' = GlobalEnv.renameType env tc in + if (tc' = Builtin_idents.sintN) || (GlobalEnv.isTycon env tc') then ( + [ Decl_TypeFFI (nm, is_export, tc', loc) ] + ) else ( + raise (IsNotA (loc, "type constructor", Ident.to_string tc)) + ) | Decl_Operator1 (op, funs, loc) -> let funs' = List.concat @@ -3332,6 +3336,8 @@ let genPrototypes (ds : AST.declaration list) : | Decl_FunDefn (qid, fty, _, loc) -> post := d :: !post; pre := Decl_FunType (qid, fty, loc) :: !pre + | Decl_FunFFI (_, _, _, _, _) -> + post := d :: !post; | _ -> pre := d :: !pre) ds; (List.rev !pre, List.rev !post) diff --git a/tests/backends/ffi_export_00.isa b/tests/backends/ffi_export_00.isa index 627d5994..3f89260c 100644 --- a/tests/backends/ffi_export_00.isa +++ b/tests/backends/ffi_export_00.isa @@ -1,8 +1,11 @@ -// RUN: %aslrun %s --import=FFI_Call128 --export=FFI_Extract128_32 --extra-c=%S/ffi_export_00.c | filecheck %s +// RUN: %aslrun %s --extra-c=%S/ffi_export_00.c | filecheck %s // Copyright (C) 2025-2026 Intel Corporation // UNSUPPORTED: interpreter +foreign import function "FFI_Call128" = FFI_Call128 with {}; +foreign export function "FFI_Extract128_32" = FFI_Extract128_32 with {}; + // Function imported from C function FFI_Call128(x : Bits(128)) -> Bits(32); diff --git a/tests/backends/ffi_export_01.isa b/tests/backends/ffi_export_01.isa index 013b0d54..3ad8a26b 100644 --- a/tests/backends/ffi_export_01.isa +++ b/tests/backends/ffi_export_01.isa @@ -1,8 +1,27 @@ -// RUN: %aslrun %s --configuration=%S/ffi_export_01.json --extra-c=%S/ffi_export_01.c | filecheck %s +// RUN: %aslrun %s --extra-c=%S/ffi_export_01.c | filecheck %s // Copyright (C) 2025-2026 Intel Corporation // UNSUPPORTED: interpreter +foreign export type "E" = E; +foreign export function "FFI_bits8" = FFI_bits8 with {}; +foreign export function "FFI_bits16" = FFI_bits16 with {}; +foreign export function "FFI_bits32" = FFI_bits32 with {}; +foreign export function "FFI_bits64" = FFI_bits64 with {}; +foreign export function "FFI_bits2" = FFI_bits2 with {}; +foreign export function "FFI_bits17" = FFI_bits17 with {}; +foreign export function "FFI_bits65" = FFI_bits65 with {}; +foreign export function "FFI_bits127" = FFI_bits127 with {}; +foreign export function "FFI_bits128" = FFI_bits128 with {}; +foreign export function "FFI_string" = FFI_string with {}; +foreign export function "FFI_E" = FFI_E with {}; +foreign export function "FFI_Boolean" = FFI_Boolean with {}; +foreign export function "FFI_integer" = FFI_integer with {}; +foreign export function "FFI_sint17" = FFI_sint17 with {}; +foreign export function "FFI_int_bool" = FFI_int_bool with {}; + +foreign import function "FFI_test_exports" = FFI_test_exports with {}; + enumeration E = { A, B, C }; // to be exported diff --git a/tests/backends/ffi_export_01.json b/tests/backends/ffi_export_01.json deleted file mode 100644 index 1f733fdc..00000000 --- a/tests/backends/ffi_export_01.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "__comment": [ - "Export the interface required by ffi_export_01.c" - ], - "exports": [ - "E", - "FFI_bits8", - "FFI_bits16", - "FFI_bits32", - "FFI_bits64", - "FFI_bits2", - "FFI_bits17", - "FFI_bits65", - "FFI_bits127", - "FFI_bits128", - "FFI_string", - "FFI_E", - "FFI_Boolean", - "FFI_integer", - "FFI_sint17", - "FFI_int_bool" - ], - "imports": [ - "FFI_test_exports" - ] -} diff --git a/tests/backends/ffi_import_00.isa b/tests/backends/ffi_import_00.isa index afaf2637..70ac913e 100644 --- a/tests/backends/ffi_import_00.isa +++ b/tests/backends/ffi_import_00.isa @@ -1,30 +1,25 @@ -// RUN: %aslrun %s --import=FFI_Invert32 --import=FFI_Invert128 --extra-c=%S/ffi_import_00.c | filecheck %s +// RUN: %aslrun %s --extra-c=%S/ffi_import_00.c | filecheck %s // Copyright (C) 2025-2026 Intel Corporation // UNSUPPORTED: interpreter +foreign import function "FFI_Invert32" = FFI_Invert with { size => 32 }; +foreign import function "FFI_Invert128" = FFI_Invert with { size => 128 }; + // The following functions will be replaced by // a version that is defined externally. // Normally, the internal and external version have // similar behaviour but, for testing purposes, the // internal version is the identity function // and the external version inverts its argument. -function FFI_Invert32(x : Bits(32)) -> Bits(32) -begin - return x; -end - -function FFI_Invert128(x : Bits(128)) -> Bits(128) -begin - return x; -end +function FFI_Invert(implicit size : {0..}, x : Bits(size)) -> Bits(size); function main() -> Builtin::Foreign::CInt begin - Std::Print::Bits::Hex(FFI_Invert32(32'x3)); Print("\n"); + Std::Print::Bits::Hex(FFI_Invert(32'x3)); Print("\n"); // CHECK: 32'xfffffffc - Std::Print::Bits::Hex(FFI_Invert128(128'x3)); Print("\n"); + Std::Print::Bits::Hex(FFI_Invert(128'x3)); Print("\n"); // CHECK: 128'xfffffffffffffffffffffffffffffffc return Builtin::Foreign::CInt::From_Integer(0); diff --git a/tests/backends/ffi_import_01.isa b/tests/backends/ffi_import_01.isa index cb1584ff..433c9d54 100644 --- a/tests/backends/ffi_import_01.isa +++ b/tests/backends/ffi_import_01.isa @@ -1,8 +1,25 @@ -// RUN: %aslrun %s --configuration=%S/ffi_import_01.json --extra-c=%S/ffi_import_01.c | filecheck %s +// RUN: %aslrun %s --extra-c=%S/ffi_import_01.c | filecheck %s // Copyright (C) 2025-2026 Intel Corporation // UNSUPPORTED: interpreter +foreign export type "E" = E; +foreign import function "FFI_null_bits8" = FFI_null_bits8 with {}; +foreign import function "FFI_null_bits16" = FFI_null_bits16 with {}; +foreign import function "FFI_null_bits32" = FFI_null_bits32 with {}; +foreign import function "FFI_null_bits64" = FFI_null_bits64 with {}; +foreign import function "FFI_null_bits2" = FFI_null_bits2 with {}; +foreign import function "FFI_null_bits17" = FFI_null_bits17 with {}; +foreign import function "FFI_null_bits65" = FFI_null_bits65 with {}; +foreign import function "FFI_null_bits127" = FFI_null_bits127 with {}; +foreign import function "FFI_null_bits128" = FFI_null_bits128 with {}; +foreign import function "FFI_null_string" = FFI_null_string with {}; +foreign import function "FFI_null_E" = FFI_null_E with {}; +foreign import function "FFI_null_Boolean" = FFI_null_Boolean with {}; +foreign import function "FFI_null_integer" = FFI_null_integer with {}; +foreign import function "FFI_null_sint17" = FFI_null_sint17 with {}; +foreign import function "FFI_int_bool" = FFI_int_bool with {}; + enumeration E = { A, B, C }; // to be defined externally diff --git a/tests/backends/ffi_import_01.json b/tests/backends/ffi_import_01.json deleted file mode 100644 index 3077f6b3..00000000 --- a/tests/backends/ffi_import_01.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "__comment": [ - "Import the interface required by ffi_import_01.asl" - ], - "exports": [ - "E" - ], - "imports": [ - "FFI_null_bits8", - "FFI_null_bits16", - "FFI_null_bits32", - "FFI_null_bits64", - "FFI_null_bits2", - "FFI_null_bits17", - "FFI_null_bits65", - "FFI_null_bits127", - "FFI_null_bits128", - "FFI_null_string", - "FFI_null_E", - "FFI_null_Boolean", - "FFI_null_integer", - "FFI_null_sint17", - "FFI_int_bool" - ] -} diff --git a/tests/lit/check_monomorphization/config.json b/tests/lit/check_monomorphization/config.json deleted file mode 100644 index 6e6570c8..00000000 --- a/tests/lit/check_monomorphization/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "exports": [ - "A" - ] -} diff --git a/tests/lit/check_monomorphization/test_00.isa b/tests/lit/check_monomorphization/test_00.isa index 87eb0d2a..0bb13d6a 100644 --- a/tests/lit/check_monomorphization/test_00.isa +++ b/tests/lit/check_monomorphization/test_00.isa @@ -1,4 +1,4 @@ -// RUN: %iii --batchmode --configuration=%S/config.json --project=%S/iii.prj %s | filecheck %s +// RUN: %iii --batchmode --project=%S/iii.prj %s | filecheck %s // Copyright (C) 2024-2026 Intel Corporation function C(x : Bits(N)) -> Integer @@ -16,6 +16,8 @@ begin return B(0b0); end +foreign export function "A" = A with {}; + // CHECK: WARNING: Bitwidth parameters are not statically known in the following definitions. // CHECK-NEXT: This will cause code generation to fail. //