From 014aa2f5fbfbb6e12a91e515bb17198a68548ff5 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 4 Feb 2024 12:07:18 +0000 Subject: [PATCH] Rewrite implementation of foreign functions Refactoring before adding full VHPIDIRECT support --- lib/nvc/polyfill-body.vhd | 23 +- lib/nvc/polyfill.vhd | 9 +- lib/nvc/text_util-body.vhd | 7 - lib/nvc/text_util.vhd | 3 + lib/nvc/verilog.vhd | 11 + lib/std.08/env-body.vhd | 4 +- lib/std.19/env-body.vhd | 178 +++++++-------- src/bounds.c | 3 + src/cgen.c | 2 +- src/common.c | 144 +++++-------- src/common.h | 2 - src/elab.c | 1 + src/eval.c | 4 +- src/jit/jit-code.c | 9 +- src/jit/jit-core.c | 19 +- src/jit/jit-dump.c | 8 +- src/jit/jit-exits.c | 21 +- src/jit/jit-ffi.c | 432 +++++++++++++++++++++++-------------- src/jit/jit-ffi.h | 15 +- src/jit/jit-interp.c | 21 +- src/jit/jit-irgen.c | 151 ++++++------- src/jit/jit-llvm.c | 157 ++++++++------ src/jit/jit-optim.c | 4 +- src/jit/jit-pack.c | 11 - src/jit/jit-priv.h | 31 ++- src/jit/jit-x86.c | 36 ---- src/lower.c | 307 +++++++++++++------------- src/names.c | 22 +- src/object.c | 2 +- src/parse.c | 24 +-- src/rt/assert.c | 25 ++- src/rt/fileio.c | 49 ++--- src/rt/mspace.c | 12 +- src/rt/rt.h | 4 +- src/rt/standard.c | 89 ++++---- src/rt/stdenv.c | 57 +++-- src/rt/verilog.c | 4 +- src/sem.c | 72 +++---- src/simp.c | 2 +- src/symbols.txt | 3 - src/tree.h | 7 +- src/vcode.c | 85 ++++---- src/vcode.h | 13 +- src/vlog/vlog-lower.c | 38 ++-- test/regress/vhpi4.vhd | 11 +- test/sem/attr.vhd | 4 +- test/simp/foreign1.vhd | 2 +- test/test_jit.c | 117 +--------- test/test_lower.c | 9 +- test/test_parse.c | 2 +- test/test_sem.c | 2 - test/test_simp.c | 2 +- test/vhpi/vhpi4.c | 3 +- 53 files changed, 1064 insertions(+), 1209 deletions(-) diff --git a/lib/nvc/polyfill-body.vhd b/lib/nvc/polyfill-body.vhd index d2826277..e7a1bbca 100644 --- a/lib/nvc/polyfill-body.vhd +++ b/lib/nvc/polyfill-body.vhd @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- Copyright (C) 2021 Nick Gasson +-- Copyright (C) 2021-2024 Nick Gasson -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. @@ -16,32 +16,11 @@ package body polyfill is - function to_string (value : real; spec : string) return string is - function impl (value : real; spec : string) return string; - attribute foreign of impl : function is "_std_to_string_real_format"; - begin - return impl(value, spec); - end function; - function to_string (value : integer) return string is begin return integer'image(value); end function; - function to_hstring (value : bit_vector) return string is - function impl (value : bit_vector) return string; - attribute foreign of impl : function is "_std_to_hstring_bit_vec"; - begin - return impl(value); - end function; - - function to_ostring (value : bit_vector) return string is - function impl (value : bit_vector) return string; - attribute foreign of impl : function is "_std_to_ostring_bit_vec"; - begin - return impl(value); - end function; - function maximum (x, y : integer) return integer is begin if x > y then diff --git a/lib/nvc/polyfill.vhd b/lib/nvc/polyfill.vhd index 8396f6ef..2fac60b8 100644 --- a/lib/nvc/polyfill.vhd +++ b/lib/nvc/polyfill.vhd @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- Copyright (C) 2021 Nick Gasson +-- Copyright (C) 2021-2024 Nick Gasson -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. @@ -33,4 +33,11 @@ package polyfill is function minimum (x, y : integer) return integer; + attribute foreign of to_hstring [bit_vector return string] : function + is "INTERNAL _std_to_hstring_bit_vec"; + attribute foreign of to_ostring [bit_vector return string] : function + is "INTERNAL _std_to_ostring_bit_vec"; + attribute foreign of to_string [real, string return string] : function + is "INTERNAL _std_to_string_real_format"; + end package; diff --git a/lib/nvc/text_util-body.vhd b/lib/nvc/text_util-body.vhd index 4c8ff978..959fdc7f 100644 --- a/lib/nvc/text_util-body.vhd +++ b/lib/nvc/text_util-body.vhd @@ -439,11 +439,4 @@ package body text_util is return change_bounds(buf(pos + 1 to buf'right), 1, buf'right - pos); end function; - function real_to_string (x : real) return string is - function impl (x : real) return string; - attribute foreign of impl : function is "_std_to_string_real"; - begin - return impl(x); - end function; - end package body; diff --git a/lib/nvc/text_util.vhd b/lib/nvc/text_util.vhd index d4ec9b7b..0054d874 100644 --- a/lib/nvc/text_util.vhd +++ b/lib/nvc/text_util.vhd @@ -47,4 +47,7 @@ package text_util is function find_open (s : string) return natural; procedure find_close (s : string; pos : natural); procedure report_bad_char (s : string; c : character); + + attribute foreign of real_to_string [real return string] + : function is "INTERNAL _std_to_string_real"; end package; diff --git a/lib/nvc/verilog.vhd b/lib/nvc/verilog.vhd index e044fdec..2a63ed89 100644 --- a/lib/nvc/verilog.vhd +++ b/lib/nvc/verilog.vhd @@ -47,4 +47,15 @@ package verilog is function "not" (x : t_packed_logic) return t_packed_logic; function "not" (x : t_packed_logic) return t_logic; + procedure sys_finish; + + -- These procedures are called with a special variadic calling convention + -- which cannot be represented in VHDL + procedure sys_display (format : string); + procedure sys_write (format : string); + + attribute foreign of sys_finish : procedure is "INTERNAL __nvc_sys_finish"; + attribute foreign of sys_write : procedure is "INTERNAL __nvc_sys_write"; + attribute foreign of sys_display : procedure is "INTERNAL __nvc_sys_display"; + end package; diff --git a/lib/std.08/env-body.vhd b/lib/std.08/env-body.vhd index 59bc7602..b1adcc6e 100644 --- a/lib/std.08/env-body.vhd +++ b/lib/std.08/env-body.vhd @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- Copyright (C) 2014-2021 Nick Gasson +-- Copyright (C) 2014-2024 Nick Gasson -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package body env is procedure stop_impl(finish, have_status : boolean; status : integer); - attribute foreign of stop_impl : procedure is "_std_env_stop"; + attribute foreign of stop_impl : procedure is "GHDL _std_env_stop"; procedure stop(status : integer) is begin diff --git a/lib/std.19/env-body.vhd b/lib/std.19/env-body.vhd index f75f9e24..11e64f67 100644 --- a/lib/std.19/env-body.vhd +++ b/lib/std.19/env-body.vhd @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- Copyright (C) 2022-2023 Nick Gasson +-- Copyright (C) 2022-2024 Nick Gasson -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ package body env is procedure stop_impl(finish, have_status : boolean; status : integer); - attribute foreign of stop_impl : procedure is "_std_env_stop"; + attribute foreign of stop_impl : procedure is "GHDL _std_env_stop"; procedure stop(status : integer) is begin @@ -62,15 +62,17 @@ package body env is end function; impure function epoch return real is + -- XXX: remove nested function impure function impl return real; - attribute foreign of impl : function is "_std_env_epoch"; + attribute foreign of impl : function is "GHDL _std_env_epoch"; begin return impl; end function; function localtime (timer : real) return time_record is + -- XXX: remove nested function procedure impl (timer : real; tr : out time_record); - attribute foreign of impl : procedure is "_std_env_localtime"; + attribute foreign of impl : procedure is "GHDL _std_env_localtime"; variable result : time_record; begin impl(timer, result); @@ -78,8 +80,9 @@ package body env is end function; function gmtime (timer : real) return time_record is + -- XXX: remove nested function procedure impl (timer : real; tr : out time_record); - attribute foreign of impl : procedure is "_std_env_gmtime"; + attribute foreign of impl : procedure is "GHDL _std_env_gmtime"; variable result : time_record; begin impl(timer, result); @@ -87,15 +90,17 @@ package body env is end function; function epoch (trec : time_record) return real is + -- XXX: remove nested function function impl (trec : in time_record) return real; - attribute foreign of impl : function is "_std_env_epoch_trec"; + attribute foreign of impl : function is "GHDL _std_env_epoch_trec"; begin return impl(trec); end function; function localtime (trec : time_record) return time_record is + -- XXX: remove nested function procedure impl (trec : in time_record; result : out time_record); - attribute foreign of impl : procedure is "_std_env_localtime_trec"; + attribute foreign of impl : procedure is "GHDL _std_env_localtime_trec"; variable result : time_record; begin impl(trec, result); @@ -103,8 +108,9 @@ package body env is end function; function gmtime (trec : time_record) return time_record is + -- XXX: remove nested function procedure impl (trec : in time_record; result : out time_record); - attribute foreign of impl : procedure is "_std_env_gmtime_trec"; + attribute foreign of impl : procedure is "GHDL _std_env_gmtime_trec"; variable result : time_record; begin impl(trec, result); @@ -112,10 +118,11 @@ package body env is end function; function "+" (trec : time_record; delta : real) return time_record is + -- XXX: remove nested function procedure impl (trec : in time_record; delta : in real; result : out time_record); - attribute foreign of impl : procedure is "_std_env_add_trec_real"; + attribute foreign of impl : procedure is "GHDL _std_env_add_trec_real"; variable result : time_record; begin impl(trec, delta, result); @@ -139,8 +146,9 @@ package body env is -- end function; function "-" (tr1, tr2 : time_record) return real is + -- XXX: remove nested function function impl (tr1, tr2 : in time_record) return real; - attribute foreign of impl : function is "_std_env_diff_trec"; + attribute foreign of impl : function is "GHDL _std_env_diff_trec"; begin return impl(tr1, tr2); end function; @@ -154,8 +162,9 @@ package body env is end function; function seconds_to_time (real_val : in real) return time is + -- XXX: remove nested function function impl (real_val : real) return time; - attribute foreign of impl : function is "_std_env_seconds_to_time"; + attribute foreign of impl : function is "GHDL _std_env_seconds_to_time"; begin return impl(real_val); end function; @@ -198,8 +207,9 @@ package body env is end function; impure function getenv (name : string) return string is + -- XXX: remove nested function impure function impl (name : string) return string; - attribute foreign of impl : function is "_std_env_getenv"; + attribute foreign of impl : function is "INTERNAL _std_env_getenv"; begin return impl(name); end function; @@ -215,8 +225,9 @@ package body env is end function; impure function vhdl_version return STRING is + -- XXX: remove nested function impure function impl return string; - attribute foreign of impl : function is "_std_env_vhdl_version"; + attribute foreign of impl : function is "INTERNAL _std_env_vhdl_version"; begin return impl; end function; @@ -242,8 +253,9 @@ package body env is end function; function tool_version return string is + -- XXX: remove nested function function impl return string; - attribute foreign of impl : function is "_std_env_tool_version"; + attribute foreign of impl : function is "INTERNAL _std_env_tool_version"; begin return impl; end function; @@ -251,10 +263,11 @@ package body env is procedure dir_open (dir : out directory; path : in string; status : out dir_open_status) is + -- XXX: remove nested function procedure impl (path : in string; dir : out directory; status : out dir_open_status); - attribute foreign of impl : procedure is "_std_env_dir_open"; + attribute foreign of impl : procedure is "GHDL _std_env_dir_open"; begin impl(path, dir, status); end procedure; @@ -275,31 +288,35 @@ package body env is end procedure; impure function dir_itemexists (path : in string) return boolean is + -- XXX: remove nested function impure function impl (path : in string) return boolean; - attribute foreign of impl : function is "_std_env_itemexists"; + attribute foreign of impl : function is "GHDL _std_env_itemexists"; begin return impl(path); end function; impure function dir_itemisdir (path : in string) return boolean is + -- XXX: remove nested function impure function impl (path : in string) return boolean; - attribute foreign of impl : function is "_std_env_itemisdir"; + attribute foreign of impl : function is "GHDL _std_env_itemisdir"; begin return impl(path); end function; impure function dir_itemisfile (path : in string) return boolean is + -- XXX: remove nested function impure function impl (path : in string) return boolean; - attribute foreign of impl : function is "_std_env_itemisfile"; + attribute foreign of impl : function is "GHDL _std_env_itemisfile"; begin return impl(path); end function; procedure dir_workingdir (path : in string; status : out dir_open_status) is + -- XXX: remove nested function procedure impl (path : in string; status : out dir_open_status); - attribute foreign of impl : procedure is "_std_env_set_workingdir"; + attribute foreign of impl : procedure is "GHDL _std_env_set_workingdir"; begin impl(path, status); end procedure; @@ -314,8 +331,9 @@ package body env is end function; impure function dir_workingdir return string is + -- XXX: remove nested function impure function impl return string; - attribute foreign of impl : function is "_std_env_get_workingdir"; + attribute foreign of impl : function is "INTERNAL _std_env_get_workingdir"; begin return impl; end function; @@ -329,10 +347,11 @@ package body env is procedure dir_createdir (path : in string; parents : in boolean; status : out dir_create_status) is + -- XXX: remove nested function procedure impl (path : in string; parents : in boolean; status : out dir_create_status); - attribute foreign of impl : procedure is "_std_env_createdir"; + attribute foreign of impl : procedure is "GHDL _std_env_createdir"; begin assert not parents report "PARENTS flag not supported" severity failure; @@ -358,10 +377,11 @@ package body env is procedure dir_deletedir (path : in string; recursive : in boolean; status : out dir_delete_status) is + -- XXX: remove nested function procedure impl (path : in string; recursive : in boolean; status : out dir_delete_status); - attribute foreign of impl : procedure is "_std_env_deletedir"; + attribute foreign of impl : procedure is "GHDL _std_env_deletedir"; begin impl(path, recursive, status); end procedure; @@ -378,9 +398,10 @@ package body env is procedure dir_deletefile (path : in string; status : out file_delete_status) is + -- XXX: remove nested function procedure impl (path : in string; status : out file_delete_status); - attribute foreign of impl : procedure is "_std_env_deletefile"; + attribute foreign of impl : procedure is "GHDL _std_env_deletefile"; begin impl(path, status); end procedure; @@ -427,60 +448,59 @@ package body env is end function; impure function get_call_path return call_path_vector_ptr is - procedure impl (ptr : out call_path_vector_ptr); - attribute foreign of impl : procedure is "_std_env_get_call_path"; - variable result : call_path_vector_ptr; + -- XXX: remove nested function + impure function impl return call_path_vector_ptr; + attribute foreign of impl : function is "INTERNAL _std_env_get_call_path"; begin - impl(result); - return result; + return impl; end function; + procedure get_caller_file_name (ptr : out line); + attribute foreign of get_caller_file_name : procedure is "GHDL _std_env_file_name"; + impure function file_name return line is - procedure impl (ptr : out line); - attribute foreign of impl : procedure is "_std_env_file_name"; variable result : line; begin - impl(result); + get_caller_file_name(result); return result; end function; impure function file_name return string is - procedure impl (ptr : out line); - attribute foreign of impl : procedure is "_std_env_file_name"; variable result : line; begin - impl(result); + get_caller_file_name(result); return result.all; end function; + procedure get_caller_file_path (ptr : out line); + attribute foreign of get_caller_file_path : procedure is "GHDL _std_env_file_path"; + impure function file_path return line is - procedure impl (ptr : out line); - attribute foreign of impl : procedure is "_std_env_file_path"; variable result : line; begin - impl(result); + get_caller_file_path(result); return result; end function; impure function file_path return string is - procedure impl (ptr : out line); - attribute foreign of impl : procedure is "_std_env_file_path"; variable result : line; begin - impl(result); + get_caller_file_path(result); return result.all; end function; impure function file_line return positive is + -- XXX: Remove nested function impure function impl return positive; - attribute foreign of impl : function is "_std_env_file_line"; + attribute foreign of impl : function is "GHDL _std_env_file_line"; begin return impl; end function; impure function file_line return string is + -- XXX: Remove nested function impure function impl return positive; - attribute foreign of impl : function is "_std_env_file_line"; + attribute foreign of impl : function is "GHDL _std_env_file_line"; begin return to_string(impl); end function; @@ -491,12 +511,9 @@ package body env is IsVhdlAssertFailed(failure); end function; - impure function IsVhdlAssertFailed (Level : severity_level) - return boolean is - function impl (Level : severity_level) return natural; - attribute foreign of impl : function is "_std_env_get_vhdl_assert_count"; + impure function IsVhdlAssertFailed (Level : severity_level) return boolean is begin - return (impl(Level) > 0); + return (GetVhdlAssertCount(Level) > 0); end function; impure function GetVhdlAssertCount return natural is @@ -505,20 +522,11 @@ package body env is GetVhdlAssertCount(failure); end function; - impure function GetVhdlAssertCount (Level : severity_level) - return natural is - function impl (Level : severity_level) return natural; - attribute foreign of impl : function is "_std_env_get_vhdl_assert_count"; - begin - return impl(Level); - end function; + attribute foreign of GetVhdlAssertCount [severity_level return natural] : + function is "INTERNAL _std_env_get_vhdl_assert_count"; - procedure ClearVhdlAssert is - procedure impl; - attribute foreign of impl : procedure is "_std_env_clear_vhdl_assert"; - begin - impl; - end procedure; + attribute foreign of ClearVhdlAssert [] : + procedure is "INTERNAL _std_env_clear_vhdl_assert"; procedure SetVhdlAssertEnable (Enable : boolean := true) is begin @@ -528,50 +536,20 @@ package body env is SetVhdlAssertEnable (failure, Enable); end procedure; - procedure SetVhdlAssertEnable (Level : severity_level := note; - Enable : boolean := true) is - procedure impl (Level : severity_level; Enable : boolean); - attribute foreign of impl : procedure is "_std_env_set_vhdl_assert_enable"; - begin - impl(Level, enable); - end procedure; + attribute foreign of SetVhdlAssertEnable [severity_level, boolean] : + procedure is "INTERNAL _std_env_set_vhdl_assert_enable"; - impure function GetVhdlAssertEnable (Level : severity_level := note) - return boolean is - function impl (Level : severity_level) return boolean; - attribute foreign of impl : function is "_std_env_get_vhdl_assert_enable"; - begin - return impl(Level); - end function; + attribute foreign of GetVhdlAssertEnable [severity_level return boolean] : + function is "INTERNAL _std_env_get_vhdl_assert_enable"; - procedure SetVhdlAssertFormat (Level : severity_level; - Format : string) is - procedure impl (Level : severity_level; - Format : string); - attribute foreign of impl : procedure is "_std_env_set_assert_format"; - begin - impl(level, format); - end procedure; + attribute foreign of SetVhdlAssertFormat [severity_level, string] : + procedure is "INTERNAL _std_env_set_assert_format"; - procedure SetVhdlAssertFormat (Level : severity_level; - Format : string; - Valid : out boolean) is - procedure impl (Level : severity_level; - Format : string; - Valid : out boolean); - attribute foreign of impl : procedure is "_std_env_set_assert_format_valid"; - begin - impl(level, format, valid); - end procedure; + attribute foreign of SetVhdlAssertFormat [severity_level, string, boolean] : + procedure is "INTERNAL _std_env_set_assert_format_valid"; - impure function GetVhdlAssertFormat (Level : severity_level) - return string - is - function impl (Level : severity_level) return string; - attribute foreign of impl : function is "_std_env_get_assert_format"; - begin - return impl(level); - end function; + attribute foreign of GetVhdlAssertFormat [severity_level return string] : + function is "INTERNAL _std_env_get_assert_format"; type read_severity_pt is protected procedure set (level : severity_level); diff --git a/src/bounds.c b/src/bounds.c index efdfd727..11e8be9b 100644 --- a/src/bounds.c +++ b/src/bounds.c @@ -178,6 +178,9 @@ static tree_t bounds_check_call_args(tree_t t) bounds_check_scalar(value, ftype, port); } + if (tree_kind(decl) == T_GENERIC_DECL) + return t; + const subprogram_kind_t kind = tree_subkind(decl); if (known_arg_length && (kind == S_ARRAY_EQ || kind == S_ARRAY_NEQ)) { // Warn if calling the predefined array equality operators and the diff --git a/src/cgen.c b/src/cgen.c index 54b4becb..76b20f4a 100644 --- a/src/cgen.c +++ b/src/cgen.c @@ -393,7 +393,7 @@ static void preload_walk_index(lib_t lib, ident_t ident, int kind, void *ctx) case T_PROC_INST: { const subprogram_kind_t kind = tree_subkind(d); - if (!is_foreign(kind) && !is_open_coded_builtin(kind)) + if (!is_open_coded_builtin(kind)) cgen_add_dependency(tree_ident2(d), args->registry, args->units); } break; diff --git a/src/common.c b/src/common.c index 9973972f..86f7b599 100644 --- a/src/common.c +++ b/src/common.c @@ -1124,33 +1124,25 @@ bool package_needs_body(tree_t pack) { assert(tree_kind(pack) == T_PACKAGE); + int missing = 0; const int ndecls = tree_decls(pack); for (int i = 0; i < ndecls; i++) { tree_t d = tree_decl(pack, i); - - switch (tree_kind(d)) { - case T_FUNC_DECL: - case T_PROC_DECL: - if (tree_flags(d) & TREE_F_PREDEFINED) - continue; - else if (is_foreign(tree_subkind(d))) - continue; + const tree_kind_t dkind = tree_kind(d); + if ((dkind == T_FUNC_DECL || dkind == T_PROC_DECL) + && !(tree_flags(d) & TREE_F_PREDEFINED)) + missing++; + else if (dkind == T_ATTR_SPEC + && is_well_known(tree_ident(d)) == W_FOREIGN) + missing--; + else if (dkind == T_CONST_DECL && !tree_has_value(d)) return true; - - case T_CONST_DECL: - if (!tree_has_value(d)) - return true; - continue; - - case T_PROT_DECL: + else if (dkind == T_PROT_DECL) return true; - - default: - continue; - } } - return false; + assert(missing >= 0); + return missing > 0; } tree_t search_decls(tree_t container, ident_t name, int nth) @@ -1364,77 +1356,55 @@ type_t reflection_type(reflect_type_t which) return cache[which]; } -bool is_builtin(subprogram_kind_t kind) -{ - return kind != S_USER && kind != S_FOREIGN && kind != S_INTERNAL; -} - -bool is_foreign(subprogram_kind_t kind) -{ - return kind == S_FOREIGN || kind == S_INTERNAL; -} - bool is_open_coded_builtin(subprogram_kind_t kind) { switch (kind) { - case S_USER: - case S_FOREIGN: - case S_INTERNAL: - case S_ARRAY_EQ: - case S_ARRAY_NEQ: - case S_ARRAY_LT: - case S_ARRAY_LE: - case S_ARRAY_GT: - case S_ARRAY_GE: - case S_RECORD_EQ: - case S_RECORD_NEQ: - case S_TO_STRING: - case S_SLL: - case S_SRL: - case S_SLA: - case S_SRA: - case S_ROL: - case S_ROR: - case S_ARRAY_NOT: - case S_ARRAY_AND: - case S_ARRAY_OR: - case S_ARRAY_XOR: - case S_ARRAY_XNOR: - case S_ARRAY_NAND: - case S_ARRAY_NOR: - case S_MIXED_AND: - case S_MIXED_OR: - case S_MIXED_XOR: - case S_MIXED_XNOR: - case S_MIXED_NAND: - case S_MIXED_NOR: - case S_REDUCE_OR: - case S_REDUCE_AND: - case S_REDUCE_NAND: - case S_REDUCE_NOR: - case S_REDUCE_XOR: - case S_REDUCE_XNOR: - case S_MATCH_EQ: - case S_MATCH_NEQ: - case S_MATCH_LT: - case S_MATCH_LE: - case S_MATCH_GT: - case S_MATCH_GE: - case S_MINIMUM: - case S_MAXIMUM: - case S_FILE_FLUSH: - case S_FILE_OPEN3: - case S_FILE_MODE: - case S_FILE_CANSEEK: - case S_FILE_SIZE: - case S_FILE_REWIND: - case S_FILE_SEEK: - case S_FILE_TRUNCATE: - case S_FILE_STATE: - case S_FILE_POSITION: - return false; - default: + case S_ADD: + case S_SUB: + case S_DIV: + case S_MUL: + case S_MUL_PR: + case S_MUL_RP: + case S_MUL_PI: + case S_MUL_IP: + case S_DIV_PR: + case S_DIV_PP: + case S_DIV_PI: + case S_IDENTITY: + case S_NEGATE: + case S_SCALAR_LT: + case S_SCALAR_LE: + case S_SCALAR_GT: + case S_SCALAR_GE: + case S_SCALAR_EQ: + case S_SCALAR_NEQ: + case S_ABS: + case S_MOD: + case S_REM: + case S_EXP: + case S_MUL_RI: + case S_MUL_IR: + case S_DIV_RI: + case S_RISING_EDGE: + case S_FALLING_EDGE: + case S_CONCAT: + case S_SCALAR_AND: + case S_SCALAR_OR: + case S_SCALAR_NOT: + case S_SCALAR_NAND: + case S_SCALAR_NOR: + case S_SCALAR_XOR: + case S_SCALAR_XNOR: + case S_FILE_OPEN1: + case S_FILE_OPEN2: + case S_FILE_CLOSE: + case S_FILE_READ: + case S_FILE_WRITE: + case S_ENDFILE: + case S_DEALLOCATE: return true; + default: + return false; } } diff --git a/src/common.h b/src/common.h index a11ae410..0c4bb680 100644 --- a/src/common.h +++ b/src/common.h @@ -67,8 +67,6 @@ bool is_literal(tree_t t); bool is_uninstantiated_package(tree_t pack); bool is_uninstantiated_subprogram(tree_t decl); tree_t search_decls(tree_t container, ident_t name, int nth); -bool is_builtin(subprogram_kind_t kind); -bool is_foreign(subprogram_kind_t kind); bool is_open_coded_builtin(subprogram_kind_t kind); bool attribute_has_param(attr_kind_t attr); tree_t name_to_ref(tree_t name); diff --git a/src/elab.c b/src/elab.c index 51c23242..72400984 100644 --- a/src/elab.c +++ b/src/elab.c @@ -1406,6 +1406,7 @@ static void elab_decls(tree_t t, const elab_ctx_t *ctx) case T_PACKAGE: case T_PACK_INST: case T_PSL: + case T_ATTR_SPEC: tree_add_decl(ctx->out, d); break; case T_FUNC_DECL: diff --git a/src/eval.c b/src/eval.c index e3413aaa..f00b5f34 100644 --- a/src/eval.c +++ b/src/eval.c @@ -289,9 +289,7 @@ bool eval_possible(tree_t t, unit_registry_t *ur) tree_t decl = tree_ref(t); const subprogram_kind_t kind = tree_subkind(decl); - if (is_foreign(kind)) - return eval_not_possible(t, "call to foreign function"); - else if (tree_flags(decl) & TREE_F_IMPURE) + if (tree_flags(decl) & TREE_F_IMPURE) return eval_not_possible(t, "call to impure function"); else if (kind != S_USER && !is_open_coded_builtin(kind) && unit_registry_get(ur, tree_ident2(decl)) == NULL) diff --git a/src/jit/jit-code.c b/src/jit/jit-code.c index 1d4e32f0..dc2c25fe 100644 --- a/src/jit/jit-code.c +++ b/src/jit/jit-code.c @@ -631,9 +631,6 @@ static void code_blob_print_value(text_buf_t *tb, jit_value_t value) case JIT_VALUE_LOC: tb_printf(tb, "<%s:%d>", loc_file_str(&value.loc), value.loc.first_line); break; - case JIT_VALUE_FOREIGN: - tb_printf(tb, "$%s", istr(ffi_get_sym(value.foreign))); - break; case JIT_VALUE_LOCUS: tb_printf(tb, "%s%+d", istr(value.ident), value.disp); break; @@ -796,6 +793,9 @@ static void code_load_pe(code_blob_t *blob, const void *data, size_t size) else ptr = ffi_find_symbol(NULL, name); + if (ptr == NULL && icmp(blob->span->name, name)) + ptr = blob->span->base; + if (ptr == NULL) fatal_trace("failed to resolve symbol %s", name); @@ -1074,6 +1074,9 @@ static void code_load_elf(code_blob_t *blob, const void *data, size_t size) break; } + if (ptr == NULL && icmp(blob->span->name, strtab + sym->st_name)) + ptr = (char *)blob->span->base; + if (ptr == NULL) fatal_trace("cannot resolve symbol %s type %d", strtab + sym->st_name, ELF64_ST_TYPE(sym->st_info)); diff --git a/src/jit/jit-core.c b/src/jit/jit-core.c index 53cc754d..a4f5dd9a 100644 --- a/src/jit/jit-core.c +++ b/src/jit/jit-core.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -294,17 +294,7 @@ static jit_handle_t jit_lazy_compile_locked(jit_t *j, ident_t name) if (descr != NULL) { for (aot_reloc_t *r = descr->relocs; r->kind != RELOC_NULL; r++) { const char *str = descr->strtab + r->off; - if (r->kind == RELOC_FOREIGN) { - const char *eptr = strchr(str, '\b'); - if (eptr == NULL) - fatal_trace("invalid foreign reloc '%s'", str); - - ffi_spec_t spec = ffi_spec_new(str, eptr - str); - - ident_t id = ident_new(eptr + 1); - r->ptr = jit_ffi_get(id) ?: jit_ffi_bind(id, spec, NULL); - } - else if (r->kind == RELOC_COVER) { + if (r->kind == RELOC_COVER) { // TODO: get rid of the double indirection here by // allocating coverage memory earlier r->ptr = &(j->cover_mem); @@ -532,6 +522,11 @@ static void jit_emit_trace(diag_t *d, const loc_t *loc, object_t *enclosing, diag_trace(d, loc, "Process$$ %s", name); } break; + case T_ATTR_SPEC: + assert(is_well_known(tree_ident(tree)) == W_FOREIGN); + diag_trace(d, loc, "Subprogram$$ %s", + type_pp(tree_type(tree_ref(tree)))); + break; case T_FUNC_BODY: case T_FUNC_DECL: diag_trace(d, loc, "Function$$ %s", type_pp(tree_type(tree))); diff --git a/src/jit/jit-dump.c b/src/jit/jit-dump.c index eed10321..dd1941ec 100644 --- a/src/jit/jit-dump.c +++ b/src/jit/jit-dump.c @@ -44,8 +44,8 @@ const char *jit_op_name(jit_op_t op) if (op >= __MACRO_BASE) { static const char *names[] = { "$COPY", "$GALLOC", "$EXIT", "$FEXP", "$EXP", "$BZERO", - "$FFICALL", "$GETPRIV", "$PUTPRIV", "$LALLOC", "$SALLOC", - "$CASE", "$TRIM", "$MOVE", "$MEMSET", + "$GETPRIV", "$PUTPRIV", "$LALLOC", "$SALLOC", "$CASE", + "$TRIM", "$MOVE", "$MEMSET", "$REEXEC", }; assert(op - __MACRO_BASE < ARRAY_LEN(names)); return names[op - __MACRO_BASE]; @@ -87,7 +87,7 @@ const char *jit_exit_name(jit_exit_t exit) "DRIVING_VALUE", "CLAIM_TLAB", "COVER_TOGGLE", "PROCESS_INIT", "CLEAR_EVENT", "IMPLICIT_EVENT", "ENTER_STATE", "REFLECT_VALUE", "REFLECT_SUBTYPE", "FUNCTION_TRIGGER", "ADD_TRIGGER", "TRANSFER_SIGNAL", - "PORT_CONVERSION", "CONVERT_IN", "CONVERT_OUT", + "PORT_CONVERSION", "CONVERT_IN", "CONVERT_OUT", "BIND_FOREIGN", }; assert(exit < ARRAY_LEN(names)); return names[exit]; @@ -151,8 +151,6 @@ static int jit_dump_value(jit_dump_t *d, jit_value_t value) return printf("%s", jit_exit_name(value.exit)); case JIT_VALUE_LOC: return printf("<%s:%d>", loc_file_str(&value.loc), value.loc.first_line); - case JIT_VALUE_FOREIGN: - return printf("$%s", istr(ffi_get_sym(value.foreign))); case JIT_VALUE_LOCUS: { object_t *obj = jit_get_locus(value); diff --git a/src/jit/jit-exits.c b/src/jit/jit-exits.c index 057bf0c9..bfb4448b 100644 --- a/src/jit/jit-exits.c +++ b/src/jit/jit-exits.c @@ -1038,6 +1038,16 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, } break; + case JIT_EXIT_BIND_FOREIGN: + { + const char *spec = args[0].pointer; + size_t length = args[1].integer; + tree_t where = args[2].pointer; + + jit_bind_foreign(anchor->func, spec, length, where); + } + break; + default: fatal_trace("unhandled exit %s", jit_exit_name(which)); } @@ -1045,17 +1055,6 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, thread->anchor = NULL; } -DLLEXPORT -void __nvc_do_fficall(jit_foreign_t *ff, jit_anchor_t *anchor, - jit_scalar_t *args) -{ - jit_thread_local_t *thread = jit_attach_thread(anchor); - - jit_ffi_call(ff, args); - - thread->anchor = NULL; -} - //////////////////////////////////////////////////////////////////////////////// // Entry points from AOT compiled code diff --git a/src/jit/jit-ffi.c b/src/jit/jit-ffi.c index 28e132da..e9dd373f 100644 --- a/src/jit/jit-ffi.c +++ b/src/jit/jit-ffi.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2021-2023 Nick Gasson +// Copyright (C) 2021-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,9 +19,11 @@ #include "hash.h" #include "ident.h" #include "jit/jit-ffi.h" +#include "jit/jit-priv.h" #include "jit/jit.h" #include "option.h" #include "thread.h" +#include "type.h" #include #include @@ -37,18 +39,18 @@ #endif typedef enum { - FFI_GENERIC, FFI_INTERNAL -} ffi_kind_t; - -typedef struct _jit_foreign { - ident_t sym; - void *ptr; - ffi_spec_t spec; - ffi_kind_t kind; - ffi_cif cif; - int nargs; - ffi_type *args[]; -} jit_foreign_t; + GHDL_ARG_DROP, + GHDL_ARG_PASS, + GHDL_ARG_LENGTH, +} ghdl_arg_t; + +typedef struct { + ffi_cif cif; + void *ptr; + unsigned nvhdl; + unsigned nforeign; + ghdl_arg_t args[0]; +} ghdl_ffi_t; typedef struct _jit_dll { jit_dll_t *next; @@ -60,146 +62,7 @@ typedef struct _jit_dll { #endif } jit_dll_t; -static hash_t *cache; -static jit_dll_t *dlls; -static nvc_lock_t lock; - -jit_foreign_t *jit_ffi_get(ident_t sym) -{ - SCOPED_LOCK(lock); - - if (cache == NULL) - cache = hash_new(128); - - return hash_get(cache, sym); -} - -static bool ffi_spec_is_internal(ffi_spec_t spec) -{ - return spec.count == 2 && ffi_spec_get(spec, 1) == FFI_ARGARRAY; -} - -static ffi_type *libffi_type_for(ffi_type_t type) -{ - switch (type) { - case FFI_INT8: return &ffi_type_sint8; - case FFI_INT16: return &ffi_type_sint16; - case FFI_INT32: return &ffi_type_sint32; - case FFI_INT64: return &ffi_type_sint64; - case FFI_FLOAT: return &ffi_type_double; - case FFI_POINTER: return &ffi_type_pointer; - case FFI_UARRAY: - case FFI_VOID: - default: return &ffi_type_void; - } -} - -jit_foreign_t *jit_ffi_bind(ident_t sym, ffi_spec_t spec, void *ptr) -{ - SCOPED_LOCK(lock); - - if (cache == NULL) - cache = hash_new(128); - else { - jit_foreign_t *exist = hash_get(cache, sym); - if (exist != NULL) - return exist; - } - - if (ffi_spec_is_internal(spec)) { - jit_foreign_t *ff = xcalloc(sizeof(jit_foreign_t)); - ff->kind = FFI_INTERNAL; - ff->ptr = ptr; - ff->sym = sym; - ff->spec = spec; - - return ff; - } - - ffi_spec_t copy = spec; - if (spec.count == 0) - copy.ext = xstrdup(spec.ext); - - int nargs = 0; - for (int i = 1; ffi_spec_has(spec, i); i++) { - const ffi_type_t type = ffi_spec_get(spec, i); - nargs += (type == FFI_UARRAY) ? 3 : 1; - } - - const ffi_type_t rtype = ffi_spec_get(spec, 0); - const int adj_nargs = (rtype == FFI_UARRAY) ? nargs + 1 : nargs; - - jit_foreign_t *ff = xcalloc_flex(sizeof(jit_foreign_t), - adj_nargs, sizeof(ffi_type *)); - ff->kind = FFI_GENERIC; - ff->ptr = ptr; - ff->sym = sym; - ff->spec = copy; - ff->nargs = nargs; - - int wptr = 0; - for (int i = 1; ffi_spec_has(spec, i); i++) { - const ffi_type_t type = ffi_spec_get(spec, i); - if (type == FFI_UARRAY) { - ff->args[wptr++] = &ffi_type_pointer; - ff->args[wptr++] = &ffi_type_sint64; // Left - ff->args[wptr++] = &ffi_type_sint64; // Length - } - else - ff->args[wptr++] = libffi_type_for(type); - } - if (rtype == FFI_UARRAY) - ff->args[wptr++] = &ffi_type_pointer; - assert(wptr == adj_nargs); - - ffi_type *ret = libffi_type_for(rtype); - - if (ffi_prep_cif(&ff->cif, FFI_DEFAULT_ABI, adj_nargs, - ret, ff->args) != FFI_OK) - fatal("ffi_prep_cif failed for %s", istr(sym)); - - hash_put(cache, sym, ff); - return ff; -} - -void jit_ffi_call(jit_foreign_t *ff, jit_scalar_t *args) -{ - if (ff->ptr == NULL) { - const char *sym = istr(ff->sym); - if ((ff->ptr = ffi_find_symbol(NULL, sym)) == NULL) - jit_msg(NULL, DIAG_FATAL, "foreign function %s not found", sym); - } - - if (ff->kind == FFI_INTERNAL) { - // Fast calling convention for internal routines - void (*entry)(jit_scalar_t *) = ff->ptr; - (*entry)(args); - return; - } - - void *aptrs[ff->nargs + 1]; - for (int i = 0; i < ff->nargs; i++) - aptrs[i] = &(args[i].integer); - - const ffi_type_t rtype = ffi_spec_get(ff->spec, 0); - - ffi_uarray_t u, *up = &u; - if (rtype == FFI_UARRAY) - aptrs[ff->nargs] = &up; - - intmax_t result; - ffi_call(&ff->cif, ff->ptr, &result, aptrs); - - if (rtype == FFI_UARRAY) { - args[0].pointer = u.ptr; - args[1].integer = u.dims[0].left; - args[2].integer = u.dims[0].length; - } - else if (ffi_is_integral(rtype)) - args[0].integer = ffi_widen_int(rtype, &result); - else - args[0].integer = result; -} +static jit_dll_t *dlls; ffi_uarray_t ffi_wrap(void *ptr, int64_t left, int64_t right) { @@ -213,6 +76,18 @@ ffi_uarray_t ffi_wrap(void *ptr, int64_t left, int64_t right) return u; } +void ffi_return_string(const char *str, jit_scalar_t *args, tlab_t *tlab) +{ + const size_t len = strlen(str); + + void *mem = tlab_alloc(tlab, len); + memcpy(mem, str, len); + + args[0].pointer = mem; + args[1].integer = 1; + args[2].integer = len; +} + bool ffi_is_integral(ffi_type_t type) { return type == FFI_INT8 || type == FFI_INT16 || type == FFI_INT32 @@ -319,11 +194,13 @@ jit_dll_t *ffi_load_dll(const char *path) ffi_load_exe(); // First time initialisation jit_dll_t *dll = xcalloc(sizeof(jit_dll_t)); - dll->next = dlls; dll->handle = handle; dll->path = abs; - return (dlls = dll); + jit_dll_t **where; + for (where = &dlls; *where != NULL; where = &((*where)->next)); + + return (*where = dll); } void ffi_unload_dll(jit_dll_t *dll) @@ -346,7 +223,9 @@ void ffi_unload_dll(jit_dll_t *dll) void *ffi_find_symbol(jit_dll_t *dll, const char *name) { - if (dll == NULL) { + if (name == NULL) + return NULL; + else if (dll == NULL) { for (jit_dll_t *it = dlls; it; it = it->next) { void *p = ffi_find_symbol(it, name); if (p != NULL) @@ -364,23 +243,13 @@ void *ffi_find_symbol(jit_dll_t *dll, const char *name) } } -ident_t ffi_get_sym(jit_foreign_t *ff) -{ - return ff->sym; -} - -ffi_spec_t ffi_get_spec(jit_foreign_t *ff) -{ - return ff->spec; -} - ffi_spec_t ffi_spec_new(const ffi_type_t *types, size_t count) { assert(count > 0); #ifdef DEBUG for (int i = 0; i < count; i++) - assert(islower(types[i]) || types[i] == FFI_ARGARRAY); + assert(islower(types[i])); #endif ffi_spec_t spec = {}; @@ -393,3 +262,232 @@ ffi_spec_t ffi_spec_new(const ffi_type_t *types, size_t count) return spec; } + +static void jit_internal_entry(jit_func_t *f, jit_anchor_t *caller, + jit_scalar_t *args, tlab_t *tlab) +{ + jit_thread_local_t *thread = jit_attach_thread(caller); + + ffi_internal_t fn = *jit_get_privdata_ptr(f->jit, f); + if (unlikely(fn == NULL)) { + // JIT has been reset, need to elaborate again + f->entry = jit_interp; + jit_interp(f, caller, args, tlab); + } + else + (*fn)(args, tlab); + + thread->anchor = NULL; +} + +static void jit_ghdl_entry(jit_func_t *f, jit_anchor_t *caller, + jit_scalar_t *args, tlab_t *tlab) +{ + jit_thread_local_t *thread = jit_attach_thread(caller); + + ghdl_ffi_t *gffi = *jit_get_privdata_ptr(f->jit, f); + if (unlikely(gffi == NULL)) { + // JIT has been reset, need to elaborate again + f->entry = jit_interp; + jit_interp(f, caller, args, tlab); + } + else { + void *aptrs[gffi->nforeign]; + int opos = 0; + for (int ipos = 0; ipos < gffi->nvhdl; ipos++) { + switch (gffi->args[ipos]) { + case GHDL_ARG_DROP: + break; + case GHDL_ARG_PASS: + aptrs[opos++] = &(args[ipos].integer); + break; + case GHDL_ARG_LENGTH: + args[ipos].integer = ffi_array_length(args[ipos].integer); + aptrs[opos++] = &(args[ipos].integer); + break; + } + } + + intmax_t result = 0; + ffi_call(&(gffi->cif), gffi->ptr, &result, aptrs); + + args[0].integer = result; + } + + thread->anchor = NULL; +} + +static ffi_type *ghdl_ffi_result_type(type_t type) +{ + type_t base = type_base_recur(type); + switch (type_kind(base)) { + case T_INTEGER: + case T_ENUM: + case T_PHYSICAL: + { + switch (type_byte_width(base)) { + case 8: return &ffi_type_sint64; + case 4: return &ffi_type_sint32; + case 2: return &ffi_type_sint16; + case 1: return &ffi_type_sint8; + } + } + break; + case T_REAL: + return &ffi_type_double; + default: + break; + } + + jit_msg(NULL, DIAG_FATAL, "cannot return type %s using GHDL " + "VHPIDIRECT calling convention", type_pp(type)); + return &ffi_type_void; +} + +static void ghdl_ffi_add_arg(ghdl_ffi_t *gffi, ffi_type **types, type_t type) +{ + type_t base = type_base_recur(type); + switch (type_kind(base)) { + case T_INTEGER: + case T_ENUM: + case T_PHYSICAL: + { + gffi->args[gffi->nvhdl++] = GHDL_ARG_PASS; + + switch (type_byte_width(base)) { + case 8: types[gffi->nforeign++] = &ffi_type_sint64; return; + case 4: types[gffi->nforeign++] = &ffi_type_sint32; return; + case 2: types[gffi->nforeign++] = &ffi_type_sint16; return; + case 1: types[gffi->nforeign++] = &ffi_type_sint8; return; + } + } + break; + case T_REAL: + gffi->args[gffi->nvhdl++] = GHDL_ARG_PASS; + types[gffi->nforeign++] = &ffi_type_double; + return; + case T_RECORD: + gffi->args[gffi->nvhdl++] = GHDL_ARG_PASS; + types[gffi->nforeign++] = &ffi_type_pointer; + return; + case T_ARRAY: + if (dimension_of(type) > 1) + break; + else if (type_is_unconstrained(type)) { + gffi->args[gffi->nvhdl++] = GHDL_ARG_PASS; // Data pointer + types[gffi->nforeign++] = &ffi_type_pointer; + + gffi->args[gffi->nvhdl++] = GHDL_ARG_DROP; // Left index + + gffi->args[gffi->nvhdl++] = GHDL_ARG_LENGTH; // Array length + types[gffi->nforeign++] = &ffi_type_sint64; + + return; + } + else { + gffi->args[gffi->nvhdl++] = GHDL_ARG_PASS; + types[gffi->nforeign++] = &ffi_type_pointer; + return; + } + default: + break; + } + + jit_msg(NULL, DIAG_FATAL, "cannot pass type %s using GHDL " + "VHPIDIRECT calling convention", type_pp(type)); +} + +static void *ffi_prepare_ghdl(tree_t decl, const char *symbol) +{ + assert(tree_kind(decl) == T_ATTR_SPEC); + + tree_t sub = tree_ref(decl); + const int nports = tree_ports(sub); + + const size_t ghdl_ffi_sz = + sizeof(ghdl_ffi_t) + (1 + nports*3) * sizeof(ghdl_arg_t); + ghdl_ffi_t *gffi = + jit_mspace_alloc(ghdl_ffi_sz + nports * 2 * sizeof(ffi_type *)); + ffi_type **types = (void *)gffi + ghdl_ffi_sz; + + ffi_type *ret; + type_t type = tree_type(sub); + if (type_kind(type) == T_FUNC) + ret = ghdl_ffi_result_type(type_result(type)); + else + ret = &ffi_type_void; + + if ((gffi->ptr = ffi_find_symbol(NULL, symbol)) == NULL) + jit_msg(NULL, DIAG_FATAL, "foreign function %s not found", symbol); + + gffi->nvhdl = gffi->nforeign = 0; + + gffi->args[gffi->nvhdl++] = GHDL_ARG_DROP; // Drop context argument + + if (type_kind(type) == T_PROC) + gffi->args[gffi->nvhdl++] = GHDL_ARG_DROP; // Drop state argument + + for (int i = 0; i < nports; i++) { + tree_t p = tree_port(sub, i); + if (tree_class(p) == C_SIGNAL) + jit_msg(tree_loc(p), DIAG_FATAL, "SIGNAL parameters are not " + "supported for VHPIDIRECT subprograms using the GHDL " + "calling convention"); + else if (tree_subkind(p) != PORT_IN) { + types[gffi->nforeign++] = &ffi_type_pointer; + gffi->args[gffi->nvhdl++] = GHDL_ARG_PASS; + } + else + ghdl_ffi_add_arg(gffi, types, tree_type(p)); + } + + assert(gffi->nvhdl <= 1 + nports * 3); + assert(gffi->nforeign <= nports * 2); + + if (ffi_prep_cif(&(gffi->cif), FFI_DEFAULT_ABI, gffi->nforeign, + ret, types) != FFI_OK) + fatal("ffi_prep_cif failed for %s", type_pp(type)); + + return gffi; +} + +void jit_bind_foreign(jit_func_t *f, const char *spec, size_t length, + tree_t where) +{ + char *tmp LOCAL = xstrndup(spec, length), *p = strtok(tmp, " "); + if (strcmp(p, "VHPIDIRECT") == 0 || strcmp(p, "GHDL") == 0) { + p = strtok(NULL, " "); + if (p != NULL) { + // The object library specifier is silently ignored + char *p2 = strtok(NULL, " "); + if (p2 != NULL) p = p2; + } + + *jit_get_privdata_ptr(f->jit, f) = ffi_prepare_ghdl(where, p); + + assert(f->entry != jit_ghdl_entry); // Should only be called once + f->entry = jit_ghdl_entry; + } + else if (strcmp(p, "INTERNAL") == 0) { + p = strtok(NULL, " "); + + jit_dll_t *exe = ffi_load_exe(); + ffi_internal_t fn = ffi_find_symbol(exe, p); + if (fn == NULL) + jit_msg(NULL, DIAG_FATAL, "missing internal symbol %s", p); + + *jit_get_privdata_ptr(f->jit, f) = fn; + + assert(f->entry != jit_internal_entry); // Should only be called once + f->entry = jit_internal_entry; + } + else { + diag_t *d = diag_new(DIAG_FATAL, NULL); + diag_printf(d, "failed to parse FOREIGN attribute"); + diag_hint(d, NULL, "expecting specification to start with " + "VHPIDIRECT or GHDL"); + diag_emit(d); + + jit_abort_with_status(1); + } +} diff --git a/src/jit/jit-ffi.h b/src/jit/jit-ffi.h index 1dee00db..6c71bdfb 100644 --- a/src/jit/jit-ffi.h +++ b/src/jit/jit-ffi.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -31,7 +31,6 @@ typedef char ffi_type_t; #define FFI_POINTER 'p' #define FFI_UARRAY 'u' #define FFI_SIGNAL 's' -#define FFI_ARGARRAY '.' typedef union { struct { @@ -49,8 +48,6 @@ STATIC_ASSERT(sizeof(ffi_spec_t) == 8); #define ffi_spec_has(s, n) \ (((s).count == 0 && (s).ext && (s).ext[(n)]) || (n) < (s).count) -typedef struct _jit_foreign jit_foreign_t; - // The code generator knows the layout of this struct typedef struct { int64_t left; @@ -73,22 +70,18 @@ typedef struct _ffi_closure { void *context; } ffi_closure_t; -jit_foreign_t *jit_ffi_bind(ident_t sym, ffi_spec_t spec, void *ptr); -jit_foreign_t *jit_ffi_get(ident_t sym); -void jit_ffi_call(jit_foreign_t *ff, jit_scalar_t *args); - -ident_t ffi_get_sym(jit_foreign_t *ff); -ffi_spec_t ffi_get_spec(jit_foreign_t *ff); - ffi_spec_t ffi_spec_new(const ffi_type_t *types, size_t count); ffi_uarray_t ffi_wrap(void *ptr, int64_t left, int64_t right); +void ffi_return_string(const char *str, jit_scalar_t *args, tlab_t *tlab); bool ffi_is_integral(ffi_type_t type); int64_t ffi_widen_int(ffi_type_t type, const void *input); void ffi_store_int(ffi_type_t type, uint64_t value, void *output); typedef struct _jit_dll jit_dll_t; +typedef void (*ffi_internal_t)(jit_scalar_t *, tlab_t *); + jit_dll_t *ffi_load_dll(const char *path); void ffi_unload_dll(jit_dll_t *dll); void *ffi_find_symbol(jit_dll_t *dll, const char *name); diff --git a/src/jit/jit-interp.c b/src/jit/jit-interp.c index f56aa398..c01dde94 100644 --- a/src/jit/jit-interp.c +++ b/src/jit/jit-interp.c @@ -200,8 +200,6 @@ static inline jit_scalar_t interp_get_scalar(jit_interp_t *state, return (jit_scalar_t){ .integer = value.label }; case JIT_VALUE_HANDLE: return (jit_scalar_t){ .integer = value.handle }; - case JIT_VALUE_FOREIGN: - return (jit_scalar_t){ .pointer = value.foreign }; case JIT_VALUE_LOCUS: return (jit_scalar_t){ .pointer = jit_get_locus(value) }; case JIT_ADDR_CPOOL: @@ -834,13 +832,6 @@ static void interp_exit(jit_interp_t *state, jit_ir_t *ir) __nvc_do_exit(ir->arg1.exit, state->anchor, state->args, state->tlab); } -static void interp_fficall(jit_interp_t *state, jit_ir_t *ir) -{ - JIT_ASSERT(ir->arg1.kind == JIT_VALUE_FOREIGN); - state->anchor->irpos = ir - state->func->irbuf; - __nvc_do_fficall(ir->arg1.foreign, state->anchor, state->args); -} - static void interp_getpriv(jit_interp_t *state, jit_ir_t *ir) { JIT_ASSERT(ir->arg1.kind == JIT_VALUE_HANDLE); @@ -872,6 +863,12 @@ static void interp_trim(jit_interp_t *state, jit_ir_t *ir) state->tlab->alloc = state->anchor->watermark; } +static void interp_reexec(jit_interp_t *state, jit_ir_t *ir) +{ + jit_entry_fn_t entry = load_acquire(&state->func->entry); + (*entry)(state->func, state->anchor->caller, state->args, state->tlab); +} + static void interp_loop(jit_interp_t *state) { for (;;) { @@ -1021,9 +1018,6 @@ static void interp_loop(jit_interp_t *state) case MACRO_EXP: interp_exp(state, ir); break; - case MACRO_FFICALL: - interp_fficall(state, ir); - break; case MACRO_GETPRIV: interp_getpriv(state, ir); break; @@ -1036,6 +1030,9 @@ static void interp_loop(jit_interp_t *state) case MACRO_TRIM: interp_trim(state, ir); break; + case MACRO_REEXEC: + interp_reexec(state, ir); + return; default: interp_dump(state); fatal_trace("cannot interpret opcode %s", jit_op_name(ir->op)); diff --git a/src/jit/jit-irgen.c b/src/jit/jit-irgen.c index 54102158..aad0f8e2 100644 --- a/src/jit/jit-irgen.c +++ b/src/jit/jit-irgen.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -179,11 +179,6 @@ static jit_value_t jit_value_from_exit(jit_exit_t exit) return (jit_value_t){ .kind = JIT_VALUE_EXIT, .exit = exit }; } -static jit_value_t jit_value_from_foreign(jit_foreign_t *ff) -{ - return (jit_value_t){ .kind = JIT_VALUE_FOREIGN, .foreign = ff }; -} - static jit_ir_t *irgen_append(jit_irgen_t *g) { if (g->func->nirs == g->bufsz) { @@ -663,13 +658,6 @@ static jit_value_t macro_exp(jit_irgen_t *g, jit_size_t sz, jit_cc_t cc, return jit_value_from_reg(r); } -static void macro_fficall(jit_irgen_t *g, jit_value_t func) -{ - assert(func.kind == JIT_VALUE_FOREIGN); - irgen_emit_unary(g, MACRO_FFICALL, JIT_SZ_UNSPEC, JIT_CC_NONE, - JIT_REG_INVALID, func); -} - static jit_value_t macro_getpriv(jit_irgen_t *g, jit_handle_t handle) { jit_reg_t r = irgen_alloc_reg(g); @@ -697,6 +685,12 @@ static void macro_trim(jit_irgen_t *g) irgen_emit_nullary(g, MACRO_TRIM, JIT_CC_NONE, JIT_REG_INVALID); } +static void macro_reexec(jit_irgen_t *g) +{ + irgen_emit_nullary(g, MACRO_REEXEC, JIT_CC_NONE, JIT_REG_INVALID); + g->flags = VCODE_INVALID_REG; +} + //////////////////////////////////////////////////////////////////////////////// // Vcode to JIT IR lowering @@ -987,35 +981,6 @@ static ffi_type_t irgen_ffi_type(vcode_type_t type) } } -static jit_foreign_t *irgen_ffi_for_call(jit_irgen_t *g, int op, bool internal) -{ - ident_t func = vcode_get_func(op); - jit_foreign_t *ff = jit_ffi_get(func); - if (ff != NULL) - return ff; - - LOCAL_TEXT_BUF tb = tb_new(); - - vcode_reg_t result = vcode_get_result(op); - if (result != VCODE_INVALID_REG) - tb_append(tb, irgen_ffi_type(vcode_reg_type(result))); - else - tb_append(tb, FFI_VOID); - - if (internal) - tb_append(tb, FFI_ARGARRAY); - else { - const int nargs = vcode_count_args(op); - for (int i = 0; i < nargs; i++) { - vcode_type_t vtype = vcode_reg_type(vcode_get_arg(op, i)); - tb_append(tb, irgen_ffi_type(vtype)); - } - } - - ffi_spec_t spec = ffi_spec_new(tb_get(tb), tb_len(tb)); - return jit_ffi_bind(func, spec, NULL); -} - static jit_handle_t irgen_get_handle(jit_irgen_t *g, int op) { ident_t func = vcode_get_func(op); @@ -2206,52 +2171,35 @@ static void irgen_op_fcall(jit_irgen_t *g, int op) { irgen_emit_debuginfo(g, op); // For stack traces - const vcode_cc_t cc = vcode_get_subkind(op); - if (cc == VCODE_CC_FOREIGN || cc == VCODE_CC_INTERNAL) { - irgen_send_args(g, op, 0); - - jit_foreign_t *ff = irgen_ffi_for_call(g, op, cc == VCODE_CC_INTERNAL); - macro_fficall(g, jit_value_from_foreign(ff)); - - vcode_reg_t result = vcode_get_result(op); - if (result != VCODE_INVALID_REG) { - const int slots = irgen_slots_for_type(vcode_reg_type(result)); - g->map[result] = j_recv(g, 0); - for (int i = 1; i < slots; i++) - j_recv(g, i); - } + vcode_reg_t result = vcode_get_result(op); + if (result == VCODE_INVALID_REG) { + // Must call using procedure calling convention + j_send(g, 0, jit_value_from_int64(0)); + irgen_send_args(g, op, 1); } - else { - vcode_reg_t result = vcode_get_result(op); - if (result == VCODE_INVALID_REG) { - // Must call using procedure calling convention - j_send(g, 0, jit_value_from_int64(0)); - irgen_send_args(g, op, 1); - } - else - irgen_send_args(g, op, 0); + else + irgen_send_args(g, op, 0); - j_call(g, irgen_get_handle(g, op)); + j_call(g, irgen_get_handle(g, op)); - if (result != VCODE_INVALID_REG) { - vcode_type_t vtype = vcode_reg_type(result); - const int slots = irgen_slots_for_type(vtype); - g->map[result] = j_recv(g, 0); - for (int i = 1; i < slots; i++) - j_recv(g, i); // Must be contiguous registers + if (result != VCODE_INVALID_REG) { + vcode_type_t vtype = vcode_reg_type(result); + const int slots = irgen_slots_for_type(vtype); + g->map[result] = j_recv(g, 0); + for (int i = 1; i < slots; i++) + j_recv(g, i); // Must be contiguous registers - const vtype_kind_t vkind = vtype_kind(vtype); - g->used_tlab |= vkind == VCODE_TYPE_UARRAY - || vkind == VCODE_TYPE_POINTER; - } - else if (vcode_unit_kind(g->func->unit) == VCODE_UNIT_FUNCTION) { - irgen_label_t *cont = irgen_alloc_label(g); - jit_value_t state = j_recv(g, 0); - j_cmp(g, JIT_CC_EQ, state, jit_null_ptr()); - j_jump(g, JIT_CC_T, cont); - macro_exit(g, JIT_EXIT_FUNC_WAIT); - irgen_bind_label(g, cont); - } + const vtype_kind_t vkind = vtype_kind(vtype); + g->used_tlab |= vkind == VCODE_TYPE_UARRAY + || vkind == VCODE_TYPE_POINTER; + } + else if (vcode_unit_kind(g->func->unit) == VCODE_UNIT_FUNCTION) { + irgen_label_t *cont = irgen_alloc_label(g); + jit_value_t state = j_recv(g, 0); + j_cmp(g, JIT_CC_EQ, state, jit_null_ptr()); + j_jump(g, JIT_CC_T, cont); + macro_exit(g, JIT_EXIT_FUNC_WAIT); + irgen_bind_label(g, cont); } } @@ -3511,6 +3459,38 @@ static void irgen_op_convert_out(jit_irgen_t *g, int op) macro_exit(g, JIT_EXIT_CONVERT_OUT); } +static void irgen_op_bind_foreign(jit_irgen_t *g, int op) +{ + jit_value_t spec = irgen_get_arg(g, op, 0); + jit_value_t length = irgen_get_arg(g, op, 1); + + jit_value_t locus; + if (vcode_count_args(op) > 2) + locus = irgen_get_arg(g, op, 2); + else + locus = jit_value_from_int64(0); + + j_send(g, 0, spec); + j_send(g, 1, length); + j_send(g, 2, locus); + macro_exit(g, JIT_EXIT_BIND_FOREIGN); + + int pslot = 0; + if (vcode_unit_result(g->func->unit) == VCODE_INVALID_TYPE) + j_send(g, pslot++, jit_value_from_int64(0)); // Procedure state + + const int nparams = vcode_count_params(); + for (int i = 0; i < nparams; i++) { + const int slots = irgen_slots_for_type(vcode_param_type(i)); + jit_value_t p = irgen_get_value(g, vcode_param_reg(i)); + j_send(g, pslot++, p); + for (int j = 1; j < slots; j++) + j_send(g, pslot++, jit_value_from_reg(jit_value_as_reg(p) + j)); + } + + macro_reexec(g); +} + static void irgen_block(jit_irgen_t *g, vcode_block_t block) { vcode_select_block(block); @@ -3903,6 +3883,9 @@ static void irgen_block(jit_irgen_t *g, vcode_block_t block) case VCODE_OP_CONVERT_OUT: irgen_op_convert_out(g, i); break; + case VCODE_OP_BIND_FOREIGN: + irgen_op_bind_foreign(g, i); + break; default: fatal_trace("cannot generate JIT IR for vcode op %s", vcode_op_string(op)); diff --git a/src/jit/jit-llvm.c b/src/jit/jit-llvm.c index 15350e44..c10b8c0a 100644 --- a/src/jit/jit-llvm.c +++ b/src/jit/jit-llvm.c @@ -126,7 +126,6 @@ typedef enum { LLVM_DO_EXIT, LLVM_PUTPRIV, LLVM_MSPACE_ALLOC, - LLVM_DO_FFICALL, LLVM_GET_OBJECT, LLVM_TLAB_ALLOC, LLVM_SCHED_WAVEFORM, @@ -859,25 +858,6 @@ static LLVMValueRef llvm_get_fn(llvm_obj_t *obj, llvm_fn_t which) } break; - case LLVM_DO_FFICALL: - { - LLVMTypeRef args[] = { - obj->types[LLVM_PTR], - obj->types[LLVM_PTR], -#ifdef LLVM_HAS_OPAQUE_POINTERS - obj->types[LLVM_PTR], -#else - LLVMPointerType(obj->types[LLVM_INT64], 0), -#endif - }; - obj->fntypes[which] = LLVMFunctionType(obj->types[LLVM_VOID], args, - ARRAY_LEN(args), false); - - fn = llvm_add_fn(obj, "__nvc_do_fficall", obj->fntypes[which]); - llvm_add_func_attr(obj, fn, FUNC_ATTR_NOUNWIND, -1); - } - break; - case LLVM_PUTPRIV: { LLVMTypeRef args[] = { @@ -1228,12 +1208,6 @@ static LLVMValueRef cgen_get_value(llvm_obj_t *obj, cgen_block_t *cgb, } else return llvm_ptr(obj, jit_get_cover_ptr(cgb->func->source->jit, value)); - case JIT_VALUE_FOREIGN: - if (cgb->func->mode == CGEN_AOT) - return cgen_load_from_reloc(obj, cgb->func, RELOC_FOREIGN, - (uintptr_t)value.foreign); - else - return llvm_ptr(obj, value.foreign); case JIT_VALUE_LOCUS: return cgen_rematerialise_object(obj, cgb->func, value.ident, value.disp); default: @@ -2130,20 +2104,6 @@ static void cgen_macro_exit(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) } } -static void cgen_macro_fficall(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) -{ - cgen_sync_irpos(obj, cgb, ir); - - LLVMValueRef ffptr = cgen_get_value(obj, cgb, ir->arg1); - - LLVMValueRef args[] = { - ffptr, - PTR(cgb->func->anchor), - cgb->func->args - }; - llvm_call_fn(obj, LLVM_DO_FFICALL, args, ARRAY_LEN(args)); -} - static void cgen_macro_galloc(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) { cgen_sync_irpos(obj, cgb, ir); @@ -2289,6 +2249,34 @@ static void cgen_macro_trim(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) LLVMBuildStore(obj->builder, watermark, alloc_ptr); } +static void cgen_macro_reexec(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) +{ + LLVMValueRef fptr = LLVMGetParam(cgb->func->llvmfn, 0); + + // Must have acquire semantics to synchronise with installing new code + LLVMValueRef entry = + LLVMBuildLoad2(obj->builder, obj->types[LLVM_PTR], fptr, "entry"); + LLVMSetAlignment(entry, sizeof(void *)); + LLVMSetOrdering(entry, LLVMAtomicOrderingAcquire); + +#ifndef LLVM_HAS_OPAQUE_POINTERS + LLVMTypeRef ptr_type = LLVMPointerType(obj->types[LLVM_ENTRY_FN], 0); + entry = LLVMBuildPointerCast(obj->builder, entry, ptr_type, ""); +#endif + + LLVMValueRef args[] = { + fptr, + LLVMGetParam(cgb->func->llvmfn, 1), + cgb->func->args, + cgb->func->tlab, + }; + LLVMValueRef call = LLVMBuildCall2(obj->builder, obj->types[LLVM_ENTRY_FN], + entry, args, ARRAY_LEN(args), ""); + LLVMSetTailCall(call, true); + + LLVMBuildRetVoid(obj->builder); +} + static void cgen_ir(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) { switch (ir->op) { @@ -2425,9 +2413,6 @@ static void cgen_ir(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) case MACRO_EXIT: cgen_macro_exit(obj, cgb, ir); break; - case MACRO_FFICALL: - cgen_macro_fficall(obj, cgb, ir); - break; case MACRO_GALLOC: cgen_macro_galloc(obj, cgb, ir); break; @@ -2449,6 +2434,9 @@ static void cgen_ir(llvm_obj_t *obj, cgen_block_t *cgb, jit_ir_t *ir) case MACRO_TRIM: cgen_macro_trim(obj, cgb, ir); break; + case MACRO_REEXEC: + cgen_macro_reexec(obj, cgb, ir); + break; default: cgen_abort(cgb, ir, "cannot generate LLVM for %s", jit_op_name(ir->op)); } @@ -2613,28 +2601,6 @@ static void cgen_aot_descr(llvm_obj_t *obj, cgen_func_t *func) APUSH(relocs, r); } } - else if (args[j].kind == JIT_VALUE_FOREIGN) { - if (cgen_find_reloc(relocs.items, RELOC_FOREIGN, relocs.count, - (uintptr_t)args[j].foreign) == NULL) { - // Encode spec in name string - LOCAL_TEXT_BUF tb = tb_new(); - ffi_spec_t spec = ffi_get_spec(args[j].foreign); - if (spec.count > 0) - tb_catn(tb, spec.embed, spec.count); - else - tb_cat(tb, spec.ext); - tb_append(tb, '\b'); - tb_istr(tb, ffi_get_sym(args[j].foreign)); - - const cgen_reloc_t r = { - .kind = RELOC_FOREIGN, - .str = cgen_reloc_str(obj, tb_get(tb)), - .key = (uintptr_t)args[j].foreign, - .nth = relocs.count, - }; - APUSH(relocs, r); - } - } else if (args[j].kind == JIT_ADDR_COVER) { if (cgen_find_reloc(relocs.items, RELOC_COVER, relocs.count, 0) == NULL) { @@ -2809,6 +2775,63 @@ static void cgen_fix_liveout_types(llvm_obj_t *obj, cgen_block_t *cgb) } } +static void cgen_reexecute_guard(llvm_obj_t *obj, cgen_func_t *func, + jit_cfg_t *cfg) +{ + // Jump to the real entry if it may be modified at runtime + // (e.g. bound to a foreign function) + + bool has_reexec = false; + for (int i = 0; i < cfg->nblocks; i++) { + jit_block_t *bb = &(cfg->blocks[i]); + if (func->source->irbuf[bb->last].op == MACRO_REEXEC) { + has_reexec = true; + break; + } + } + + if (!has_reexec) + return; + + LLVMValueRef fptr = LLVMGetParam(func->llvmfn, 0); + + // Must have acquire semantics to synchronise with installing new code + LLVMValueRef entry = + LLVMBuildLoad2(obj->builder, obj->types[LLVM_PTR], fptr, "entry"); + LLVMSetAlignment(entry, sizeof(void *)); + LLVMSetOrdering(entry, LLVMAtomicOrderingAcquire); + + LLVMBasicBlockRef reexec_bb = llvm_append_block(obj, func->llvmfn, "reexec"); + LLVMBasicBlockRef cont_bb = llvm_append_block(obj, func->llvmfn, "cont"); + + LLVMValueRef changed = LLVMBuildICmp(obj->builder, LLVMIntNE, entry, + PTR(func->llvmfn), "changed"); + LLVMBuildCondBr(obj->builder, changed, reexec_bb, cont_bb); + + LLVMPositionBuilderAtEnd(obj->builder, reexec_bb); + +#ifndef LLVM_HAS_OPAQUE_POINTERS + LLVMTypeRef ptr_type = LLVMPointerType(obj->types[LLVM_ENTRY_FN], 0); + entry = LLVMBuildPointerCast(obj->builder, entry, ptr_type, ""); +#endif + + LLVMValueRef anchor = LLVMGetParam(func->llvmfn, 1); + + LLVMValueRef args[] = { + fptr, + anchor, + func->args, + func->tlab, + }; + LLVMValueRef call = LLVMBuildCall2(obj->builder, obj->types[LLVM_ENTRY_FN], + entry, args, ARRAY_LEN(args), ""); + LLVMSetTailCall(call, true); + + LLVMBuildRetVoid(obj->builder); + + LLVMPositionBuilderAtEnd(obj->builder, cont_bb); +} + static void cgen_function(llvm_obj_t *obj, cgen_func_t *func) { func->llvmfn = llvm_add_fn(obj, func->name, obj->types[LLVM_ENTRY_FN]); @@ -2876,8 +2899,11 @@ static void cgen_function(llvm_obj_t *obj, cgen_func_t *func) cgen_cache_args(obj, func); jit_cfg_t *cfg = func->cfg = jit_get_cfg(func->source); + cgen_reexecute_guard(obj, func, cfg); cgen_basic_blocks(obj, func, cfg); + entry_bb = LLVMGetInsertBlock(obj->builder); + cgen_pointer_mask(func); cgen_block_t *cgb = func->blocks; @@ -3396,7 +3422,6 @@ void jit_register_llvm_plugin(jit_t *j) warnf("invalid NVC_JIT_THRESOLD setting %d", threshold); } - //////////////////////////////////////////////////////////////////////////////// // Ahead-of-time code generation diff --git a/src/jit/jit-optim.c b/src/jit/jit-optim.c index 148dbde2..eca86b21 100644 --- a/src/jit/jit-optim.c +++ b/src/jit/jit-optim.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -32,7 +32,7 @@ static bool cfg_is_terminator(jit_func_t *func, jit_ir_t *ir) if (ir->op == MACRO_CASE) return ir + 1 < func->irbuf + func->nirs && (ir + 1)->op != MACRO_CASE; else - return ir->op == J_JUMP || ir->op == J_RET; + return ir->op == J_JUMP || ir->op == J_RET || ir->op == MACRO_REEXEC; } static void cfg_add_one_edge(jit_edge_list_t *list, unsigned edge) diff --git a/src/jit/jit-pack.c b/src/jit/jit-pack.c index 95f3c1c4..8a46afee 100644 --- a/src/jit/jit-pack.c +++ b/src/jit/jit-pack.c @@ -225,10 +225,6 @@ static void pack_value(pack_writer_t *pw, jit_t *j, jit_value_t value) case JIT_VALUE_LOCUS: pack_locus(pw, value.ident, value.disp); break; - case JIT_VALUE_FOREIGN: - pack_uint(pw, ffi_get_spec(value.foreign).bits); - pack_str(pw, istr(ffi_get_sym(value.foreign))); - break; default: fatal_trace("cannot handle value kind %d in pack_value", value.kind); } @@ -506,13 +502,6 @@ static jit_value_t unpack_value(pack_func_t *pf, jit_t *j) value.ident = ident_new(unpack_str(pf)); value.disp = unpack_uint(pf); break; - case JIT_VALUE_FOREIGN: - { - const ffi_spec_t spec = { .bits = unpack_uint(pf) }; - ident_t sym = ident_new(unpack_str(pf)); - value.foreign = jit_ffi_bind(sym, spec, NULL); - } - break; default: fatal_trace("cannot handle value kind %d in unpack_value", value.kind); } diff --git a/src/jit/jit-priv.h b/src/jit/jit-priv.h index a09a103f..9e4ea652 100644 --- a/src/jit/jit-priv.h +++ b/src/jit/jit-priv.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -76,7 +76,6 @@ typedef enum { MACRO_FEXP, MACRO_EXP, MACRO_BZERO, - MACRO_FFICALL, MACRO_GETPRIV, MACRO_PUTPRIV, MACRO_LALLOC, @@ -85,6 +84,7 @@ typedef enum { MACRO_TRIM, MACRO_MOVE, MACRO_MEMSET, + MACRO_REEXEC, } jit_op_t; typedef enum { @@ -166,6 +166,7 @@ typedef enum { JIT_EXIT_PORT_CONVERSION, JIT_EXIT_CONVERT_IN, JIT_EXIT_CONVERT_OUT, + JIT_EXIT_BIND_FOREIGN, } jit_exit_t; typedef uint16_t jit_reg_t; @@ -184,7 +185,6 @@ typedef enum { JIT_VALUE_HANDLE, JIT_VALUE_EXIT, JIT_VALUE_LOC, - JIT_VALUE_FOREIGN, JIT_VALUE_VPOS, JIT_VALUE_LOCUS, } jit_value_kind_t; @@ -201,16 +201,15 @@ typedef struct { jit_value_kind_t kind : 8; int32_t disp; union { - jit_reg_t reg; - int64_t int64; - double dval; - jit_label_t label; - jit_handle_t handle; - jit_exit_t exit; - loc_t loc; - jit_foreign_t *foreign; - jit_vpos_t vpos; - ident_t ident; + jit_reg_t reg; + int64_t int64; + double dval; + jit_label_t label; + jit_handle_t handle; + jit_exit_t exit; + loc_t loc; + jit_vpos_t vpos; + ident_t ident; }; } jit_value_t; @@ -273,7 +272,6 @@ typedef enum { RELOC_NULL, RELOC_HANDLE, RELOC_FUNC, - RELOC_FOREIGN, RELOC_PRIVDATA, RELOC_COVER, RELOC_PROCESSED, @@ -432,10 +430,11 @@ void pack_writer_string_table(pack_writer_t *pw, const char **tab, size_t *size); void pack_writer_free(pack_writer_t *pw); +void jit_bind_foreign(jit_func_t *f, const char *spec, size_t length, + tree_t where); + DLLEXPORT void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, tlab_t *tlab); -DLLEXPORT void __nvc_do_fficall(jit_foreign_t *ff, jit_anchor_t *anchor, - jit_scalar_t *args); DLLEXPORT void *__nvc_mspace_alloc(uintptr_t size, jit_anchor_t *anchor); DLLEXPORT void _debug_out(intptr_t val, int32_t reg); diff --git a/src/jit/jit-x86.c b/src/jit/jit-x86.c index f149fe6d..e9717c2b 100644 --- a/src/jit/jit-x86.c +++ b/src/jit/jit-x86.c @@ -1535,13 +1535,6 @@ static void jit_x86_macro_putpriv(code_blob_t *blob, jit_ir_t *ir) MOV(ADDR(__EAX, 0), __ECX, __QWORD); } -static void jit_x86_macro_fficall(code_blob_t *blob, jit_x86_state_t *state, - jit_ir_t *ir) -{ - MOV(__EAX, PTR(ir->arg1.foreign), __QWORD); - CALL(PTR(state->stubs[FFI_STUB])); -} - static void jit_x86_macro_case(code_blob_t *blob, jit_ir_t *ir) { jit_x86_get(blob, __EAX, ir->arg1); @@ -1740,9 +1733,6 @@ static void jit_x86_op(code_blob_t *blob, jit_x86_state_t *state, jit_ir_t *ir) case MACRO_PUTPRIV: jit_x86_macro_putpriv(blob, ir); break; - case MACRO_FFICALL: - jit_x86_macro_fficall(blob, state, ir); - break; case MACRO_CASE: jit_x86_macro_case(blob, ir); break; @@ -1992,31 +1982,6 @@ static void jit_x86_gen_tlab_stub(jit_x86_state_t *state) code_blob_finalise(blob, &(state->stubs[TLAB_STUB])); } -static void jit_x86_gen_ffi_stub(jit_x86_state_t *state) -{ - ident_t name = ident_new("ffi stub"); - code_blob_t *blob = code_blob_new(state->code, name, 0); - - PUSH(__EBP); - MOV(__EBP, __ESP, __QWORD); - - jit_x86_push_call_clobbered(blob); - - MOV(CARG0_REG, __EAX, __QWORD); - MOV(CARG1_REG, ANCHOR_REG, __QWORD); - MOV(CARG2_REG, ARGS_REG, __QWORD); - - MOV(__EAX, PTR(__nvc_do_fficall), __QWORD); - CALL(__EAX); - - jit_x86_pop_call_clobbered(blob); - - LEAVE(); - RET(); - - code_blob_finalise(blob, &(state->stubs[FFI_STUB])); -} - #if DEBUG static void jit_x86_gen_debug_stub(jit_x86_state_t *state) { @@ -2074,7 +2039,6 @@ static void *jit_x86_init(jit_t *jit) jit_x86_gen_call_stub(state); jit_x86_gen_alloc_stub(state); jit_x86_gen_tlab_stub(state); - jit_x86_gen_ffi_stub(state); jit_x86_gen_fexp_stub(state); DEBUG_ONLY(jit_x86_gen_debug_stub(state)); diff --git a/src/lower.c b/src/lower.c index 5aa0a4b1..3d998440 100644 --- a/src/lower.c +++ b/src/lower.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2023 Nick Gasson +// Copyright (C) 2014-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -155,6 +155,8 @@ static vcode_reg_t lower_conversion(lower_unit_t *lu, vcode_reg_t value_reg, tree_t where, type_t from, type_t to); static vcode_reg_t lower_get_type_bounds(lower_unit_t *lu, type_t type); static vcode_reg_t lower_attr_prefix(lower_unit_t *lu, tree_t prefix); +static void lower_subprogram_ports(lower_unit_t *lu, tree_t body, + bool params_as_vars); typedef vcode_reg_t (*lower_signal_flag_fn_t)(vcode_reg_t, vcode_reg_t); typedef vcode_reg_t (*arith_fn_t)(vcode_reg_t, vcode_reg_t); @@ -1949,7 +1951,7 @@ static bool lower_side_effect_free(tree_t expr) return false; else if (kind == S_CONCAT) return false; // Allocates memory - else if (!is_builtin(kind)) + else if (kind == S_USER || !is_open_coded_builtin(kind)) return false; const int nparams = tree_params(expr); @@ -2489,21 +2491,6 @@ static vcode_type_t lower_param_type(type_t type, class_t class, } } -static vcode_cc_t lower_cc_for_call(tree_t call) -{ - tree_t decl = tree_ref(call); - const subprogram_kind_t skind = tree_subkind(decl); - - if (skind == S_FOREIGN) - return VCODE_CC_FOREIGN; - else if (skind == S_INTERNAL) - return VCODE_CC_INTERNAL; - else if (is_builtin(skind)) - return VCODE_CC_PREDEF; - else - return VCODE_CC_VHDL; -} - static vcode_reg_t lower_context_for_call(lower_unit_t *lu, ident_t unit_name) { if (lu->registry != NULL && unit_registry_query(lu->registry, unit_name)) { @@ -2569,29 +2556,16 @@ static vcode_reg_t lower_fcall(lower_unit_t *lu, tree_t fcall, const int nparams = tree_params(fcall); SCOPED_A(vcode_reg_t) args = AINIT; - const vcode_cc_t cc = lower_cc_for_call(fcall); ident_t name = tree_ident2(decl); if (tree_kind(fcall) == T_PROT_FCALL && tree_has_name(fcall)) APUSH(args, lower_rvalue(lu, tree_name(fcall))); - else if (cc != VCODE_CC_FOREIGN && cc != VCODE_CC_INTERNAL) + else APUSH(args, lower_context_for_call(lu, name)); for (int i = 0; i < nparams; i++) { vcode_reg_t arg_reg = lower_subprogram_arg(lu, fcall, i); - if (kind == S_FOREIGN && vcode_reg_kind(arg_reg) == VCODE_TYPE_UARRAY) { - // Do not pass wrapped arrays into foreign functions - APUSH(args, emit_unwrap(arg_reg)); - - vcode_type_t vint64 = vtype_int(INT64_MIN, INT64_MAX); - const int ndims = vtype_dims(vcode_reg_type(arg_reg)); - for (int i = 0; i < ndims; i++) { - vcode_reg_t len_reg = emit_uarray_len(arg_reg, 0); - APUSH(args, emit_cast(vint64, vint64, len_reg)); - } - } - else - APUSH(args, arg_reg); + APUSH(args, arg_reg); } if (bounds_reg != VCODE_INVALID_REG) @@ -2604,7 +2578,7 @@ static vcode_reg_t lower_fcall(lower_unit_t *lu, tree_t fcall, if (cover_enabled(lu->cover, COVER_MASK_EXPRESSION)) lower_logic_expr_coverage(lu, fcall, decl, args.items); - return emit_fcall(name, rtype, rbounds, cc, args.items, args.count); + return emit_fcall(name, rtype, rbounds, args.items, args.count); } static vcode_reg_t lower_known_subtype(lower_unit_t *lu, tree_t value, @@ -3260,7 +3234,7 @@ static vcode_reg_t lower_resolved(lower_unit_t *lu, type_t type, vcode_type_t vrtype = lower_func_result_type(type); vcode_reg_t args[] = { lower_context_for_call(lu, helper_func), arg_reg }; - return emit_fcall(helper_func, vrtype, vrtype, VCODE_CC_VHDL, args, 2); + return emit_fcall(helper_func, vrtype, vrtype, args, 2); } } @@ -4548,7 +4522,7 @@ static void lower_new_record(lower_unit_t *lu, type_t type, src_ptr, }; emit_fcall(helper_func, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, args, ARRAY_LEN(args)); + args, ARRAY_LEN(args)); } } @@ -5116,7 +5090,7 @@ static vcode_reg_t lower_attr_ref(lower_unit_t *lu, tree_t expr) lower_context_for_call(lu, func), arg_reg, }; - return emit_fcall(func, strtype, strtype, VCODE_CC_PREDEF, args, 2); + return emit_fcall(func, strtype, strtype, args, 2); } case ATTR_VALUE: @@ -5140,8 +5114,7 @@ static vcode_reg_t lower_attr_ref(lower_unit_t *lu, tree_t expr) value_reg }; vcode_reg_t reg = emit_fcall(func, lower_type(base), - lower_bounds(base), - VCODE_CC_PREDEF, args, 2); + lower_bounds(base), args, 2); if (type_is_scalar(name_type)) lower_check_scalar_bounds(lu, reg, name_type, expr, NULL); @@ -5738,9 +5711,11 @@ static void lower_clear_event(lower_unit_t *lu, tree_t on) static vcode_reg_t lower_call_now(void) { - ident_t func = ident_new("_std_standard_now"); + ident_t func = ident_new("STD.STANDARD.NOW()25STD.STANDARD.DELAY_LENGTH"); vcode_type_t rtype = vtype_time(); - return emit_fcall(func, rtype, rtype, VCODE_CC_INTERNAL, NULL, 0); + vcode_reg_t context_reg = emit_link_package(well_known(W_STD_STANDARD)); + vcode_reg_t args[1] = { context_reg }; + return emit_fcall(func, rtype, rtype, args, ARRAY_LEN(args)); } static void lower_wait(lower_unit_t *lu, tree_t wait) @@ -5988,7 +5963,7 @@ static void lower_copy_record(lower_unit_t *lu, type_t type, locus }; emit_fcall(helper_func, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, args, ARRAY_LEN(args)); + args, ARRAY_LEN(args)); } } @@ -6022,7 +5997,7 @@ static void lower_copy_array(lower_unit_t *lu, type_t dst_type, type_t src_type, locus }; emit_fcall(helper_func, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, args, ARRAY_LEN(args)); + args, ARRAY_LEN(args)); } } @@ -6639,12 +6614,11 @@ static void lower_pcall(lower_unit_t *lu, tree_t pcall) const int nparams = tree_params(pcall); SCOPED_A(vcode_reg_t) args = AINIT; - const vcode_cc_t cc = lower_cc_for_call(pcall); ident_t name = tree_ident2(decl); if (tree_kind(pcall) == T_PROT_PCALL && tree_has_name(pcall)) APUSH(args, lower_rvalue(lu, tree_name(pcall))); - else if (cc != VCODE_CC_FOREIGN && cc != VCODE_CC_INTERNAL) + else APUSH(args, lower_context_for_call(lu, name)); const int arg0 = args.count; @@ -6653,24 +6627,12 @@ static void lower_pcall(lower_unit_t *lu, tree_t pcall) if (!use_fcall) vcode_heap_allocate(arg_reg); - if (kind == S_FOREIGN && vcode_reg_kind(arg_reg) == VCODE_TYPE_UARRAY) { - // Do not pass wrapped arrays into foreign functions - APUSH(args, emit_unwrap(arg_reg)); - - vcode_type_t vint64 = vtype_int(INT64_MIN, INT64_MAX); - const int ndims = vtype_dims(vcode_reg_type(arg_reg)); - for (int i = 0; i < ndims; i++) { - vcode_reg_t len_reg = emit_uarray_len(arg_reg, 0); - APUSH(args, emit_cast(vint64, vint64, len_reg)); - } - } - else - APUSH(args, arg_reg); + APUSH(args, arg_reg); } if (use_fcall) emit_fcall(name, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - cc, args.items, args.count); + args.items, args.count); else { vcode_block_t resume_bb = emit_block(); @@ -7249,8 +7211,7 @@ static void lower_case_array(lower_unit_t *lu, tree_t stmt, loop_stack_t *loops) vcode_reg_t context_reg = lower_context_for_call(lu, cmp_func); vcode_reg_t args[] = { context_reg, name_reg, val_reg }; - vcode_reg_t eq_reg = emit_fcall(cmp_func, vbool, vbool, - VCODE_CC_PREDEF, args, 3); + vcode_reg_t eq_reg = emit_fcall(cmp_func, vbool, vbool, args, 3); emit_cond(eq_reg, hit_bb, chain_bb); } @@ -7338,7 +7299,7 @@ static void lower_match_case(lower_unit_t *lu, tree_t stmt, loop_stack_t *loops) vcode_reg_t args[] = { context_reg, value_reg }; emit_fcall(func, VCODE_INVALID_REG, VCODE_INVALID_REG, - VCODE_CC_VHDL, args, ARRAY_LEN(args)); + args, ARRAY_LEN(args)); } ident_t func = ident_new(is_array ? "IEEE.STD_LOGIC_1164.\"?=\"(YY)U$predef" @@ -7427,8 +7388,7 @@ static void lower_match_case(lower_unit_t *lu, tree_t stmt, loop_stack_t *loops) test_reg, }; vcode_reg_t result_reg = emit_fcall(func, vscalar, vscalar, - VCODE_CC_VHDL, args, - ARRAY_LEN(args)); + args, ARRAY_LEN(args)); vcode_reg_t cmp_reg = emit_cmp(VCODE_CMP_EQ, result_reg, true_reg); if (loop_bb != VCODE_INVALID_BLOCK) @@ -8396,8 +8356,7 @@ static void lower_physical_image_helper(lower_unit_t *lu, type_t type, lower_context_for_call(lu, conv_fn), cast_reg }; - vcode_reg_t num_reg = emit_fcall(conv_fn, vstring, vstring, - VCODE_CC_VHDL, conv_args, 2); + vcode_reg_t num_reg = emit_fcall(conv_fn, vstring, vstring, conv_args, 2); vcode_reg_t num_len = emit_uarray_len(num_reg, 0); @@ -8452,8 +8411,7 @@ static void lower_numeric_image_helper(lower_unit_t *lu, type_t type, lower_context_for_call(lu, conv_fn), arg_reg }; - vcode_reg_t str_reg = emit_fcall(conv_fn, vstring, vstring, - VCODE_CC_VHDL, conv_args, 2); + vcode_reg_t str_reg = emit_fcall(conv_fn, vstring, vstring, conv_args, 2); emit_return(str_reg); } @@ -8481,7 +8439,7 @@ static void lower_record_image_helper(lower_unit_t *lu, type_t type, field_reg = emit_load_indirect(field_reg); vcode_reg_t args[] = { context_reg, field_reg }; - regs[i] = emit_fcall(func, strtype, strtype, VCODE_CC_PREDEF, args, 2); + regs[i] = emit_fcall(func, strtype, strtype, args, 2); lengths[i] = emit_uarray_len(regs[i], 0); sum_reg = emit_add(sum_reg, lengths[i]); @@ -8562,8 +8520,7 @@ static void lower_array_image_helper(lower_unit_t *lu, type_t type, elem_reg = emit_load_indirect(elem_reg); vcode_reg_t args[] = { context_reg, elem_reg }; - vcode_reg_t str_reg = emit_fcall(func, strtype, strtype, - VCODE_CC_PREDEF, args, 2); + vcode_reg_t str_reg = emit_fcall(func, strtype, strtype, args, 2); vcode_reg_t edata_reg = emit_unwrap(str_reg); vcode_reg_t elen_reg = emit_uarray_len(str_reg, 0); @@ -8782,7 +8739,7 @@ static void lower_enum_value_helper(lower_unit_t *lu, type_t type, lower_context_for_call(lu, canon_fn), preg, }; - vcode_reg_t canon_reg = emit_fcall(canon_fn, vstring, vchar, VCODE_CC_VHDL, + vcode_reg_t canon_reg = emit_fcall(canon_fn, vstring, vchar, canon_args, ARRAY_LEN(canon_args)); vcode_reg_t canon_len_reg = emit_uarray_len(canon_reg, 0); @@ -8852,10 +8809,10 @@ static void lower_enum_value_helper(lower_unit_t *lu, type_t type, type_t std_string = std_type(NULL, STD_STRING); ident_t func = lower_predef_func_name(std_string, "="); + vcode_type_t vbool = vtype_bool(); vcode_reg_t context_reg = lower_context_for_call(lu, func); vcode_reg_t str_cmp_args[] = { context_reg, str_reg, canon_reg }; - vcode_reg_t eq_reg = emit_fcall(func, vtype_bool(), vtype_bool(), - VCODE_CC_PREDEF, str_cmp_args, 3); + vcode_reg_t eq_reg = emit_fcall(func, vbool, vbool, str_cmp_args, 3); emit_cond(eq_reg, match_bb, skip_bb); vcode_select_block(skip_bb); @@ -8931,7 +8888,7 @@ static void lower_physical_value_helper(lower_unit_t *lu, type_t type, used_ptr, }; emit_fcall(conv_fn, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, conv_args, ARRAY_LEN(conv_args)); + conv_args, ARRAY_LEN(conv_args)); vcode_reg_t int_reg = emit_load(int_var); vcode_reg_t used_reg = emit_cast(voffset, voffset, emit_load(used_var)); @@ -8955,7 +8912,7 @@ static void lower_physical_value_helper(lower_unit_t *lu, type_t type, text_util_reg, tail_reg, }; - vcode_reg_t canon_reg = emit_fcall(canon_fn, vstring, vchar, VCODE_CC_VHDL, + vcode_reg_t canon_reg = emit_fcall(canon_fn, vstring, vchar, canon_args, ARRAY_LEN(canon_args)); vcode_reg_t canon_len_reg = emit_uarray_len(canon_reg, 0); @@ -9039,9 +8996,9 @@ static void lower_physical_value_helper(lower_unit_t *lu, type_t type, ident_t func = lower_predef_func_name(std_string, "="); vcode_reg_t std_reg = emit_link_package(well_known(W_STD_STANDARD)); + vcode_type_t vbool = vtype_bool(); vcode_reg_t str_cmp_args[] = { std_reg, str_reg, canon_reg }; - vcode_reg_t eq_reg = emit_fcall(func, vtype_bool(), vtype_bool(), - VCODE_CC_PREDEF, str_cmp_args, 3); + vcode_reg_t eq_reg = emit_fcall(func, vbool, vbool, str_cmp_args, 3); emit_cond(eq_reg, match_bb, skip_bb); vcode_select_block(skip_bb); @@ -9102,7 +9059,7 @@ static void lower_numeric_value_helper(lower_unit_t *lu, type_t type, vcode_type_t vreal = vtype_real(-DBL_MAX, DBL_MAX); - result_reg = emit_fcall(conv_fn, vreal, vreal, VCODE_CC_VHDL, + result_reg = emit_fcall(conv_fn, vreal, vreal, conv_args, ARRAY_LEN(conv_args)); } else { @@ -9114,7 +9071,7 @@ static void lower_numeric_value_helper(lower_unit_t *lu, type_t type, vcode_type_t vint64 = vtype_int(INT64_MIN, INT64_MAX); - result_reg = emit_fcall(conv_fn, vint64, vint64, VCODE_CC_VHDL, + result_reg = emit_fcall(conv_fn, vint64, vint64, conv_args, ARRAY_LEN(conv_args)); } @@ -9145,8 +9102,7 @@ static void lower_record_value_helper(lower_unit_t *lu, type_t type, vcode_reg_t text_util_reg = lower_context_for_call(lu, next_delim_fn); vcode_reg_t open_args[] = { text_util_reg, preg }; - vcode_reg_t open_reg = emit_fcall(find_open_fn, vnat, vnat, - VCODE_CC_VHDL, open_args, 2); + vcode_reg_t open_reg = emit_fcall(find_open_fn, vnat, vnat, open_args, 2); vcode_reg_t off_reg = emit_cast(voffset, voffset, open_reg); vcode_reg_t ptr_reg = emit_index(result_var, VCODE_INVALID_REG); @@ -9155,7 +9111,7 @@ static void lower_record_value_helper(lower_unit_t *lu, type_t type, for (int i = 0; i < nfields; i++) { vcode_reg_t nd_args[] = { text_util_reg, preg, off_reg }; vcode_reg_t nd_reg = emit_fcall(next_delim_fn, strtype, ctype, - VCODE_CC_VHDL, nd_args, 3); + nd_args, 3); type_t ftype = type_base_recur(tree_type(type_field(type, i))); @@ -9168,8 +9124,7 @@ static void lower_record_value_helper(lower_unit_t *lu, type_t type, vcode_type_t fvtype = lower_func_result_type(ftype); vcode_type_t fvbounds = lower_bounds(ftype); - vcode_reg_t value_reg = emit_fcall(func, fvtype, fvbounds, - VCODE_CC_PREDEF, args, 2); + vcode_reg_t value_reg = emit_fcall(func, fvtype, fvbounds, args, 2); vcode_reg_t vlen_reg = emit_uarray_len(nd_reg, 0); off_reg = emit_add(off_reg, vlen_reg); @@ -9189,7 +9144,7 @@ static void lower_record_value_helper(lower_unit_t *lu, type_t type, vcode_reg_t close_args[] = { text_util_reg, preg, off_reg }; emit_fcall(find_close_fn, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, close_args, ARRAY_LEN(close_args)); + close_args, ARRAY_LEN(close_args)); emit_return(ptr_reg); } @@ -9237,7 +9192,7 @@ static void lower_array_value_helper(lower_unit_t *lu, type_t type, vcode_reg_t trim_args[] = { text_util_reg, preg, first_ptr, last_ptr }; emit_fcall(trim_ws_fn, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, trim_args, ARRAY_LEN(trim_args)); + trim_args, ARRAY_LEN(trim_args)); vcode_reg_t first_reg = emit_cast(voffset, voffset, emit_load(first_var)); vcode_reg_t last_reg = emit_cast(voffset, voffset, emit_load(last_var)); @@ -9300,7 +9255,7 @@ static void lower_array_value_helper(lower_unit_t *lu, type_t type, vcode_reg_t bad_char_args[] = { text_util_reg, preg, char_reg }; emit_fcall(bad_char_fn, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, bad_char_args, ARRAY_LEN(bad_char_args)); + bad_char_args, ARRAY_LEN(bad_char_args)); emit_unreachable(locus); @@ -9326,14 +9281,13 @@ static void lower_array_value_helper(lower_unit_t *lu, type_t type, vcode_reg_t count_args[] = { text_util_reg, preg }; vcode_reg_t count_result_reg = emit_fcall(count_delim_fn, vnat, vnat, - VCODE_CC_VHDL, count_args, 2); + count_args, 2); vcode_reg_t count_reg = emit_cast(voffset, voffset, count_result_reg); vcode_reg_t mem_reg = emit_alloc(velem, vbounds, count_reg); vcode_reg_t open_args[] = { text_util_reg, preg }; - vcode_reg_t open_reg = emit_fcall(find_open_fn, vnat, vnat, - VCODE_CC_VHDL, open_args, 2); + vcode_reg_t open_reg = emit_fcall(find_open_fn, vnat, vnat, open_args, 2); vcode_var_t pos_var = lower_temp_var(lu, "pos", voffset, voffset); emit_store(emit_cast(voffset, voffset, open_reg), pos_var); @@ -9347,8 +9301,7 @@ static void lower_array_value_helper(lower_unit_t *lu, type_t type, vcode_reg_t pos_reg = emit_load(pos_var); vcode_reg_t nd_args[] = { text_util_reg, preg, pos_reg }; - vcode_reg_t nd_reg = emit_fcall(next_delim_fn, strtype, ctype, - VCODE_CC_VHDL, nd_args, 3); + vcode_reg_t nd_reg = emit_fcall(next_delim_fn, strtype, ctype, nd_args, 3); ident_t func = ident_prefix(type_ident(elem), ident_new("value"), '$'); vcode_reg_t args[] = { @@ -9357,7 +9310,7 @@ static void lower_array_value_helper(lower_unit_t *lu, type_t type, }; vcode_reg_t value_reg = emit_fcall(func, lower_func_result_type(elem), - vbounds, VCODE_CC_PREDEF, args, 2); + vbounds, args, 2); vcode_reg_t ptr_reg = emit_array_ref(mem_reg, i_reg); if (type_is_record(elem)) @@ -9379,7 +9332,7 @@ static void lower_array_value_helper(lower_unit_t *lu, type_t type, vcode_reg_t close_args[] = { text_util_reg, preg, emit_load(pos_var) }; emit_fcall(find_close_fn, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, - VCODE_CC_VHDL, close_args, ARRAY_LEN(close_args)); + close_args, ARRAY_LEN(close_args)); vcode_dim_t dims[] = { { .left = emit_const(vtype_offset(), 1), @@ -9680,6 +9633,35 @@ static void lower_type_bounds_var(lower_unit_t *lu, type_t type) lower_put_vcode_obj(type, var, lu); } +static void lower_foreign_stub(lower_unit_t *lu, object_t *obj) +{ + tree_t spec = tree_from_object(obj); + assert(tree_kind(spec) == T_ATTR_SPEC); + + tree_t value = tree_value(spec); + type_t str_type = tree_type(value); + + tree_t sub = tree_ref(spec); + assert(is_subprogram(sub)); + + type_t type = tree_type(sub); + if (type_kind(type) == T_FUNC) + vcode_set_result(lower_func_result_type(type_result(type))); + + vcode_type_t vcontext = vtype_context(lu->parent->name); + emit_param(vcontext, vcontext, ident_new("context")); + + lower_subprogram_ports(lu, sub, false); + + vcode_reg_t spec_reg = lower_rvalue(lu, value); + vcode_reg_t data_reg = lower_array_data(spec_reg); + vcode_reg_t length_reg = lower_array_len(lu, str_type, 0, spec_reg); + vcode_reg_t locus = lower_debug_locus(spec); + + emit_bind_foreign(data_reg, length_reg, locus); + emit_unreachable(VCODE_INVALID_REG); +} + static void lower_decl(lower_unit_t *lu, tree_t decl) { PUSH_DEBUG_INFO(decl); @@ -9755,7 +9737,6 @@ static void lower_decl(lower_unit_t *lu, tree_t decl) case T_FUNC_DECL: case T_PROC_DECL: - case T_ATTR_SPEC: case T_ATTR_DECL: case T_COMPONENT: case T_USE: @@ -9765,6 +9746,7 @@ static void lower_decl(lower_unit_t *lu, tree_t decl) case T_VIEW_DECL: case T_PROT_DECL: case T_HIER: + case T_ATTR_SPEC: break; case T_PACKAGE: @@ -9836,15 +9818,32 @@ static void lower_protected_body(lower_unit_t *lu, object_t *obj) static void lower_decls(lower_unit_t *lu, tree_t scope) { + bool has_foreign = false; const int ndecls = tree_decls(scope); + for (int i = 0; i < ndecls; i++) { + tree_t d = tree_decl(scope, i); + if (tree_kind(d) != T_ATTR_SPEC) + continue; + else if (is_well_known(tree_ident(d)) == W_FOREIGN) { + unit_registry_defer(lu->registry, tree_ident2(tree_ref(d)), lu, + emit_function, lower_foreign_stub, NULL, + tree_to_object(d)); + has_foreign = true; + } + } + for (int i = 0; i < ndecls; i++) { tree_t d = tree_decl(scope, i); switch (tree_kind(d)) { case T_FUNC_INST: case T_FUNC_BODY: - unit_registry_defer(lu->registry, tree_ident2(d), - lu, emit_function, lower_func_body, - lu->cover, tree_to_object(d)); + { + ident_t mangled = tree_ident2(d); + if (!has_foreign || !unit_registry_query(lu->registry, mangled)) + unit_registry_defer(lu->registry, mangled, lu, + emit_function, lower_func_body, + lu->cover, tree_to_object(d)); + } break; case T_PROC_INST: case T_PROC_BODY: @@ -9852,10 +9851,12 @@ static void lower_decls(lower_unit_t *lu, tree_t scope) const bool never_waits = !!(tree_flags(d) & TREE_F_NEVER_WAITS) || (tree_flags(d) & TREE_F_PROTECTED); emit_fn_t emitfn = never_waits ? emit_function : emit_procedure; + ident_t mangled = tree_ident2(d); - unit_registry_defer(lu->registry, tree_ident2(d), - lu, emitfn, lower_proc_body, lu->cover, - tree_to_object(d)); + if (!has_foreign || !unit_registry_query(lu->registry, mangled)) + unit_registry_defer(lu->registry, tree_ident2(d), + lu, emitfn, lower_proc_body, lu->cover, + tree_to_object(d)); } break; case T_PROT_BODY: @@ -9867,7 +9868,7 @@ static void lower_decls(lower_unit_t *lu, tree_t scope) case T_PROC_DECL: { const subprogram_kind_t kind = tree_subkind(d); - if (!is_builtin(kind) || is_open_coded_builtin(kind)) + if (kind == S_USER || is_open_coded_builtin(kind)) continue; unit_registry_defer(lu->registry, tree_ident2(d), @@ -10175,8 +10176,7 @@ static void lower_predef_to_string(lower_unit_t *lu, tree_t decl) vcode_type_t ctype = vtype_char(); vcode_type_t rtype = vtype_uarray(1, ctype, ctype); vcode_reg_t args[] = { context_reg, r0 }; - vcode_reg_t str_reg = - emit_fcall(func, rtype, ctype, VCODE_CC_PREDEF, args, 2); + vcode_reg_t str_reg = emit_fcall(func, rtype, ctype, args, 2); if (type_is_enum(arg_type)) { // If the result is a character literal return just the character @@ -10701,7 +10701,7 @@ static void lower_predef_match_op(lower_unit_t *lu, tree_t decl, ident_t func = ident_new("NVC.IEEE_SUPPORT.REL_MATCH_EQ(UU)U"); vcode_reg_t context_reg = lower_context_for_call(lu, func); vcode_reg_t args[] = { context_reg, r0_src_reg, r1_src_reg }; - tmp = emit_fcall(func, vtype, vbounds, VCODE_CC_PREDEF, args, 3); + tmp = emit_fcall(func, vtype, vbounds, args, 3); } emit_store_indirect(tmp, emit_array_ref(mem_reg, i_reg)); @@ -10726,7 +10726,7 @@ static void lower_predef_match_op(lower_unit_t *lu, tree_t decl, : ident_new("IEEE.STD_LOGIC_1164.\"and\"(Y)U"); vcode_reg_t context_reg = lower_context_for_call(lu, func); vcode_reg_t args[] = { context_reg, wrap_reg }; - result = emit_fcall(func, vtype, vbounds, VCODE_CC_PREDEF, args, 2); + result = emit_fcall(func, vtype, vbounds, args, 2); } else if (is_bit) result = emit_cmp(cmp, r0, r1); @@ -10750,7 +10750,7 @@ static void lower_predef_match_op(lower_unit_t *lu, tree_t decl, vcode_reg_t args[3] = { context_reg, r0, r1 }; vcode_type_t rtype = lower_type(r0_type); - result = emit_fcall(func, rtype, rtype, VCODE_CC_PREDEF, args, 3); + result = emit_fcall(func, rtype, rtype, args, 3); } if (invert && is_bit) @@ -10761,7 +10761,7 @@ static void lower_predef_match_op(lower_unit_t *lu, tree_t decl, vcode_reg_t context_reg = lower_context_for_call(lu, func); vcode_reg_t args[2] = { context_reg, result }; vcode_type_t rtype = vcode_reg_type(result); - emit_return(emit_fcall(func, rtype, rtype, VCODE_CC_PREDEF, args, 2)); + emit_return(emit_fcall(func, rtype, rtype, args, 2)); } else emit_return(result); @@ -10836,7 +10836,7 @@ static void lower_predef_min_max(lower_unit_t *lu, tree_t decl, vcode_cmp_t cmp) ident_t func = lower_predef_func_name(type, op); vcode_reg_t args[] = { context_reg, r0, r1 }; vcode_type_t vbool = vtype_bool(); - test_reg = emit_fcall(func, vbool, vbool, VCODE_CC_PREDEF, args, 3); + test_reg = emit_fcall(func, vbool, vbool, args, 3); } emit_return(emit_select(test_reg, r0, r1)); @@ -10849,40 +10849,33 @@ static void lower_predef_negate(tree_t decl, const char *op) vcode_type_t vbool = vtype_bool(); vcode_reg_t args[] = { 0, 1, 2 }; vcode_reg_t eq_reg = emit_fcall(lower_predef_func_name(type, op), - vbool, vbool, VCODE_CC_PREDEF, args, 3); + vbool, vbool, args, 3); emit_return(emit_not(eq_reg)); } -static void lower_predef_file_op(lower_unit_t *lu, tree_t decl, const char *fn) +static void lower_foreign_predef(lower_unit_t *lu, tree_t decl, const char *fn) { - ident_t func = ident_new(fn); - - vcode_type_t rtype = vcode_unit_result(lu->vunit); - vcode_var_t result_var = VCODE_INVALID_VAR; - if (rtype != VCODE_INVALID_TYPE) - result_var = emit_var(rtype, rtype, ident_new("result"), 0); - - vcode_reg_t args[4]; - const int nparams = vcode_count_params(); - - int nargs = 0; - for (int i = 1; i < nparams; i++) - args[nargs++] = vcode_param_reg(i); - - if (result_var != VCODE_INVALID_VAR) - args[nargs++] = emit_index(result_var, VCODE_INVALID_REG); - - assert(nargs < ARRAY_LEN(args)); + static const char prefix[] = "INTERNAL "; + const size_t fnlen = strlen(fn); + const size_t nchars = fnlen + sizeof(prefix) - 1; + vcode_reg_t *chars LOCAL = xmalloc_array(nchars, sizeof(vcode_reg_t)); + vcode_type_t ctype = vtype_char(); + vcode_type_t stype = vtype_carray(nchars, ctype, ctype); - emit_fcall(func, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, VCODE_CC_INTERNAL, - args, nargs); + int pos = 0; + for (int i = 0; i < sizeof(prefix) - 1; i++) + chars[pos++] = emit_const(ctype, prefix[i]); + for (int i = 0; i < fnlen; i++) + chars[pos++] = emit_const(ctype, fn[i]); + assert(pos == nchars); - vcode_reg_t result_reg = VCODE_INVALID_REG; - if (result_var != VCODE_INVALID_VAR) - result_reg = emit_load(result_var); + vcode_reg_t array_reg = emit_const_array(stype, chars, nchars); + vcode_reg_t data_reg = emit_address_of(array_reg); + vcode_reg_t length_reg = emit_const(vtype_offset(), nchars); - emit_return(result_reg); + emit_bind_foreign(data_reg, length_reg, VCODE_INVALID_REG); + emit_unreachable(VCODE_INVALID_REG); } static void lower_predef_file_open3(lower_unit_t *lu, tree_t decl) @@ -10903,7 +10896,7 @@ static void lower_predef(lower_unit_t *lu, object_t *obj) tree_t decl = tree_from_object(obj); const subprogram_kind_t kind = tree_subkind(decl); - assert(is_builtin(kind)); + assert(kind != S_USER); assert(!is_open_coded_builtin(kind)); type_t type = tree_type(decl); @@ -10941,6 +10934,21 @@ static void lower_predef(lower_unit_t *lu, object_t *obj) case S_TO_STRING: lower_predef_to_string(lu, decl); break; + case S_TO_STRING_TIME: + lower_foreign_predef(lu, decl, "_std_to_string_time"); + break; + case S_TO_STRING_REAL_DIGITS: + lower_foreign_predef(lu, decl, "_std_to_string_real_digits"); + break; + case S_TO_STRING_REAL_FORMAT: + lower_foreign_predef(lu, decl, "_std_to_string_real_format"); + break; + case S_TO_HSTRING_BITVEC: + lower_foreign_predef(lu, decl, "_std_to_hstring_bit_vec"); + break; + case S_TO_OSTRING_BITVEC: + lower_foreign_predef(lu, decl, "_std_to_ostring_bit_vec"); + break; case S_SLL: case S_SRL: case S_SLA: @@ -10989,34 +10997,34 @@ static void lower_predef(lower_unit_t *lu, object_t *obj) lower_predef_min_max(lu, decl, VCODE_CMP_LT); break; case S_FILE_FLUSH: - lower_predef_file_op(lu, decl, "__nvc_flush"); + lower_foreign_predef(lu, decl, "__nvc_flush"); break; case S_FILE_OPEN3: lower_predef_file_open3(lu, decl); break; case S_FILE_MODE: - lower_predef_file_op(lu, decl, "__nvc_file_mode"); + lower_foreign_predef(lu, decl, "__nvc_file_mode"); break; case S_FILE_CANSEEK: - lower_predef_file_op(lu, decl, "__nvc_file_canseek"); + lower_foreign_predef(lu, decl, "__nvc_file_canseek"); break; case S_FILE_SIZE: - lower_predef_file_op(lu, decl, "__nvc_file_size"); + lower_foreign_predef(lu, decl, "__nvc_file_size"); break; case S_FILE_REWIND: - lower_predef_file_op(lu, decl, "__nvc_rewind"); + lower_foreign_predef(lu, decl, "__nvc_rewind"); break; case S_FILE_SEEK: - lower_predef_file_op(lu, decl, "__nvc_seek"); + lower_foreign_predef(lu, decl, "__nvc_seek"); break; case S_FILE_TRUNCATE: - lower_predef_file_op(lu, decl, "__nvc_truncate"); + lower_foreign_predef(lu, decl, "__nvc_truncate"); break; case S_FILE_STATE: - lower_predef_file_op(lu, decl, "__nvc_file_state"); + lower_foreign_predef(lu, decl, "__nvc_file_state"); break; case S_FILE_POSITION: - lower_predef_file_op(lu, decl, "__nvc_file_position"); + lower_foreign_predef(lu, decl, "__nvc_file_position"); break; default: fatal_trace("cannot lower predefined function %s", type_pp(type)); @@ -11394,9 +11402,8 @@ static ident_t lower_converter(lower_unit_t *parent, tree_t expr, ident_t func = tree_ident2(fdecl); vcode_reg_t context_reg = lower_context_for_call(lu, func); vcode_reg_t args[] = { context_reg, arg_reg }; - vcode_reg_t result_reg = emit_fcall(func, lower_type(rtype), vrbounds, - VCODE_CC_VHDL, args, 2); - + vcode_reg_t result_reg = emit_fcall(func, lower_type(rtype), + vrbounds, args, 2); if (r_uarray) { vcode_reg_t locus = lower_debug_locus(expr); lower_check_array_sizes(lu, check_type, rtype, VCODE_INVALID_REG, @@ -12215,7 +12222,7 @@ static void lower_check_generic_constraint(lower_unit_t *lu, tree_t expect, vcode_reg_t context_reg = lower_context_for_call(lu, func); vcode_reg_t args[] = { context_reg, left_reg, right_reg }; vcode_type_t vbool = vtype_bool(); - test_reg = emit_fcall(func, vbool, vbool, VCODE_CC_PREDEF, args, 3); + test_reg = emit_fcall(func, vbool, vbool, args, 3); } vcode_type_t vseverity = vtype_int(0, SEVERITY_FAILURE - 1); @@ -12577,7 +12584,7 @@ vcode_unit_t lower_case_generate_thunk(lower_unit_t *parent, tree_t t) lower_context_for_call(lu, cmp_func); vcode_reg_t args[] = { context_reg, name_reg, value_reg }; vcode_reg_t eq_reg = emit_fcall(cmp_func, vbool, vbool, - VCODE_CC_PREDEF, args, 3); + args, 3); emit_cond(eq_reg, match_bb, skip_bb); } else { diff --git a/src/names.c b/src/names.c index cdf6527d..fa740f89 100644 --- a/src/names.c +++ b/src/names.c @@ -808,15 +808,9 @@ static symbol_t *make_visible(scope_t *s, ident_t name, tree_t decl, continue; else if ((!overload || dd->visibility != OVERLOAD) && kind == DIRECT) { if (dd->origin == s && is_forward_decl(decl, dd->tree)) { - if ((mask & N_SUBPROGRAM) && is_foreign(tree_subkind(dd->tree))) { - // Ignore redundant bodies of foreign subprograms - return sym; - } - else { - // Replace forward declaration in same region with full - // definition - dd->visibility = HIDDEN; - } + // Replace forward declaration in same region with full + // definition + dd->visibility = HIDDEN; } else if (dd->origin == s && s->formal_kind == F_SUBPROGRAM) ; // Resolving subprogram formal names @@ -845,14 +839,8 @@ static symbol_t *make_visible(scope_t *s, ident_t name, tree_t decl, // subprograms in the same region dd->visibility = HIDDEN; } - else if (is_forward_decl(decl, dd->tree)) { - if ((dd->mask & N_SUBPROGRAM) && is_foreign(tree_subkind(dd->tree))) { - // Hide bodies of subprograms declared with 'FOREIGN attribute - return sym; - } - else - dd->visibility = HIDDEN; - } + else if (is_forward_decl(decl, dd->tree)) + dd->visibility = HIDDEN; else if (overload && (mask & dd->mask & (N_SUBPROGRAM | N_OBJECT)) && kind == DIRECT && type_eq(type, tree_type(dd->tree))) { if (dd->origin != s) { diff --git a/src/object.c b/src/object.c index 7c266738..108f2b12 100644 --- a/src/object.c +++ b/src/object.c @@ -377,7 +377,7 @@ void object_one_time_init(void) // Increment this each time a incompatible change is made to // the on-disk format not expressed in the object items table - const uint32_t format_fudge = 35; + const uint32_t format_fudge = 36; format_digest += format_fudge * UINT32_C(2654435761); diff --git a/src/parse.c b/src/parse.c index 33ad4518..e50a99fb 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1443,31 +1443,31 @@ static void declare_additional_standard_operators(tree_t unit) // The following special cases are implicitly defined - tree_t d1 = builtin_fn(to_string, std_string, S_FOREIGN, + tree_t d1 = builtin_fn(to_string, std_string, S_TO_STRING_TIME, "VALUE", std_time, "UNIT", std_time, NULL); - tree_set_ident2(d1, ident_new("_std_to_string_time")); + mangle_func(nametab, d1); tree_add_decl(unit, d1); - tree_t d2 = builtin_fn(to_string, std_string, S_FOREIGN, + tree_t d2 = builtin_fn(to_string, std_string, S_TO_STRING_REAL_DIGITS, "VALUE", std_real, "DIGITS", std_natural, NULL); - tree_set_ident2(d2, ident_new("_std_to_string_real_digits")); + mangle_func(nametab, d2); tree_add_decl(unit, d2); - tree_t d3 = builtin_fn(to_string, std_string, S_FOREIGN, + tree_t d3 = builtin_fn(to_string, std_string, S_TO_STRING_REAL_FORMAT, "VALUE", std_real, "FORMAT", std_string, NULL); - tree_set_ident2(d3, ident_new("_std_to_string_real_format")); + mangle_func(nametab, d3); tree_add_decl(unit, d3); - tree_t d4 = builtin_fn(ident_new("TO_HSTRING"), std_string, S_FOREIGN, - "VALUE", std_bit_vec, NULL); - tree_set_ident2(d4, ident_new("_std_to_hstring_bit_vec")); + tree_t d4 = builtin_fn(ident_new("TO_HSTRING"), std_string, + S_TO_HSTRING_BITVEC, "VALUE", std_bit_vec, NULL); + mangle_func(nametab, d4); tree_add_decl(unit, d4); declare_alias(unit, d4, ident_new("TO_HEX_STRING")); - tree_t d5 = builtin_fn(ident_new("TO_OSTRING"), std_string, S_FOREIGN, - "VALUE", std_bit_vec, NULL); - tree_set_ident2(d5, ident_new("_std_to_ostring_bit_vec")); + tree_t d5 = builtin_fn(ident_new("TO_OSTRING"), std_string, + S_TO_OSTRING_BITVEC, "VALUE", std_bit_vec, NULL); + mangle_func(nametab, d5); tree_add_decl(unit, d5); declare_alias(unit, d5, ident_new("TO_OCTAL_STRING")); diff --git a/src/rt/assert.c b/src/rt/assert.c index 3215e443..946ff22b 100644 --- a/src/rt/assert.c +++ b/src/rt/assert.c @@ -187,9 +187,13 @@ static format_part_t *check_format(const char *str) } DLLEXPORT -void _std_env_set_assert_format_valid(uint8_t level, const uint8_t *format_ptr, - int64_t format_len, int8_t *valid) +void _std_env_set_assert_format_valid(jit_scalar_t *args) { + uint8_t level = args[2].integer; + const uint8_t *format_ptr = args[3].pointer; + int64_t format_len = ffi_array_length(args[5].integer); + int8_t *valid = args[6].pointer; + assert(level <= SEVERITY_FAILURE); char *cstr LOCAL = null_terminate(format_ptr, format_len); @@ -204,9 +208,12 @@ void _std_env_set_assert_format_valid(uint8_t level, const uint8_t *format_ptr, } DLLEXPORT -void _std_env_set_assert_format(uint8_t level, const uint8_t *format_ptr, - int64_t format_len) +void _std_env_set_assert_format(jit_scalar_t *args) { + uint8_t level = args[2].integer; + const uint8_t *format_ptr = args[3].pointer; + int64_t format_len = ffi_array_length(args[5].integer); + assert(level <= SEVERITY_FAILURE); char *cstr LOCAL = null_terminate(format_ptr, format_len); @@ -220,12 +227,13 @@ void _std_env_set_assert_format(uint8_t level, const uint8_t *format_ptr, } DLLEXPORT -void _std_env_get_assert_format(uint8_t level, ffi_uarray_t *u) +void _std_env_get_assert_format(jit_scalar_t *args, tlab_t *tlab) { + uint8_t level = args[1].integer; assert(level <= SEVERITY_FAILURE); if (format[level] == NULL) - *u = ffi_wrap(NULL, 1, 0); + ffi_return_string("", args, tlab); else { LOCAL_TEXT_BUF tb = tb_new(); for (format_part_t *p = format[level]; p; p = p->next) { @@ -257,10 +265,7 @@ void _std_env_get_assert_format(uint8_t level, ffi_uarray_t *u) } } - const size_t nchars = tb_len(tb); - char *mem = rt_tlab_alloc(nchars); - memcpy(mem, tb_get(tb), nchars); - *u = ffi_wrap(mem, 1, nchars); + ffi_return_string(tb_get(tb), args, tlab); } } diff --git a/src/rt/fileio.c b/src/rt/fileio.c index d045efa3..4c013f45 100644 --- a/src/rt/fileio.c +++ b/src/rt/fileio.c @@ -59,7 +59,7 @@ typedef enum { DLLEXPORT void __nvc_flush(jit_scalar_t *args) { - FILE **fp = args[0].pointer; + FILE **fp = args[2].pointer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FLUSH called on closed file"); @@ -70,7 +70,7 @@ void __nvc_flush(jit_scalar_t *args) DLLEXPORT void __nvc_rewind(jit_scalar_t *args) { - FILE **fp = args[0].pointer; + FILE **fp = args[2].pointer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_REWIND called on closed file"); @@ -81,9 +81,9 @@ void __nvc_rewind(jit_scalar_t *args) DLLEXPORT void __nvc_seek(jit_scalar_t *args) { - FILE **fp = args[0].pointer; - off_t offset = args[1].integer; - int8_t origin = args[2].integer; + FILE **fp = args[2].pointer; + off_t offset = args[3].integer; + int8_t origin = args[4].integer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_SEEK called on closed file"); @@ -98,9 +98,9 @@ void __nvc_seek(jit_scalar_t *args) DLLEXPORT void __nvc_truncate(jit_scalar_t *args) { - FILE **fp = args[0].pointer; - int64_t size = args[1].integer; - int8_t origin = args[2].integer; + FILE **fp = args[2].pointer; + int64_t size = args[3].integer; + int8_t origin = args[4].integer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_TRUNCATE called on closed file"); @@ -153,17 +153,15 @@ void __nvc_truncate(jit_scalar_t *args) DLLEXPORT void __nvc_file_state(jit_scalar_t *args) { - FILE **fp = args[0].pointer; - int8_t *result = args[1].pointer; + FILE **fp = args[1].pointer; - *result = (*fp == NULL ? STATE_CLOSED : STATE_OPEN); + args[0].integer = (*fp == NULL ? STATE_CLOSED : STATE_OPEN); } DLLEXPORT void __nvc_file_mode(jit_scalar_t *args) { - FILE **fp = args[0].pointer; - int8_t *result = args[1].pointer; + FILE **fp = args[1].pointer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_MODE called on closed file"); @@ -173,18 +171,17 @@ void __nvc_file_mode(jit_scalar_t *args) file_mode_t mode; if (!get_handle_mode(fileno(*fp), &mode)) { jit_msg(NULL, DIAG_WARN, "cannot determine file mode"); - *result = READ_MODE; + args[0].integer = READ_MODE; } else - *result = mode; + args[0].integer = mode; } DLLEXPORT void __nvc_file_position(jit_scalar_t *args) { - FILE **fp = args[0].pointer; - int8_t origin = args[1].integer; - int64_t *result = args[2].pointer; + FILE **fp = args[1].pointer; + int8_t origin = args[2].integer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_POSITION called on closed file"); @@ -195,18 +192,18 @@ void __nvc_file_position(jit_scalar_t *args) switch (origin) { case FILE_ORIGIN_BEGIN: - *result = off; + args[0].integer = off; break; case FILE_ORIGIN_END: { fseeko(*fp, 0, SEEK_END); off_t end = ftello(*fp); fseeko(*fp, off, SEEK_SET); - *result = end - off; + args[0].integer = end - off; } break; case FILE_ORIGIN_CURRENT: - *result = 0; + args[0].integer = 0; break; } } @@ -214,8 +211,7 @@ void __nvc_file_position(jit_scalar_t *args) DLLEXPORT void __nvc_file_size(jit_scalar_t *args) { - FILE **fp = args[0].pointer; - int64_t *result = args[1].pointer; + FILE **fp = args[1].pointer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_SIZE called on closed file"); @@ -226,19 +222,18 @@ void __nvc_file_size(jit_scalar_t *args) if (!get_handle_info(fileno(*fp), &info)) jit_msg(NULL, DIAG_FATAL, "FILE_SIZE failed: %s", strerror(errno)); - *result = info.size; + args[0].integer = info.size; } DLLEXPORT void __nvc_file_canseek(jit_scalar_t *args) { - FILE **fp = args[0].pointer; - int8_t *result = args[1].pointer; + FILE **fp = args[1].pointer; if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_CANSEEK called on closed file"); - *result = fseek(*fp, 0, SEEK_CUR) == 0; + args[0].integer = fseek(*fp, 0, SEEK_CUR) == 0; } void _file_io_init(void) diff --git a/src/rt/mspace.c b/src/rt/mspace.c index e45d68a1..399d8d19 100644 --- a/src/rt/mspace.c +++ b/src/rt/mspace.c @@ -407,16 +407,6 @@ static void mspace_mark_root(mspace_t *m, intptr_t p, gc_state_t *state) } } -static void mspace_mark_mptr(mspace_t *m, mptr_t ptr, gc_state_t *state) -{ -#ifdef DEBUG - if (unlikely(ptr->ptr != NULL && !is_mspace_ptr(m, ptr->ptr))) - fatal_trace("mptr %s points to unknown address %p", ptr->name, ptr->ptr); -#endif - - mspace_mark_root(m, (intptr_t)ptr->ptr, state); -} - static void mspace_suspend_cb(int thread_id, struct cpu_state *cpu, void *arg) { struct cpu_state *array = arg; @@ -467,7 +457,7 @@ static void mspace_gc(mspace_t *m) } for (mptr_t p = m->roots; p; p = p->next) - mspace_mark_mptr(m, p, &state); + mspace_mark_root(m, (intptr_t)p->ptr, &state); while (state.worklist.count > 0) { const uint64_t enc = APOP(state.worklist); diff --git a/src/rt/rt.h b/src/rt/rt.h index 5a0fc4b5..27c482e0 100644 --- a/src/rt/rt.h +++ b/src/rt/rt.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2011-2023 Nick Gasson +// Copyright (C) 2011-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,7 +23,7 @@ #include -#define RT_ABI_VERSION 23 +#define RT_ABI_VERSION 24 #define RT_ALIGN_MASK 0x7 #define RT_MULTITHREADED 0 diff --git a/src/rt/standard.c b/src/rt/standard.c index f0462440..2c5a00bf 100644 --- a/src/rt/standard.c +++ b/src/rt/standard.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2011-2022 Nick Gasson +// Copyright (C) 2011-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #include "jit/jit-exits.h" #include "jit/jit-ffi.h" #include "rt/model.h" +#include "rt/mspace.h" #include "rt/rt.h" #include @@ -28,19 +29,23 @@ #include #include -static ffi_uarray_t bit_vec_to_string(const uint8_t *vec, size_t vec_len, - int log_base) +static void bit_vec_to_string(jit_scalar_t *args, tlab_t *tlab, int log_base) { + const uint8_t *vec_ptr = args[1].pointer; + int64_t vec_len = ffi_array_length(args[3].integer); + const size_t result_len = (vec_len + log_base - 1) / log_base; const int left_pad = (log_base - (vec_len % log_base)) % log_base; - char *buf = rt_tlab_alloc(result_len); + char *buf = tlab_alloc(tlab, result_len); + + printf("result_len=%zu\n", result_len); for (int i = 0; i < result_len; i++) { unsigned nibble = 0; for (int j = 0; j < log_base; j++) { if (i > 0 || j >= left_pad) { nibble <<= 1; - nibble |= !!(vec[i*log_base + j - left_pad]); + nibble |= !!(vec_ptr[i*log_base + j - left_pad]); } } @@ -48,7 +53,9 @@ static ffi_uarray_t bit_vec_to_string(const uint8_t *vec, size_t vec_len, buf[i] = map[nibble]; } - return ffi_wrap(buf, 1, result_len); + args[0].pointer = buf; + args[1].integer = 1; + args[2].integer = result_len; } DLLEXPORT @@ -59,8 +66,11 @@ void _std_standard_now(jit_scalar_t *args) } DLLEXPORT -void _std_to_string_time(int64_t value, int64_t unit, ffi_uarray_t *u) +void _std_to_string_time(jit_scalar_t *args, tlab_t *tlab) { + int64_t value = args[1].integer; + int64_t unit = args[2].integer; + const char *unit_str = ""; switch (unit) { case 1ll: unit_str = "fs"; break; @@ -76,39 +86,39 @@ void _std_to_string_time(int64_t value, int64_t unit, ffi_uarray_t *u) unit); } - size_t max_len = 32 + strlen(unit_str) + 1; - char *buf = rt_tlab_alloc(max_len); - - size_t len; + static char buf[64]; if (value % unit == 0) - len = checked_sprintf(buf, max_len, "%"PRIi64" %s", - value / unit, unit_str); + checked_sprintf(buf, sizeof(buf), "%"PRIi64" %s", + value / unit, unit_str); else - len = checked_sprintf(buf, max_len, "%.*g %s", DBL_DIG, - (double)value / (double)unit, unit_str); + checked_sprintf(buf, sizeof(buf), "%.*g %s", DBL_DIG, + (double)value / (double)unit, unit_str); - *u = ffi_wrap(buf, 1, len); + ffi_return_string(buf, args, tlab); } DLLEXPORT -void _std_to_string_real_digits(double value, int32_t digits, ffi_uarray_t *u) +void _std_to_string_real_digits(jit_scalar_t *args, tlab_t *tlab) { - size_t max_len = 32; - char *buf = rt_tlab_alloc(max_len); + double value = args[1].real; + int32_t digits = args[2].integer; - size_t len; + char buf[32]; if (digits == 0) - len = checked_sprintf(buf, max_len, "%.17g", value); + checked_sprintf(buf, sizeof(buf), "%.17g", value); else - len = checked_sprintf(buf, max_len, "%.*f", digits, value); + checked_sprintf(buf, sizeof(buf), "%.*f", digits, value); - *u = ffi_wrap(buf, 1, len); + ffi_return_string(buf, args, tlab); } DLLEXPORT -void _std_to_string_real_format(double value, const void *fmt_ptr, - int64_t fmt_length, ffi_uarray_t *u) +void _std_to_string_real_format(jit_scalar_t *args, tlab_t *tlab) { + double value = args[1].real; + const void *fmt_ptr = args[2].pointer; + size_t fmt_length = ffi_array_length(args[4].integer); + char *fmt_cstr LOCAL = null_terminate(fmt_ptr, fmt_length); if (fmt_cstr[0] != '%') @@ -130,33 +140,34 @@ void _std_to_string_real_format(double value, const void *fmt_ptr, } } - size_t max_len = 64; - char *buf = rt_tlab_alloc(max_len); - size_t len = checked_sprintf(buf, max_len, fmt_cstr, value); - *u = ffi_wrap(buf, 1, len); + char buf[64]; + checked_sprintf(buf, sizeof(buf), fmt_cstr, value); + + ffi_return_string(buf, args, tlab); } DLLEXPORT -void _std_to_hstring_bit_vec(const uint8_t *vec_ptr, int64_t vec_len, - ffi_uarray_t *u) +void _std_to_hstring_bit_vec(jit_scalar_t *args, tlab_t *tlab) { - *u = bit_vec_to_string(vec_ptr, vec_len, 4); + bit_vec_to_string(args, tlab, 4); } DLLEXPORT -void _std_to_ostring_bit_vec(const uint8_t *vec_ptr, int64_t vec_len, - ffi_uarray_t *u) +void _std_to_ostring_bit_vec(jit_scalar_t *args, tlab_t *tlab) { - *u = bit_vec_to_string(vec_ptr, vec_len, 3); + bit_vec_to_string(args, tlab, 3); } DLLEXPORT -void _std_to_string_real(double value, ffi_uarray_t *u) +void _std_to_string_real(jit_scalar_t *args, tlab_t *tlab) { const size_t max = 32; - char *buf = jit_mspace_alloc(max); - size_t len = checked_sprintf(buf, max, "%.*g", DBL_DIG, value); - *u = ffi_wrap(buf, 1, len); + char *buf = tlab_alloc(tlab, max); + size_t len = checked_sprintf(buf, max, "%.*g", DBL_DIG, args[1].real); + + args[0].pointer = buf; + args[1].integer = 1; + args[2].integer = len; } void _std_standard_init(void) diff --git a/src/rt/stdenv.c b/src/rt/stdenv.c index 40873505..d647e41d 100644 --- a/src/rt/stdenv.c +++ b/src/rt/stdenv.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include "jit/jit-ffi.h" #include "jit/jit-exits.h" #include "scan.h" +#include "rt/mspace.h" #include "rt/rt.h" #include @@ -97,14 +98,6 @@ typedef struct { int64_t file_line; } call_path_element_t; -static void copy_str(const char *str, ffi_uarray_t *u) -{ - const size_t len = strlen(str); - char *buf = rt_tlab_alloc(len); - memcpy(buf, str, len); - *u = ffi_wrap(buf, 1, len); -} - static ffi_uarray_t *to_line_n(const char *str, size_t len) { char *buf = jit_mspace_alloc(len); @@ -223,37 +216,34 @@ void _std_env_stop(int32_t finish, int32_t have_status, int32_t status) } DLLEXPORT -void _std_env_getenv(const uint8_t *name_ptr, int64_t name_len, ffi_uarray_t *u) +void _std_env_getenv(jit_scalar_t *args, tlab_t *tlab) { - char *LOCAL cstr = null_terminate(name_ptr, name_len); + char *LOCAL cstr = + null_terminate(args[1].pointer, ffi_array_length(args[3].integer)); // LRM19 section 16.5.6: conditional analysis identifiers are part of // the queried environment and take precedence over possibly // inherited environment variables of identical names. const char *pp = pp_defines_get(cstr); if (pp != NULL) - copy_str(pp, u); + ffi_return_string(pp, args, tlab); else { const char *env = getenv(cstr); - if (env == NULL) - *u = ffi_wrap(NULL, 1, 0); - else - copy_str(env, u); + ffi_return_string(env ?: "", args, tlab); } } DLLEXPORT -void _std_env_vhdl_version(ffi_uarray_t *u) +void _std_env_vhdl_version(jit_scalar_t *args, tlab_t *tlab) { const char *str = standard_text(standard()); - *u = ffi_wrap((char *)str, 1, strlen(str)); + ffi_return_string(str, args, tlab); } DLLEXPORT -void _std_env_tool_version(ffi_uarray_t *u) +void _std_env_tool_version(jit_scalar_t *args, tlab_t *tlab) { - const char *str = PACKAGE_VERSION; - *u = ffi_wrap((char *)str, 1, strlen(str)); + ffi_return_string(PACKAGE_VERSION, args, tlab); } DLLEXPORT @@ -375,13 +365,13 @@ double _std_env_diff_trec(const time_record_t *tr1, const time_record_t *tr2) } DLLEXPORT -void _std_env_get_workingdir(ffi_uarray_t *u) +void _std_env_get_workingdir(jit_scalar_t *args, tlab_t *tlab) { char buf[PATH_MAX]; if (getcwd(buf, sizeof(buf)) == NULL) jit_msg(NULL, DIAG_FATAL, "getcwd failed: %s", strerror(errno)); - copy_str(buf, u); + ffi_return_string(buf, args, tlab); } DLLEXPORT @@ -579,7 +569,7 @@ void _std_env_dir_open(const uint8_t *path_ptr, int64_t path_len, } DLLEXPORT -void _std_env_get_call_path(ffi_uarray_t **ptr) +void _std_env_get_call_path(jit_scalar_t *args, tlab_t *tlab) { jit_stack_trace_t *stack LOCAL = jit_stack_trace(); @@ -611,7 +601,8 @@ void _std_env_get_call_path(ffi_uarray_t **ptr) ffi_uarray_t *u = jit_mspace_alloc(sizeof(ffi_uarray_t)); *u = ffi_wrap(array, 0, stack->count - 2); - *ptr = u; + + args[0].pointer = u; } DLLEXPORT @@ -657,9 +648,10 @@ int64_t _std_env_seconds_to_time(double real) } DLLEXPORT -int64_t _std_env_get_vhdl_assert_count(vhdl_severity_t severity_level) +void _std_env_get_vhdl_assert_count(jit_scalar_t *args) { - return get_vhdl_assert_count(severity_level); + vhdl_severity_t severity_level = args[1].integer; + args[0].integer = get_vhdl_assert_count(severity_level); } DLLEXPORT @@ -669,16 +661,19 @@ void _std_env_clear_vhdl_assert(void) } DLLEXPORT -void _std_env_set_vhdl_assert_enable(vhdl_severity_t severity_level, - bool enable) +void _std_env_set_vhdl_assert_enable(jit_scalar_t *args) { + vhdl_severity_t severity_level = args[2].integer; + bool enable = !!args[3].integer; + set_vhdl_assert_enable(severity_level, enable); } DLLEXPORT -bool _std_env_get_vhdl_assert_enable(vhdl_severity_t severity_level) +void _std_env_get_vhdl_assert_enable(jit_scalar_t *args) { - return get_vhdl_assert_enable(severity_level); + vhdl_severity_t severity_level = args[1].integer; + args[0].integer = get_vhdl_assert_enable(severity_level); } void _std_env_init(void) diff --git a/src/rt/verilog.c b/src/rt/verilog.c index 644f9f4f..eb50bb7a 100644 --- a/src/rt/verilog.c +++ b/src/rt/verilog.c @@ -97,7 +97,7 @@ void __nvc_sys_finish(void) DLLEXPORT void __nvc_sys_display(jit_scalar_t *args) { - verilog_printf(args); + verilog_printf(args + 2); fputc('\n', stdout); fflush(stdout); } @@ -105,7 +105,7 @@ void __nvc_sys_display(jit_scalar_t *args) DLLEXPORT void __nvc_sys_write(jit_scalar_t *args) { - verilog_printf(args); + verilog_printf(args + 2); fflush(stdout); } diff --git a/src/sem.c b/src/sem.c index d6894f5c..f1f82b33 100644 --- a/src/sem.c +++ b/src/sem.c @@ -2086,6 +2086,8 @@ static bool sem_check_missing_body(tree_t body, tree_t spec) const tree_kind_t dkind = tree_kind(d); if (dkind != T_FUNC_DECL && dkind != T_PROC_DECL && dkind != T_PROT_DECL) continue; + else if (dkind != T_PROT_DECL && (tree_flags(d) & TREE_F_PREDEFINED)) + continue; type_t dtype = tree_type(d); @@ -2095,22 +2097,31 @@ static bool sem_check_missing_body(tree_t body, tree_t spec) for (int j = start; !found && (j < nbody_decls); j++) { tree_t b = tree_decl(body, j); const tree_kind_t bkind = tree_kind(b); - if (bkind != T_FUNC_BODY && bkind != T_PROC_BODY - && bkind != T_PROT_BODY) + if (bkind == T_ATTR_SPEC && is_well_known(tree_ident(b)) == W_FOREIGN + && tree_has_ref(b) && tree_ref(b) == d) + found = true; + else if (bkind != T_FUNC_BODY && bkind != T_PROC_BODY + && bkind != T_PROT_BODY) continue; else if (type_eq(dtype, tree_type(b))) found = true; } - if (found) - continue; - - const bool missing = dkind == T_PROT_DECL - || (!(tree_flags(d) & TREE_F_PREDEFINED) - && !is_foreign(tree_subkind(d))); + if (!found && (dkind == T_FUNC_DECL || dkind == T_PROC_DECL)) { + // Check if there was a later foreign attribute declaration + for (int j = i + 1; j < ndecls; j++) { + tree_t d2 = tree_decl(spec, j); + if (tree_kind(d2) != T_ATTR_SPEC || !tree_has_ref(d2)) + continue; + else if (tree_ref(d2) == d) { + found = true; + break; + } + } + } - if (missing && opt_get_int(OPT_MISSING_BODY)) { - warn_at(tree_loc(d), "missing body for %s %s", + if (!found && opt_get_int(OPT_MISSING_BODY)) { + error_at(tree_loc(d), "missing body for %s %s", (dkind == T_PROT_DECL) ? "protected type" : (dkind == T_PROC_DECL ? "procedure" : "function"), type_pp(dtype)); @@ -5094,8 +5105,7 @@ static bool sem_locally_static(tree_t t) tree_t decl = tree_ref(t); if (tree_kind(decl) == T_GENERIC_DECL) return false; // Not known at this point - else if (!is_builtin(tree_subkind(decl)) - && !sem_ieee_locally_static(decl)) + else if (tree_subkind(decl) == S_USER && !sem_ieee_locally_static(decl)) return false; const int nparams = tree_params(t); @@ -5768,48 +5778,14 @@ static bool sem_check_attr_spec(tree_t t, nametab_t *tab) else if (class != C_PROCEDURE) sem_error(t, "NEVER_WAITS attribute can only be applied to " "procedures"); - else if (flag) + else if (flag && !tree_frozen(decl)) tree_set_flag(decl, TREE_F_NEVER_WAITS); } break; case W_FOREIGN: - { - // See LRM 08 section 20.2.4.3 - - if (tree_kind(value) != T_STRING) - sem_error(value, "FOREIGN attribute must have string " - "literal value"); - - const int nchars = tree_chars(value); - char *buf LOCAL = xmalloc(nchars + 1); - for (int i = 0; i < nchars; i++) - buf[i] = tree_pos(tree_ref(tree_char(value, i))); - buf[nchars] = '\0'; - - subprogram_kind_t kind = S_FOREIGN; - char *p = strtok(buf, " "); - if (strcmp(p, "VHPIDIRECT") == 0) { - p = strtok(NULL, " "); - if (p != NULL) { - // The object library specifier is silently ignored - char *p2 = strtok(NULL, " "); - if (p2 != NULL) p = p2; - } - } - else if (strcmp(p, "INTERNAL") == 0) { - p = strtok(NULL, " "); - kind = S_INTERNAL; - } - else if (strtok(NULL, " ") != NULL) - sem_error(value, "failed to parse FOREIGN attribute"); - - ident_t name = ident_new(p); - tree_set_ident2(decl, name); - - tree_set_subkind(decl, kind); + if (!tree_frozen(decl)) tree_set_flag(decl, TREE_F_NEVER_WAITS); - } break; default: diff --git a/src/simp.c b/src/simp.c index 29170aa1..aa71398e 100644 --- a/src/simp.c +++ b/src/simp.c @@ -242,7 +242,7 @@ static tree_t simp_fcall(tree_t t, simp_ctx_t *ctx) if (kind == S_CONCAT) return simp_concat(t); - else if (kind != S_USER && kind != S_FOREIGN) { + else if (kind != S_USER) { // Simplify basic operations on literals without the overhead of // generating code int64_t p0, p1, result; diff --git a/src/symbols.txt b/src/symbols.txt index be3ebde1..9a264b67 100644 --- a/src/symbols.txt +++ b/src/symbols.txt @@ -70,7 +70,6 @@ # Exported from src/jit/jit-exits.c __nvc_do_exit; - __nvc_do_fficall; __nvc_get_object; __nvc_last_event; __nvc_mspace_alloc; @@ -119,6 +118,4 @@ # Unit test global_func; - test_ffi_add; - test_ffi_fma; }; diff --git a/src/tree.h b/src/tree.h index 49780616..89a49bfb 100644 --- a/src/tree.h +++ b/src/tree.h @@ -103,7 +103,6 @@ typedef enum { typedef enum { S_USER, - S_FOREIGN, S_ARRAY_EQ, S_ARRAY_NEQ, S_ARRAY_LT, @@ -189,7 +188,6 @@ typedef enum { S_MATCH_GE, S_MATCH_EQ, S_MATCH_NEQ, - S_INTERNAL, S_FILE_FLUSH, S_FILE_OPEN3, S_FILE_MODE, @@ -200,6 +198,11 @@ typedef enum { S_FILE_TRUNCATE, S_FILE_STATE, S_FILE_POSITION, + S_TO_STRING_TIME, + S_TO_STRING_REAL_DIGITS, + S_TO_STRING_REAL_FORMAT, + S_TO_HSTRING_BITVEC, + S_TO_OSTRING_BITVEC, } subprogram_kind_t; typedef enum { diff --git a/src/vcode.c b/src/vcode.c index 9979bd93..2521344a 100644 --- a/src/vcode.c +++ b/src/vcode.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2023 Nick Gasson +// Copyright (C) 2014-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -45,11 +45,7 @@ DECLARE_AND_DEFINE_ARRAY(vcode_type); (x == VCODE_OP_LOAD || x == VCODE_OP_STORE || x == VCODE_OP_INDEX \ || x == VCODE_OP_VAR_UPREF) #define OP_HAS_SUBKIND(x) \ - (x == VCODE_OP_PCALL \ - || x == VCODE_OP_FCALL || x == VCODE_OP_RESOLUTION_WRAPPER \ - || x == VCODE_OP_CLOSURE || x == VCODE_OP_PROTECTED_INIT \ - || x == VCODE_OP_PACKAGE_INIT || x == VCODE_OP_COVER_BRANCH \ - || x == VCODE_OP_PROCESS_INIT) + (x == VCODE_OP_COVER_BRANCH) #define OP_HAS_FUNC(x) \ (x == VCODE_OP_FCALL || x == VCODE_OP_PCALL || x == VCODE_OP_RESUME \ || x == VCODE_OP_CLOSURE || x == VCODE_OP_PROTECTED_INIT \ @@ -966,7 +962,7 @@ const char *vcode_op_string(vcode_op_t op) "unreachable", "package init", "trap neg", "process init", "clear event", "trap exp", "implicit event", "enter state", "reflect value", "reflect subtype", "function trigger", "add trigger", "transfer signal", - "port conversion", "convert in", "convert out", + "port conversion", "convert in", "convert out", "bind foreign", }; if ((unsigned)op >= ARRAY_LEN(strs)) return "???"; @@ -1315,10 +1311,6 @@ void vcode_dump_with_mark(int mark_op, vcode_dump_fn_t callback, void *arg) col += vcode_dump_reg(op->result); col += printf(" := "); } - if (op->subkind == VCODE_CC_FOREIGN) - col += printf("foreign "); - else if (op->subkind == VCODE_CC_INTERNAL) - col += printf("internal "); col += color_printf("%s $magenta$%s$$ ", vcode_op_string(op->kind), istr(op->func)); @@ -2306,6 +2298,19 @@ void vcode_dump_with_mark(int mark_op, vcode_dump_fn_t callback, void *arg) vcode_dump_reg(op->args.items[2]); } break; + + case VCODE_OP_BIND_FOREIGN: + { + color_printf("%s ", vcode_op_string(op->kind)); + vcode_dump_reg(op->args.items[0]); + printf(" length "); + vcode_dump_reg(op->args.items[1]); + if (op->args.count > 2) { + printf(" locus "); + vcode_dump_reg(op->args.items[1]); + } + } + break; } if (j == mark_op && i == old_block) @@ -3332,12 +3337,11 @@ vcode_reg_t emit_cmp(vcode_cmp_t cmp, vcode_reg_t lhs, vcode_reg_t rhs) } vcode_reg_t emit_fcall(ident_t func, vcode_type_t type, vcode_type_t bounds, - vcode_cc_t cc, const vcode_reg_t *args, int nargs) + const vcode_reg_t *args, int nargs) { op_t *o = vcode_add_op(VCODE_OP_FCALL); - o->func = func; - o->type = type; - o->subkind = cc; + o->func = func; + o->type = type; for (int i = 0; i < nargs; i++) vcode_add_arg(o, args[i]); @@ -3345,9 +3349,8 @@ vcode_reg_t emit_fcall(ident_t func, vcode_type_t type, vcode_type_t bounds, VCODE_ASSERT(args[i] != VCODE_INVALID_REG, "invalid argument to function"); - if (cc != VCODE_CC_FOREIGN && cc != VCODE_CC_INTERNAL) - VCODE_ASSERT(nargs > 0 && vcode_reg_kind(args[0]) == VCODE_TYPE_CONTEXT, - "first argument to VHDL function must be context pointer"); + VCODE_ASSERT(nargs > 0 && vcode_reg_kind(args[0]) == VCODE_TYPE_CONTEXT, + "first argument to VHDL function must be context pointer"); if (type == VCODE_INVALID_TYPE) return (o->result = VCODE_INVALID_REG); @@ -3365,8 +3368,7 @@ void emit_pcall(ident_t func, const vcode_reg_t *args, int nargs, vcode_block_t resume_bb) { op_t *o = vcode_add_op(VCODE_OP_PCALL); - o->func = func; - o->subkind = VCODE_CC_VHDL; + o->func = func; for (int i = 0; i < nargs; i++) vcode_add_arg(o, args[i]); @@ -4897,7 +4899,6 @@ vcode_reg_t emit_resolution_wrapper(vcode_type_t type, vcode_reg_t closure, vcode_add_arg(op, closure); vcode_add_arg(op, ileft); vcode_add_arg(op, nlits); - op->subkind = VCODE_CC_VHDL; return (op->result = vcode_add_reg(vtype_resolution(type))); } @@ -4906,16 +4907,14 @@ vcode_reg_t emit_closure(ident_t func, vcode_reg_t context, vcode_type_t atype, vcode_type_t rtype) { VCODE_FOR_EACH_MATCHING_OP(other, VCODE_OP_CLOSURE) { - if (other->func == func && other->subkind == VCODE_CC_VHDL - && other->args.items[0] == context) + if (other->func == func && other->args.items[0] == context) return other->result; } op_t *op = vcode_add_op(VCODE_OP_CLOSURE); vcode_add_arg(op, context); - op->func = func; - op->subkind = VCODE_CC_VHDL; - op->type = atype; + op->func = func; + op->type = atype; VCODE_ASSERT(vcode_reg_kind(context) == VCODE_TYPE_CONTEXT, "invalid closure context argument"); @@ -4926,13 +4925,12 @@ vcode_reg_t emit_closure(ident_t func, vcode_reg_t context, vcode_type_t atype, vcode_reg_t emit_package_init(ident_t name, vcode_reg_t context) { VCODE_FOR_EACH_MATCHING_OP(other, VCODE_OP_PACKAGE_INIT) { - if (other->func == name && other->subkind == VCODE_CC_VHDL) + if (other->func == name) return other->result; } op_t *op = vcode_add_op(VCODE_OP_PACKAGE_INIT); - op->func = name; - op->subkind = VCODE_CC_VHDL; + op->func = name; if (context != VCODE_INVALID_REG) vcode_add_arg(op, context); @@ -4953,8 +4951,7 @@ vcode_reg_t emit_protected_init(vcode_type_t type, vcode_reg_t context, { op_t *op = vcode_add_op(VCODE_OP_PROTECTED_INIT); vcode_add_arg(op, context); - op->func = vtype_name(type); - op->subkind = VCODE_CC_VHDL; + op->func = vtype_name(type); if (path_name != VCODE_INVALID_REG && inst_name != VCODE_INVALID_REG) { vcode_add_arg(op, path_name); @@ -4978,8 +4975,7 @@ void emit_process_init(ident_t name, vcode_reg_t locus) { op_t *op = vcode_add_op(VCODE_OP_PROCESS_INIT); vcode_add_arg(op, locus); - op->func = name; - op->subkind = VCODE_CC_VHDL; + op->func = name; VCODE_ASSERT(vcode_reg_kind(locus) == VCODE_TYPE_DEBUG_LOCUS, "locus argument to process init must be a debug locus"); @@ -5976,6 +5972,23 @@ void emit_convert_out(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count) "count argument to convert must be offset"); } +void emit_bind_foreign(vcode_reg_t spec, vcode_reg_t length, vcode_reg_t locus) +{ + op_t *op = vcode_add_op(VCODE_OP_BIND_FOREIGN); + vcode_add_arg(op, spec); + vcode_add_arg(op, length); + if (locus != VCODE_INVALID_REG) + vcode_add_arg(op, locus); + + VCODE_ASSERT(vcode_reg_kind(spec) == VCODE_TYPE_POINTER, + "spec argument to bind foreign must be a pointer"); + VCODE_ASSERT(vcode_reg_kind(length) == VCODE_TYPE_OFFSET, + "length argument to bind foreign must be offset"); + VCODE_ASSERT(locus == VCODE_INVALID_REG + || vcode_reg_kind(locus) == VCODE_TYPE_DEBUG_LOCUS, + "locus argument to bind foreign value must be a debug locus"); +} + void vcode_walk_dependencies(vcode_unit_t vu, vcode_dep_fn_t fn, void *ctx) { vcode_select_unit(vu); @@ -5995,11 +6008,7 @@ void vcode_walk_dependencies(vcode_unit_t vu, vcode_dep_fn_t fn, void *ctx) case VCODE_OP_CLOSURE: case VCODE_OP_PROTECTED_INIT: case VCODE_OP_PACKAGE_INIT: - { - const vcode_cc_t cc = vcode_get_subkind(op); - if (cc != VCODE_CC_FOREIGN && cc != VCODE_CC_INTERNAL) - (*fn)(vcode_get_func(op), ctx); - } + (*fn)(vcode_get_func(op), ctx); break; default: break; diff --git a/src/vcode.h b/src/vcode.h index 9eb6ac10..c54cf275 100644 --- a/src/vcode.h +++ b/src/vcode.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2023 Nick Gasson +// Copyright (C) 2014-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -165,6 +165,7 @@ typedef enum { VCODE_OP_PORT_CONVERSION, VCODE_OP_CONVERT_IN, VCODE_OP_CONVERT_OUT, + VCODE_OP_BIND_FOREIGN, } vcode_op_t; typedef enum { @@ -211,13 +212,6 @@ typedef enum { VCODE_UNIT_SHAPE, } vunit_kind_t; -typedef enum { - VCODE_CC_VHDL, - VCODE_CC_PREDEF, - VCODE_CC_FOREIGN, - VCODE_CC_INTERNAL, -} vcode_cc_t; - typedef struct { vcode_reg_t left; vcode_reg_t right; @@ -398,7 +392,7 @@ void emit_report(vcode_reg_t message, vcode_reg_t length, vcode_reg_t severity, vcode_reg_t locus); vcode_reg_t emit_cmp(vcode_cmp_t cmp, vcode_reg_t lhs, vcode_reg_t rhs); vcode_reg_t emit_fcall(ident_t func, vcode_type_t type, vcode_type_t bounds, - vcode_cc_t cc, const vcode_reg_t *args, int nargs); + const vcode_reg_t *args, int nargs); void emit_pcall(ident_t func, const vcode_reg_t *args, int nargs, vcode_block_t resume_bb); void emit_wait(vcode_block_t target, vcode_reg_t time); @@ -531,5 +525,6 @@ void emit_add_trigger(vcode_reg_t trigger); vcode_reg_t emit_port_conversion(vcode_reg_t driving, vcode_reg_t effective); void emit_convert_in(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count); void emit_convert_out(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count); +void emit_bind_foreign(vcode_reg_t spec, vcode_reg_t length, vcode_reg_t locus); #endif // _VCODE_H diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index 115030db..713e515d 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -134,7 +134,7 @@ static vcode_reg_t vlog_lower_resize(lower_unit_t *lu, vcode_reg_t value_reg, vcode_reg_t context_reg = vlog_helper_package(); vcode_reg_t args[] = { context_reg, arg_reg, count_reg }; - return emit_fcall(fn, vpacked, vlogic, VCODE_CC_VHDL, args, ARRAY_LEN(args)); + return emit_fcall(fn, vpacked, vlogic, args, ARRAY_LEN(args)); } static vcode_reg_t vlog_lower_to_time(lower_unit_t *lu, vcode_reg_t reg) @@ -145,7 +145,7 @@ static vcode_reg_t vlog_lower_to_time(lower_unit_t *lu, vcode_reg_t reg) vcode_type_t vtime = vtype_time(); ident_t func = ident_new("NVC.VERILOG.TO_TIME(" T_PACKED_LOGIC ")" "25STD.STANDARD.DELAY_LENGTH"); - return emit_fcall(func, vtime, vtime, VCODE_CC_VHDL, args, ARRAY_LEN(args)); + return emit_fcall(func, vtime, vtime, args, ARRAY_LEN(args)); } static vcode_reg_t vlog_lower_to_integer(lower_unit_t *lu, vcode_reg_t reg) @@ -156,8 +156,7 @@ static vcode_reg_t vlog_lower_to_integer(lower_unit_t *lu, vcode_reg_t reg) vcode_type_t vint64 = vtype_int(INT64_MIN, INT64_MAX); ident_t func = ident_new("NVC.VERILOG.TO_INTEGER(" T_PACKED_LOGIC ")" T_INT64); - return emit_fcall(func, vint64, vint64, VCODE_CC_VHDL, - args, ARRAY_LEN(args)); + return emit_fcall(func, vint64, vint64, args, ARRAY_LEN(args)); } static vcode_reg_t vlog_lower_to_bool(lower_unit_t *lu, vcode_reg_t reg) @@ -283,8 +282,7 @@ static vcode_reg_t vlog_lower_unary(lower_unit_t *lu, vlog_unary_t op, ident_t func = ident_new(tb_get(tb)); vcode_reg_t args[] = { context_reg, reg }; - return emit_fcall(func, rtype, vlogic, VCODE_CC_VHDL, - args, ARRAY_LEN(args)); + return emit_fcall(func, rtype, vlogic, args, ARRAY_LEN(args)); } static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) @@ -397,8 +395,7 @@ static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) vcode_type_t vlogic = vlog_logic_type(); vcode_type_t vpacked = vlog_packed_logic_type(); - return emit_fcall(func, vpacked, vlogic, VCODE_CC_VHDL, - args, ARRAY_LEN(args)); + return emit_fcall(func, vpacked, vlogic, args, ARRAY_LEN(args)); } case V_UNARY: { @@ -518,32 +515,39 @@ static void vlog_lower_bassign(lower_unit_t *lu, vlog_node_t v) static void vlog_lower_systask(lower_unit_t *lu, vlog_node_t v) { const v_systask_kind_t kind = vlog_subkind(v); - const char *fns[] = { - "__nvc_sys_display", - "__nvc_sys_write", - "__nvc_sys_finish" + static const char *fns[] = { + "NVC.VERILOG.SYS_DISPLAY(S)", + "NVC.VERILOG.SYS_WRITE(S)", + "NVC.VERILOG.SYS_FINISH" }; assert(kind < ARRAY_LEN(fns)); + vcode_reg_t context_reg = vlog_helper_package(); + switch (kind) { case V_SYS_DISPLAY: case V_SYS_WRITE: { const int nparams = vlog_params(v); - vcode_reg_t *args LOCAL = xmalloc_array(nparams, sizeof(vcode_reg_t)); + vcode_reg_t *args LOCAL = + xmalloc_array(nparams + 1, sizeof(vcode_reg_t)); + args[0] = context_reg; for (int i = 0; i < nparams; i++) { vcode_reg_t p_reg = vlog_lower_rvalue(lu, vlog_param(v, i)); - args[i] = vlog_lower_wrap(lu, p_reg); + args[i + 1] = vlog_lower_wrap(lu, p_reg); } emit_fcall(ident_new(fns[kind]), VCODE_INVALID_TYPE, - VCODE_INVALID_TYPE, VCODE_CC_INTERNAL, args, nparams); + VCODE_INVALID_TYPE, args, nparams + 1); } break; case V_SYS_FINISH: - emit_fcall(ident_new(fns[kind]), VCODE_INVALID_TYPE, - VCODE_INVALID_TYPE, VCODE_CC_FOREIGN, NULL, 0); + { + vcode_reg_t args[] = { context_reg }; + emit_fcall(ident_new(fns[kind]), VCODE_INVALID_TYPE, + VCODE_INVALID_TYPE, args, ARRAY_LEN(args)); + } break; default: diff --git a/test/regress/vhpi4.vhd b/test/regress/vhpi4.vhd index 0e109c10..fc1857fe 100644 --- a/test/regress/vhpi4.vhd +++ b/test/regress/vhpi4.vhd @@ -13,12 +13,21 @@ architecture test of vhpi4 is function sum_array (a : int_vec; len : integer) return integer; attribute foreign of sum_array : function is "VHPIDIRECT __vhpi_sum_array"; - function my_not (x : std_logic) return std_logic; + function my_not (x : std_logic) return std_logic is + begin + report "do not call this" severity failure; + end function; + attribute foreign of my_not : function is "VHPIDIRECT __vhpi_my_not"; procedure test_proc (x : out integer; arr : out int_vec); attribute foreign of test_proc : procedure is "VHPIDIRECT __vhpi_test_proc"; + procedure test_proc (x : out integer; arr : out int_vec) is + begin + report "do not call this" severity failure; + end procedure; + begin main: process is diff --git a/test/sem/attr.vhd b/test/sem/attr.vhd index 414408e6..759507f1 100644 --- a/test/sem/attr.vhd +++ b/test/sem/attr.vhd @@ -307,8 +307,8 @@ architecture builtins of e is procedure q (x : integer); procedure q (x : boolean); attribute never_waits of q [integer] : procedure is true; -- OK - attribute foreign of f [return integer] : function is "bad string"; -- Error - attribute foreign of f [return integer] : function is e'path_name; -- Error + attribute foreign of f [return integer] : function is "bad string"; -- OK + attribute foreign of f [return integer] : function is e'path_name; -- OK begin end architecture; diff --git a/test/simp/foreign1.vhd b/test/simp/foreign1.vhd index ce593c82..8a53fa1d 100644 --- a/test/simp/foreign1.vhd +++ b/test/simp/foreign1.vhd @@ -5,7 +5,7 @@ architecture test of foreign1 is function f (x : integer) return integer; - attribute foreign of f : function is "symbol"; + attribute foreign of f : function is "VHPIDIRECT symbol"; function f (x : integer) return integer is begin diff --git a/test/test_jit.c b/test/test_jit.c index f72a8f58..c40bd9c2 100644 --- a/test/test_jit.c +++ b/test/test_jit.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2021-2023 Nick Gasson +// Copyright (C) 2021-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -1138,120 +1138,6 @@ START_TEST(test_value1) } END_TEST -DLLEXPORT -int test_ffi_add(int x, int y) -{ - return x + y; -} - -DLLEXPORT -double test_ffi_fma(double x, double y, double z) -{ - return x * y + z; -} - -static int test_ffi_arraylen(const void *ptr, int64_t len) -{ - return len; -} - -static int test_ffi_arraysum(const void *ptr, int64_t len) -{ - int sum = 0; - for (int i = 0; i < len; i++) - sum += *((int *)ptr + i); - return sum; -} - -START_TEST(test_ffi1) -{ - ffi_load_dll(NULL); - - ident_t add_i = ident_new("test_ffi_add"); - - fail_unless(jit_ffi_get(add_i) == NULL); - - const ffi_type_t add_types[] = { FFI_INT32, FFI_INT32, FFI_INT32 }; - ffi_spec_t add_spec = ffi_spec_new(add_types, 3); - - jit_foreign_t *add_ff = jit_ffi_bind(add_i, add_spec, NULL); - fail_if(add_ff == NULL); - - fail_unless(jit_ffi_get(add_i) == add_ff); - - { - jit_scalar_t args[] = { { .integer = 5 }, { .integer = 3 } }; - jit_ffi_call(add_ff, args); - ck_assert_int_eq(args[0].integer, 8); - } - - { - jit_scalar_t args[] = { { .integer = 5 }, { .integer = -7 } }; - jit_ffi_call(add_ff, args); - ck_assert_int_eq(args[0].integer, -2); - } - - ident_t fma_i = ident_new("test_ffi_fma"); - - const ffi_type_t fma_types[] = { - FFI_FLOAT, FFI_FLOAT, FFI_FLOAT, FFI_FLOAT - }; - ffi_spec_t fma_spec = ffi_spec_new(fma_types, 4); - - jit_foreign_t *fma_ff = jit_ffi_bind(fma_i, fma_spec, NULL); - fail_if(fma_ff == NULL); - - { - jit_scalar_t args[] = { { .real = 2.0 }, - { .real = 3.0 }, - { .real = 1.0 } }; - jit_ffi_call(fma_ff, args); - ck_assert_double_eq(args[0].real, 7.0); - } - - { - jit_scalar_t args[] = { { .real = -2.0 }, - { .real = 3.0 }, - { .real = 1.0 } }; - jit_ffi_call(fma_ff, args); - ck_assert_double_eq(args[0].real, -5.0); - } - - ident_t len_i = ident_new("len"); - - const ffi_type_t len_types[] = { FFI_INT32, FFI_POINTER, FFI_INT64 }; - ffi_spec_t len_spec = ffi_spec_new(len_types, 3); - - jit_foreign_t *len_ff = jit_ffi_bind(len_i, len_spec, test_ffi_arraylen); - fail_if(len_ff == NULL); - - { - jit_scalar_t args[] = { - { .pointer = NULL }, { .integer = 4 } - }; - jit_ffi_call(len_ff, args); - ck_assert_int_eq(args[0].integer, 4); - } - - ident_t sum_i = ident_new("sum"); - - const ffi_type_t sum_types[] = { FFI_INT32, FFI_POINTER, FFI_INT64 }; - ffi_spec_t sum_spec = ffi_spec_new(sum_types, 3); - - jit_foreign_t *sum_ff = jit_ffi_bind(sum_i, sum_spec, test_ffi_arraysum); - fail_if(sum_ff == NULL); - - { - int data[4] = { 1, 2, 3, 4 }; - jit_scalar_t args[] = { - { .pointer = data }, { .integer = 4 } - }; - jit_ffi_call(sum_ff, args); - ck_assert_int_eq(args[0].integer, 10); - } -} -END_TEST - START_TEST(test_assemble1) { jit_t *j = jit_new(NULL); @@ -2099,7 +1985,6 @@ Suite *get_jit_tests(void) tcase_add_test(tc, test_issue496); tcase_add_test(tc, test_process1); tcase_add_test(tc, test_value1); - tcase_add_test(tc, test_ffi1); tcase_add_test(tc, test_assemble1); tcase_add_test(tc, test_assemble2); tcase_add_test(tc, test_cfg1); diff --git a/test/test_lower.c b/test/test_lower.c index 79c3d98a..1d34fe35 100644 --- a/test/test_lower.c +++ b/test/test_lower.c @@ -382,7 +382,8 @@ START_TEST(test_wait1) CHECK_BB(0); const check_bb_t bb1[] = { - { VCODE_OP_FCALL, .func = "_std_standard_now" }, + { VCODE_OP_LINK_PACKAGE, .name = "STD.STANDARD" }, + { VCODE_OP_FCALL, .func = "*STD.STANDARD.NOW()" }, { VCODE_OP_CONST, .value = 0 }, { VCODE_OP_CMP, .cmp = VCODE_CMP_EQ }, { VCODE_OP_CONST, .value = 2 }, @@ -395,7 +396,8 @@ START_TEST(test_wait1) CHECK_BB(1); const check_bb_t bb2[] = { - { VCODE_OP_FCALL, .func = "_std_standard_now" }, + { VCODE_OP_LINK_PACKAGE, .name = "STD.STANDARD" }, + { VCODE_OP_FCALL, .func = "*STD.STANDARD.NOW()" }, { VCODE_OP_CONST, .value = 1000000 }, { VCODE_OP_CMP, .cmp = VCODE_CMP_EQ }, { VCODE_OP_CONST, .value = 2 }, @@ -408,7 +410,8 @@ START_TEST(test_wait1) CHECK_BB(2); const check_bb_t bb3[] = { - { VCODE_OP_FCALL, .func = "_std_standard_now" }, + { VCODE_OP_LINK_PACKAGE, .name = "STD.STANDARD" }, + { VCODE_OP_FCALL, .func = "*STD.STANDARD.NOW()" }, { VCODE_OP_CONST, .value = 1000001 }, { VCODE_OP_CMP, .cmp = VCODE_CMP_EQ }, { VCODE_OP_CONST, .value = 2 }, diff --git a/test/test_parse.c b/test/test_parse.c index 6193e385..4fa54c07 100644 --- a/test/test_parse.c +++ b/test/test_parse.c @@ -3555,7 +3555,7 @@ START_TEST(test_names) fail_unless(tree_kind(b) == T_BLOCK); d = tree_decl(b, 0); fail_unless(tree_kind(d) == T_FUNC_DECL); - fail_unless(tree_ident2(d) == ident_new("_get_foo")); + fail_unless(tree_ident2(d) == ident_new("WORK.EE-AA.B8.GET_FOO()I")); p = tree_stmt(a, 8); fail_unless(tree_kind(p) == T_PROCESS); diff --git a/test/test_sem.c b/test/test_sem.c index d5cfd7dc..f5dcb729 100644 --- a/test/test_sem.c +++ b/test/test_sem.c @@ -725,8 +725,6 @@ START_TEST(test_attr) { 261, "cannot use attribute VALUE with non-scalar type BIT2D" }, { 304, "expression must be a BOOLEAN literal" }, { 306, "NEVER_WAITS attribute can only be applied to procedures" }, - { 310, "failed to parse FOREIGN attribute" }, - { 311, "FOREIGN attribute must have string literal value" }, { -1, NULL } }; expect_errors(expect); diff --git a/test/test_simp.c b/test/test_simp.c index 1e170871..f4b2c3f1 100644 --- a/test/test_simp.c +++ b/test/test_simp.c @@ -1276,7 +1276,7 @@ START_TEST(test_foreign1) input_from_file(TESTDIR "/simp/foreign1.vhd"); const error_t expect[] = { - { 17, "foreign function symbol not found" }, + { 8, "foreign function symbol not found" }, { -1, NULL } }; expect_errors(expect); diff --git a/test/vhpi/vhpi4.c b/test/vhpi/vhpi4.c index 35d186a5..45f645cd 100644 --- a/test/vhpi/vhpi4.c +++ b/test/vhpi/vhpi4.c @@ -26,8 +26,9 @@ uint8_t __vhpi_my_not(uint8_t x) } } -void __vhpi_test_proc(int32_t *x, int32_t *arr) +int __vhpi_test_proc(int32_t *x, int32_t *arr) { *x = 42; arr[1] = 5; + return 66; // Should be ignored } -- 2.39.2