Skip to content

Commit

Permalink
Collapse output ports in restricted circumstances
Browse files Browse the repository at this point in the history
  • Loading branch information
nickg committed Aug 13, 2023
1 parent 2034d8d commit 967f50a
Show file tree
Hide file tree
Showing 12 changed files with 290 additions and 50 deletions.
118 changes: 118 additions & 0 deletions src/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
#include "driver.h"
#include "hash.h"
#include "ident.h"
#include "mask.h"
#include "option.h"
#include "phase.h"
#include "tree.h"
#include "type.h"

#include <assert.h>
#include <stdlib.h>
Expand Down Expand Up @@ -327,6 +329,122 @@ driver_info_t *get_drivers(driver_set_t *ds, tree_t what)
return hash_get(ds->map, what);
}

bool has_unique_driver(driver_set_t *ds, tree_t what)
{
// True if signal has exactly one driver for each sub-element

driver_info_t *di = get_drivers(ds, what);

if (di == NULL)
return false;

// First pass: look for assignments to the full signal

bool saw_ref = false, saw_multiple = false;
tree_t proc = di->where;
for (driver_info_t *it = di; it; it = it->chain_decl) {
if (it->where != proc)
saw_multiple = true;
else if (tree_kind(it->prefix) == T_REF)
saw_ref = true;
}

if (saw_ref && !saw_multiple)
return true;

type_t type = tree_type(what);
int64_t length = 0, left = 0, right = 0;
range_kind_t rkind = RANGE_ERROR;
if (type_is_array(type) && dimension_of(type) == 1) {
tree_t r = range_of(type, 0);
if (folded_length(r, &length)) {
left = assume_int(tree_left(r));
right = assume_int(tree_right(r));
rkind = tree_subkind(r);
}
}
else if (type_is_record(type))
length = type_fields(type);

if (length == 0)
return false;

// Second pass: look for assignments to disjoint sub-elements

bit_mask_t mask = {};
mask_init(&mask, length);

bool covered = false;
for (driver_info_t *it = di; it; it = it->chain_decl) {
const tree_kind_t kind = tree_kind(it->prefix);
if (kind != T_ARRAY_REF && kind != T_ARRAY_SLICE && kind != T_RECORD_REF)
goto out_free;

tree_t value = tree_value(it->prefix);
if (tree_kind(value) != T_REF)
goto out_free;

assert(tree_ref(value) == what);

switch (kind) {
case T_ARRAY_REF:
{
assert(tree_params(it->prefix) == 1);
tree_t p0 = tree_value(tree_param(it->prefix, 0));

int64_t ival;
if (!folded_int(p0, &ival))
goto out_free;

const int zero = rkind == RANGE_TO ? ival - left : ival - right;
if (mask_test(&mask, zero))
goto out_free;

mask_set(&mask, zero);
}
break;

case T_ARRAY_SLICE:
{
tree_t r = tree_range(it->prefix, 0);

int64_t low, high;
if (!folded_bounds(r, &low, &high) || high < low)
goto out_free;

const int count = high - low + 1;
const int zero = rkind == RANGE_TO ? low - left : low - right;

if (mask_test_range(&mask, zero, count))
goto out_free;

mask_set_range(&mask, zero, count);
}
break;

case T_RECORD_REF:
{
const int pos = tree_pos(tree_ref(it->prefix));

if (mask_test(&mask, pos))
goto out_free;

mask_set(&mask, pos);
}
break;

default:
goto out_free;
}
}

covered = (mask_popcount(&mask) == length);

out_free:
mask_free(&mask);
return covered;
}

LCOV_EXCL_START
void dump_drivers(driver_set_t *ds)
{
Expand Down
1 change: 1 addition & 0 deletions src/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ typedef struct _driver_info {

driver_set_t *find_drivers(tree_t block);
driver_info_t *get_drivers(driver_set_t *ds, tree_t what);
bool has_unique_driver(driver_set_t *ds, tree_t what);
void free_drivers(driver_set_t *ds);
void dump_drivers(driver_set_t *ds);

Expand Down
22 changes: 11 additions & 11 deletions src/elab.c
Original file line number Diff line number Diff line change
Expand Up @@ -1241,10 +1241,20 @@ static void elab_inherit_context(elab_ctx_t *ctx, const elab_ctx_t *parent)
ctx->inst = ctx->inst ?: parent->inst;
}

static driver_set_t *elab_driver_set(const elab_ctx_t *ctx)
{
if (ctx->drivers != NULL)
return ctx->drivers;
else if (ctx->parent != NULL)
return elab_driver_set(ctx->parent);
else
return NULL;
}

static void elab_lower(tree_t b, elab_ctx_t *ctx)
{
ctx->lowered = lower_instance(ctx->registry, ctx->parent->lowered,
ctx->cover, b);
elab_driver_set(ctx), ctx->cover, b);

if (ctx->cover != NULL)
eval_alloc_cover_mem(ctx->jit, ctx->cover);
Expand Down Expand Up @@ -1877,16 +1887,6 @@ static void elab_case_generate(tree_t t, const elab_ctx_t *ctx)
elab_pop_scope(&new_ctx);
}

static driver_set_t *elab_driver_set(const elab_ctx_t *ctx)
{
if (ctx->drivers != NULL)
return ctx->drivers;
else if (ctx->parent != NULL)
return elab_driver_set(ctx->parent);
else
return NULL;
}

static void elab_process(tree_t t, const elab_ctx_t *ctx)
{
elab_external_names(t, ctx);
Expand Down
52 changes: 42 additions & 10 deletions src/lower.c
Original file line number Diff line number Diff line change
Expand Up @@ -5002,13 +5002,17 @@ static vcode_reg_t lower_default_value(lower_unit_t *lu, type_t type,
return emit_address_of(lower_nested_default_value(lu, type));
}

assert(hint_reg != VCODE_INVALID_REG);

vcode_reg_t count_reg =
lower_array_total_len(lu, type, VCODE_INVALID_REG);
vcode_reg_t def_reg =
lower_default_value(lu, elem_type, VCODE_INVALID_REG);

if (hint_reg == VCODE_INVALID_REG) {
vcode_type_t velem = lower_type(elem_type);
vcode_type_t vbounds = lower_bounds(elem_type);
hint_reg = emit_alloc(velem, vbounds, count_reg);
}

if (type_is_scalar(elem_type))
emit_memset(hint_reg, def_reg, count_reg);
else {
Expand Down Expand Up @@ -10714,6 +10718,8 @@ static void lower_map_signal_field_cb(lower_unit_t *lu, tree_t field,
src_reg = emit_load_indirect(dst_ptr);
else if (!args->is_const)
src_reg = emit_load_indirect(src_ptr);
else if (have_uarray_ptr(src_ptr))
src_reg = emit_load_indirect(src_ptr);
else
src_reg = src_ptr;

Expand Down Expand Up @@ -11014,8 +11020,9 @@ static void lower_port_map(lower_unit_t *lu, tree_t block, tree_t map,
}
}

static bool lower_direct_mapped_port(lower_unit_t *lu, tree_t block, tree_t map,
hset_t *direct, hset_t **poison)
static bool lower_direct_mapped_port(lower_unit_t *lu, driver_set_t *ds,
tree_t block, tree_t map, hset_t *direct,
hset_t **poison)
{
tree_t port = NULL;
int field = -1;
Expand Down Expand Up @@ -11046,8 +11053,18 @@ static bool lower_direct_mapped_port(lower_unit_t *lu, tree_t block, tree_t map,
assert(tree_kind(port) == T_PORT_DECL);

const class_t class = tree_class(port);
if (class == C_SIGNAL && tree_subkind(port) != PORT_IN)
return false;
if (class == C_SIGNAL) {
switch (tree_subkind(port)) {
case PORT_IN:
break; // Always safe
case PORT_OUT:
if (!has_unique_driver(ds, port))
return false;
break;
default:
return false;
}
}

tree_t value = tree_value(map);

Expand Down Expand Up @@ -11212,7 +11229,7 @@ static vcode_reg_t lower_constrain_port(lower_unit_t *lu, tree_t port, int pos,
istr(tree_ident(port)));
}

static void lower_ports(lower_unit_t *lu, tree_t block)
static void lower_ports(lower_unit_t *lu, driver_set_t *ds, tree_t block)
{
const int nports = tree_ports(block);
const int nparams = tree_params(block);
Expand All @@ -11227,7 +11244,8 @@ static void lower_ports(lower_unit_t *lu, tree_t block)
for (int i = 0; i < nparams; i++) {
tree_t p = tree_param(block, i);

if (collapse && lower_direct_mapped_port(lu, block, p, direct, &poison))
if (collapse && lower_direct_mapped_port(lu, ds, block,
p, direct, &poison))
map_regs[i] = VCODE_INVALID_REG;
else {
tree_t value = tree_value(p);
Expand All @@ -11253,6 +11271,19 @@ static void lower_ports(lower_unit_t *lu, tree_t block)
lower_port_signal(lu, port, bounds_reg);
else if (poison != NULL && hset_contains(poison, port))
lower_port_signal(lu, port, bounds_reg);
else if (tree_class(port) == C_SIGNAL && tree_subkind(port) != PORT_IN) {
// Any drivers for collapsed output ports must take their
// initial value from the port declaration
vcode_reg_t def_reg;
if (tree_has_value(port))
def_reg = lower_rvalue(lu, tree_value(port));
else
def_reg = lower_default_value(lu, type, VCODE_INVALID_REG);

vcode_reg_t nets_reg = lower_port_ref(lu, port);
lower_map_signal(lu, def_reg, nets_reg, type, type,
VCODE_INVALID_REG, port);
}
}

for (int i = 0; i < nparams; i++) {
Expand Down Expand Up @@ -11730,7 +11761,8 @@ vcode_unit_t lower_thunk(lower_unit_t *parent, tree_t t)
}

lower_unit_t *lower_instance(unit_registry_t *ur, lower_unit_t *parent,
cover_tagging_t *cover, tree_t block)
driver_set_t *ds, cover_tagging_t *cover,
tree_t block)
{
assert(tree_kind(block) == T_BLOCK);

Expand All @@ -11753,7 +11785,7 @@ lower_unit_t *lower_instance(unit_registry_t *ur, lower_unit_t *parent,

lower_dependencies(block, tree_ref(hier));
lower_generics(lu, block);
lower_ports(lu, block);
lower_ports(lu, ds, block);
lower_decls(lu, block);

emit_return(VCODE_INVALID_REG);
Expand Down
3 changes: 2 additions & 1 deletion src/lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ vcode_reg_t lower_lvalue(lower_unit_t *lu, tree_t expr);
vcode_reg_t lower_rvalue(lower_unit_t *lu, tree_t expr);

lower_unit_t *lower_instance(unit_registry_t *ur, lower_unit_t *parent,
cover_tagging_t *cover, tree_t block);
driver_set_t *ds, cover_tagging_t *cover,
tree_t block);
void lower_process(lower_unit_t *parent, tree_t proc, driver_set_t *ds);
vcode_unit_t lower_thunk(lower_unit_t *parent, tree_t fcall);
vcode_unit_t lower_case_generate_thunk(lower_unit_t *parent, tree_t t);
Expand Down
40 changes: 40 additions & 0 deletions src/mask.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,46 @@ void mask_set_range(bit_mask_t *m, int start, int count)
}
}

bool mask_test_range(bit_mask_t *m, int start, int count)
{
if (m->size <= 64)
return !!(m->bits & mask_for_range(start, start + count - 1));

if (count > 0 && start % 64 != 0) {
// Pre-loop: test range of bits in first 64-bit word
const int low = start % 64;
const int high = MIN(low + count - 1, 63);
const int nbits = high - low + 1;

if (m->ptr[start / 64] & mask_for_range(low, high))
return true;

start += nbits;
count -= nbits;
}

if (count > 0) {
// Main loop: test complete 64-bit words
for (; count >= 64; count -= 64, start += 64) {
if (m->ptr[start / 64])
return true;
}
}

if (count > 0) {
// Post-loop: test range of bits in last 64-bit word
assert(start % 64 == 0);
const int high = MIN(count - 1, 63);

if (m->ptr[start / 64] & mask_for_range(0, high))
return true;

assert(count == high + 1);
}

return false;
}

int mask_popcount(bit_mask_t *m)
{
if (m->size > 64) {
Expand Down
1 change: 1 addition & 0 deletions src/mask.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void mask_init(bit_mask_t *m, size_t size);
void mask_free(bit_mask_t *m);
void mask_clear_range(bit_mask_t *m, int start, int count);
void mask_set_range(bit_mask_t *m, int start, int count);
bool mask_test_range(bit_mask_t *m, int start, int count);
int mask_popcount(bit_mask_t *m);
void mask_setall(bit_mask_t *m);
void mask_clearall(bit_mask_t *m);
Expand Down
Loading

0 comments on commit 967f50a

Please sign in to comment.