Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 53 additions & 25 deletions xlsynth-vastly/fuzz/fuzz_targets/diff_iverilog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use std::process::Stdio;
use std::time::Duration;
use std::time::SystemTime;

use xlsynth_vastly::ast::Expr;
use xlsynth_vastly::Env;
use xlsynth_vastly::LogicBit;
use xlsynth_vastly::Signedness;
use xlsynth_vastly::Value4;
use xlsynth_vastly::ast::Expr;

const MAX_BASED_LITERAL_WIDTH: u32 = 384;
const MAX_EXPR_AST_DEPTH: usize = 16;
Expand All @@ -35,8 +35,9 @@ fuzz_target!(|data: &[u8]| {
if expr.trim().is_empty() {
return;
}
// Extremely wide explicit based literals are not interesting signal for this target;
// they mainly drive pathological width-sensitive parser/evaluator costs.
// Extremely wide explicit based literals are not interesting signal for this
// target; they mainly drive pathological width-sensitive parser/evaluator
// costs.
if has_based_literal_width_over_limit(&expr, MAX_BASED_LITERAL_WIDTH) {
return;
}
Expand All @@ -48,8 +49,8 @@ fuzz_target!(|data: &[u8]| {
Err(_) => return,
};
let (depth, nodes) = expr_depth_and_node_count(&ast);
// Very deep/large parsed trees mostly surface evaluator performance limits rather than
// semantic mismatches for this differential target.
// Very deep/large parsed trees mostly surface evaluator performance limits
// rather than semantic mismatches for this differential target.
if depth > MAX_EXPR_AST_DEPTH || nodes > MAX_EXPR_AST_NODES {
return;
}
Expand All @@ -62,13 +63,15 @@ fuzz_target!(|data: &[u8]| {
// Feed a normalized rendering of the parsed AST into both evaluators.
let normalized_expr = render_expr(&ast);

let oracle = match run_oracle_with_timeout(&normalized_expr, &case.env, Duration::from_millis(250)) {
OracleOutcome::Accepted(x) => x,
// Oracle-side rejection/runtime failures are not sample failures for this differential check.
OracleOutcome::RejectedOrFailed => return,
// Timeout is infrastructure noise; skip instead of treating as semantic mismatch.
OracleOutcome::TimedOut => return,
};
let oracle =
match run_oracle_with_timeout(&normalized_expr, &case.env, Duration::from_millis(250)) {
OracleOutcome::Accepted(x) => x,
// Oracle-side rejection/runtime failures are not sample failures for this differential
// check.
OracleOutcome::RejectedOrFailed => return,
// Timeout is infrastructure noise; skip instead of treating as semantic mismatch.
OracleOutcome::TimedOut => return,
};

let ours = match xlsynth_vastly::eval_expr(&normalized_expr, &case.env) {
Ok(x) => x,
Expand Down Expand Up @@ -114,7 +117,13 @@ fn has_based_literal_width_over_limit(expr: &str, limit: u32) -> bool {
if j < bytes.len() && matches!(bytes[j], b's' | b'S') {
j += 1;
}
if j < bytes.len() && matches!(bytes[j], b'b' | b'B' | b'd' | b'D' | b'h' | b'H' | b'o' | b'O') && width > limit {
if j < bytes.len()
&& matches!(
bytes[j],
b'b' | b'B' | b'd' | b'D' | b'h' | b'H' | b'o' | b'O'
)
&& width > limit
{
return true;
}
}
Expand Down Expand Up @@ -144,6 +153,14 @@ fn expr_depth_and_node_count(expr: &Expr) -> (usize, usize) {
1 + count_nodes + expr_nodes,
)
}
Expr::Cast { width, expr } => {
let (width_depth, width_nodes) = expr_depth_and_node_count(width);
let (expr_depth, expr_nodes) = expr_depth_and_node_count(expr);
(
1 + width_depth.max(expr_depth),
1 + width_nodes + expr_nodes,
)
}
Expr::Index { expr, index } => {
let (expr_depth, expr_nodes) = expr_depth_and_node_count(expr);
let (index_depth, index_nodes) = expr_depth_and_node_count(index);
Expand All @@ -162,10 +179,7 @@ fn expr_depth_and_node_count(expr: &Expr) -> (usize, usize) {
)
}
Expr::IndexedSlice {
expr,
base,
width,
..
expr, base, width, ..
} => {
let (expr_depth, expr_nodes) = expr_depth_and_node_count(expr);
let (base_depth, base_nodes) = expr_depth_and_node_count(base);
Expand Down Expand Up @@ -226,6 +240,10 @@ fn visit_expr_idents(expr: &Expr, f: &mut dyn FnMut(&str)) {
visit_expr_idents(count, f);
visit_expr_idents(expr, f);
}
Expr::Cast { width, expr } => {
visit_expr_idents(width, f);
visit_expr_idents(expr, f);
}
Expr::Index { expr, index } => {
visit_expr_idents(expr, f);
visit_expr_idents(index, f);
Expand All @@ -236,10 +254,7 @@ fn visit_expr_idents(expr: &Expr, f: &mut dyn FnMut(&str)) {
visit_expr_idents(lsb, f);
}
Expr::IndexedSlice {
expr,
base,
width,
..
expr, base, width, ..
} => {
visit_expr_idents(expr, f);
visit_expr_idents(base, f);
Expand Down Expand Up @@ -299,6 +314,11 @@ fn render_expr(expr: &Expr) -> String {
let e = render_expr(expr);
format!("{{{c}{{{e}}}}}")
}
Expr::Cast { width, expr } => {
let w = render_expr(width);
let e = render_expr(expr);
format!("({w})'({e})")
}
Expr::Index { expr, index } => {
let e = render_expr(expr);
let i = render_expr(index);
Expand Down Expand Up @@ -478,7 +498,11 @@ impl<'a> Arbitrary<'a> for ArbValue4 {
let width = 1 + ((w as u32) % 64); // 1..=64

let signed: bool = u.arbitrary()?;
let signedness = if signed { Signedness::Signed } else { Signedness::Unsigned };
let signedness = if signed {
Signedness::Signed
} else {
Signedness::Unsigned
};

let mut bits = Vec::with_capacity(width as usize);
for _ in 0..width {
Expand Down Expand Up @@ -532,7 +556,8 @@ impl<'a> Arbitrary<'a> for ExprText {
}

fn map_expr_char(b: u8) -> char {
// Restricted set to improve chance of valid tokens for our subset + numeric literals.
// Restricted set to improve chance of valid tokens for our subset + numeric
// literals.
const TABLE: &[u8] =
b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_()?:!~&|=<>^+-*/%{}[],: 'bdhBDHxoXOzZ\t\n";
let idx = (b as usize) % TABLE.len();
Expand Down Expand Up @@ -698,8 +723,11 @@ fn build_sv(expr: &str, env: &Env) -> String {

s.push_str(" initial begin\n");
// Avoid $bits(...) in a constant context; some iverilog versions are picky.
// Printing the expression directly lets iverilog choose the self-determined size for %b.
s.push_str(&format!(" $display(\"W=%0d V=%b\", $bits({expr}), ({expr}));\n"));
// Printing the expression directly lets iverilog choose the self-determined
// size for %b.
s.push_str(&format!(
" $display(\"W=%0d V=%b\", $bits({expr}), ({expr}));\n"
));
s.push_str(" end\n");
s.push_str("endmodule\n");
s
Expand Down
4 changes: 4 additions & 0 deletions xlsynth-vastly/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ pub enum Expr {
count: Box<Expr>,
expr: Box<Expr>,
},
Cast {
width: Box<Expr>,
expr: Box<Expr>,
},
Index {
expr: Box<Expr>,
index: Box<Expr>,
Expand Down
8 changes: 8 additions & 0 deletions xlsynth-vastly/src/ast_spanned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ pub enum SpannedExprKind {
count: Box<SpannedExpr>,
expr: Box<SpannedExpr>,
},
Cast {
width: Box<SpannedExpr>,
expr: Box<SpannedExpr>,
},
Index {
expr: Box<SpannedExpr>,
index: Box<SpannedExpr>,
Expand Down Expand Up @@ -82,6 +86,10 @@ impl SpannedExpr {
count.shift_spans(delta);
expr.shift_spans(delta);
}
SpannedExprKind::Cast { width, expr } => {
width.shift_spans(delta);
expr.shift_spans(delta);
}
SpannedExprKind::Index { expr, index } => {
expr.shift_spans(delta);
index.shift_spans(delta);
Expand Down
11 changes: 11 additions & 0 deletions xlsynth-vastly/src/combo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::collections::BTreeMap;

use crate::Result;
use crate::Signedness;
use crate::Value4;
use crate::ast::Expr;
use crate::ast_spanned::SpannedExpr;
use crate::ast_spanned::SpannedExprKind;
Expand Down Expand Up @@ -92,6 +93,7 @@ pub enum ComboFunctionImpl {
#[derive(Debug, Clone)]
pub struct CompiledComboModule {
pub module_name: String,
pub consts: BTreeMap<String, Value4>,
pub input_ports: Vec<Port>,
pub output_ports: Vec<Port>,
pub decls: BTreeMap<String, DeclInfo>,
Expand Down Expand Up @@ -276,6 +278,7 @@ pub fn compile_combo_module(src: &str) -> Result<CompiledComboModule> {

Ok(CompiledComboModule {
module_name,
consts: parsed.params.clone(),
input_ports,
output_ports,
decls,
Expand Down Expand Up @@ -845,6 +848,10 @@ fn denormalize_expr(expr: Expr, placeholders: &BTreeMap<String, String>) -> Expr
count: Box::new(denormalize_expr(*count, placeholders)),
expr: Box::new(denormalize_expr(*expr, placeholders)),
},
Expr::Cast { width, expr } => Expr::Cast {
width: Box::new(denormalize_expr(*width, placeholders)),
expr: Box::new(denormalize_expr(*expr, placeholders)),
},
Expr::Index { expr, index } => Expr::Index {
expr: Box::new(denormalize_expr(*expr, placeholders)),
index: Box::new(denormalize_expr(*index, placeholders)),
Expand Down Expand Up @@ -906,6 +913,10 @@ fn denormalize_spanned_expr(expr: &mut SpannedExpr, placeholders: &BTreeMap<Stri
denormalize_spanned_expr(count, placeholders);
denormalize_spanned_expr(expr, placeholders);
}
SpannedExprKind::Cast { width, expr } => {
denormalize_spanned_expr(width, placeholders);
denormalize_spanned_expr(expr, placeholders);
}
SpannedExprKind::Index { expr, index } => {
denormalize_spanned_expr(expr, placeholders);
denormalize_spanned_expr(index, placeholders);
Expand Down
Loading
Loading