Skip to content

Commit 51f112b

Browse files
authored
Merge pull request #3166 from veryl-lang/pr/01-wide-rhs-select
fix(simulator): AOT-C coverage for a narrow rhs_select field of a wide RHS
2 parents 9e585f2 + 99ea561 commit 51f112b

1 file changed

Lines changed: 105 additions & 1 deletion

File tree

  • crates/simulator/src/backend/aot_c

crates/simulator/src/backend/aot_c/emit.rs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3763,7 +3763,27 @@ pub fn emit_stmt(stmt: &ProtoStatement) -> Option<String> {
37633763
let cty = native_c_type(nb)?;
37643764
// Compute the rhs after rhs_select extraction (mirrors
37653765
// AssignStatement::eval_step's `value.select(beg, end)`).
3766-
let rhs_raw = emit_expr_root(eff_expr)?;
3766+
// A wide-pointer RHS value is not a C scalar, but a ≤64-bit
3767+
// rhs_select field of it is. Scalar emit is tried first:
3768+
// building a wide pointer does not mean scalar emit fails (a
3769+
// narrow dynamic select on a >128-bit var is a C scalar too).
3770+
let (rhs_raw, eff_rhs_select) = if let Some(s) = emit_expr_root(eff_expr) {
3771+
(s, eff_rhs_select)
3772+
} else if eff_expr.builds_wide_pointer() {
3773+
let (rhs_hi, rhs_lo) = eff_rhs_select?;
3774+
let nbits = rhs_hi.checked_sub(rhs_lo)?.checked_add(1)?;
3775+
if nbits > 64 {
3776+
return None;
3777+
}
3778+
let mut pre = String::new();
3779+
let f = emit_wide_rhs_field(eff_expr, rhs_hi, rhs_lo, &mut pre)?;
3780+
(
3781+
format!("({{ {pre}(uint64_t)VW_RD({addr}, 0); }})", addr = f.addr),
3782+
None,
3783+
)
3784+
} else {
3785+
return None;
3786+
};
37673787
// Sign-extend a bare signed RHS to dst_width before the store
37683788
// (`se_from` = the RHS width). dst_width <= 128 is guaranteed here
37693789
// (wider bailed above). The extension fills bits [w..dst_width) with
@@ -7180,6 +7200,90 @@ mod tests {
71807200
let _ = fs::remove_dir_all(&tmp);
71817201
}
71827202

7203+
#[test]
7204+
fn emit_narrow_dst_rhs_select_on_wide_pointer_rhs() {
7205+
// `assign narrow = (a192 | b192)[150:125];` — the scalar sibling
7206+
// of `emit_rhs_select_on_wide_pointer_store`.
7207+
if !cc_available() {
7208+
eprintln!("emit_narrow_dst_rhs_select_on_wide_pointer_rhs: cc unavailable, skipping");
7209+
return;
7210+
}
7211+
let or192 = || ProtoExpression::Binary {
7212+
x: Box::new(var_expr(VarOffset::Comb(0), 192)),
7213+
op: Op::BitOr,
7214+
y: Box::new(var_expr(VarOffset::Comb(24), 192)),
7215+
width: 192,
7216+
expr_context: ctx(192, false),
7217+
};
7218+
let mk = |dst: isize, dw: usize, sel: Option<(usize, usize)>| {
7219+
ProtoStatement::Assign(ProtoAssignStatement {
7220+
dst: VarOffset::Comb(dst),
7221+
dst_width: dw,
7222+
select: sel,
7223+
dynamic_select: None,
7224+
rhs_select: Some((150, 125)),
7225+
expr: or192(),
7226+
dst_ff_current_offset: 0,
7227+
token: dummy_token(),
7228+
})
7229+
};
7230+
let src = emit_function(&[
7231+
mk(48, 26, None), // plain narrow store
7232+
mk(56, 64, Some((30, 5))), // field into a dst bit-select RMW
7233+
])
7234+
.expect("narrow-dst rhs_select on a wide-pointer RHS must stay AOT-covered");
7235+
let tmp = std::env::temp_dir().join(format!("veryl_aot_nwsel_{}", std::process::id()));
7236+
let Some(module) =
7237+
compile_for_test(&tmp, &src, "emit_narrow_dst_rhs_select_on_wide_pointer_rhs")
7238+
else {
7239+
return;
7240+
};
7241+
let a: [u64; 3] = [
7242+
0x1111_2222_3333_4444,
7243+
0x5555_6666_7777_8888,
7244+
0x9999_AAAA_BBBB_CCCC,
7245+
];
7246+
let b: [u64; 3] = [
7247+
0x0F0F_0F0F_0F0F_0F0F,
7248+
0xF0F0_F0F0_F0F0_F0F0,
7249+
0x00FF_00FF_00FF_00FF,
7250+
];
7251+
let mut ff = vec![0u8; 16];
7252+
let mut comb = vec![0u8; 80];
7253+
for (i, w) in a.iter().enumerate() {
7254+
comb[i * 8..i * 8 + 8].copy_from_slice(&w.to_le_bytes());
7255+
}
7256+
for (i, w) in b.iter().enumerate() {
7257+
comb[24 + i * 8..24 + i * 8 + 8].copy_from_slice(&w.to_le_bytes());
7258+
}
7259+
// Pre-set dst2 bits outside the [30:5] window to check the RMW.
7260+
comb[56..64].copy_from_slice(&0xDEAD_0000_0000_0003u64.to_le_bytes());
7261+
let mut log = vec![0u64; 16];
7262+
unsafe {
7263+
(module.func)(
7264+
ff.as_mut_ptr(),
7265+
comb.as_mut_ptr(),
7266+
log.as_mut_ptr() as *mut u8,
7267+
0,
7268+
);
7269+
}
7270+
let or: Vec<u64> = a.iter().zip(&b).map(|(x, y)| x | y).collect();
7271+
// Bits [150:125] span or1/or2 (125 = 64 + 61).
7272+
let shifted = (or[1] >> 61) | (or[2] << 3);
7273+
let field = shifted & ((1u64 << 26) - 1);
7274+
let plain = u64::from_le_bytes(comb[48..56].try_into().unwrap());
7275+
assert_eq!(plain & ((1 << 26) - 1), field, "plain narrow store");
7276+
let rmw = u64::from_le_bytes(comb[56..64].try_into().unwrap());
7277+
assert_eq!((rmw >> 5) & ((1 << 26) - 1), field, "field into [30:5]");
7278+
assert_eq!(rmw & 0x3, 0x3, "RMW keeps bits below the window");
7279+
assert_eq!(
7280+
rmw & 0xFFFF_0000_0000_0000,
7281+
0xDEAD_0000_0000_0000,
7282+
"RMW keeps bits above the window"
7283+
);
7284+
let _ = fs::remove_dir_all(&tmp);
7285+
}
7286+
71837287
#[test]
71847288
fn emit_narrow_dst_from_wide_pointer_rhs() {
71857289
// `assign bit1 = a192 & b192;` — SV truncation of a wide-op RHS

0 commit comments

Comments
 (0)