1- // Copyright 2022 Oxide Computer Company
1+ // Copyright 2026 Oxide Computer Company
22
33use p4:: ast:: { BinOp , DeclarationInfo , Expression , ExpressionKind , Lvalue } ;
44use p4:: hlir:: Hlir ;
@@ -101,6 +101,15 @@ impl<'a> ExpressionGenerator<'a> {
101101 ts. extend ( op_tks) ;
102102 ts. extend ( rhs_tks_) ;
103103 }
104+ BinOp :: BitOr | BinOp :: BitAnd | BinOp :: Xor | BinOp :: Mask => {
105+ ts. extend ( quote ! {
106+ {
107+ let __lhs = #lhs_tks. clone( ) ;
108+ let __rhs = #rhs_tks. clone( ) ;
109+ __lhs #op_tks __rhs
110+ }
111+ } ) ;
112+ }
104113 _ => {
105114 ts. extend ( lhs_tks) ;
106115 ts. extend ( op_tks) ;
@@ -111,22 +120,42 @@ impl<'a> ExpressionGenerator<'a> {
111120 }
112121 ExpressionKind :: Index ( lval, xpr) => {
113122 let mut ts = self . generate_lvalue ( lval) ;
114- ts. extend ( self . generate_expression ( xpr. as_ref ( ) ) ) ;
123+ // For slices, look up the parent field's bit width
124+ // so generate_slice can adjust for header.rs byte
125+ // reversal.
126+ if let ExpressionKind :: Slice ( begin, end) = & xpr. kind {
127+ let ni =
128+ self . hlir . lvalue_decls . get ( lval) . unwrap_or_else ( || {
129+ panic ! ( "unresolved lvalue {:#?} in slice" , lval)
130+ } ) ;
131+
132+ let field_width = match & ni. ty {
133+ p4:: ast:: Type :: Bit ( w)
134+ | p4:: ast:: Type :: Varbit ( w)
135+ | p4:: ast:: Type :: Int ( w) => * w,
136+ ty => panic ! (
137+ "slice on non-bit type {:?} reached codegen" ,
138+ ty,
139+ ) ,
140+ } ;
141+ let ( hi, lo) = Self :: slice_bounds ( begin, end) ;
142+ if Self :: slice_is_contiguous ( hi, lo, field_width) {
143+ ts. extend ( self . generate_slice ( begin, end, field_width) ) ;
144+ } else {
145+ // Non-contiguous after byte reversal;
146+ // replace the lvalue suffix with arithmetic.
147+ return Self :: generate_slice_read_arith ( & ts, hi, lo) ;
148+ }
149+ } else {
150+ ts. extend ( self . generate_expression ( xpr. as_ref ( ) ) ) ;
151+ }
115152 ts
116153 }
117- ExpressionKind :: Slice ( begin, end) => {
118- let l = match & begin. kind {
119- ExpressionKind :: IntegerLit ( v) => * v as usize ,
120- _ => panic ! ( "slice ranges can only be integer literals" ) ,
121- } ;
122- let l = l + 1 ;
123- let r = match & end. kind {
124- ExpressionKind :: IntegerLit ( v) => * v as usize ,
125- _ => panic ! ( "slice ranges can only be integer literals" ) ,
126- } ;
127- quote ! {
128- [ #r..#l]
129- }
154+ ExpressionKind :: Slice ( _begin, _end) => {
155+ // The HLIR rejects bare slices outside an Index
156+ // expression, so this is unreachable for well-typed
157+ // programs.
158+ unreachable ! ( "bare Slice reached codegen" ) ;
130159 }
131160 ExpressionKind :: Call ( call) => {
132161 let lv: Vec < TokenStream > = call
@@ -158,6 +187,79 @@ impl<'a> ExpressionGenerator<'a> {
158187 }
159188 }
160189
190+ /// Extract compile-time hi and lo from slice bound expressions.
191+ pub ( crate ) fn slice_bounds (
192+ begin : & Expression ,
193+ end : & Expression ,
194+ ) -> ( P4Bit , P4Bit ) {
195+ let hi: P4Bit = match & begin. kind {
196+ ExpressionKind :: IntegerLit ( v) => * v as usize ,
197+ _ => panic ! ( "slice ranges can only be integer literals" ) ,
198+ } ;
199+ let lo: P4Bit = match & end. kind {
200+ ExpressionKind :: IntegerLit ( v) => * v as usize ,
201+ _ => panic ! ( "slice ranges can only be integer literals" ) ,
202+ } ;
203+ ( hi, lo)
204+ }
205+
206+ /// Whether `[hi:lo]` on a field of `field_width` bits can be
207+ /// expressed as a contiguous bitvec range after byte reversal.
208+ pub ( crate ) fn slice_is_contiguous (
209+ hi : P4Bit ,
210+ lo : P4Bit ,
211+ field_width : FieldWidth ,
212+ ) -> bool {
213+ if field_width <= 8 {
214+ return true ;
215+ }
216+ reversed_slice_range ( hi, lo, field_width) . is_some ( )
217+ }
218+
219+ pub ( crate ) fn generate_slice (
220+ & self ,
221+ begin : & Expression ,
222+ end : & Expression ,
223+ field_width : FieldWidth ,
224+ ) -> TokenStream {
225+ let ( hi, lo) = Self :: slice_bounds ( begin, end) ;
226+
227+ if field_width > 8 {
228+ let ( r, l) = reversed_slice_range ( hi, lo, field_width) . expect (
229+ "non-contiguous slice reads must be handled \
230+ by the caller via generate_slice_read_arith",
231+ ) ;
232+ quote ! { [ #r..#l] }
233+ } else {
234+ // Fields <= 8 bits are not byte-reversed by header.rs,
235+ // so the naive P4-to-bitvec mapping is correct.
236+ let l = hi + 1 ;
237+ let r = lo;
238+ quote ! { [ #r..#l] }
239+ }
240+ }
241+
242+ /// Emit an arithmetic slice read for non-contiguous slices.
243+ /// Loads the field as an integer, shifts and masks to extract
244+ /// the requested bits, then packs into a new bitvec.
245+ pub ( crate ) fn generate_slice_read_arith (
246+ lhs : & TokenStream ,
247+ hi : P4Bit ,
248+ lo : P4Bit ,
249+ ) -> TokenStream {
250+ let slice_width = hi - lo + 1 ;
251+ let mask_val = ( 1u128 << slice_width) - 1 ;
252+ quote ! {
253+ {
254+ let __v: u128 = #lhs. load_le( ) ;
255+ let __extracted = ( __v >> #lo) & #mask_val;
256+ let mut __out = bitvec![ u8 , Msb0 ; 0 ; #slice_width] ;
257+ __out. store_le( __extracted) ;
258+ __out
259+ }
260+ }
261+ }
262+
161263 pub ( crate ) fn generate_bit_literal (
162264 & self ,
163265 width : u16 ,
@@ -223,3 +325,160 @@ impl<'a> ExpressionGenerator<'a> {
223325 }
224326 }
225327}
328+
329+ /// P4 bit position (MSB-first index within a field).
330+ type P4Bit = usize ;
331+
332+ /// Width of a P4 header field in bits.
333+ type FieldWidth = usize ;
334+
335+ /// Half-open bitvec range `(start, end)` into the storage representation.
336+ type BitvecRange = ( usize , usize ) ;
337+
338+ /// Map a P4 slice `[hi:lo]` to a bitvec range in byte-reversed storage.
339+ ///
340+ /// header.rs reverses byte order for fields wider than 8 bits. Bit
341+ /// positions within each byte are preserved (Msb0). The mapping from
342+ /// P4 bit positions to storage indices:
343+ ///
344+ /// ```text
345+ /// wire_idx = W - 1 - b
346+ /// wire_byte = wire_idx / 8
347+ /// bit_in_byte = wire_idx % 8
348+ /// storage_byte = W/8 - 1 - wire_byte
349+ /// bitvec_idx = storage_byte * 8 + bit_in_byte
350+ /// ```
351+ ///
352+ /// # Returns
353+ ///
354+ /// `Some(range)` when the slice maps to a contiguous bitvec range
355+ /// (single-byte slices or byte-aligned multi-byte slices), `None`
356+ /// for non-byte-aligned multi-byte slices where byte reversal makes
357+ /// the bits non-contiguous.
358+ pub ( crate ) fn reversed_slice_range (
359+ hi : P4Bit ,
360+ lo : P4Bit ,
361+ field_width : FieldWidth ,
362+ ) -> Option < BitvecRange > {
363+ // Wire byte indices for the slice endpoints. P4 bit W-1 is in wire
364+ // byte 0 (MSB-first), so higher bit numbers map to lower byte indices.
365+ let wire_byte_hi = ( field_width - 1 - hi) / 8 ;
366+ let wire_byte_lo = ( field_width - 1 - lo) / 8 ;
367+
368+ if wire_byte_hi == wire_byte_lo {
369+ // Single-byte slice: map each endpoint individually.
370+ let map_bit = |bit_pos : usize | -> usize {
371+ let wire_idx = field_width - 1 - bit_pos;
372+ let wire_byte = wire_idx / 8 ;
373+ let bit_in_byte = wire_idx % 8 ;
374+ let storage_byte = field_width / 8 - 1 - wire_byte;
375+ storage_byte * 8 + bit_in_byte
376+ } ;
377+
378+ let mapped_hi = map_bit ( hi) ;
379+ let mapped_lo = map_bit ( lo) ;
380+ Some ( ( mapped_hi. min ( mapped_lo) , mapped_hi. max ( mapped_lo) + 1 ) )
381+ } else if ( hi + 1 ) . is_multiple_of ( 8 ) && lo. is_multiple_of ( 8 ) {
382+ // Multi-byte byte-aligned slice: reversed bytes form a
383+ // contiguous block.
384+ let storage_byte_start = field_width / 8 - 1 - wire_byte_lo;
385+ let storage_byte_end = field_width / 8 - 1 - wire_byte_hi;
386+ Some ( ( storage_byte_start * 8 , ( storage_byte_end + 1 ) * 8 ) )
387+ } else {
388+ // Non-byte-aligned multi-byte slice: byte reversal makes the
389+ // bits non-contiguous, so there is no single bitvec range.
390+ None
391+ }
392+ }
393+
394+ #[ cfg( test) ]
395+ mod test {
396+ use super :: * ;
397+
398+ // Verify the reversed slice range mapping against the byte reversal
399+ // in header.rs. For each case we check that the bitvec range lands
400+ // on the correct bits in the reversed storage layout.
401+
402+ // Sub-byte slices within a single wire byte.
403+
404+ #[ test]
405+ fn slice_32bit_top_nibble ( ) {
406+ // P4 [31:28] on 32-bit: top nibble of wire byte 0.
407+ // Storage: wire byte 0 -> storage byte 3.
408+ // High nibble of storage byte 3 = bitvec [24..28].
409+ assert_eq ! ( reversed_slice_range( 31 , 28 , 32 ) , Some ( ( 24 , 28 ) ) ) ;
410+ }
411+
412+ #[ test]
413+ fn slice_32bit_bottom_nibble ( ) {
414+ // P4 [3:0] on 32-bit: bottom nibble of wire byte 3.
415+ // Storage: wire byte 3 -> storage byte 0.
416+ // Low nibble (Msb0) of storage byte 0 = bitvec [4..8].
417+ assert_eq ! ( reversed_slice_range( 3 , 0 , 32 ) , Some ( ( 4 , 8 ) ) ) ;
418+ }
419+
420+ #[ test]
421+ fn slice_16bit_top_nibble ( ) {
422+ // P4 [15:12] on 16-bit: top nibble of wire byte 0.
423+ // Storage: wire byte 0 -> storage byte 1.
424+ // High nibble of storage byte 1 = bitvec [8..12].
425+ assert_eq ! ( reversed_slice_range( 15 , 12 , 16 ) , Some ( ( 8 , 12 ) ) ) ;
426+ }
427+
428+ // Full-byte slices (single byte).
429+
430+ #[ test]
431+ fn slice_128bit_top_byte ( ) {
432+ // P4 [127:120] on 128-bit: wire byte 0 -> storage byte 15.
433+ // bitvec [120..128].
434+ assert_eq ! ( reversed_slice_range( 127 , 120 , 128 ) , Some ( ( 120 , 128 ) ) ) ;
435+ }
436+
437+ #[ test]
438+ fn slice_16bit_low_byte ( ) {
439+ // P4 [7:0] on 16-bit: wire byte 1 -> storage byte 0.
440+ // bitvec [0..8].
441+ assert_eq ! ( reversed_slice_range( 7 , 0 , 16 ) , Some ( ( 0 , 8 ) ) ) ;
442+ }
443+
444+ #[ test]
445+ fn slice_32bit_middle_byte ( ) {
446+ // P4 [23:16] on 32-bit: wire byte 1 -> storage byte 2.
447+ // bitvec [16..24].
448+ assert_eq ! ( reversed_slice_range( 23 , 16 , 32 ) , Some ( ( 16 , 24 ) ) ) ;
449+ }
450+
451+ // Multi-byte byte-aligned slices.
452+
453+ #[ test]
454+ fn slice_128bit_top_two_bytes ( ) {
455+ // P4 [127:112] on 128-bit: wire bytes 0-1 -> storage bytes 14-15.
456+ // bitvec [112..128].
457+ assert_eq ! ( reversed_slice_range( 127 , 112 , 128 ) , Some ( ( 112 , 128 ) ) ) ;
458+ }
459+
460+ #[ test]
461+ fn slice_32bit_top_three_bytes ( ) {
462+ // P4 [31:8] on 32-bit: wire bytes 0-2 -> storage bytes 1-3.
463+ // bitvec [8..32].
464+ assert_eq ! ( reversed_slice_range( 31 , 8 , 32 ) , Some ( ( 8 , 32 ) ) ) ;
465+ }
466+
467+ #[ test]
468+ fn slice_32bit_bottom_two_bytes ( ) {
469+ // P4 [15:0] on 32-bit: wire bytes 2-3 -> storage bytes 0-1.
470+ // bitvec [0..16].
471+ assert_eq ! ( reversed_slice_range( 15 , 0 , 32 ) , Some ( ( 0 , 16 ) ) ) ;
472+ }
473+
474+ #[ test]
475+ fn slice_48bit_upper_24 ( ) {
476+ assert_eq ! ( reversed_slice_range( 47 , 24 , 48 ) , Some ( ( 24 , 48 ) ) ) ;
477+ }
478+
479+ #[ test]
480+ fn slice_non_contiguous_returns_none ( ) {
481+ assert_eq ! ( reversed_slice_range( 11 , 4 , 32 ) , None ) ;
482+ assert_eq ! ( reversed_slice_range( 22 , 0 , 32 ) , None ) ;
483+ }
484+ }
0 commit comments