Skip to content

Commit cf1b5a9

Browse files
authored
Merge pull request #2535 from veryl-lang/sim_fix12_port
Improve simulator performance
2 parents 717684b + ae34d89 commit cf1b5a9

16 files changed

Lines changed: 326 additions & 111 deletions

File tree

crates/analyzer/src/conv/declaration.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ impl Conv<&GenerateForDeclaration> for ir::DeclarationBlock {
319319

320320
let block = context.block(|c| {
321321
let id = c.insert_var_path(path.clone(), comptime.clone());
322+
let array_limit = c.config.evaluate_array_limit;
322323
let variable = Variable::new(
323324
id,
324325
path,
@@ -327,6 +328,7 @@ impl Conv<&GenerateForDeclaration> for ir::DeclarationBlock {
327328
vec![comptime.get_value().unwrap().clone()],
328329
c.get_affiliation(),
329330
&token,
331+
array_limit,
330332
);
331333
c.insert_variable(id, variable);
332334

@@ -561,6 +563,7 @@ impl Conv<&PortDeclarationItem> for () {
561563

562564
// TODO for array
563565
let id = context.insert_var_path(path.clone(), comptime);
566+
let array_limit = context.config.evaluate_array_limit;
564567
let variable = Variable::new(
565568
id,
566569
path,
@@ -569,6 +572,7 @@ impl Conv<&PortDeclarationItem> for () {
569572
vec![value.clone()],
570573
context.get_affiliation(),
571574
&variable_token,
575+
array_limit,
572576
);
573577
context.insert_variable(id, variable);
574578
} else {
@@ -981,17 +985,17 @@ impl Conv<(&FunctionDeclaration, Option<&FuncPath>)> for () {
981985
let kind = VarKind::Variable;
982986
let r#type = ret_type.r#type.clone();
983987

984-
if let Some(total_array) = r#type.total_array()
988+
if let Some(_total_array) = r#type.total_array()
985989
&& let Some(total_width) = r#type.total_width()
986990
{
987991
// type.expand is not necessary
988992
// because member access is not allowed for return value
989-
let mut values = vec![];
990-
for _ in 0..total_array {
991-
values.push(Value::new_x(total_width, false));
992-
}
993+
// All elements are initialized to the same x-state
994+
// value, so a single template suffices.
995+
let values = vec![Value::new_x(total_width, false)];
993996

994997
let ret_id = c.insert_var_path(path.clone(), ret_type.clone());
998+
let array_limit = c.config.evaluate_array_limit;
995999
let variable = Variable::new(
9961000
ret_id,
9971001
path,
@@ -1000,6 +1004,7 @@ impl Conv<(&FunctionDeclaration, Option<&FuncPath>)> for () {
10001004
values,
10011005
c.get_affiliation(),
10021006
&token,
1007+
array_limit,
10031008
);
10041009
c.insert_variable(ret_id, variable);
10051010
Some(ret_id)

crates/analyzer/src/conv/statement.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ fn unroll_for(
704704
}
705705

706706
let id = c.insert_var_path(path.clone(), comptime.clone());
707+
let array_limit = c.config.evaluate_array_limit;
707708
let variable = ir::Variable::new(
708709
id,
709710
path,
@@ -712,6 +713,7 @@ fn unroll_for(
712713
vec![comptime.get_value().unwrap().clone()],
713714
c.get_affiliation(),
714715
&token,
716+
array_limit,
715717
);
716718
c.insert_variable(id, variable);
717719

crates/analyzer/src/conv/utils.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ pub fn eval_const_assign(
630630
if let Some(exprs) = exprs {
631631
let values = eval_array_literal_expressions(context, r#type, exprs, token)?;
632632
let id = context.insert_var_path(path.clone(), comptime);
633+
let array_limit = context.config.evaluate_array_limit;
633634
let variable = Variable::new(
634635
id,
635636
path.clone(),
@@ -638,6 +639,7 @@ pub fn eval_const_assign(
638639
values,
639640
context.get_affiliation(),
640641
&dst.token,
642+
array_limit,
641643
);
642644
context.insert_variable(id, variable);
643645
} else {
@@ -667,6 +669,7 @@ pub fn eval_const_assign(
667669
value.trunc(total_width);
668670
}
669671

672+
let array_limit = context.config.evaluate_array_limit;
670673
let variable = Variable::new(
671674
id,
672675
path.clone(),
@@ -675,6 +678,7 @@ pub fn eval_const_assign(
675678
vec![value],
676679
context.get_affiliation(),
677680
&dst.token,
681+
array_limit,
678682
);
679683
context.insert_variable(id, variable);
680684
}
@@ -709,14 +713,12 @@ pub fn eval_variable(
709713
let signed = comptime.r#type.signed;
710714
let id = context.insert_var_path(path.clone(), comptime);
711715

712-
let values = if let Some(total_array) = r#type.total_array()
713-
&& let Some(total_width) = r#type.total_width()
714-
{
715-
let mut values = vec![];
716-
for _ in 0..total_array {
717-
values.push(Value::new_x(total_width, signed));
718-
}
719-
values
716+
// Every element starts as the same x-state template, so store it once;
717+
// the simulator replicates it across the full `r#type.total_array()` at
718+
// fill time. Const/param arrays with per-element literals go through
719+
// `eval_array_literal` instead and keep their Vec<Value> intact.
720+
let values = if let Some(total_width) = r#type.total_width() {
721+
vec![Value::new_x(total_width, signed)]
720722
} else {
721723
vec![]
722724
};
@@ -729,6 +731,7 @@ pub fn eval_variable(
729731
context.insert_var_path_with_id(path, id, comptime);
730732
}
731733

734+
let array_limit = context.config.evaluate_array_limit;
732735
let variable = Variable::new(
733736
id,
734737
path.clone(),
@@ -737,6 +740,7 @@ pub fn eval_variable(
737740
values,
738741
context.get_affiliation(),
739742
&token,
743+
array_limit,
740744
);
741745
context.insert_variable(id, variable);
742746
}
@@ -1438,6 +1442,7 @@ pub fn build_for_statement(
14381442
} else {
14391443
vec![]
14401444
};
1445+
let array_limit = context.config.evaluate_array_limit;
14411446
let variable = Variable::new(
14421447
loop_var_id,
14431448
path,
@@ -1446,6 +1451,7 @@ pub fn build_for_statement(
14461451
values,
14471452
context.get_affiliation(),
14481453
&token,
1454+
array_limit,
14491455
);
14501456
context.insert_variable(loop_var_id, variable);
14511457

crates/analyzer/src/ir/assign_table.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ mod tests {
444444
vec![],
445445
Affiliation::Module,
446446
&TokenRange::default(),
447+
context.config.evaluate_array_limit,
447448
);
448449
let variable = VariableInfo::new(&variable);
449450

crates/analyzer/src/ir/expression.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,17 @@ impl Factor {
733733
) {
734734
match self {
735735
Factor::Variable(id, index, select, _) => {
736+
// `insert_reference` bails out on arrays over `array_limit`;
737+
// short-circuit to avoid cloning the full Variable (value
738+
// vec scales with array size).
739+
let total_array = context
740+
.variables
741+
.get(id)
742+
.map(|v| v.r#type.total_array().unwrap_or(0))
743+
.unwrap_or(0);
744+
if total_array > assign_table.array_limit {
745+
return;
746+
}
736747
if let Some(index) = index.eval_value(context)
737748
&& let Some(variable) = context.variables.get(id).cloned()
738749
&& let Some((beg, end)) = select.eval_value(context, &variable.r#type, false)

crates/analyzer/src/ir/statement.rs

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -465,28 +465,38 @@ impl AssignDestination {
465465

466466
let mut errors = vec![];
467467
if let Some((beg, end)) = range {
468-
for i in beg..=end {
469-
let index = VarIndex::from_index(i, &variable.r#type.array);
470-
if let Some(index) = index.eval_value(context) {
471-
if assign_context.is_comb()
472-
&& assign_table.check_refered(&variable.id, &index, &mask)
473-
{
474-
let mut text = variable.path.to_string();
475-
for i in &index {
476-
text.push_str(&format!("[{i}]"));
468+
// `insert_assign` / `check_refered` both bail out on arrays
469+
// over `array_limit`; iterating beg..=end just to hit that
470+
// guard is pure waste when the index is non-const.
471+
let array_size = end.saturating_sub(beg).saturating_add(1);
472+
let skip_large_array = !is_index_const
473+
&& variable.r#type.total_array().unwrap_or(0) > assign_table.array_limit
474+
&& array_size > assign_table.array_limit;
475+
476+
if !skip_large_array {
477+
for i in beg..=end {
478+
let index = VarIndex::from_index(i, &variable.r#type.array);
479+
if let Some(index) = index.eval_value(context) {
480+
if assign_context.is_comb()
481+
&& assign_table.check_refered(&variable.id, &index, &mask)
482+
{
483+
let mut text = variable.path.to_string();
484+
for i in &index {
485+
text.push_str(&format!("[{i}]"));
486+
}
487+
// ignore `#[allow(unassign_variable)]` attribute
488+
errors.push(AnalyzerError::unassign_variable(&text, &self.token));
477489
}
478-
// ignore `#[allow(unassign_variable)]` attribute
479-
errors.push(AnalyzerError::unassign_variable(&text, &self.token));
480-
}
481490

482-
let maybe = !is_const | assign_context.is_system_verilog();
483-
let _ = assign_table.insert_assign(
484-
&variable,
485-
index,
486-
mask.clone(),
487-
maybe,
488-
self.token,
489-
);
491+
let maybe = !is_const | assign_context.is_system_verilog();
492+
let _ = assign_table.insert_assign(
493+
&variable,
494+
index,
495+
mask.clone(),
496+
maybe,
497+
self.token,
498+
);
499+
}
490500
}
491501
}
492502
}

crates/analyzer/src/ir/variable.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ pub struct Variable {
741741
}
742742

743743
impl Variable {
744+
#[allow(clippy::too_many_arguments)]
744745
pub fn new(
745746
id: VarId,
746747
path: VarPath,
@@ -749,11 +750,19 @@ impl Variable {
749750
value: Vec<Value>,
750751
affiliation: Affiliation,
751752
token: &TokenRange,
753+
array_limit: usize,
752754
) -> Self {
753-
let mut assigned = vec![];
754-
for _ in 0..value.len() {
755-
assigned.push(0u32.into());
756-
}
755+
// `assigned` tracks per-element coverage and must match the full
756+
// array length (not `value.len()`, since `value` may hold a single
757+
// template entry for all-same init). `set_assigned` / `unassigned()`
758+
// callers gate on `> array_limit` before touching `assigned`, so
759+
// arrays past the limit can leave the vec empty.
760+
let total_array = r#type.total_array().unwrap_or(value.len()).max(value.len());
761+
let assigned: Vec<BigUint> = if total_array > array_limit {
762+
Vec::new()
763+
} else {
764+
vec![0u32.into(); total_array]
765+
};
757766

758767
Self {
759768
id,
@@ -866,8 +875,23 @@ impl fmt::Display for Variable {
866875
}
867876
r#type.array.clear();
868877

869-
let is_array = self.value.len() != 1;
870-
for (i, value) in self.value.iter().enumerate() {
878+
// Template form (`value.len() == 1 && total_array > 1`) means every
879+
// element shares value[0]; otherwise treat value.len() as the
880+
// effective element count.
881+
let type_len = self.r#type.total_array();
882+
let is_template = self.value.len() == 1 && matches!(type_len, Some(n) if n > 1);
883+
let display_len = if is_template {
884+
type_len.unwrap()
885+
} else {
886+
self.value.len()
887+
};
888+
let is_array = display_len != 1;
889+
for i in 0..display_len {
890+
let value = if is_template {
891+
&self.value[0]
892+
} else {
893+
&self.value[i]
894+
};
871895
if is_array {
872896
ret.push_str(&format!(
873897
"{} {}[{}]({}): ",

crates/simulator/src/cranelift.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ fn build_binary_inner(
172172
if !config.dump_cranelift {
173173
settings_builder.set("enable_verifier", "false").unwrap();
174174
}
175+
// Disable alias analysis for unified comb (no_cache path) to avoid
176+
// incorrect cross-block load CSE that conflicts with the simulator's
177+
// own comb evaluation semantics.
178+
if disable_load_cache {
179+
settings_builder
180+
.set("enable_alias_analysis", "false")
181+
.unwrap();
182+
}
175183
let flags = settings::Flags::new(settings_builder);
176184

177185
let isa = match isa::lookup(Triple::host()) {

crates/simulator/src/ir/expression.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,12 +1393,20 @@ impl ProtoExpression {
13931393
let cache_key = *var_offset;
13941394
let wide = read_width > 64;
13951395

1396-
// Load CSE: reuse previously loaded values for the same address
1396+
// Load CSE: reuse previously loaded values for the same address.
1397+
// For nb==4 variables, mask cached values to 32 bits to match
1398+
// the I32 load + uextend behavior of fresh loads.
13971399
let (mut payload, mut mask_xz) = if !context.disable_load_cache
13981400
&& let Some(&(cached_payload, cached_mask_xz)) =
13991401
context.load_cache.get(&cache_key)
14001402
{
1401-
(cached_payload, cached_mask_xz)
1403+
if nb == 4 {
1404+
let p = builder.ins().band_imm(cached_payload, 0xFFFFFFFF_i64);
1405+
let m = cached_mask_xz.map(|v| builder.ins().band_imm(v, 0xFFFFFFFF_i64));
1406+
(p, m)
1407+
} else {
1408+
(cached_payload, cached_mask_xz)
1409+
}
14021410
} else {
14031411
let load_mem_flag = MemFlags::trusted();
14041412

crates/simulator/src/ir/module.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,18 @@ fn fill_buffers_recursive(
8484
sorted.sort_by_key(|(k, _)| **k);
8585

8686
for (_, meta) in &sorted {
87-
for (element, initial) in meta.elements.iter().zip(meta.initial_values.iter()) {
87+
// Single-entry initial_values on a multi-element variable is the
88+
// compact template form used for large arrays.
89+
let template_mode = meta.initial_values.len() == 1 && meta.elements.len() > 1;
90+
for (i, element) in meta.elements.iter().enumerate() {
91+
let initial = if template_mode {
92+
&meta.initial_values[0]
93+
} else {
94+
match meta.initial_values.get(i) {
95+
Some(v) => v,
96+
None => continue,
97+
}
98+
};
8899
let nb = element.native_bytes;
89100
let _vs = value_size(nb, use_4state);
90101
if element.is_ff() {

0 commit comments

Comments
 (0)