From 975ae50ed180236389dcdf316b1d94899c9f2d76 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Fri, 23 Feb 2024 11:11:07 +0000 Subject: [PATCH] Use triggers for sensitised VHDL processes --- src/jit/jit-core.c | 11 ++++++++ src/jit/jit-exits.c | 8 +++--- src/jit/jit-exits.h | 5 ++-- src/jit/jit-irgen.c | 15 ++++++++--- src/jit/jit.h | 2 ++ src/lower.c | 54 ++++++++++++++++++++++++++++++++++++++ src/psl/psl-lower.c | 5 ++-- src/rt/model.c | 57 +++++++++++++++++++++++++++++------------ src/rt/structs.h | 7 ++++- src/vcode.c | 21 ++++++++++----- src/vcode.h | 3 ++- test/lower/trigger1.vhd | 20 +++++++++++++++ test/test_lower.c | 32 +++++++++++++++++++++++ 13 files changed, 200 insertions(+), 40 deletions(-) create mode 100644 test/lower/trigger1.vhd diff --git a/src/jit/jit-core.c b/src/jit/jit-core.c index a4f5dd9a..c1133268 100644 --- a/src/jit/jit-core.c +++ b/src/jit/jit-core.c @@ -760,6 +760,17 @@ bool jit_fastcall(jit_t *j, jit_handle_t handle, jit_scalar_t *result, return jit_try_vcall(j, f, result, args, tlab); } +bool jit_vfastcall(jit_t *j, jit_handle_t handle, jit_scalar_t *result, + unsigned nargs, const jit_scalar_t *inargs, tlab_t *tlab) +{ + jit_func_t *f = jit_get_func(j, handle); + + jit_scalar_t args[JIT_MAX_ARGS]; + memcpy(args, inargs, nargs * sizeof(jit_scalar_t)); + + return jit_try_vcall(j, f, result, args, tlab); +} + bool jit_try_call_packed(jit_t *j, jit_handle_t handle, jit_scalar_t context, void *input, size_t insz, void *output, size_t outsz) { diff --git a/src/jit/jit-exits.c b/src/jit/jit-exits.c index ff6d05ee..f4d228ff 100644 --- a/src/jit/jit-exits.c +++ b/src/jit/jit-exits.c @@ -926,12 +926,10 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, case JIT_EXIT_FUNCTION_TRIGGER: { jit_handle_t handle = args[0].integer; - void *context = args[1].pointer; + unsigned nargs = args[1].integer; - if (jit_has_runtime(thread->jit)) { - ffi_closure_t closure = { handle, context }; - args[0].pointer = x_function_trigger(&closure); - } + if (jit_has_runtime(thread->jit)) + args[0].pointer = x_function_trigger(handle, nargs, args + 2); else args[0].pointer = NULL; // Called during constant folding } diff --git a/src/jit/jit-exits.h b/src/jit/jit-exits.h index 30042868..7887ad42 100644 --- a/src/jit/jit-exits.h +++ b/src/jit/jit-exits.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 @@ -90,7 +90,8 @@ void *x_reflect_value(void *context, jit_scalar_t value, tree_t where, const jit_scalar_t *bounds); void *x_reflect_subtype(void *context, tree_t where, const jit_scalar_t *bounds); -void *x_function_trigger(const ffi_closure_t *closure); +void *x_function_trigger(jit_handle_t handle, unsigned nargs, + const jit_scalar_t *args); void x_add_trigger(void *ptr); void *x_port_conversion(const ffi_closure_t *driving, const ffi_closure_t *effective); diff --git a/src/jit/jit-irgen.c b/src/jit/jit-irgen.c index 00091442..fb5b993e 100644 --- a/src/jit/jit-irgen.c +++ b/src/jit/jit-irgen.c @@ -3374,11 +3374,18 @@ static void irgen_op_enter_state(jit_irgen_t *g, int op) static void irgen_op_function_trigger(jit_irgen_t *g, int op) { - jit_value_t closure = irgen_get_arg(g, op, 0); - jit_value_t context = jit_value_from_reg(jit_value_as_reg(closure) + 1); + jit_handle_t handle = jit_lazy_compile(g->func->jit, vcode_get_func(op)); + + const int nargs = vcode_count_args(op); + + int nslots = 0; + for (int i = 0; i < nargs; i++) + nslots += irgen_slots_for_type(vcode_reg_type(vcode_get_arg(op, i))); + + j_send(g, 0, jit_value_from_handle(handle)); + j_send(g, 1, jit_value_from_int64(nslots)); + irgen_send_args(g, op, 2); - j_send(g, 0, closure); - j_send(g, 1, context); macro_exit(g, JIT_EXIT_FUNCTION_TRIGGER); g->map[vcode_get_result(op)] = j_recv(g, 0); diff --git a/src/jit/jit.h b/src/jit/jit.h index a80ed4b8..0f636667 100644 --- a/src/jit/jit.h +++ b/src/jit/jit.h @@ -87,6 +87,8 @@ void *jit_call_thunk(jit_t *j, vcode_unit_t unit, void *context, thunk_result_fn_t fn, void *arg); bool jit_fastcall(jit_t *j, jit_handle_t handle, jit_scalar_t *result, jit_scalar_t p1, jit_scalar_t p2, tlab_t *tlab); +bool jit_vfastcall(jit_t *j, jit_handle_t handle, jit_scalar_t *result, + unsigned nargs, const jit_scalar_t *inargs, tlab_t *tlab); tlab_t jit_null_tlab(jit_t *j); diff --git a/src/lower.c b/src/lower.c index b351a32f..387d1bf9 100644 --- a/src/lower.c +++ b/src/lower.c @@ -11194,6 +11194,56 @@ static bool can_use_transfer_signal(tree_t proc, driver_set_t *ds) return true; } +static vcode_reg_t lower_func_trigger(lower_unit_t *lu, tree_t fcall) +{ + tree_t decl = tree_ref(fcall); + + if (tree_subkind(decl) != S_USER) + return VCODE_INVALID_REG; + else if (tree_flags(decl) & TREE_F_IMPURE) + return VCODE_INVALID_REG; + + const int nparams = tree_params(fcall); + for (int i = 0; i < nparams; i++) { + tree_t p = tree_value(tree_param(fcall, i)); + const tree_kind_t kind = tree_kind(p); + if (kind != T_LITERAL && (kind != T_REF || class_of(p) != C_SIGNAL)) + return VCODE_INVALID_REG; + } + + ident_t name = tree_ident2(decl); + + vcode_reg_t *args LOCAL = xmalloc_array(nparams + 1, sizeof(vcode_reg_t)); + args[0] = lower_context_for_call(lu, name); + + for (int i = 0; i < nparams; i++) + args[i + 1] = lower_subprogram_arg(lu, fcall, i); + + return emit_function_trigger(name, args, nparams + 1); +} + +static vcode_reg_t lower_process_trigger(lower_unit_t *lu, tree_t proc) +{ + if (tree_stmts(proc) != 2) + return VCODE_INVALID_REG; + + // Preconditions + assert(tree_kind(tree_stmt(proc, 1)) == T_WAIT); + assert(tree_flags(tree_stmt(proc, 1)) & TREE_F_STATIC_WAIT); + + tree_t s0 = tree_stmt(proc, 0); + if (tree_kind(s0) != T_IF) + return VCODE_INVALID_REG; + else if (tree_conds(s0) != 1) + return VCODE_INVALID_REG; + + tree_t value = tree_value(tree_cond(s0, 0)); + if (tree_kind(value) != T_FCALL) + return VCODE_INVALID_REG; + + return lower_func_trigger(lu, value); +} + void lower_process(lower_unit_t *parent, tree_t proc, driver_set_t *ds) { assert(tree_kind(proc) == T_PROCESS); @@ -11279,6 +11329,10 @@ void lower_process(lower_unit_t *parent, tree_t proc, driver_set_t *ds) const int ntriggers = tree_triggers(wait); for (int i = 0; i < ntriggers; i++) lower_sched_event(lu, tree_trigger(wait, i), VCODE_INVALID_REG); + + vcode_reg_t trigger_reg = lower_process_trigger(lu, proc); + if (trigger_reg != VCODE_INVALID_REG) + emit_add_trigger(trigger_reg); } } diff --git a/src/psl/psl-lower.c b/src/psl/psl-lower.c index 20fbf97b..8dc4f7da 100644 --- a/src/psl/psl-lower.c +++ b/src/psl/psl-lower.c @@ -279,9 +279,8 @@ static void psl_lower_clock_decl(unit_registry_t *ur, lower_unit_t *parent, vcode_var_t var = emit_var(vtrigger, vtrigger, label, 0); vcode_reg_t context_reg = emit_context_upref(0); - vcode_reg_t closure_reg = emit_closure(name, context_reg, - VCODE_INVALID_TYPE, vtype_bool()); - vcode_reg_t trigger_reg = emit_function_trigger(closure_reg); + vcode_reg_t args[] = { context_reg }; + vcode_reg_t trigger_reg = emit_function_trigger(name, args, ARRAY_LEN(args)); emit_store(trigger_reg, var); lower_put_vcode_obj(p, var, parent); diff --git a/src/rt/model.c b/src/rt/model.c index 8e5d5160..48ea3557 100644 --- a/src/rt/model.c +++ b/src/rt/model.c @@ -58,6 +58,7 @@ typedef enum { #define MEMBLOCK_LINE_SZ 64 #define MEMBLOCK_PAGE_SZ 0x800000 +#define TRIGGER_TAB_SIZE 64 typedef struct _memblock { memblock_t *chain; @@ -121,6 +122,7 @@ typedef struct _rt_model { model_thread_t *threads[MAX_THREADS]; ptr_list_t eventsigs; bool shuffle; + rt_trigger_t *triggertab[TRIGGER_TAB_SIZE]; } rt_model_t; #define FMT_VALUES_SZ 128 @@ -2636,19 +2638,20 @@ static void wakeup_one(rt_model_t *m, rt_wakeable_t *obj) return; // Already scheduled if (obj->trigger != NULL) { - tlab_t tlab = jit_null_tlab(m->jit); - jit_scalar_t result, - arg1 = { .pointer = obj->trigger->closure.context }, - arg2 = { .integer = 0 }; + rt_trigger_t *t = obj->trigger; + + if (t->when != m->now || t->iteration != m->iteration) { + tlab_t tlab = jit_null_tlab(m->jit); + jit_vfastcall(m->jit, t->handle, &t->result, t->nargs, t->args, &tlab); - jit_fastcall(m->jit, obj->trigger->closure.handle, &result, - arg1, arg2, &tlab); + TRACE("run trigger %p %s ==> %"PRIi64, t, + istr(jit_get_name(m->jit, t->handle)), t->result.integer); - TRACE("run trigger %s ==> %"PRIi64, - istr(jit_get_name(m->jit, obj->trigger->closure.handle)), - result.integer); + t->when = m->now; + t->iteration = m->iteration; + } - if (result.integer == 0) + if (t->result.integer == 0) return; // Filtered } @@ -4253,17 +4256,37 @@ void x_process_init(jit_handle_t handle, tree_t where) list_add(&s->procs, p); } -void *x_function_trigger(const ffi_closure_t *closure) +void *x_function_trigger(jit_handle_t handle, unsigned nargs, + const jit_scalar_t *args) { rt_model_t *m = get_model(); - TRACE("function trigger %s context %p", - istr(jit_get_name(m->jit, closure->handle)), closure->context); + uint64_t hash = mix_bits_32(handle); + for (int i = 0; i < nargs; i++) + hash ^= mix_bits_64(args[i].integer); + + TRACE("function trigger %s nargs=%u hash=%"PRIx64, + istr(jit_get_name(m->jit, handle)), nargs, hash); + + rt_trigger_t *exist = m->triggertab[hash % TRIGGER_TAB_SIZE]; + if (exist != NULL) { + bool hit = exist->handle == handle && exist->nargs == nargs; + for (int i = 0; hit && i < nargs; i++) + hit &= (exist->args[i].integer == args[i].integer); + + if (hit) + return exist; + } + + const size_t argsz = nargs * sizeof(jit_scalar_t); - rt_trigger_t *t = static_alloc(m, sizeof(rt_trigger_t)); - t->closure = *closure; + rt_trigger_t *t = static_alloc(m, sizeof(rt_trigger_t) + argsz); + t->handle = handle; + t->nargs = nargs; + t->when = TIME_HIGH; + memcpy(t->args, args, argsz); - return t; + return (m->triggertab[hash % TRIGGER_TAB_SIZE] = t); } void x_add_trigger(void *ptr) @@ -4271,7 +4294,7 @@ void x_add_trigger(void *ptr) rt_model_t *m = get_model(); TRACE("add trigger %p %s", ptr, - istr(jit_get_name(m->jit, ((rt_trigger_t *)ptr)->closure.handle))); + istr(jit_get_name(m->jit, ((rt_trigger_t *)ptr)->handle))); rt_wakeable_t *obj = get_active_wakeable(); assert(obj->trigger == NULL); diff --git a/src/rt/structs.h b/src/rt/structs.h index 08814a1c..995ce69c 100644 --- a/src/rt/structs.h +++ b/src/rt/structs.h @@ -35,7 +35,12 @@ typedef enum { typedef uint32_t wakeup_gen_t; typedef struct { - ffi_closure_t closure; + jit_handle_t handle; + unsigned nargs; + uint64_t when; + unsigned iteration; + jit_scalar_t result; + jit_scalar_t args[0]; } rt_trigger_t; typedef struct { diff --git a/src/vcode.c b/src/vcode.c index e3014ac0..cbd5fac4 100644 --- a/src/vcode.c +++ b/src/vcode.c @@ -49,7 +49,8 @@ DECLARE_AND_DEFINE_ARRAY(vcode_type); #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 \ - || x == VCODE_OP_PACKAGE_INIT || x == VCODE_OP_PROCESS_INIT) + || x == VCODE_OP_PACKAGE_INIT || x == VCODE_OP_PROCESS_INIT \ + || x == VCODE_OP_FUNCTION_TRIGGER) #define OP_HAS_IDENT(x) \ (x == VCODE_OP_LINK_VAR || x == VCODE_OP_LINK_PACKAGE \ || x == VCODE_OP_DEBUG_LOCUS || x == VCODE_OP_LINK_INSTANCE) @@ -2244,8 +2245,12 @@ void vcode_dump_with_mark(int mark_op, vcode_dump_fn_t callback, void *arg) case VCODE_OP_FUNCTION_TRIGGER: { col += vcode_dump_reg(op->result); - col += color_printf(" := %s ", vcode_op_string(op->kind)); - col += vcode_dump_reg(op->args.items[0]); + col += color_printf(" := %s $magenta$%s$$ ", + vcode_op_string(op->kind), istr(op->func)); + for (int i = 0; i < op->args.count; i++) { + if (i > 0) col += printf(", "); + col += vcode_dump_reg(op->args.items[i]); + } vcode_dump_result_type(col, op); } break; @@ -5869,13 +5874,14 @@ vcode_reg_t emit_reflect_subtype(ident_t ptype, vcode_reg_t context, return (op->result = vcode_add_reg(vtype_access(vtype_context(ptype)))); } -vcode_reg_t emit_function_trigger(vcode_reg_t closure) +vcode_reg_t emit_function_trigger(ident_t func, const vcode_reg_t *args, + int nargs) { op_t *op = vcode_add_op(VCODE_OP_FUNCTION_TRIGGER); - vcode_add_arg(op, closure); + op->func = func; - VCODE_ASSERT(vcode_reg_kind(closure) == VCODE_TYPE_CLOSURE, - "argument to function trigger must be a closure"); + for (int i = 0; i < nargs; i++) + vcode_add_arg(op, args[i]); return (op->result = vcode_add_reg(vtype_trigger())); } @@ -5971,6 +5977,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: + case VCODE_OP_FUNCTION_TRIGGER: (*fn)(vcode_get_func(op), ctx); break; default: diff --git a/src/vcode.h b/src/vcode.h index f5ac6189..d7e92aa2 100644 --- a/src/vcode.h +++ b/src/vcode.h @@ -516,7 +516,8 @@ vcode_reg_t emit_reflect_value(ident_t ptype, vcode_reg_t value, vcode_reg_t bounds); vcode_reg_t emit_reflect_subtype(ident_t ptype, vcode_reg_t context, vcode_reg_t locus, vcode_reg_t bounds); -vcode_reg_t emit_function_trigger(vcode_reg_t closure); +vcode_reg_t emit_function_trigger(ident_t func, const vcode_reg_t *args, + int nargs); 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); diff --git a/test/lower/trigger1.vhd b/test/lower/trigger1.vhd new file mode 100644 index 00000000..50a55aa8 --- /dev/null +++ b/test/lower/trigger1.vhd @@ -0,0 +1,20 @@ +entity trigger1 is +end entity; + +architecture test of trigger1 is + function rising (signal x : bit) return boolean is + begin + return x'event and x = '1'; + end function; + + signal clk, x : bit; +begin + + p1: process (clk) is + begin + if rising(clk) then + x <= not x; + end if; + end process; + +end architecture; diff --git a/test/test_lower.c b/test/test_lower.c index b77bcdc3..e4a39ceb 100644 --- a/test/test_lower.c +++ b/test/test_lower.c @@ -90,6 +90,7 @@ static void check_bb(int bb, const check_bb_t *expect, int len) // Fall-through case VCODE_OP_FCALL: case VCODE_OP_CLOSURE: + case VCODE_OP_FUNCTION_TRIGGER: if (!fuzzy_cmp(vcode_get_func(i), e->func)) { vcode_dump_with_mark(i, NULL, NULL); fail("expected op %d in block %d to call %s but calls %s", @@ -248,6 +249,7 @@ static void check_bb(int bb, const check_bb_t *expect, int len) case VCODE_OP_TRAP_EXP: case VCODE_OP_PUSH_SCOPE: case VCODE_OP_POP_SCOPE: + case VCODE_OP_ADD_TRIGGER: break; case VCODE_OP_CONST_ARRAY: @@ -6030,6 +6032,35 @@ START_TEST(test_issue844) } END_TEST +START_TEST(test_trigger1) +{ + input_from_file(TESTDIR "/lower/trigger1.vhd"); + + run_elab(); + + { + vcode_unit_t vu = find_unit("WORK.TRIGGER1.P1"); + vcode_select_unit(vu); + + EXPECT_BB(0) = { + { VCODE_OP_VAR_UPREF, .name = "X", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_DRIVE_SIGNAL }, + { VCODE_OP_VAR_UPREF, .name = "CLK", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_SCHED_EVENT }, + { VCODE_OP_CONTEXT_UPREF, .hops = 1 }, + { VCODE_OP_FUNCTION_TRIGGER, .func = "WORK.TRIGGER1.RISING(sJ)B" }, + { VCODE_OP_ADD_TRIGGER }, + { VCODE_OP_RETURN }, + }; + + CHECK_BB(0); + } +} +END_TEST + Suite *get_lower_tests(void) { Suite *s = suite_create("lower"); @@ -6174,6 +6205,7 @@ Suite *get_lower_tests(void) tcase_add_test(tc, test_issue837); tcase_add_test(tc, test_directmap6); tcase_add_test(tc, test_issue844); + tcase_add_test(tc, test_trigger1); suite_add_tcase(s, tc); return s; -- 2.39.2