@@ -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